Skip to content

Commit 0d1d26a

Browse files
committed
SUPER HACK: fix it
1 parent 4177b57 commit 0d1d26a

File tree

7 files changed

+108
-55
lines changed

7 files changed

+108
-55
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { loggingTransport } from '@sentry-internal/node-integration-tests';
2+
import * as Sentry from '@sentry/node';
3+
4+
export type TestAPIResponse = { test_data: { host: string; 'sentry-trace': string; baggage: string } };
5+
6+
Sentry.init({
7+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
8+
release: '1.0',
9+
environment: 'prod',
10+
integrations: [
11+
// TODO: This used to use the Express integration
12+
],
13+
tracesSampleRate: 1.0,
14+
transport: loggingTransport,
15+
});
16+
17+
import http from 'http';
18+
import { startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests';
19+
import cors from 'cors';
20+
import express from 'express';
21+
22+
const app = express();
23+
24+
app.use(cors());
25+
26+
app.get('/test/express', (_req, res) => {
27+
const headers = http.get('http://somewhere.not.sentry/').getHeaders();
28+
29+
// Responding with the headers outgoing request headers back to the assertions.
30+
res.send({ test_data: headers });
31+
});
32+
33+
Sentry.setupExpressErrorHandler(app);
34+
35+
startExpressServerAndSendPortToRunner(app);

dev-packages/node-integration-tests/suites/express/sentry-trace/trace-header-assign/test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ afterAll(() => {
77
});
88

99
test('Should assign `sentry-trace` header which sets parent trace id of an outgoing request.', async () => {
10-
const runner = createRunner(__dirname, '..', 'server.ts').start();
10+
const runner = createRunner(__dirname, 'server.ts').start();
1111

1212
const response = await runner.makeRequest<TestAPIResponse>('get', '/test/express', {
1313
'sentry-trace': '12312012123120121231201212312012-1121201211212012-0',

dev-packages/node-integration-tests/suites/tracing/tracingNoSampleRate/test.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,31 @@
11
import { createRunner } from '../../../utils/runner';
22
import { createTestServer } from '../../../utils/server';
33

4-
test('HttpIntegration should instrument correct requests even without tracesSampleRate xxx', done => {
5-
expect.assertions(11);
4+
test('HttpIntegration should instrument correct requests even without tracesSampleRate', done => {
5+
expect.assertions(15);
66

7-
createTestServer(done)
7+
createTestServer(() => {})
88
.get('/api/v0', headers => {
9-
expect(headers['baggage']).toBeUndefined();
109
expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})$/));
1110
expect(headers['sentry-trace']).not.toEqual('00000000000000000000000000000000-0000000000000000');
11+
expect(headers['baggage']).toEqual(expect.any(String));
12+
expect(headers['__requestUrl']).toBeUndefined();
1213
})
1314
.get('/api/v1', headers => {
14-
expect(headers['baggage']).toBeUndefined();
1515
expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})$/));
1616
expect(headers['sentry-trace']).not.toEqual('00000000000000000000000000000000-0000000000000000');
17+
expect(headers['baggage']).toEqual(expect.any(String));
18+
expect(headers['__requestUrl']).toBeUndefined();
1719
})
1820
.get('/api/v2', headers => {
1921
expect(headers['baggage']).toBeUndefined();
2022
expect(headers['sentry-trace']).toBeUndefined();
23+
expect(headers['__requestUrl']).toBeUndefined();
2124
})
2225
.get('/api/v3', headers => {
2326
expect(headers['baggage']).toBeUndefined();
2427
expect(headers['sentry-trace']).toBeUndefined();
28+
expect(headers['__requestUrl']).toBeUndefined();
2529
})
2630
.start()
2731
.then(SERVER_URL => {

dev-packages/node-integration-tests/suites/tracing/tracingUnsampled/scenario.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@ Sentry.init({
1313
import * as http from 'http';
1414

1515
async function run(): Promise<void> {
16-
await makeHttpRequest(`${process.env.SERVER_URL}/api/v0`);
17-
await makeHttpRequest(`${process.env.SERVER_URL}/api/v1`);
18-
await makeHttpRequest(`${process.env.SERVER_URL}/api/v2`);
19-
await makeHttpRequest(`${process.env.SERVER_URL}/api/v3`);
16+
// Wrap in span that is not sampled
17+
await Sentry.startSpan({ name: 'outer' }, async () => {
18+
await makeHttpRequest(`${process.env.SERVER_URL}/api/v0`);
19+
await makeHttpRequest(`${process.env.SERVER_URL}/api/v1`);
20+
await makeHttpRequest(`${process.env.SERVER_URL}/api/v2`);
21+
await makeHttpRequest(`${process.env.SERVER_URL}/api/v3`);
22+
});
2023

2124
Sentry.captureException(new Error('foo'));
2225
}

dev-packages/node-integration-tests/suites/tracing/tracingUnsampled/test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ test('HttpIntegration should instrument correct requests even when not sampled',
66

77
createTestServer(done)
88
.get('/api/v0', headers => {
9-
expect(headers['baggage']).toBeUndefined();
9+
expect(headers['baggage']).toEqual(expect.any(String));
1010
expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})-0$/));
1111
expect(headers['sentry-trace']).not.toEqual('00000000000000000000000000000000-0000000000000000-0');
1212
})
1313
.get('/api/v1', headers => {
14-
expect(headers['baggage']).toBeUndefined();
14+
expect(headers['baggage']).toEqual(expect.any(String));
1515
expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})-0$/));
1616
expect(headers['sentry-trace']).not.toEqual('00000000000000000000000000000000-0000000000000000-0');
1717
})

