Skip to content

Commit b808fcc

Browse files
committed
make serverless handle incoming tracestate headers
1 parent 613114c commit b808fcc

File tree

4 files changed

+141
-13
lines changed

4 files changed

+141
-13
lines changed

packages/serverless/src/awslambda.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import {
99
withScope,
1010
} from '@sentry/node';
1111
import * as Sentry from '@sentry/node';
12-
import { extractTraceparentData } from '@sentry/tracing';
12+
import { extractSentrytraceData, extractTracestateData } from '@sentry/tracing';
1313
import { Integration } from '@sentry/types';
14-
import { isString, logger } from '@sentry/utils';
14+
import { logger } from '@sentry/utils';
1515
// NOTE: I have no idea how to fix this right now, and don't want to waste more time, as it builds just fine — Kamil
1616
// eslint-disable-next-line import/no-unresolved
1717
import { Context, Handler } from 'aws-lambda';
@@ -192,7 +192,7 @@ export function wrapHandler<TEvent, TResult>(
192192
};
193193
let timeoutWarningTimer: NodeJS.Timeout;
194194

195-
// AWSLambda is like Express. It makes a distinction about handlers based on it's last argument
195+
// AWSLambda is like Express. It makes a distinction about handlers based on its last argument
196196
// async (event) => async handler
197197
// async (event, context) => async handler
198198
// (event, context, callback) => sync handler
@@ -243,16 +243,22 @@ export function wrapHandler<TEvent, TResult>(
243243
}, timeoutWarningDelay);
244244
}
245245

