Repository: vercel/next.js
Pull request: Update otel test assertions and pages span_name

This updates our otel tests to remove extra span assertions from `next start` with edge runtime that can cause confusion and fixes a couple span_names for pages handlers. 

x-ref: https://github.com/vercel/next.js/pull/75416#discussion_r2393016147

Changed files:
filename: packages/next/src/server/route-modules/pages/pages-handler.ts
patch: @@ -400,7 +400,7 @@ export const getHandler = ({
                     })
                     span.updateName(name)
                   } else {
-                    span.updateName(`${method} ${req.url}`)
+                    span.updateName(`${method} ${srcPage}`)
                   }
                 })
             } catch (err: unknown) {
@@ -722,7 +722,7 @@ export const getHandler = ({
           tracer.trace(
             BaseServerSpan.handleRequest,
             {
-              spanName: `${method} ${req.url}`,
+              spanName: `${method} ${srcPage}`,
               kind: SpanKind.SERVER,
               attributes: {
                 'http.method': method,

filename: packages/next/src/server/web/adapter.ts
patch: @@ -252,7 +252,7 @@ export async function adapter(
       return getTracer().trace(
         MiddlewareSpan.execute,
         {
-          spanName: `middleware ${request.method} ${request.nextUrl.pathname}`,
+          spanName: `middleware ${request.method}`,
           attributes: {
             'http.target': request.nextUrl.pathname,
             'http.method': request.method,

filename: test/e2e/opentelemetry/instrumentation/opentelemetry.test.ts
patch: @@ -225,137 +225,115 @@ describe('opentelemetry', () => {
           it('should handle RSC with fetch on edge', async () => {
             await next.fetch('/app/param/rsc-fetch/edge', env.fetchInit)
 
-            await expectTrace(getCollector(), [
-              {
-                traceId: env.span.traceId,
-                parentId: env.span.rootParentId,
-                runtime: 'edge',
-                name: 'GET /app/[param]/rsc-fetch/edge',
-                kind: 1,
-                attributes: {
-                  'next.span_name': 'GET /app/[param]/rsc-fetch/edge',
-                  'next.span_type': 'BaseServer.handleRequest',
-                  'http.method': 'GET',
-                  'http.target': '/app/param/rsc-fetch/edge?param=param',
-                  'http.status_code': 200,
-                  'next.route': '/app/[param]/rsc-fetch/edge',
-                  'http.route': '/app/[param]/rsc-fetch/edge',
-                },
-                status: { code: 0 },
-                spans: [
-                  {
-                    name: 'render route (app) /app/[param]/rsc-fetch/edge',
-                    kind: 0,
-                    attributes: {
-                      'next.span_name':
-                        'render route (app) /app/[param]/rsc-fetch/edge',
-                      'next.span_type': 'AppRender.getBodyResult',
-                      'next.route': '/app/[param]/rsc-fetch/edge',
-                    },
-                    status: { code: 0 },
-                    spans: [
-                      {
-                        name: 'build component tree',
-                        kind: 0,
-                        attributes: {
-                          'next.span_name': 'build component tree',
-                          'next.span_type':
-                            'NextNodeServer.createComponentTree',
-                        },
-                        status: { code: 0 },
-                        spans: [
-                          {
-                            name: 'resolve segment modules',
-                            kind: 0,
-                            attributes: {
-                              'next.span_name': 'resolve segment modules',
-                              'next.span_type':
-                                'NextNodeServer.getLayoutOrPageModule',
-                              'next.segment': '__PAGE__',
-                            },
-                            status: { code: 0 },
+            await expectTrace(
+              getCollector(),
+              [
+                {
+                  traceId: env.span.traceId,
+                  parentId: env.span.rootParentId,
+                  runtime: 'edge',
+                  name: 'GET /app/[param]/rsc-fetch/edge',
+                  kind: 1,
+                  attributes: {
+                    'next.span_name': 'GET /app/[param]/rsc-fetch/edge',
+                    'next.span_type': 'BaseServer.handleRequest',
+                    'http.method': 'GET',
+                    'http.target': '/app/param/rsc-fetch/edge?param=param',
+                    'http.status_code': 200,
+                    'next.route': '/app/[param]/rsc-fetch/edge',
+                    'http.route': '/app/[param]/rsc-fetch/edge',
+                  },
+                  status: { code: 0 },
+                  spans: [
+                    {
+                      name: 'render route (app) /app/[param]/rsc-fetch/edge',
+                      kind: 0,
+                      attributes: {
+                        'next.span_name':
+                          'render route (app) /app/[param]/rsc-fetch/edge',
+                        'next.span_type': 'AppRender.getBodyResult',
+                        'next.route': '/app/[param]/rsc-fetch/edge',
+                      },
+                      status: { code: 0 },
+                      spans: [
+                        {
+                          name: 'build component tree',
+                          kind: 0,
+                          attributes: {
+                            'next.span_name': 'build component tree',
+                            'next.span_type':
+                              'NextNodeServer.createComponentTree',
                           },
-                          {
-                            name: 'resolve segment modules',
-                            kind: 0,
-                            attributes: {
-                              'next.span_name': 'resolve segment modules',
-                              'next.span_type':
-                                'NextNodeServer.getLayoutOrPageModule',
-                              'next.segment': '[param]',
+                          status: { code: 0 },
+                          spans: [
+                            {
+                              name: 'resolve segment modules',
+                              kind: 0,
+                              attributes: {
+                                'next.span_name': 'resolve segment modules',
+                                'next.span_type':
+                                  'NextNodeServer.getLayoutOrPageModule',
+                                'next.segment': '__PAGE__',
+                              },
+                              status: { code: 0 },
                             },
-                            status: { code: 0 },
+                            {
+                              name: 'resolve segment modules',
+                              kind: 0,
+                              attributes: {
+                                'next.span_name': 'resolve segment modules',
+                                'next.span_type':
+                                  'NextNodeServer.getLayoutOrPageModule',
+                                'next.segment': '[param]',
+                              },
+                              status: { code: 0 },
+                            },
+                          ],
+                        },
+                        {
+                          name: 'fetch GET https://example.vercel.sh/',
+                          kind: 2,
+                          attributes: {
+                            'next.span_name':
+                              'fetch GET https://example.vercel.sh/',
+                            'next.span_type': 'AppRender.fetch',
+                            'http.url': 'https://example.vercel.sh/',
+                            'http.method': 'GET',
+                            'net.peer.name': 'example.vercel.sh',
                           },
-                        ],
-                      },
-                      {
-                        name: 'fetch GET https://example.vercel.sh/',
-                        kind: 2,
-                        attributes: {
-                          'next.span_name':
-                            'fetch GET https://example.vercel.sh/',
-                          'next.span_type': 'AppRender.fetch',
-                          'http.url': 'https://example.vercel.sh/',
-                          'http.method': 'GET',
-                          'net.peer.name': 'example.vercel.sh',
+                          status: { code: 0 },
                         },
-                        status: { code: 0 },
-                      },
-                      {
-                        name: 'generateMetadata /app/[param]/layout',
-                        kind: 0,
-                        attributes: {
-                          'next.span_name':
-                            'generateMetadata /app/[param]/layout',
-                          'next.span_type': 'ResolveMetadata.generateMetadata',
-                          'next.page': '/app/[param]/layout',
+                        {
+                          name: 'generateMetadata /app/[param]/layout',
+                          kind: 0,
+                          attributes: {
+                            'next.span_name':
+                              'generateMetadata /app/[param]/layout',
+                            'next.span_type':
+                              'ResolveMetadata.generateMetadata',
+                            'next.page': '/app/[param]/layout',
+                          },
+                          status: { code: 0 },
                         },
-                        status: { code: 0 },
-                      },
-                      {
-                        name: 'generateMetadata /app/[param]/rsc-fetch/edge/page',
-                        kind: 0,
-                        attributes: {
-                          'next.span_name':
-                            'generateMetadata /app/[param]/rsc-fetch/edge/page',
-                          'next.span_type': 'ResolveMetadata.generateMetadata',
-                          'next.page': '/app/[param]/rsc-fetch/edge/page',
+                        {
+                          name: 'generateMetadata /app/[param]/rsc-fetch/edge/page',
+                          kind: 0,
+                          attributes: {
+                            'next.span_name':
+                              'generateMetadata /app/[param]/rsc-fetch/edge/page',
+                            'next.span_type':
+                              'ResolveMetadata.generateMetadata',
+                            'next.page': '/app/[param]/rsc-fetch/edge/page',
+                          },
+                          status: { code: 0 },
                         },
-                        status: { code: 0 },
-                      },
-                    ],
-                  },
-                ],
-              },
-
-              // TODO: what is this trace? What's the value in it?
-              {
-                traceId: env.span.traceId,
-                parentId: env.span.rootParentId,
-                runtime: 'nodejs',
-                name: 'GET',
-                kind: 1,
-                attributes: {
-                  'next.span_name': 'GET',
-                  'next.span_type': 'BaseServer.handleRequest',
-                  'http.method': 'GET',
-                  'http.target': '/app/param/rsc-fetch/edge',
-                  'http.status_code': 200,
-                },
-                status: { code: 0 },
-                spans: [
-                  {
-                    name: 'start response',
-                    kind: 0,
-                    attributes: {
-                      'next.span_name': 'start response',
-                      'next.span_type': 'NextNodeServer.startResponse',
+                      ],
                     },
-                    status: { code: 0 },
-                  },
-                ],
-              },
-            ])
+                  ],
+                },
+              ],
+              true
+            )
           })
 
           it('should handle RSC with fetch in RSC mode', async () => {
@@ -447,50 +425,26 @@ describe('opentelemetry', () => {
           it('should handle route handlers in app router on edge', async () => {
             await next.fetch('/api/app/param/data/edge', env.fetchInit)
 
-            await expectTrace(getCollector(), [
-              {
-                runtime: 'edge',
-                traceId: env.span.traceId,
-                parentId: env.span.rootParentId,
-                name: 'executing api route (app) /api/app/[param]/data/edge',
-                attributes: {
-                  'next.route': '/api/app/[param]/data/edge',
-                  'next.span_name':
-                    'executing api route (app) /api/app/[param]/data/edge',
-                  'next.span_type': 'AppRouteRouteHandlers.runHandler',
-                },
-                kind: 0,
-                status: { code: 0 },
-              },
-
-              // TODO: what is this trace? What's the value in it?
-              {
-                runtime: 'nodejs',
-                traceId: env.span.traceId,
-                parentId: env.span.rootParentId,
-                name: 'GET',
-                kind: 1,
-                attributes: {
-                  'next.span_name': 'GET',
-                  'next.span_type': 'BaseServer.handleRequest',
-                  'http.method': 'GET',
-                  'http.target': '/api/app/param/data/edge',
-                  'http.status_code': 200,
-                },
-                status: { code: 0 },
-                spans: [
-                  {
-                    name: 'start response',
-                    kind: 0,
-                    attributes: {
-                      'next.span_name': 'start response',
-                      'next.span_type': 'NextNodeServer.startResponse',
-                    },
-                    status: { code: 0 },
+            await expectTrace(
+              getCollector(),
+              [
+                {
+                  runtime: 'edge',
+                  traceId: env.span.traceId,
+                  parentId: env.span.rootParentId,
+                  name: 'executing api route (app) /api/app/[param]/data/edge',
+                  attributes: {
+                    'next.route': '/api/app/[param]/data/edge',
+                    'next.span_name':
+                      'executing api route (app) /api/app/[param]/data/edge',
+                    'next.span_type': 'AppRouteRouteHandlers.runHandler',
                   },
-                ],
-              },
-            ])
+                  kind: 0,
+                  status: { code: 0 },
+                },
+              ],
+              true
+            )
           })
 
           it('should trace middleware', async () => {
@@ -501,11 +455,11 @@ describe('opentelemetry', () => {
                 runtime: 'edge',
                 traceId: env.span.traceId,
                 parentId: env.span.rootParentId,
-                name: 'middleware GET /behind-middleware',
+                name: 'middleware GET',
                 attributes: {
                   'http.method': 'GET',
                   'http.target': '/behind-middleware',
-                  'next.span_name': 'middleware GET /behind-middleware',
+                  'next.span_name': 'middleware GET',
                   'next.span_type': 'Middleware.execute',
                 },
                 status: { code: 0 },
@@ -781,79 +735,55 @@ describe('opentelemetry', () => {
               env.fetchInit
             )
 
-            await expectTrace(getCollector(), [
-              {
-                runtime: 'edge',
-                traceId: env.span.traceId,
-                parentId: env.span.rootParentId,
-                name: 'GET /pages/[param]/edge/getServerSideProps',
-                kind: 1,
-                attributes: {
-                  'next.span_name':
-                    'GET /pages/[param]/edge/getServerSideProps',
-                  'next.span_type': 'BaseServer.handleRequest',
-                  'http.method': 'GET',
-                  'http.target':
-                    '/pages/param/edge/getServerSideProps?param=param',
-                  'http.status_code': 200,
-                  'next.route': '/pages/[param]/edge/getServerSideProps',
-                  'http.route': '/pages/[param]/edge/getServerSideProps',
-                },
-                status: { code: 0 },
-                spans: [
-                  {
-                    name: 'getServerSideProps /pages/[param]/edge/getServerSideProps',
-                    kind: 0,
-                    attributes: {
-                      'next.span_name':
-                        'getServerSideProps /pages/[param]/edge/getServerSideProps',
-                      'next.span_type': 'Render.getServerSideProps',
-                      'next.route': '/pages/[param]/edge/getServerSideProps',
-                    },
-                    status: { code: 0 },
+            await expectTrace(
+              getCollector(),
+              [
+                {
+                  runtime: 'edge',
+                  traceId: env.span.traceId,
+                  parentId: env.span.rootParentId,
+                  name: 'GET /pages/[param]/edge/getServerSideProps',
+                  kind: 1,
+                  attributes: {
+                    'next.span_name':
+                      'GET /pages/[param]/edge/getServerSideProps',
+                    'next.span_type': 'BaseServer.handleRequest',
+                    'http.method': 'GET',
+                    'http.target':
+                      '/pages/param/edge/getServerSideProps?param=param',
+                    'http.status_code': 200,
+                    'next.route': '/pages/[param]/edge/getServerSideProps',
+                    'http.route': '/pages/[param]/edge/getServerSideProps',
                   },
-                  {
-                    name: 'render route (pages) /pages/[param]/edge/getServerSideProps',
-                    kind: 0,
-                    attributes: {
-                      'next.span_name':
-                        'render route (pages) /pages/[param]/edge/getServerSideProps',
-                      'next.span_type': 'Render.renderDocument',
-                      'next.route': '/pages/[param]/edge/getServerSideProps',
+                  status: { code: 0 },
+                  spans: [
+                    {
+                      name: 'getServerSideProps /pages/[param]/edge/getServerSideProps',
+                      kind: 0,
+                      attributes: {
+                        'next.span_name':
+                          'getServerSideProps /pages/[param]/edge/getServerSideProps',
+                        'next.span_type': 'Render.getServerSideProps',
+                        'next.route': '/pages/[param]/edge/getServerSideProps',
+                      },
+                      status: { code: 0 },
                     },
-                    status: { code: 0 },
-                  },
-                ],
-              },
-
-              // TODO: what is this trace? What's the value in it?
-              {
-                runtime: 'nodejs',
-                traceId: env.span.traceId,
-                parentId: env.span.rootParentId,
-                name: 'GET',
-                kind: 1,
-                attributes: {
-                  'next.span_name': 'GET',
-                  'next.span_type': 'BaseServer.handleRequest',
-                  'http.method': 'GET',
-                  'http.target': '/pages/param/edge/getServerSideProps',
-                  'http.status_code': 200,
-                },
-                status: { code: 0 },
-                spans: [
-                  {
-                    name: 'start response',
-                    kind: 0,
-                    attributes: {
-                      'next.span_name': 'start response',
-                      'next.span_type': 'NextNodeServer.startResponse',
+                    {
+                      name: 'render route (pages) /pages/[param]/edge/getServerSideProps',
+                      kind: 0,
+                      attributes: {
+                        'next.span_name':
+                          'render route (pages) /pages/[param]/edge/getServerSideProps',
+                        'next.span_type': 'Render.renderDocument',
+                        'next.route': '/pages/[param]/edge/getServerSideProps',
+                      },
+                      status: { code: 0 },
                     },
-                    status: { code: 0 },
-                  },
-                ],
-              },
-            ])
+                  ],
+                },
+              ],
+              true
+            )
           })
 
           it('should handle getServerSideProps exceptions', async () => {
@@ -1080,49 +1010,25 @@ describe('opentelemetry', () => {
           it('should handle api routes in pages on edge', async () => {
             await next.fetch('/api/pages/param/edge', env.fetchInit)
 
-            await expectTrace(getCollector(), [
-              {
-                runtime: 'edge',
-                traceId: env.span.traceId,
-                parentId: env.span.rootParentId,
-                name: 'executing api route (pages) /api/pages/[param]/edge',
-                attributes: {
-                  'next.span_name':
-                    'executing api route (pages) /api/pages/[param]/edge',
-                  'next.span_type': 'Node.runHandler',
-                },
-                kind: 0,
-                status: { code: 0 },
-              },
-
-              // TODO: what is this trace? What's the value in it?
-              {
-                runtime: 'nodejs',
-                traceId: env.span.traceId,
-                parentId: env.span.rootParentId,
-                name: 'GET',
-                kind: 1,
-                attributes: {
-                  'next.span_name': 'GET',
-                  'next.span_type': 'BaseServer.handleRequest',
-                  'http.method': 'GET',
-                  'http.target': '/api/pages/param/edge',
-                  'http.status_code': 200,
-                },
-                status: { code: 0 },
-                spans: [
-                  {
-                    name: 'start response',
-                    kind: 0,
-                    attributes: {
-                      'next.span_name': 'start response',
-                      'next.span_type': 'NextNodeServer.startResponse',
-                    },
-                    status: { code: 0 },
+            await expectTrace(
+              getCollector(),
+              [
+                {
+                  runtime: 'edge',
+                  traceId: env.span.traceId,
+                  parentId: env.span.rootParentId,
+                  name: 'executing api route (pages) /api/pages/[param]/edge',
+                  attributes: {
+                    'next.span_name':
+                      'executing api route (pages) /api/pages/[param]/edge',
+                    'next.span_type': 'Node.runHandler',
                   },
-                ],
-              },
-            ])
+                  kind: 0,
+                  status: { code: 0 },
+                },
+              ],
+              true
+            )
           })
         })
       }
@@ -1214,7 +1120,11 @@ describe('opentelemetry with disabled fetch tracing', () => {
 type HierSavedSpan = SavedSpan & { spans?: HierSavedSpan[] }
 type SpanMatch = Omit<Partial<HierSavedSpan>, 'spans'> & { spans?: SpanMatch[] }
 
-async function expectTrace(collector: Collector, match: SpanMatch[]) {
+async function expectTrace(
+  collector: Collector,
+  match: SpanMatch[],
+  edgeOnly?: boolean
+) {
   await check(async () => {
     const traces = collector.getSpans()
 
@@ -1224,6 +1134,14 @@ async function expectTrace(collector: Collector, match: SpanMatch[]) {
       spans: [],
     }))
     for (const span of spansForTree) {
+      // for edge runtime with `next start` the nodejs runtime spans
+      // will not be updated as the sandbox can't update spans across
+      // node -> edge boundary, these spans also are not present when
+      // deployed as next-server is not invoking edge functions there
+      if (span.runtime === 'nodejs' && edgeOnly) {
+        continue
+      }
+
       const parent =
         !span.parentId || span.parentId === EXTERNAL.spanId
           ? null