packages/node/src/integrations/http.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
import { getClient, getRequestSpanData, getSpanKind } from '@sentry/opentelemetry';
1818
import type { IntegrationFn } from '@sentry/types';
1919

20-
import { stripUrlQueryAndFragment } from '@sentry/utils';
20+
import { addNonEnumerableProperty, stripUrlQueryAndFragment } from '@sentry/utils';
2121
import type { NodeClient } from '../sdk/client';
2222
import { setIsolationScope } from '../sdk/scope';
2323
import type { HTTPModuleRequestIncomingMessage } from '../transports/http-module';
@@ -65,6 +65,15 @@ const _httpIntegration = ((options: HttpOptions = {}) => {
6565
return true;
6666
}
6767

68+
// SUPER HACK:
69+
// We store the URL on the headers object, because this is passed to the propagator
70+
// Where we can pick the URL off to deterime if we should attach trace headers.
71+
const headers = request.headers || {};
72+
// Can't use a non-enumerable property because http instrumentation clones this
73+
// We remove this in the propagator
74+
headers['__requestUrl'] = url;
75+
request.headers = headers;
76+
6877
if (_ignoreOutgoingRequests && _ignoreOutgoingRequests(url)) {
6978
return true;
7079
}
@@ -93,18 +102,18 @@ const _httpIntegration = ((options: HttpOptions = {}) => {
93102
requestHook: (span, req) => {
94103
addOriginToSpan(span, 'auto.http.otel.http');
95104

105+
const scopes = getCapturedScopesOnSpan(span);
106+
107+
const isolationScope = (scopes.isolationScope || getIsolationScope()).clone();
108+
const scope = scopes.scope || getCurrentScope();
109+
96110
// both, incoming requests and "client" requests made within the app trigger the requestHook
97111
// we only want to isolate and further annotate incoming requests (IncomingMessage)
98112
if (_isClientRequest(req)) {
99113
return;
100114
}
101115

102-
const scopes = getCapturedScopesOnSpan(span);
103-
104116
// Update the isolation scope, isolate this request
105-
const isolationScope = (scopes.isolationScope || getIsolationScope()).clone();
106-
const scope = scopes.scope || getCurrentScope();
107-
108117
isolationScope.setSDKProcessingMetadata({ request: req });
109118

110119
const client = getClient<NodeClient>();

packages/opentelemetry/src/propagator.ts

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
import type { Baggage, Context, Span, SpanContext, TextMapGetter, TextMapSetter } from '@opentelemetry/api';
1+
import { Baggage, Context, ROOT_CONTEXT, Span, SpanContext, TextMapGetter, TextMapSetter } from '@opentelemetry/api';
22
import { context } from '@opentelemetry/api';
33
import { TraceFlags, propagation, trace } from '@opentelemetry/api';
44
import { TraceState, W3CBaggagePropagator, isTracingSuppressed } from '@opentelemetry/core';
55
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
6-
import { continueTrace, hasTracingEnabled } from '@sentry/core';
6+
import { continueTrace, getDefaultCurrentScope, getDefaultIsolationScope, hasTracingEnabled } from '@sentry/core';
77
import { getRootSpan } from '@sentry/core';
88
import { spanToJSON } from '@sentry/core';
99
import { getClient, getCurrentScope, getDynamicSamplingContextFromClient, getIsolationScope } from '@sentry/core';
10-
import type { DynamicSamplingContext, Options, PropagationContext } from '@sentry/types';
10+
import type { DynamicSamplingContext, Options, PolymorphicRequest, PropagationContext } from '@sentry/types';
1111
import {
1212
LRUMap,
1313
SENTRY_BAGGAGE_KEY_PREFIX,
1414
baggageHeaderToDynamicSamplingContext,
1515
dynamicSamplingContextToSentryBaggageHeader,
16+
extractRequestData,
1617
generateSentryTraceHeader,
1718
logger,
1819
parseBaggageHeader,
@@ -84,7 +85,8 @@ export class SentryPropagator extends W3CBaggagePropagator {
8485
}
8586

8687
const activeSpan = trace.getSpan(context);
87-
const url = activeSpan && spanToJSON(activeSpan).data?.[SemanticAttributes.HTTP_URL];
88+
const url = getCurrentURL(carrier as { __requestUrl?: string }, activeSpan);
89+
8890
const tracePropagationTargets = getClient()?.getOptions()?.tracePropagationTargets;
8991
if (
9092
typeof url === 'string' &&
@@ -120,7 +122,10 @@ export class SentryPropagator extends W3CBaggagePropagator {
120122
}, baggage);
121123
}
122124

123-
setter.set(carrier, SENTRY_TRACE_HEADER, generateSentryTraceHeader(traceId, spanId, sampled));
125+
// We also want to avoid setting the default OTEL trace ID, if we get that for whatever reason
126+
if (traceId && traceId !== '00000000000000000000000000000000') {
127+
setter.set(carrier, SENTRY_TRACE_HEADER, generateSentryTraceHeader(traceId, spanId, sampled));
128+
}
124129

125130
super.inject(propagation.setBaggage(context, baggage), carrier, setter);
126131
}
@@ -230,40 +235,15 @@ function getInjectionData(context: Context): {
230235
}
231236

232237
// Else we try to use the propagation context from the scope
233-
const scope = getScopesFromContext(context)?.scope;
234-
235-
if (scope) {
236-
const propagationContext = scope.getPropagationContext();
237-
const dynamicSamplingContext = getDynamicSamplingContext(propagationContext, propagationContext.traceId);
238-
return {
239-
dynamicSamplingContext,
240-
traceId: propagationContext.traceId,
241-
spanId: propagationContext.spanId,
242-
sampled: propagationContext.sampled,
243-
};
244-
}
245-
246-
// Else, we look at the remote span context
247-
if (span) {
248-
const spanContext = span.spanContext();
249-
const propagationContext = getPropagationContextFromSpan(span);
250-
const dynamicSamplingContext = getDynamicSamplingContext(propagationContext, spanContext.traceId);
238+
const scope = getScopesFromContext(context)?.scope || getCurrentScope();
251239

252-
return {
253-
dynamicSamplingContext,
254-
traceId: spanContext.traceId,
255-
spanId: spanContext.spanId,
256-
sampled: getSamplingDecision(spanContext),
257-
};
258-
}
259-
260-
// If we have neither, there is nothing much we can do, but that should not happen usually
261-
// Unless there is a detached OTEL context being passed around
240+
const propagationContext = scope.getPropagationContext();
241+
const dynamicSamplingContext = getDynamicSamplingContext(propagationContext, propagationContext.traceId);
262242
return {
263-
dynamicSamplingContext: undefined,
264-
traceId: undefined,
265-
spanId: undefined,
266-
sampled: undefined,
243+
dynamicSamplingContext,
244+
traceId: propagationContext.traceId,
245+
spanId: propagationContext.spanId,
246+
sampled: propagationContext.sampled,
267247
};
268248
}
269249

@@ -334,3 +314,25 @@ function getExistingBaggage(carrier: unknown): string | undefined {
334314
return undefined;
335315
}
336316
}
317+
318+
function getCurrentURL(carrier: { __requestUrl?: string }, span: Span | undefined): string | undefined {
319+
try {
320+
if (carrier.__requestUrl) {
321+
const url = carrier.__requestUrl;
322+
delete carrier.__requestUrl;
323+
return url;
324+
}
325+
} catch {
326+
// ignore errors here
327+
}
328+
329+
if (span) {
330+
const url = spanToJSON(span).data?.[SemanticAttributes.HTTP_URL];
331+
332+
if (url) {
333+
return url;
334+
}
335+
}
336+
337+
return undefined;
338+
}

0 commit comments

Comments
 (0)