Skip to content

Commit

Permalink
Update trace fetcher wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
lforst committed Sep 20, 2024
1 parent ec8a468 commit bb8ba5b
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 51 deletions.
98 changes: 54 additions & 44 deletions packages/nextjs/src/common/utils/wrapperUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
SPAN_STATUS_OK,
captureException,
continueTrace,
getActiveSpan,
getRootSpan,
getTraceData,
startInactiveSpan,
startSpan,
Expand All @@ -17,7 +19,7 @@ import type { Span } from '@sentry/types';
import { isString } from '@sentry/utils';

import { autoEndSpanOnResponseEnd, flushSafelyWithTimeout } from './responseEnd';
import { commonObjectToIsolationScope, escapeNextjsTracing } from './tracingUtils';
import { commonObjectToIsolationScope } from './tracingUtils';
import { vercelWaitUntil } from './vercelWaitUntil';

declare module 'http' {
Expand Down Expand Up @@ -94,49 +96,49 @@ export function withTracedServerSideDataFetcher<F extends (...args: any[]) => Pr
this: unknown,
...args: Parameters<F>
): Promise<{ data: ReturnType<F>; sentryTrace?: string; baggage?: string }> {
return escapeNextjsTracing(() => {
const isolationScope = commonObjectToIsolationScope(req);
return withIsolationScope(isolationScope, () => {
isolationScope.setTransactionName(`${options.dataFetchingMethodName} (${options.dataFetcherRouteName})`);
isolationScope.setSDKProcessingMetadata({
request: req,
});
const isolationScope = commonObjectToIsolationScope(req);
return withIsolationScope(isolationScope, () => {
isolationScope.setTransactionName(`${options.dataFetchingMethodName} (${options.dataFetcherRouteName})`);
isolationScope.setSDKProcessingMetadata({
request: req,
});

const sentryTrace =
req.headers && isString(req.headers['sentry-trace']) ? req.headers['sentry-trace'] : undefined;
const baggage = req.headers?.baggage;

return continueTrace({ sentryTrace, baggage }, () => {
const requestSpan = getOrStartRequestSpan(req, res, options.requestedRouteName);
return withActiveSpan(requestSpan, () => {
return startSpanManual(
{
op: 'function.nextjs',
name: `${options.dataFetchingMethodName} (${options.dataFetcherRouteName})`,
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs',
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
},
const sentryTrace =
req.headers && isString(req.headers['sentry-trace']) ? req.headers['sentry-trace'] : undefined;
const baggage = req.headers?.baggage;

return continueTrace({ sentryTrace, baggage }, () => {
const overarchingSpan = getOrStartOverarchingSpan(req, res, options.requestedRouteName);
getRootSpan(overarchingSpan).setAttribute('sentry.datafetcher', true);

return withActiveSpan(overarchingSpan, () => {
return startSpanManual(
{
op: 'function.nextjs',
name: `${options.dataFetchingMethodName} (${options.dataFetcherRouteName})`,
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs',
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
},
async dataFetcherSpan => {
dataFetcherSpan.setStatus({ code: SPAN_STATUS_OK });
const { 'sentry-trace': sentryTrace, baggage } = getTraceData();
try {
return {
sentryTrace: sentryTrace,
baggage: baggage,
data: await origDataFetcher.apply(this, args),
};
} catch (e) {
dataFetcherSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
requestSpan?.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
throw e;
} finally {
dataFetcherSpan.end();
}
},
);
});
},
async dataFetcherSpan => {
dataFetcherSpan.setStatus({ code: SPAN_STATUS_OK });
const { 'sentry-trace': sentryTrace, baggage } = getTraceData();
try {
return {
sentryTrace: sentryTrace,
baggage: baggage,
data: await origDataFetcher.apply(this, args),
};
} catch (e) {
dataFetcherSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
overarchingSpan?.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
throw e;
} finally {
dataFetcherSpan.end();
}
},
);
});
});
}).finally(() => {
Expand All @@ -145,14 +147,22 @@ export function withTracedServerSideDataFetcher<F extends (...args: any[]) => Pr
};
}

function getOrStartRequestSpan(req: IncomingMessage, res: ServerResponse, name: string): Span {
/**
* TODO
*/
function getOrStartOverarchingSpan(req: IncomingMessage, res: ServerResponse, routeName: string): Span {
const activeSpan = getActiveSpan();
if (activeSpan) {
return activeSpan;
}

const existingSpan = getSpanFromRequest(req);
if (existingSpan) {
return existingSpan;
}

const requestSpan = startInactiveSpan({
name,
name: req.method ? `${req.method} ${routeName}` : routeName,
forceTransaction: true,
op: 'http.server',
attributes: {
Expand Down
21 changes: 14 additions & 7 deletions packages/nextjs/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { GLOBAL_OBJ, logger } from '@sentry/utils';
import {
ATTR_HTTP_REQUEST_METHOD,
ATTR_HTTP_ROUTE,
ATTR_URL_QUERY,
SEMATTRS_HTTP_METHOD,
SEMATTRS_HTTP_TARGET,
} from '@opentelemetry/semantic-conventions';
Expand Down Expand Up @@ -157,11 +158,14 @@ export function init(options: NodeOptions): NodeClient | undefined {
// We need to drop these spans.
if (
// eslint-disable-next-line deprecation/deprecation
typeof spanAttributes[SEMATTRS_HTTP_TARGET] === 'string' &&
// eslint-disable-next-line deprecation/deprecation
spanAttributes[SEMATTRS_HTTP_TARGET].includes('sentry_key') &&
// eslint-disable-next-line deprecation/deprecation
spanAttributes[SEMATTRS_HTTP_TARGET].includes('sentry_client')
(typeof spanAttributes[SEMATTRS_HTTP_TARGET] === 'string' &&
// eslint-disable-next-line deprecation/deprecation
spanAttributes[SEMATTRS_HTTP_TARGET].includes('sentry_key') &&
// eslint-disable-next-line deprecation/deprecation
spanAttributes[SEMATTRS_HTTP_TARGET].includes('sentry_client')) ||
(typeof spanAttributes[ATTR_URL_QUERY] === 'string' &&
spanAttributes[ATTR_URL_QUERY].includes('sentry_key') &&
spanAttributes[ATTR_URL_QUERY].includes('sentry_client'))
) {
samplingDecision.decision = false;
}
Expand Down Expand Up @@ -211,11 +215,14 @@ export function init(options: NodeOptions): NodeClient | undefined {
return null;
}

// We only want to use our HTTP integration/instrumentation for app router requests, which are marked with the `sentry.rsc` attribute.
// We only want to use our HTTP integration/instrumentation for
// - app router requests, which are marked with the `sentry.rsc` attribute.
// - pages router requests, which are marked with the `sentry.datafetcher` attribute.
if (
(event.contexts?.trace?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] === 'auto.http.otel.http' ||
event.contexts?.trace?.data?.['next.span_type'] === 'BaseServer.handleRequest') &&
event.contexts?.trace?.data?.['sentry.rsc'] !== true
event.contexts?.trace?.data?.['sentry.rsc'] !== true &&
event.contexts?.trace?.data?.['sentry.datafetcher'] !== true
) {
return null;
}
Expand Down

0 comments on commit bb8ba5b

Please sign in to comment.