246-
// Applying `sentry-trace` to context
247-
let traceparentData;
246+
// Extract tracing data from headers
247+
let traceparentData, tracestateData;
248248
const eventWithHeaders = event as { headers?: { [key: string]: string } };
249-
if (eventWithHeaders.headers && isString(eventWithHeaders.headers['sentry-trace'])) {
250-
traceparentData = extractTraceparentData(eventWithHeaders.headers['sentry-trace'] as string);
249+
250+
if (eventWithHeaders.headers?.['sentry-trace']) {
251+
traceparentData = extractSentrytraceData(eventWithHeaders.headers['sentry-trace'] as string);
252+
}
253+
if (eventWithHeaders.headers?.tracestate) {
254+
tracestateData = extractTracestateData(eventWithHeaders.headers.tracestate as string);
251255
}
256+
252257
const transaction = startTransaction({
253258
name: context.functionName,
254259
op: 'awslambda.handler',
255260
...traceparentData,
261+
...(tracestateData && { metadata: { tracestate: tracestateData } }),
256262
});
257263

258264
const hub = getCurrentHub();

packages/serverless/src/gcpfunction/http.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { captureException, flush, getCurrentHub, Handlers, startTransaction } from '@sentry/node';
2-
import { extractTraceparentData } from '@sentry/tracing';
3-
import { isString, logger, stripUrlQueryAndFragment } from '@sentry/utils';
2+
import { extractSentrytraceData, extractTracestateData } from '@sentry/tracing';
3+
import { logger, stripUrlQueryAndFragment } from '@sentry/utils';
44

55
import { domainify, getActiveDomain, proxyFunction } from './../utils';
66
import { HttpFunction, WrapperOptions } from './general';
@@ -49,16 +49,22 @@ function _wrapHttpFunction(fn: HttpFunction, wrapOptions: Partial<HttpFunctionWr
4949
const reqMethod = (req.method || '').toUpperCase();
5050
const reqUrl = stripUrlQueryAndFragment(req.originalUrl || req.url || '');
5151

52-
// Applying `sentry-trace` to context
53-
let traceparentData;
52+
// Extract tracing data from headers
53+
let traceparentData, tracestateData;
5454
const reqWithHeaders = req as { headers?: { [key: string]: string } };
55-
if (reqWithHeaders.headers && isString(reqWithHeaders.headers['sentry-trace'])) {
56-
traceparentData = extractTraceparentData(reqWithHeaders.headers['sentry-trace'] as string);
55+
56+
if (reqWithHeaders.headers?.['sentry-trace']) {
57+
traceparentData = extractSentrytraceData(reqWithHeaders.headers['sentry-trace'] as string);
58+
}
59+
if (reqWithHeaders.headers?.tracestate) {
60+
tracestateData = extractTracestateData(reqWithHeaders.headers.tracestate as string);
5761
}
62+
5863
const transaction = startTransaction({
5964
name: `${reqMethod} ${reqUrl}`,
6065
op: 'gcp.function.http',
6166
...traceparentData,
67+
...(tracestateData && { metadata: { tracestate: tracestateData } }),
6268
});
6369

6470
// getCurrentHub() is expected to use current active domain as a carrier

packages/serverless/test/awslambda.test.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,35 @@ describe('AWSLambda', () => {
221221
await wrappedHandler(fakeEvent, fakeContext, fakeCallback);
222222
});
223223

224+
test('incoming trace headers are correctly parsed and used', async () => {
225+
expect.assertions(1);
226+
227+
fakeEvent.headers = {
228+
'sentry-trace': '12312012123120121231201212312012-1121201211212012-0',
229+
tracestate: 'sentry=doGsaREgReaT,maisey=silly,charlie=goofy',
230+
};
231+
232+
const handler: Handler = (_event, _context, callback) => {
233+
expect(Sentry.startTransaction).toBeCalledWith(
234+
expect.objectContaining({
235+
traceId: '12312012123120121231201212312012',
236+
parentSpanId: '1121201211212012',
237+
parentSampled: false,
238+
metadata: {
239+
tracestate: {
240+
sentry: 'sentry=doGsaREgReaT',
241+
thirdparty: 'maisey=silly,charlie=goofy',
242+
},
243+
},
244+
}),
245+
);
246+
247+
callback(undefined, { its: 'fine' });
248+
};
249+
const wrappedHandler = wrapHandler(handler);
250+
await wrappedHandler(fakeEvent, fakeContext, fakeCallback);
251+
});
252+
224253
test('capture error', async () => {
225254
expect.assertions(10);
226255

@@ -278,6 +307,35 @@ describe('AWSLambda', () => {
278307
await wrappedHandler(fakeEvent, fakeContext, fakeCallback);
279308
});
280309

310+
test('incoming trace headers are correctly parsed and used', async () => {
311+
expect.assertions(1);
312+
313+
fakeEvent.headers = {
314+
'sentry-trace': '12312012123120121231201212312012-1121201211212012-0',
315+
tracestate: 'sentry=doGsaREgReaT,maisey=silly,charlie=goofy',
316+
};
317+
318+
const handler: Handler = async (_event, _context, callback) => {
319+
expect(Sentry.startTransaction).toBeCalledWith(
320+
expect.objectContaining({
321+
traceId: '12312012123120121231201212312012',
322+
parentSpanId: '1121201211212012',
323+
parentSampled: false,
324+
metadata: {
325+
tracestate: {
326+
sentry: 'sentry=doGsaREgReaT',
327+
thirdparty: 'maisey=silly,charlie=goofy',
328+
},
329+
},
330+
}),
331+
);
332+
333+
callback(undefined, { its: 'fine' });
334+
};
335+
const wrappedHandler = wrapHandler(handler);
336+
await wrappedHandler(fakeEvent, fakeContext, fakeCallback);
337+
});
338+
281339
test('capture error', async () => {
282340
expect.assertions(10);
283341

@@ -328,6 +386,35 @@ describe('AWSLambda', () => {
328386
await wrappedHandler(fakeEvent, fakeContext, fakeCallback);
329387
});
330388

389+
test('incoming trace headers are correctly parsed and used', async () => {
390+
expect.assertions(1);
391+
392+
fakeEvent.headers = {
393+
'sentry-trace': '12312012123120121231201212312012-1121201211212012-0',
394+
tracestate: 'sentry=doGsaREgReaT,maisey=silly,charlie=goofy',
395+
};
396+
397+
const handler: Handler = async (_event, _context, callback) => {
398+
expect(Sentry.startTransaction).toBeCalledWith(
399+
expect.objectContaining({
400+
traceId: '12312012123120121231201212312012',
401+
parentSpanId: '1121201211212012',
402+
parentSampled: false,
403+
metadata: {
404+
tracestate: {
405+
sentry: 'sentry=doGsaREgReaT',
406+
thirdparty: 'maisey=silly,charlie=goofy',
407+
},
408+
},
409+
}),
410+
);
411+
412+
callback(undefined, { its: 'fine' });
413+
};
414+
const wrappedHandler = wrapHandler(handler);
415+
await wrappedHandler(fakeEvent, fakeContext, fakeCallback);
416+
});
417+
331418
test('capture error', async () => {
332419
expect.assertions(10);
333420

packages/serverless/test/gcpfunction.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,35 @@ describe('GCPFunction', () => {
120120
expect(Sentry.flush).toBeCalledWith(2000);
121121
});
122122

123+
test('incoming trace headers are correctly parsed and used', async () => {
124+
expect.assertions(1);
125+
126+
const handler: HttpFunction = (_req, res) => {
127+
res.statusCode = 200;
128+
res.end();
129+
};
130+
const wrappedHandler = wrapHttpFunction(handler);
131+
const traceHeaders = {
132+
'sentry-trace': '12312012123120121231201212312012-1121201211212012-0',
133+
tracestate: 'sentry=doGsaREgReaT,maisey=silly,charlie=goofy',
134+
};
135+
await handleHttp(wrappedHandler, traceHeaders);
136+
137+
expect(Sentry.startTransaction).toBeCalledWith(
138+
expect.objectContaining({
139+
traceId: '12312012123120121231201212312012',
140+
parentSpanId: '1121201211212012',
141+
parentSampled: false,
142+
metadata: {
143+
tracestate: {
144+
sentry: 'sentry=doGsaREgReaT',
145+
thirdparty: 'maisey=silly,charlie=goofy',
146+
},
147+
},
148+
}),
149+
);
150+
});
151+
123152
test('capture error', async () => {
124153
expect.assertions(5);
125154

0 commit comments

Comments
 (0)