-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(nextjs): Extract tracing logic from server component wrapper templates #18408
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
45f14ab
fbb8f37
2a2613d
960dcd1
40f32cc
805e1af
076c4df
6b15148
c6751b3
8f07b99
e30ea5c
4fec975
ecf4dab
1b8b439
01cbe44
ba33529
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import { PropsWithChildren } from 'react'; | ||
|
|
||
| export const dynamic = 'force-dynamic'; | ||
|
|
||
| export default function Layout({ children }: PropsWithChildren<{}>) { | ||
| return ( | ||
| <div> | ||
| <p>DynamicLayout</p> | ||
| {children} | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| export const dynamic = 'force-dynamic'; | ||
|
|
||
| export default async function Page() { | ||
| return ( | ||
| <div> | ||
| <p>Dynamic Page</p> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| export async function generateMetadata() { | ||
| return { | ||
| title: 'I am dynamic page generated metadata', | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| const packageJson = require('../package.json'); | ||
| const nextjsVersion = packageJson.dependencies.next; | ||
| const nextjsMajor = Number(nextjsVersion.split('.')[0]); | ||
|
|
||
| export const isNext13 = !isNaN(nextjsMajor) && nextjsMajor === 13; | ||
| export const nextjsMajorVersion = nextjsMajor; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| export const ATTR_NEXT_SPAN_TYPE = 'next.span_type'; | ||
| export const ATTR_NEXT_SPAN_NAME = 'next.span_name'; | ||
| export const ATTR_NEXT_ROUTE = 'next.route'; | ||
| export const ATTR_NEXT_SPAN_DESCRIPTION = 'next.span_description'; | ||
| export const ATTR_NEXT_SEGMENT = 'next.segment'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,23 @@ | ||
| import type { PropagationContext } from '@sentry/core'; | ||
| import { debug, getActiveSpan, getRootSpan, GLOBAL_OBJ, Scope, spanToJSON, startNewTrace } from '@sentry/core'; | ||
| import { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions'; | ||
| import type { PropagationContext, Span, SpanAttributes } from '@sentry/core'; | ||
| import { | ||
| debug, | ||
| getActiveSpan, | ||
| getRootSpan, | ||
| GLOBAL_OBJ, | ||
| Scope, | ||
| SEMANTIC_ATTRIBUTE_SENTRY_OP, | ||
| spanToJSON, | ||
| startNewTrace, | ||
| } from '@sentry/core'; | ||
| import { DEBUG_BUILD } from '../debug-build'; | ||
| import { ATTR_NEXT_SEGMENT, ATTR_NEXT_SPAN_NAME, ATTR_NEXT_SPAN_TYPE } from '../nextSpanAttributes'; | ||
| import { TRANSACTION_ATTR_SHOULD_DROP_TRANSACTION } from '../span-attributes-with-logic-attached'; | ||
|
|
||
| const commonPropagationContextMap = new WeakMap<object, PropagationContext>(); | ||
|
|
||
| const PAGE_SEGMENT = '__PAGE__'; | ||
|
|
||
| /** | ||
| * Takes a shared (garbage collectable) object between resources, e.g. a headers object shared between Next.js server components and returns a common propagation context. | ||
| * | ||
|
|
@@ -108,3 +121,61 @@ export function dropNextjsRootContext(): void { | |
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Checks if the span is a resolve segment span. | ||
| * @param spanAttributes The attributes of the span to check. | ||
| * @returns True if the span is a resolve segment span, false otherwise. | ||
| */ | ||
| export function isResolveSegmentSpan(spanAttributes: SpanAttributes): boolean { | ||
| return ( | ||
| spanAttributes[ATTR_NEXT_SPAN_TYPE] === 'NextNodeServer.getLayoutOrPageModule' && | ||
| spanAttributes[ATTR_NEXT_SPAN_NAME] === 'resolve segment modules' && | ||
| typeof spanAttributes[ATTR_NEXT_SEGMENT] === 'string' | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the enhanced name for a resolve segment span. | ||
| * @param segment The segment of the resolve segment span. | ||
| * @param route The route of the resolve segment span. | ||
| * @returns The enhanced name for the resolve segment span. | ||
| */ | ||
| export function getEnhancedResolveSegmentSpanName({ segment, route }: { segment: string; route: string }): string { | ||
| if (segment === PAGE_SEGMENT) { | ||
| return `resolve page server component "${route}"`; | ||
| } | ||
|
|
||
| if (segment === '') { | ||
| return 'resolve root layout server component'; | ||
| } | ||
|
|
||
| return `resolve layout server component "${segment}"`; | ||
| } | ||
|
|
||
| /** | ||
| * Maybe enhances the span name for a resolve segment span. | ||
| * If the span is not a resolve segment span, this function does nothing. | ||
| * @param activeSpan The active span. | ||
| * @param spanAttributes The attributes of the span to check. | ||
| * @param rootSpanAttributes The attributes of the according root span. | ||
| */ | ||
| export function maybeEnhanceServerComponentSpanName( | ||
| activeSpan: Span, | ||
| spanAttributes: SpanAttributes, | ||
| rootSpanAttributes: SpanAttributes, | ||
| ): void { | ||
| if (!isResolveSegmentSpan(spanAttributes)) { | ||
| return; | ||
| } | ||
|
|
||
| const segment = spanAttributes[ATTR_NEXT_SEGMENT] as string; | ||
| const route = rootSpanAttributes[ATTR_HTTP_ROUTE]; | ||
| const enhancedName = getEnhancedResolveSegmentSpanName({ segment, route: typeof route === 'string' ? route : '' }); | ||
| activeSpan.updateName(enhancedName); | ||
| activeSpan.setAttributes({ | ||
| 'sentry.nextjs.ssr.function.type': segment === PAGE_SEGMENT ? 'Page' : 'Layout', | ||
| 'sentry.nextjs.ssr.function.route': route, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: Inconsistent type handling for route span attributeThe |
||
| }); | ||
| activeSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'function.nextjs'); | ||
| } | ||
chargome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Debug console.log left in test code
A
console.log(transactionEvent?.transaction)statement appears to have been left in the test code, likely from debugging. While this is in test code, it will produce unnecessary output during test runs.