Skip to content

Commit 434507d

Browse files
authored
fix(node-experimental): Ensure we only create HTTP spans when outgoing (#8966)
This is a fork of #8937, with only the "uncontroversial" stuff, mainly fixing that we only create HTTP breadcrumbs for _outgoing_ requests. In addition, this also migrates to using `requestHook` and `responseHook` instead of `applyCustomAttributesOnSpan`. We may have to revisit this later, but these hooks seem to have a better context awareness (=they are called in a more reasonable OTEL context, which gives the callbacks there better access to scope data etc). However that means we cannot (easily) pass both request and response as breadcrumb hints - not sure how important that is to us... For now I'd say that's OK. Note that also `requestHook` is only called when the request finishes, so we already have all the response OTEL span attributes correctly set there.
1 parent 2ab1f50 commit 434507d

File tree

2 files changed

+58
-54
lines changed

2 files changed

+58
-54
lines changed

packages/node-experimental/src/integrations/http.ts

Lines changed: 57 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,11 @@ export class Http implements Integration {
9595
new HttpInstrumentation({
9696
requireParentforOutgoingSpans: true,
9797
requireParentforIncomingSpans: false,
98-
applyCustomAttributesOnSpan: (span, req, res) => {
99-
this._onSpan(span as unknown as OtelSpan, req, res);
98+
requestHook: (span, req) => {
99+
this._updateSentrySpan(span as unknown as OtelSpan, req);
100+
},
101+
responseHook: (span, res) => {
102+
this._addRequestBreadcrumb(span as unknown as OtelSpan, res);
100103
},
101104
}),
102105
],
@@ -136,64 +139,70 @@ export class Http implements Integration {
136139
return;
137140
};
138141

139-
/** Handle an emitted span from the HTTP instrumentation. */
140-
private _onSpan(
141-
span: OtelSpan,
142-
request: ClientRequest | IncomingMessage,
143-
response: IncomingMessage | ServerResponse,
144-
): void {
145-
const data = getRequestSpanData(span, request, response);
142+
/** Update the Sentry span data based on the OTEL span. */
143+
private _updateSentrySpan(span: OtelSpan, request: ClientRequest | IncomingMessage): void {
144+
const data = getRequestSpanData(span);
146145
const { attributes } = span;
147146

148147
const sentrySpan = _INTERNAL_getSentrySpan(span.spanContext().spanId);
149-
if (sentrySpan) {
150-
sentrySpan.origin = 'auto.http.otel.http';
148+
if (!sentrySpan) {
149+
return;
150+
}
151151

152-
const additionalData: Record<string, unknown> = {
153-
url: data.url,
154-
};
152+
sentrySpan.origin = 'auto.http.otel.http';
155153

156-
if (sentrySpan instanceof Transaction && span.kind === SpanKind.SERVER) {
157-
sentrySpan.setMetadata({ request });
158-
}
154+
const additionalData: Record<string, unknown> = {
155+
url: data.url,
156+
};
159157

160-
if (attributes[SemanticAttributes.HTTP_STATUS_CODE]) {
161-
const statusCode = attributes[SemanticAttributes.HTTP_STATUS_CODE] as string;
162-
additionalData['http.response.status_code'] = statusCode;
158+
if (sentrySpan instanceof Transaction && span.kind === SpanKind.SERVER) {
159+
sentrySpan.setMetadata({ request });
160+
}
163161

164-
sentrySpan.setTag('http.status_code', statusCode);
165-
}
162+
if (attributes[SemanticAttributes.HTTP_STATUS_CODE]) {
163+
const statusCode = attributes[SemanticAttributes.HTTP_STATUS_CODE] as string;
164+
additionalData['http.response.status_code'] = statusCode;
166165

167-
if (data['http.query']) {
168-
additionalData['http.query'] = data['http.query'].slice(1);
169-
}
170-
if (data['http.fragment']) {
171-
additionalData['http.fragment'] = data['http.fragment'].slice(1);
172-
}
166+
sentrySpan.setTag('http.status_code', statusCode);
167+
}
173168

174-
Object.keys(additionalData).forEach(prop => {
175-
const value = additionalData[prop];
176-
sentrySpan.setData(prop, value);
177-
});
169+
if (data['http.query']) {
170+
additionalData['http.query'] = data['http.query'].slice(1);
171+
}
172+
if (data['http.fragment']) {
173+
additionalData['http.fragment'] = data['http.fragment'].slice(1);
178174
}
179175

180-
if (this._breadcrumbs) {
181-
getCurrentHub().addBreadcrumb(
182-
{
183-
category: 'http',
184-
data: {
185-
status_code: response.statusCode,
186-
...data,
187-
},
188-
type: 'http',
189-
},
190-
{
191-
event: 'response',
192-
request,
193-
response,
194-
},
195-
);
176+
Object.keys(additionalData).forEach(prop => {
177+
const value = additionalData[prop];
178+
sentrySpan.setData(prop, value);
179+
});
180+
}
181+
182+
/** Add a breadcrumb for outgoing requests. */
183+
private _addRequestBreadcrumb(span: OtelSpan, response: IncomingMessage | ServerResponse): void {
184+
if (!this._breadcrumbs || span.kind !== SpanKind.CLIENT) {
185+
return;
196186
}
187+
188+
const data = getRequestSpanData(span);
189+
getCurrentHub().addBreadcrumb(
190+
{
191+
category: 'http',
192+
data: {
193+
status_code: response.statusCode,
194+
...data,
195+
},
196+
type: 'http',
197+
},
198+
{
199+
event: 'response',
200+
// TODO FN: Do we need access to `request` here?
201+
// If we do, we'll have to use the `applyCustomAttributesOnSpan` hook instead,
202+
// but this has worse context semantics than request/responseHook.
203+
response,
204+
},
205+
);
197206
}
198207
}
199208

packages/node-experimental/src/utils/getRequestSpanData.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,11 @@ import type { Span as OtelSpan } from '@opentelemetry/sdk-trace-base';
22
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
33
import type { SanitizedRequestData } from '@sentry/types';
44
import { getSanitizedUrlString, parseUrl } from '@sentry/utils';
5-
import type { ClientRequest, IncomingMessage, ServerResponse } from 'http';
65

76
/**
87
* Get sanitizied request data from an OTEL span.
98
*/
10-
export function getRequestSpanData(
11-
span: OtelSpan,
12-
_request: ClientRequest | IncomingMessage,
13-
_response: IncomingMessage | ServerResponse,
14-
): SanitizedRequestData {
9+
export function getRequestSpanData(span: OtelSpan): SanitizedRequestData {
1510
const data: SanitizedRequestData = {
1611
url: span.attributes[SemanticAttributes.HTTP_URL] as string,
1712
'http.method': (span.attributes[SemanticAttributes.HTTP_METHOD] as string) || 'GET',

0 commit comments

Comments
 (0)