Skip to content

Commit

Permalink
feat(node): Populate propagation context using env variables (#8422)
Browse files Browse the repository at this point in the history
Requires #8421 to be
merged

ref #8352

This PR adds support for
https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md
via propagation context. When the Node SDK initializes, it grabs
`process.env.SENTRY_TRACE` and `process.env.SENTRY_BAGGAGE` and uses
them to populate the existing propagation context.

In future PRs (tracked by #8352), we will be adding support for
transactions/outgoing requests to use propagation context, but for now
we are only storing in propagation context, not doing anything with it.
  • Loading branch information
AbhiPrasad authored Jun 29, 2023
1 parent f1ede57 commit e02c27f
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 3 deletions.
24 changes: 22 additions & 2 deletions packages/node/src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
logger,
nodeStackLineParser,
stackParserFromStackParserOptions,
tracingContextFromHeaders,
} from '@sentry/utils';

import { setNodeAsyncContextStrategy } from './async';
Expand Down Expand Up @@ -129,8 +130,9 @@ export function init(options: NodeOptions = {}): void {
options.dsn = process.env.SENTRY_DSN;
}

if (options.tracesSampleRate === undefined && process.env.SENTRY_TRACES_SAMPLE_RATE) {
const tracesSampleRate = parseFloat(process.env.SENTRY_TRACES_SAMPLE_RATE);
const sentryTracesSampleRate = process.env.SENTRY_TRACES_SAMPLE_RATE;
if (options.tracesSampleRate === undefined && sentryTracesSampleRate) {
const tracesSampleRate = parseFloat(sentryTracesSampleRate);
if (isFinite(tracesSampleRate)) {
options.tracesSampleRate = tracesSampleRate;
}
Expand Down Expand Up @@ -171,6 +173,8 @@ export function init(options: NodeOptions = {}): void {
if (options.autoSessionTracking) {
startSessionTracking();
}

updateScopeFromEnvVariables();
}

/**
Expand Down Expand Up @@ -285,3 +289,19 @@ function startSessionTracking(): void {
if (session && !terminalStates.includes(session.status)) hub.endSession();
});
}

/**
* Update scope and propagation context based on environmental variables.
*
* See https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md
* for more details.
*/
function updateScopeFromEnvVariables(): void {
const sentryUseEnvironment = (process.env.SENTRY_USE_ENVIRONMENT || '').toLowerCase();
if (!['false', 'n', 'no', 'off', '0'].includes(sentryUseEnvironment)) {
const sentryTraceEnv = process.env.SENTRY_TRACE;
const baggageEnv = process.env.SENTRY_BAGGAGE;
const { propagationContext } = tracingContextFromHeaders(sentryTraceEnv, baggageEnv);
getCurrentHub().getScope().setPropagationContext(propagationContext);
}
}
43 changes: 43 additions & 0 deletions packages/node/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,4 +488,47 @@ describe('SentryNode initialization', () => {
expect(instrumenter).toEqual('otel');
});
});

describe('propagation context', () => {
beforeEach(() => {
process.env.SENTRY_TRACE = '12312012123120121231201212312012-1121201211212012-0';
process.env.SENTRY_BAGGAGE = 'sentry-release=1.0.0,sentry-environment=production';

getCurrentHub().getScope().clear();
});

afterEach(() => {
delete process.env.SENTRY_TRACE;
delete process.env.SENTRY_BAGGAGE;
});

it('reads from environmental variables', () => {
init({ dsn });

// @ts-expect-error accessing private method for test
expect(getCurrentHub().getScope()._propagationContext).toEqual({
traceId: '12312012123120121231201212312012',
parentSpanId: '1121201211212012',
spanId: expect.any(String),
sampled: false,
dsc: {
release: '1.0.0',
environment: 'production',
},
});
});

it.each(['false', 'False', 'FALSE', 'n', 'no', 'No', 'NO', 'off', 'Off', 'OFF', '0'])(
'does not read from environmental variable if SENTRY_USE_ENVIRONMENT is set to %s',
useEnvValue => {
process.env.SENTRY_USE_ENVIRONMENT = useEnvValue;
init({ dsn });

// @ts-expect-error accessing private method for test
expect(getCurrentHub().getScope()._propagationContext.traceId).not.toEqual('12312012123120121231201212312012');

delete process.env.SENTRY_USE_ENVIRONMENT;
},
);
});
});
42 changes: 41 additions & 1 deletion packages/utils/src/tracing.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import type { TraceparentData } from '@sentry/types';
import type { DynamicSamplingContext, PropagationContext, TraceparentData } from '@sentry/types';

import { baggageHeaderToDynamicSamplingContext } from './baggage';
import { uuid4 } from './misc';

export const TRACEPARENT_REGEXP = new RegExp(
'^[ \\t]*' + // whitespace
Expand Down Expand Up @@ -36,3 +39,40 @@ export function extractTraceparentData(traceparent: string): TraceparentData | u
parentSpanId: matches[2],
};
}

/**
* Create tracing context from incoming headers.
*/
export function tracingContextFromHeaders(
sentryTrace: Parameters<typeof extractTraceparentData>[0] = '',
baggage: Parameters<typeof baggageHeaderToDynamicSamplingContext>[0] = '',
): {
traceparentData: ReturnType<typeof extractTraceparentData>;
dynamicSamplingContext: ReturnType<typeof baggageHeaderToDynamicSamplingContext>;
propagationContext: PropagationContext;
} {
const traceparentData = extractTraceparentData(sentryTrace);
const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggage);

const { traceId, parentSpanId, parentSampled } = traceparentData || {};

const propagationContext: PropagationContext = {
traceId: traceId || uuid4(),
spanId: uuid4().substring(16),
sampled: parentSampled === undefined ? false : parentSampled,
};

if (parentSpanId) {
propagationContext.parentSpanId = parentSpanId;
}

if (dynamicSamplingContext) {
propagationContext.dsc = dynamicSamplingContext as DynamicSamplingContext;
}

return {
traceparentData,
dynamicSamplingContext,
propagationContext,
};
}

0 comments on commit e02c27f

Please sign in to comment.