Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9cfe4c7
add TextEncoder and TextDecoder to jsdom test environment
lobsterkatie Aug 18, 2021
37b4944
add TransactionMetadata to DebugMeta type
lobsterkatie Aug 18, 2021
2ec4722
add segment to user type
lobsterkatie Aug 18, 2021
f9ba96c
add TraceHeaders and SpanContext.getTraceHeaders() to types
lobsterkatie Aug 18, 2021
bbf472d
add base64 string functions
lobsterkatie Aug 18, 2021
9474a4e
fix circular dependency
lobsterkatie Aug 19, 2021
6728b26
move setting default environment to SDK initialization rather than ev…
lobsterkatie Aug 18, 2021
0af6717
remove unused traceHeaders function
lobsterkatie Aug 18, 2021
5e57193
polyfill Object.fromEntries for tracing tests
lobsterkatie Aug 18, 2021
5d53567
s/TRACEPARENT_REGEXP/SENTRY_TRACE_REGEX
lobsterkatie Aug 18, 2021
2a50338
s/extractTraceparentData/extractSentrytraceData
lobsterkatie Aug 18, 2021
54ac0d6
s/traceparent/sentrytrace in various spots
lobsterkatie Aug 19, 2021
58eeda9
add functions for computing tracestate and combined tracing headers
lobsterkatie Aug 19, 2021
a758e16
add tracestate header to outgoing requests
lobsterkatie Aug 19, 2021
a007a83
deprecate toTraceparent
lobsterkatie Aug 19, 2021
1a4336e
add extractTracestateData() function
lobsterkatie Aug 19, 2021
fcb41b3
make transactions accept metadata in their incoming transaction context
lobsterkatie Aug 19, 2021
f0f7ed6
propagate incoming tracestate headers
lobsterkatie Aug 19, 2021
9ab5a8f
extract and propagate tracestate data from <meta> tags
lobsterkatie Aug 19, 2021
08d9ba4
add missing tracestate when capturing transaction, if necessary
lobsterkatie Aug 19, 2021
5c6070e
only deal with envelope header data if we're using an envelope
lobsterkatie Aug 19, 2021
50819b6
add tracestate to envelope header
lobsterkatie Aug 19, 2021
756e855
clean up comments
lobsterkatie Aug 19, 2021
a0b5caf
add http header tests
lobsterkatie Aug 19, 2021
f1a0dbf
random cleanup
lobsterkatie Aug 19, 2021
6415a5a
don't stringify event data until the end
lobsterkatie Aug 19, 2021
15a2e85
rework and add request tests
lobsterkatie Aug 19, 2021
dd76840
remove redundant string test, add browser test for non-base64 string
lobsterkatie Aug 20, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
propagate incoming tracestate headers
  • Loading branch information
