|
1 | 1 | import { context } from '@opentelemetry/api'; |
2 | 2 | import { |
3 | | - ATTR_HTTP_REQUEST_METHOD, |
4 | 3 | ATTR_HTTP_ROUTE, |
5 | 4 | ATTR_URL_QUERY, |
6 | 5 | SEMATTRS_HTTP_METHOD, |
@@ -160,33 +159,68 @@ export function init(options: NodeOptions): NodeClient | undefined { |
160 | 159 |
|
161 | 160 | client?.on('spanStart', span => { |
162 | 161 | const spanAttributes = spanToJSON(span).data; |
| 162 | + const spanType = spanAttributes?.['next.span_type'] as string | undefined; |
| 163 | + let functionType = ''; |
| 164 | + |
| 165 | + if (spanType) { |
| 166 | + const [category, functionName] = spanType.split('.') as [string, string]; |
| 167 | + |
| 168 | + const isNextjsSpan = [ |
| 169 | + 'BaseServer', |
| 170 | + 'LoadComponents', |
| 171 | + 'NextServer', |
| 172 | + 'NextNodeServer', |
| 173 | + 'StartServer', |
| 174 | + 'Render', |
| 175 | + 'Router', |
| 176 | + 'AppRender', |
| 177 | + 'Node', |
| 178 | + 'AppRouteRouteHandlers', |
| 179 | + 'ResolveMetadata', |
| 180 | + 'Middleware', |
| 181 | + 'NextNodeServerSpan' |
| 182 | + ].includes(category); |
| 183 | + |
| 184 | + if (isNextjsSpan && functionName) { |
| 185 | + functionType = functionName; |
| 186 | + |
| 187 | + if (category === 'NextNodeServerSpan' && functionName === 'clientComponentLoading') { |
| 188 | + functionType = 'Component'; |
| 189 | + } else if (category === 'NextNodeServer' && functionName === 'findPageComponents') { |
| 190 | + functionType = 'Page'; |
| 191 | + } |
| 192 | + else if (category === 'NextNodeServerSpan' && functionName === 'getLayoutOrPageModule') { |
| 193 | + if (spanAttributes?.['next.segment'] === '__LAYOUT__') { |
| 194 | + functionType = 'Layout'; |
| 195 | + } else { |
| 196 | + functionType = 'Page'; |
| 197 | + } |
| 198 | + |
| 199 | + } |
| 200 | + } |
| 201 | + } |
163 | 202 |
|
164 | | - // What we do in this glorious piece of code, is hoist any information about parameterized routes from spans emitted |
165 | | - // by Next.js via the `next.route` attribute, up to the transaction by setting the http.route attribute. |
166 | | - if (typeof spanAttributes?.['next.route'] === 'string') { |
167 | | - const rootSpan = getRootSpan(span); |
168 | | - const rootSpanAttributes = spanToJSON(rootSpan).data; |
169 | 203 |
|
170 | | - // Only hoist the http.route attribute if the transaction doesn't already have it |
171 | | - if ( |
172 | | - // eslint-disable-next-line deprecation/deprecation |
173 | | - (rootSpanAttributes?.[ATTR_HTTP_REQUEST_METHOD] || rootSpanAttributes?.[SEMATTRS_HTTP_METHOD]) && |
174 | | - !rootSpanAttributes?.[ATTR_HTTP_ROUTE] |
175 | | - ) { |
176 | | - const route = spanAttributes['next.route'].replace(/\/route$/, ''); |
177 | | - rootSpan.updateName(route); |
178 | | - rootSpan.setAttribute(ATTR_HTTP_ROUTE, route); |
| 204 | + if (functionType) { |
| 205 | + let route = '/'; |
| 206 | + const rawRoute = spanAttributes?.['next.route']; |
| 207 | + if (typeof rawRoute === 'string') { |
| 208 | + route = rawRoute; |
179 | 209 | } |
| 210 | + |
| 211 | + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'function.nextjs'); |
| 212 | + span.setAttribute('sentry.nextjs.function.type', functionType); |
| 213 | + span.setAttribute('sentry.nextjs.function.route', route); |
180 | 214 | } |
181 | 215 |
|
182 | 216 | // We want to skip span data inference for any spans generated by Next.js. Reason being that Next.js emits spans |
183 | 217 | // with patterns (e.g. http.server spans) that will produce confusing data. |
184 | | - if (spanAttributes?.['next.span_type'] !== undefined) { |
| 218 | + if (spanType !== undefined) { |
185 | 219 | span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto'); |
186 | 220 | } |
187 | 221 |
|
188 | 222 | // We want to fork the isolation scope for incoming requests |
189 | | - if (spanAttributes?.['next.span_type'] === 'BaseServer.handleRequest' && span === getRootSpan(span)) { |
| 223 | + if (spanType === 'BaseServer.handleRequest' && span === getRootSpan(span)) { |
190 | 224 | const scopes = getCapturedScopesOnSpan(span); |
191 | 225 |
|
192 | 226 | const isolationScope = (scopes.isolationScope || getIsolationScope()).clone(); |
|
0 commit comments