lobsterkatie committed Aug 23, 2021
commit f0f7ed62a493e62d83e4194406a23a90de6f106f
24 changes: 17 additions & 7 deletions packages/nextjs/src/utils/instrumentServer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { captureException, deepReadDirSync, getCurrentHub, Handlers, startTransaction } from '@sentry/node';
import { extractSentrytraceData, getActiveTransaction, hasTracingEnabled } from '@sentry/tracing';
import { fill, isString, logger, stripUrlQueryAndFragment } from '@sentry/utils';
import {
extractSentrytraceData,
extractTracestateData,
getActiveTransaction,
hasTracingEnabled,
} from '@sentry/tracing';
import { fill, logger, stripUrlQueryAndFragment } from '@sentry/utils';
import * as domain from 'domain';
import * as http from 'http';
import { default as createNextServer } from 'next';
Expand Down Expand Up @@ -199,10 +204,14 @@ function makeWrappedReqHandler(origReqHandler: ReqHandler): WrappedReqHandler {
// We only want to record page and API requests
if (hasTracingEnabled() && shouldTraceRequest(req.url, publicDirFiles)) {
// If there is a trace header set, extract the data from it (parentSpanId, traceId, and sampling decision)
let traceparentData;
if (req.headers && isString(req.headers['sentry-trace'])) {
traceparentData = extractSentrytraceData(req.headers['sentry-trace'] as string);
logger.log(`[Tracing] Continuing trace ${traceparentData?.traceId}.`);
// Extract data from trace headers
let sentrytraceData, tracestateData;
if (req.headers?.['sentry-trace']) {
sentrytraceData = extractSentrytraceData(req.headers['sentry-trace'] as string);
logger.log(`[Tracing] Continuing trace ${sentrytraceData?.traceId}.`);
}
if (req.headers?.tracestate) {
tracestateData = extractTracestateData(req.headers.tracestate as string);
}

// pull off query string, if any
Expand All @@ -217,7 +226,8 @@ function makeWrappedReqHandler(origReqHandler: ReqHandler): WrappedReqHandler {
name: `${namePrefix}${reqPath}`,
op: 'http.server',
metadata: { requestPath: reqPath },
...traceparentData,
...sentrytraceData,
...(tracestateData && { metadata: { tracestate: tracestateData } }),
},
// extra context passed to the `tracesSampler`
{ request: req },
Expand Down
20 changes: 12 additions & 8 deletions packages/nextjs/src/utils/withSentry.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { captureException, flush, getCurrentHub, Handlers, startTransaction } from '@sentry/node';
import { extractSentrytraceData, hasTracingEnabled } from '@sentry/tracing';
import { extractSentrytraceData, extractTracestateData, hasTracingEnabled } from '@sentry/tracing';
import { Transaction } from '@sentry/types';
import { addExceptionMechanism, isString, logger, stripUrlQueryAndFragment } from '@sentry/utils';
import { addExceptionMechanism, logger, stripUrlQueryAndFragment } from '@sentry/utils';
import * as domain from 'domain';
import { NextApiHandler, NextApiResponse } from 'next';

Expand Down Expand Up @@ -36,11 +36,14 @@ export const withSentry = (handler: NextApiHandler): WrappedNextApiHandler => {
currentScope.addEventProcessor(event => parseRequest(event, req));

if (hasTracingEnabled()) {
// If there is a trace header set, extract the data from it (parentSpanId, traceId, and sampling decision)
let traceparentData;
if (req.headers && isString(req.headers['sentry-trace'])) {
traceparentData = extractSentrytraceData(req.headers['sentry-trace'] as string);
logger.log(`[Tracing] Continuing trace ${traceparentData?.traceId}.`);
// Extract data from trace headers
let sentrytraceData, tracestateData;
if (req.headers?.['sentry-trace']) {
sentrytraceData = extractSentrytraceData(req.headers['sentry-trace'] as string);
logger.log(`[Tracing] Continuing trace ${sentrytraceData?.traceId}.`);
}
if (req.headers?.tracestate) {
tracestateData = extractTracestateData(req.headers.tracestate as string);
}

const url = `${req.url}`;
Expand All @@ -60,7 +63,8 @@ export const withSentry = (handler: NextApiHandler): WrappedNextApiHandler => {
{
name: `${reqMethod}${reqPath}`,
op: 'http.server',
...traceparentData,
...sentrytraceData,
...(tracestateData && { metadata: { tracestate: tracestateData } }),
},
// extra context passed to the `tracesSampler`
{ request: req },
Expand Down
12 changes: 8 additions & 4 deletions packages/node/src/handlers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable max-lines */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { captureException, getCurrentHub, startTransaction, withScope } from '@sentry/core';
import { extractSentrytraceData, Span } from '@sentry/tracing';
import { extractSentrytraceData, extractTracestateData, Span } from '@sentry/tracing';
import { Event, ExtractedNodeRequestData, RequestSessionStatus, Transaction } from '@sentry/types';
import { isPlainObject, isString, logger, normalize, stripUrlQueryAndFragment } from '@sentry/utils';
import * as cookie from 'cookie';
Expand Down Expand Up @@ -53,17 +53,21 @@ export function tracingHandler(): (
res: http.ServerResponse,
next: (error?: any) => void,
): void {
// If there is a trace header set, we extract the data from it (parentSpanId, traceId, and sampling decision)
let sentrytraceData;
if (req.headers && isString(req.headers['sentry-trace'])) {
// Extract data from trace headers
let sentrytraceData, tracestateData;
if (req.headers?.['sentry-trace']) {
sentrytraceData = extractSentrytraceData(req.headers['sentry-trace'] as string);
}
if (req.headers?.tracestate) {
tracestateData = extractTracestateData(req.headers.tracestate as string);
}

const transaction = startTransaction(
{
name: extractExpressTransactionName(req, { path: true, method: true }),
op: 'http.server',
...sentrytraceData,
...(tracestateData && { metadata: { tracestate: tracestateData } }),
},
// extra context passed to the tracesSampler
{ request: extractRequestData(req) },
Expand Down
8 changes: 6 additions & 2 deletions packages/node/test/handlers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,11 @@ describe('tracingHandler', () => {
expect(startTransaction).toHaveBeenCalled();
});

it("pulls parent's data from tracing header on the request", () => {
req.headers = { 'sentry-trace': '12312012123120121231201212312012-1121201211212012-0' };
it('pulls data from tracing headers on the request', () => {
req.headers = {
'sentry-trace': '12312012123120121231201212312012-1121201211212012-0',
tracestate: 'sentry=doGsaREgReaT',
};

sentryTracingMiddleware(req, res, next);

Expand All @@ -336,6 +339,7 @@ describe('tracingHandler', () => {
expect(transaction.traceId).toEqual('12312012123120121231201212312012');
expect(transaction.parentSpanId).toEqual('1121201211212012');
expect(transaction.sampled).toEqual(false);
expect(transaction.metadata?.tracestate).toEqual({ sentry: 'sentry=doGsaREgReaT' });
});

it('extracts request data for sampling context', () => {
Expand Down
18 changes: 12 additions & 6 deletions packages/serverless/src/awslambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import {
withScope,
} from '@sentry/node';
import * as Sentry from '@sentry/node';
import { extractSentrytraceData } from '@sentry/tracing';
import { extractSentrytraceData, extractTracestateData } from '@sentry/tracing';
import { Integration } from '@sentry/types';
import { isString, logger } from '@sentry/utils';
import { logger } from '@sentry/utils';
// 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
// eslint-disable-next-line import/no-unresolved
import { Context, Handler } from 'aws-lambda';
Expand Down Expand Up @@ -201,7 +201,7 @@ export function wrapHandler<TEvent, TResult>(
};
let timeoutWarningTimer: NodeJS.Timeout;

// AWSLambda is like Express. It makes a distinction about handlers based on it's last argument
// AWSLambda is like Express. It makes a distinction about handlers based on its last argument
// async (event) => async handler
// async (event, context) => async handler
// (event, context, callback) => sync handler
Expand Down Expand Up @@ -252,16 +252,22 @@ export function wrapHandler<TEvent, TResult>(
}, timeoutWarningDelay);
}

// Applying `sentry-trace` to context
let sentrytraceData;
// Extract tracing data from headers
let sentrytraceData, tracestateData;
const eventWithHeaders = event as { headers?: { [key: string]: string } };
if (eventWithHeaders.headers && isString(eventWithHeaders.headers['sentry-trace'])) {

if (eventWithHeaders.headers?.['sentry-trace']) {
sentrytraceData = extractSentrytraceData(eventWithHeaders.headers['sentry-trace'] as string);
}
if (eventWithHeaders.headers?.tracestate) {
tracestateData = extractTracestateData(eventWithHeaders.headers.tracestate as string);
}

const transaction = startTransaction({
name: context.functionName,
op: 'awslambda.handler',
...sentrytraceData,
...(tracestateData && { metadata: { tracestate: tracestateData } }),
});

const hub = getCurrentHub();
Expand Down
16 changes: 11 additions & 5 deletions packages/serverless/src/gcpfunction/http.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { captureException, flush, getCurrentHub, Handlers, startTransaction } from '@sentry/node';
import { extractSentrytraceData } from '@sentry/tracing';
import { isString, logger, stripUrlQueryAndFragment } from '@sentry/utils';
import { extractSentrytraceData, extractTracestateData } from '@sentry/tracing';
import { logger, stripUrlQueryAndFragment } from '@sentry/utils';

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

// Applying `sentry-trace` to context
let sentrytraceData;
// Extract tracing data from headers
let sentrytraceData, tracestateData;
const reqWithHeaders = req as { headers?: { [key: string]: string } };
if (reqWithHeaders.headers && isString(reqWithHeaders.headers['sentry-trace'])) {

if (reqWithHeaders.headers?.['sentry-trace']) {
sentrytraceData = extractSentrytraceData(reqWithHeaders.headers['sentry-trace'] as string);
}
if (reqWithHeaders.headers?.tracestate) {
tracestateData = extractTracestateData(reqWithHeaders.headers.tracestate as string);
}

const transaction = startTransaction({
name: `${reqMethod} ${reqUrl}`,
op: 'gcp.function.http',
...sentrytraceData,
...(tracestateData && { metadata: { tracestate: tracestateData } }),
});

// getCurrentHub() is expected to use current active domain as a carrier
Expand Down
87 changes: 87 additions & 0 deletions packages/serverless/test/awslambda.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,35 @@ describe('AWSLambda', () => {
await wrappedHandler(fakeEvent, fakeContext, fakeCallback);
});

test('incoming trace headers are correctly parsed and used', async () => {
expect.assertions(1);

fakeEvent.headers = {
'sentry-trace': '12312012123120121231201212312012-1121201211212012-0',
tracestate: 'sentry=doGsaREgReaT,maisey=silly,charlie=goofy',
};

const handler: Handler = (_event, _context, callback) => {
expect(Sentry.startTransaction).toBeCalledWith(
expect.objectContaining({
traceId: '12312012123120121231201212312012',
parentSpanId: '1121201211212012',
parentSampled: false,
metadata: {
tracestate: {
sentry: 'sentry=doGsaREgReaT',
thirdparty: 'maisey=silly,charlie=goofy',
},
},
}),
);

callback(undefined, { its: 'fine' });
};
const wrappedHandler = wrapHandler(handler);
await wrappedHandler(fakeEvent, fakeContext, fakeCallback);
});

test('capture error', async () => {
expect.assertions(10);

Expand Down Expand Up @@ -278,6 +307,35 @@ describe('AWSLambda', () => {
await wrappedHandler(fakeEvent, fakeContext, fakeCallback);
});

test('incoming trace headers are correctly parsed and used', async () => {
expect.assertions(1);

fakeEvent.headers = {
'sentry-trace': '12312012123120121231201212312012-1121201211212012-0',
tracestate: 'sentry=doGsaREgReaT,maisey=silly,charlie=goofy',
};

const handler: Handler = async (_event, _context, callback) => {
expect(Sentry.startTransaction).toBeCalledWith(
expect.objectContaining({
traceId: '12312012123120121231201212312012',
parentSpanId: '1121201211212012',
parentSampled: false,
metadata: {
tracestate: {
sentry: 'sentry=doGsaREgReaT',
thirdparty: 'maisey=silly,charlie=goofy',
},
},
}),
);

callback(undefined, { its: 'fine' });
};
const wrappedHandler = wrapHandler(handler);
await wrappedHandler(fakeEvent, fakeContext, fakeCallback);
});

test('capture error', async () => {
expect.assertions(10);

Expand Down Expand Up @@ -328,6 +386,35 @@ describe('AWSLambda', () => {
await wrappedHandler(fakeEvent, fakeContext, fakeCallback);
});

test('incoming trace headers are correctly parsed and used', async () => {
expect.assertions(1);

fakeEvent.headers = {
'sentry-trace': '12312012123120121231201212312012-1121201211212012-0',
tracestate: 'sentry=doGsaREgReaT,maisey=silly,charlie=goofy',
};

const handler: Handler = async (_event, _context, callback) => {
expect(Sentry.startTransaction).toBeCalledWith(
expect.objectContaining({
traceId: '12312012123120121231201212312012',
parentSpanId: '1121201211212012',
parentSampled: false,
metadata: {
tracestate: {
sentry: 'sentry=doGsaREgReaT',
thirdparty: 'maisey=silly,charlie=goofy',
},
},
}),
);

callback(undefined, { its: 'fine' });
};
const wrappedHandler = wrapHandler(handler);
await wrappedHandler(fakeEvent, fakeContext, fakeCallback);
});

test('capture error', async () => {
expect.assertions(10);

Expand Down
29 changes: 29 additions & 0 deletions packages/serverless/test/gcpfunction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,35 @@ describe('GCPFunction', () => {
expect(Sentry.flush).toBeCalledWith(2000);
});

test('incoming trace headers are correctly parsed and used', async () => {
expect.assertions(1);

const handler: HttpFunction = (_req, res) => {
res.statusCode = 200;
res.end();
};
const wrappedHandler = wrapHttpFunction(handler);
const traceHeaders = {
'sentry-trace': '12312012123120121231201212312012-1121201211212012-0',
tracestate: 'sentry=doGsaREgReaT,maisey=silly,charlie=goofy',
};
await handleHttp(wrappedHandler, traceHeaders);

expect(Sentry.startTransaction).toBeCalledWith(
expect.objectContaining({
traceId: '12312012123120121231201212312012',
parentSpanId: '1121201211212012',
parentSampled: false,
metadata: {
tracestate: {
sentry: 'sentry=doGsaREgReaT',
thirdparty: 'maisey=silly,charlie=goofy',
},
},
}),
);
});

test('capture error', async () => {
expect.assertions(5);

Expand Down