diff --git a/packages/next-swc/crates/next-core/src/next_config.rs b/packages/next-swc/crates/next-core/src/next_config.rs index 93cf4bda13ae7..38026e5c77e26 100644 --- a/packages/next-swc/crates/next-core/src/next_config.rs +++ b/packages/next-swc/crates/next-core/src/next_config.rs @@ -514,6 +514,7 @@ pub struct ExperimentalConfig { gzip_size: Option, instrumentation_hook: Option, + client_trace_metadata: Option>, large_page_data_bytes: Option, logging: Option, memory_based_workers_count: Option, diff --git a/packages/next/src/build/webpack-config.ts b/packages/next/src/build/webpack-config.ts index d0a5f322bc504..262b0c718f9bb 100644 --- a/packages/next/src/build/webpack-config.ts +++ b/packages/next/src/build/webpack-config.ts @@ -2057,6 +2057,7 @@ export default async function getBaseWebpackConfig( emotion: config.compiler?.emotion, modularizeImports: config.modularizeImports, imageLoaderFile: config.images.loaderFile, + clientTraceMetadata: config.experimental.clientTraceMetadata, }) const cache: any = { diff --git a/packages/next/src/export/index.ts b/packages/next/src/export/index.ts index f428fcfcf8ec6..b52db8cd6d3dd 100644 --- a/packages/next/src/export/index.ts +++ b/packages/next/src/export/index.ts @@ -420,6 +420,7 @@ export async function exportAppImpl( deploymentId: nextConfig.deploymentId, experimental: { isAppPPREnabled: checkIsAppPPREnabled(nextConfig.experimental.ppr), + clientTraceMetadata: nextConfig.experimental.clientTraceMetadata, swrDelta: nextConfig.experimental.swrDelta, }, } diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx index 5d9b7c7d2e0d2..23facb23c4902 100644 --- a/packages/next/src/server/app-render/app-render.tsx +++ b/packages/next/src/server/app-render/app-render.tsx @@ -76,7 +76,10 @@ import { appendMutableCookies } from '../web/spec-extension/adapters/request-coo import { createServerInsertedHTML } from './server-inserted-html' import { getRequiredScripts } from './required-scripts' import { addPathPrefix } from '../../shared/lib/router/utils/add-path-prefix' -import { makeGetServerInsertedHTML } from './make-get-server-inserted-html' +import { + getTracedMetadata, + makeGetServerInsertedHTML, +} from './make-get-server-inserted-html' import { walkTreeWithFlightRouterState } from './walk-tree-with-flight-router-state' import { createComponentTree } from './create-component-tree' import { getAssetQueryString } from './get-asset-query-string' @@ -912,6 +915,11 @@ async function renderToHTMLOrFlightImpl( tree, formState, }: RenderToStreamOptions): Promise => { + const tracingMetadata = getTracedMetadata( + getTracer().getTracePropagationData(), + renderOpts.experimental.clientTraceMetadata + ) + const polyfills: JSX.IntrinsicElements['script'][] = buildManifest.polyfillFiles .filter( @@ -995,6 +1003,7 @@ async function renderToHTMLOrFlightImpl( renderServerInsertedHTML, serverCapturedErrors: allCapturedErrors, basePath: renderOpts.basePath, + tracingMetadata: tracingMetadata, }) const renderer = createStaticRenderer({ @@ -1319,6 +1328,7 @@ async function renderToHTMLOrFlightImpl( renderServerInsertedHTML, serverCapturedErrors: [], basePath: renderOpts.basePath, + tracingMetadata: tracingMetadata, }), serverInsertedHTMLToHead: true, validateRootLayout, diff --git a/packages/next/src/server/app-render/make-get-server-inserted-html.tsx b/packages/next/src/server/app-render/make-get-server-inserted-html.tsx index 976733f19644f..d3fb3b9f29826 100644 --- a/packages/next/src/server/app-render/make-get-server-inserted-html.tsx +++ b/packages/next/src/server/app-render/make-get-server-inserted-html.tsx @@ -9,15 +9,26 @@ import { renderToReadableStream } from 'react-dom/server.edge' import { streamToString } from '../stream-utils/node-web-streams-helper' import { RedirectStatusCode } from '../../client/components/redirect-status-code' import { addPathPrefix } from '../../shared/lib/router/utils/add-path-prefix' +import type { ClientTraceDataEntry } from '../lib/trace/tracer' + +export function getTracedMetadata( + traceData: ClientTraceDataEntry[], + clientTraceMetadata: string[] | undefined +): ClientTraceDataEntry[] | undefined { + if (!clientTraceMetadata) return undefined + return traceData.filter(({ key }) => clientTraceMetadata.includes(key)) +} export function makeGetServerInsertedHTML({ polyfills, renderServerInsertedHTML, serverCapturedErrors, + tracingMetadata, basePath, }: { polyfills: JSX.IntrinsicElements['script'][] renderServerInsertedHTML: () => React.ReactNode + tracingMetadata: ClientTraceDataEntry[] | undefined serverCapturedErrors: Error[] basePath: string }) { @@ -82,6 +93,17 @@ export function makeGetServerInsertedHTML({ }) } {serverInsertedHTML} + {tracingMetadata + ? tracingMetadata.map(({ key, value }) => { + return ( + + ) + }) + : null} {errorMetaTags} , { diff --git a/packages/next/src/server/app-render/types.ts b/packages/next/src/server/app-render/types.ts index 8cc63bdd82ace..7709994149763 100644 --- a/packages/next/src/server/app-render/types.ts +++ b/packages/next/src/server/app-render/types.ts @@ -170,6 +170,7 @@ export interface RenderOptsPartial { */ isRoutePPREnabled?: boolean swrDelta: SwrDelta | undefined + clientTraceMetadata: string[] | undefined } postponed?: string /** diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts index e2b7b4c6d0b0a..3f66e33d2ea52 100644 --- a/packages/next/src/server/base-server.ts +++ b/packages/next/src/server/base-server.ts @@ -556,6 +556,7 @@ export default abstract class Server< experimental: { isAppPPREnabled, swrDelta: this.nextConfig.experimental.swrDelta, + clientTraceMetadata: this.nextConfig.experimental.clientTraceMetadata, }, } diff --git a/packages/next/src/server/config-schema.ts b/packages/next/src/server/config-schema.ts index 5a86133987986..4c00c7cce8386 100644 --- a/packages/next/src/server/config-schema.ts +++ b/packages/next/src/server/config-schema.ts @@ -390,6 +390,7 @@ export const configSchema: zod.ZodType = z.lazy(() => optimizePackageImports: z.array(z.string()).optional(), optimizeServerReact: z.boolean().optional(), instrumentationHook: z.boolean().optional(), + clientTraceMetadata: z.array(z.string()).optional(), turbotrace: z .object({ logLevel: z diff --git a/packages/next/src/server/config-shared.ts b/packages/next/src/server/config-shared.ts index 2f46d3d53939b..555444d3bac7b 100644 --- a/packages/next/src/server/config-shared.ts +++ b/packages/next/src/server/config-shared.ts @@ -365,6 +365,11 @@ export interface ExperimentalConfig { */ instrumentationHook?: boolean + /** + * The array of the meta tags to the client injected by tracing propagation data. + */ + clientTraceMetadata?: string[] + /** * Using this feature will enable the `react@experimental` for the `app` directory. */ @@ -919,6 +924,7 @@ export const defaultConfig: NextConfig = { turbotrace: undefined, typedRoutes: false, instrumentationHook: false, + clientTraceMetadata: undefined, parallelServerCompiles: false, parallelServerBuildTraces: false, ppr: diff --git a/packages/next/src/server/lib/trace/tracer.ts b/packages/next/src/server/lib/trace/tracer.ts index 80223dc54e953..1636c43758885 100644 --- a/packages/next/src/server/lib/trace/tracer.ts +++ b/packages/next/src/server/lib/trace/tracer.ts @@ -1,4 +1,5 @@ import type { FetchEventResult } from '../../web/types' +import type { TextMapSetter } from '@opentelemetry/api' import type { SpanTypes } from './constants' import { LogSpanAllowList, NextVanillaSpanAllowlist } from './constants' @@ -149,6 +150,12 @@ interface NextTracer { * Returns undefined otherwise. */ getActiveScopeSpan(): Span | undefined + + /** + * Returns trace propagation data for the currently active context. The format is equal to data provided + * through the OpenTelemetry propagator API. + */ + getTracePropagationData(): ClientTraceDataEntry[] } type NextAttributeNames = @@ -171,6 +178,20 @@ const rootSpanIdKey = api.createContextKey('next.rootSpanId') let lastSpanId = 0 const getSpanId = () => lastSpanId++ +export interface ClientTraceDataEntry { + key: string + value: string +} + +const clientTraceDataSetter: TextMapSetter = { + set(carrier, key, value) { + carrier.push({ + key, + value, + }) + }, +} + class NextTracerImpl implements NextTracer { /** * Returns an instance to the trace with configured name. @@ -185,6 +206,13 @@ class NextTracerImpl implements NextTracer { return context } + public getTracePropagationData(): ClientTraceDataEntry[] { + const activeContext = context.active() + const entries: ClientTraceDataEntry[] = [] + propagation.inject(activeContext, entries, clientTraceDataSetter) + return entries + } + public getActiveScopeSpan(): Span | undefined { return trace.getSpan(context?.active()) } diff --git a/test/e2e/opentelemetry/client-trace-metadata/.gitignore b/test/e2e/opentelemetry/client-trace-metadata/.gitignore new file mode 100644 index 0000000000000..0181c386fa8d6 --- /dev/null +++ b/test/e2e/opentelemetry/client-trace-metadata/.gitignore @@ -0,0 +1,2 @@ +# files generated by next.js +node_modules/ diff --git a/test/e2e/opentelemetry/client-trace-metadata/app/dynamic-page/page.tsx b/test/e2e/opentelemetry/client-trace-metadata/app/dynamic-page/page.tsx new file mode 100644 index 0000000000000..e1679f32f8fc0 --- /dev/null +++ b/test/e2e/opentelemetry/client-trace-metadata/app/dynamic-page/page.tsx @@ -0,0 +1,5 @@ +export const dynamic = 'force-dynamic' + +export default function DynamicPage() { + return

Dynamic Page

+} diff --git a/test/e2e/opentelemetry/client-trace-metadata/app/layout.tsx b/test/e2e/opentelemetry/client-trace-metadata/app/layout.tsx new file mode 100644 index 0000000000000..e7077399c03ce --- /dev/null +++ b/test/e2e/opentelemetry/client-trace-metadata/app/layout.tsx @@ -0,0 +1,7 @@ +export default function Root({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ) +} diff --git a/test/e2e/opentelemetry/client-trace-metadata/app/static-page-2/page.tsx b/test/e2e/opentelemetry/client-trace-metadata/app/static-page-2/page.tsx new file mode 100644 index 0000000000000..50cc7d891ee46 --- /dev/null +++ b/test/e2e/opentelemetry/client-trace-metadata/app/static-page-2/page.tsx @@ -0,0 +1,3 @@ +export default function StaticPage() { + return

Static Page 2

+} diff --git a/test/e2e/opentelemetry/client-trace-metadata/app/static-page/page.tsx b/test/e2e/opentelemetry/client-trace-metadata/app/static-page/page.tsx new file mode 100644 index 0000000000000..dd675c61e47fe --- /dev/null +++ b/test/e2e/opentelemetry/client-trace-metadata/app/static-page/page.tsx @@ -0,0 +1,15 @@ +import Link from 'next/link' + +export default function StaticPage() { + return ( + <> +

Static Page

+ + Go to dynamic page + + + Go to static page + + + ) +} diff --git a/test/e2e/opentelemetry/client-trace-metadata/client-trace-metadata.test.ts b/test/e2e/opentelemetry/client-trace-metadata/client-trace-metadata.test.ts new file mode 100644 index 0000000000000..0300ba638efc9 --- /dev/null +++ b/test/e2e/opentelemetry/client-trace-metadata/client-trace-metadata.test.ts @@ -0,0 +1,160 @@ +import { nextTestSetup } from 'e2e-utils' + +describe('clientTraceMetadata', () => { + const { next, isNextDev } = nextTestSetup({ + files: __dirname, + dependencies: require('./package.json').dependencies, + }) + + it('should inject propagation data for a dynamically server-side-rendered page', async () => { + const $ = await next.render$('/dynamic-page') + const headHtml = $.html('head') + expect(headHtml).toContain( + '' + ) + expect(headHtml).toContain( + '' + ) + expect(headHtml).toMatch( + // + ) + expect(headHtml).not.toContain('non-metadata-key-3') + }) + + it('hard loading a dynamic page twice should yield different dynamic trace data', async () => { + const browser1 = await next.browser('/dynamic-page') + const firstLoadSpanIdContent = await browser1 + .elementByCss('meta[name="my-parent-span-id"]') + .getAttribute('content') + + const browser2 = await next.browser('/dynamic-page') + const secondLoadSpanIdContent = await browser2 + .elementByCss('meta[name="my-parent-span-id"]') + .getAttribute('content') + + expect(firstLoadSpanIdContent).toMatch(/[a-f0-9]{16}/) + expect(secondLoadSpanIdContent).toMatch(/[a-f0-9]{16}/) + expect(firstLoadSpanIdContent).not.toBe(secondLoadSpanIdContent) + }) + + if (isNextDev) { + describe('next dev only', () => { + it('should inject propagation data for a statically server-side-rendered page', async () => { + const $ = await next.render$('/static-page') + const headHtml = $.html('head') + expect(headHtml).toContain( + '' + ) + expect(headHtml).toContain( + '' + ) + expect(headHtml).toMatch( + // + ) + expect(headHtml).not.toContain('non-metadata-key-3') + }) + + it('soft navigating to a dynamic page should not transform previous propagation data', async () => { + const browser = await next.browser('/static-page') + + const initialSpanIdTagContent = await browser + .elementByCss('meta[name="my-parent-span-id"]') + .getAttribute('content') + + // We are in dev mode so the static page should contain propagation data + expect(initialSpanIdTagContent).toMatch(/[a-f0-9]{16}/) + + await browser.elementByCss('#go-to-dynamic-page').click() + await browser.elementByCss('#dynamic-page-header') + + const updatedSpanIdTagContent = await browser + .elementByCss('meta[name="my-parent-span-id"]') + .getAttribute('content') + + expect(initialSpanIdTagContent).toBe(updatedSpanIdTagContent) + }) + + it('soft navigating to a static page should not transform previous propagation data', async () => { + const browser = await next.browser('/static-page') + + const initialSpanIdTagContent = await browser + .elementByCss('meta[name="my-parent-span-id"]') + .getAttribute('content') + + // We are in dev mode so the static page should contain propagation data + expect(initialSpanIdTagContent).toMatch(/[a-f0-9]{16}/) + + await browser.elementByCss('#go-to-static-page').click() + await browser.elementByCss('#static-page-2-header') + + const updatedSpanIdTagContent = await browser + .elementByCss('meta[name="my-parent-span-id"]') + .getAttribute('content') + + expect(initialSpanIdTagContent).toBe(updatedSpanIdTagContent) + }) + }) + } else { + describe('next start only', () => { + it('should inject propagation data for a statically server-side-rendered page', async () => { + const $ = await next.render$('/static-page') + const headHtml = $.html('head') + expect(headHtml).not.toContain( + '' + ) + expect($.html('head')).not.toContain( + '' + ) + expect($.html('head')).not.toMatch( + // + ) + }) + + it('soft navigating to a dynamic page should not transform previous propagation data', async () => { + const browser = await next.browser('/static-page') + + await browser.elementByCss('#static-page-header') + + const initialSpanIdTag = await browser.eval( + 'document.querySelector(\'meta[name="my-parent-span-id"]\')' + ) + + // We are in prod mode so we are not expecting propagation data to be present for a static page + expect(initialSpanIdTag).toBeNull() + + await browser.elementByCss('#go-to-dynamic-page').click() + await browser.elementByCss('#dynamic-page-header') + + const updatedSpanIdTag = await browser.eval( + 'document.querySelector(\'meta[name="my-parent-span-id"]\')' + ) + + // After the navigation to the dynamic page, there should still be no meta tag with propagation data + expect(updatedSpanIdTag).toBeNull() + }) + + it('soft navigating to a static page should not transform previous propagation data', async () => { + const browser = await next.browser('/static-page') + + await browser.elementByCss('#static-page-header') + + const initialSpanIdTag = await browser.eval( + 'document.querySelector(\'meta[name="my-parent-span-id"]\')' + ) + + // We are in prod mode so we are not expecting propagation data to be present for a static page + expect(initialSpanIdTag).toBeNull() + + await browser.elementByCss('#go-to-static-page').click() + await browser.elementByCss('#static-page-2-header') + + const updatedSpanIdTag = await browser.eval( + 'document.querySelector(\'meta[name="my-parent-span-id"]\')' + ) + + // After the navigation to the dynamic page, there should still be no meta tag with propagation data + expect(updatedSpanIdTag).toBeNull() + }) + }) + } +}) diff --git a/test/e2e/opentelemetry/client-trace-metadata/instrumentation.ts b/test/e2e/opentelemetry/client-trace-metadata/instrumentation.ts new file mode 100644 index 0000000000000..5a3b001be9676 --- /dev/null +++ b/test/e2e/opentelemetry/client-trace-metadata/instrumentation.ts @@ -0,0 +1,35 @@ +import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node' +import { trace } from '@opentelemetry/api' + +export async function register() { + if (process.env.NEXT_RUNTIME === 'nodejs') { + const provider = new NodeTracerProvider() + provider.register({ + propagator: { + inject(context, carrier, setter) { + setter.set(carrier, 'my-test-key-1', 'my-test-value-1') + setter.set(carrier, 'my-test-key-2', 'my-test-value-2') + // This non-metadata-key-3 is not going to be injected into the page + setter.set(carrier, 'non-metadata-key-3', 'non-metadata-key-3') + setter.set( + carrier, + 'my-parent-span-id', + trace.getSpanContext(context).spanId + ) + }, + extract(context) { + // This is a noop because we don't extract in this test + return context + }, + fields() { + return [ + 'my-parent-span-id', + 'my-test-key-1', + 'my-test-key-2', + 'non-metadata-key-3', + ] + }, + }, + }) + } +} diff --git a/test/e2e/opentelemetry/client-trace-metadata/next.config.js b/test/e2e/opentelemetry/client-trace-metadata/next.config.js new file mode 100644 index 0000000000000..529a5ed29d0cf --- /dev/null +++ b/test/e2e/opentelemetry/client-trace-metadata/next.config.js @@ -0,0 +1,10 @@ +module.exports = { + experimental: { + instrumentationHook: true, + clientTraceMetadata: [ + 'my-parent-span-id', + 'my-test-key-1', + 'my-test-key-2', + ], + }, +} diff --git a/test/e2e/opentelemetry/client-trace-metadata/package.json b/test/e2e/opentelemetry/client-trace-metadata/package.json new file mode 100644 index 0000000000000..59db9002b5857 --- /dev/null +++ b/test/e2e/opentelemetry/client-trace-metadata/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "@opentelemetry/api": "^1.8.0", + "@opentelemetry/sdk-trace-node": "^1.21.0" + } +} diff --git a/test/e2e/opentelemetry/.gitignore b/test/e2e/opentelemetry/instrumentation/.gitignore similarity index 100% rename from test/e2e/opentelemetry/.gitignore rename to test/e2e/opentelemetry/instrumentation/.gitignore diff --git a/test/e2e/opentelemetry/app/api/app/[param]/data/edge/route.ts b/test/e2e/opentelemetry/instrumentation/app/api/app/[param]/data/edge/route.ts similarity index 100% rename from test/e2e/opentelemetry/app/api/app/[param]/data/edge/route.ts rename to test/e2e/opentelemetry/instrumentation/app/api/app/[param]/data/edge/route.ts diff --git a/test/e2e/opentelemetry/app/api/app/[param]/data/route.ts b/test/e2e/opentelemetry/instrumentation/app/api/app/[param]/data/route.ts similarity index 100% rename from test/e2e/opentelemetry/app/api/app/[param]/data/route.ts rename to test/e2e/opentelemetry/instrumentation/app/api/app/[param]/data/route.ts diff --git a/test/e2e/opentelemetry/app/app/[param]/layout.tsx b/test/e2e/opentelemetry/instrumentation/app/app/[param]/layout.tsx similarity index 100% rename from test/e2e/opentelemetry/app/app/[param]/layout.tsx rename to test/e2e/opentelemetry/instrumentation/app/app/[param]/layout.tsx diff --git a/test/e2e/opentelemetry/app/app/[param]/loading/loading.tsx b/test/e2e/opentelemetry/instrumentation/app/app/[param]/loading/loading.tsx similarity index 100% rename from test/e2e/opentelemetry/app/app/[param]/loading/loading.tsx rename to test/e2e/opentelemetry/instrumentation/app/app/[param]/loading/loading.tsx diff --git a/test/e2e/opentelemetry/app/app/[param]/loading/page1/page.tsx b/test/e2e/opentelemetry/instrumentation/app/app/[param]/loading/page1/page.tsx similarity index 100% rename from test/e2e/opentelemetry/app/app/[param]/loading/page1/page.tsx rename to test/e2e/opentelemetry/instrumentation/app/app/[param]/loading/page1/page.tsx diff --git a/test/e2e/opentelemetry/app/app/[param]/loading/page2/page.tsx b/test/e2e/opentelemetry/instrumentation/app/app/[param]/loading/page2/page.tsx similarity index 100% rename from test/e2e/opentelemetry/app/app/[param]/loading/page2/page.tsx rename to test/e2e/opentelemetry/instrumentation/app/app/[param]/loading/page2/page.tsx diff --git a/test/e2e/opentelemetry/app/app/[param]/rsc-fetch/edge/page.tsx b/test/e2e/opentelemetry/instrumentation/app/app/[param]/rsc-fetch/edge/page.tsx similarity index 100% rename from test/e2e/opentelemetry/app/app/[param]/rsc-fetch/edge/page.tsx rename to test/e2e/opentelemetry/instrumentation/app/app/[param]/rsc-fetch/edge/page.tsx diff --git a/test/e2e/opentelemetry/app/app/[param]/rsc-fetch/page.tsx b/test/e2e/opentelemetry/instrumentation/app/app/[param]/rsc-fetch/page.tsx similarity index 100% rename from test/e2e/opentelemetry/app/app/[param]/rsc-fetch/page.tsx rename to test/e2e/opentelemetry/instrumentation/app/app/[param]/rsc-fetch/page.tsx diff --git a/test/e2e/opentelemetry/app/behind-middleware/layout.tsx b/test/e2e/opentelemetry/instrumentation/app/behind-middleware/layout.tsx similarity index 100% rename from test/e2e/opentelemetry/app/behind-middleware/layout.tsx rename to test/e2e/opentelemetry/instrumentation/app/behind-middleware/layout.tsx diff --git a/test/e2e/opentelemetry/app/behind-middleware/page.tsx b/test/e2e/opentelemetry/instrumentation/app/behind-middleware/page.tsx similarity index 100% rename from test/e2e/opentelemetry/app/behind-middleware/page.tsx rename to test/e2e/opentelemetry/instrumentation/app/behind-middleware/page.tsx diff --git a/test/e2e/opentelemetry/collector.ts b/test/e2e/opentelemetry/instrumentation/collector.ts similarity index 100% rename from test/e2e/opentelemetry/collector.ts rename to test/e2e/opentelemetry/instrumentation/collector.ts diff --git a/test/e2e/opentelemetry/constants.ts b/test/e2e/opentelemetry/instrumentation/constants.ts similarity index 100% rename from test/e2e/opentelemetry/constants.ts rename to test/e2e/opentelemetry/instrumentation/constants.ts diff --git a/test/e2e/opentelemetry/instrumentation-minimal.ts b/test/e2e/opentelemetry/instrumentation/instrumentation-minimal.ts similarity index 100% rename from test/e2e/opentelemetry/instrumentation-minimal.ts rename to test/e2e/opentelemetry/instrumentation/instrumentation-minimal.ts diff --git a/test/e2e/opentelemetry/instrumentation-node.ts b/test/e2e/opentelemetry/instrumentation/instrumentation-node.ts similarity index 100% rename from test/e2e/opentelemetry/instrumentation-node.ts rename to test/e2e/opentelemetry/instrumentation/instrumentation-node.ts diff --git a/test/e2e/opentelemetry/instrumentation-pages-app-only.test.ts b/test/e2e/opentelemetry/instrumentation/instrumentation-pages-app-only.test.ts similarity index 100% rename from test/e2e/opentelemetry/instrumentation-pages-app-only.test.ts rename to test/e2e/opentelemetry/instrumentation/instrumentation-pages-app-only.test.ts diff --git a/test/e2e/opentelemetry/instrumentation-polyfill.ts b/test/e2e/opentelemetry/instrumentation/instrumentation-polyfill.ts similarity index 100% rename from test/e2e/opentelemetry/instrumentation-polyfill.ts rename to test/e2e/opentelemetry/instrumentation/instrumentation-polyfill.ts diff --git a/test/e2e/opentelemetry/instrumentation-test.ts b/test/e2e/opentelemetry/instrumentation/instrumentation-test.ts similarity index 100% rename from test/e2e/opentelemetry/instrumentation-test.ts rename to test/e2e/opentelemetry/instrumentation/instrumentation-test.ts diff --git a/test/e2e/opentelemetry/instrumentation.ts b/test/e2e/opentelemetry/instrumentation/instrumentation.ts similarity index 100% rename from test/e2e/opentelemetry/instrumentation.ts rename to test/e2e/opentelemetry/instrumentation/instrumentation.ts diff --git a/test/e2e/opentelemetry/middleware.ts b/test/e2e/opentelemetry/instrumentation/middleware.ts similarity index 100% rename from test/e2e/opentelemetry/middleware.ts rename to test/e2e/opentelemetry/instrumentation/middleware.ts diff --git a/test/e2e/opentelemetry/next.config.js b/test/e2e/opentelemetry/instrumentation/next.config.js similarity index 100% rename from test/e2e/opentelemetry/next.config.js rename to test/e2e/opentelemetry/instrumentation/next.config.js diff --git a/test/e2e/opentelemetry/opentelemetry.test.ts b/test/e2e/opentelemetry/instrumentation/opentelemetry.test.ts similarity index 100% rename from test/e2e/opentelemetry/opentelemetry.test.ts rename to test/e2e/opentelemetry/instrumentation/opentelemetry.test.ts diff --git a/test/e2e/opentelemetry/package.json b/test/e2e/opentelemetry/instrumentation/package.json similarity index 100% rename from test/e2e/opentelemetry/package.json rename to test/e2e/opentelemetry/instrumentation/package.json diff --git a/test/e2e/opentelemetry/pages/api/pages/[param]/basic.ts b/test/e2e/opentelemetry/instrumentation/pages/api/pages/[param]/basic.ts similarity index 100% rename from test/e2e/opentelemetry/pages/api/pages/[param]/basic.ts rename to test/e2e/opentelemetry/instrumentation/pages/api/pages/[param]/basic.ts diff --git a/test/e2e/opentelemetry/pages/api/pages/[param]/edge.ts b/test/e2e/opentelemetry/instrumentation/pages/api/pages/[param]/edge.ts similarity index 100% rename from test/e2e/opentelemetry/pages/api/pages/[param]/edge.ts rename to test/e2e/opentelemetry/instrumentation/pages/api/pages/[param]/edge.ts diff --git a/test/e2e/opentelemetry/pages/pages/[param]/edge/getServerSideProps.tsx b/test/e2e/opentelemetry/instrumentation/pages/pages/[param]/edge/getServerSideProps.tsx similarity index 100% rename from test/e2e/opentelemetry/pages/pages/[param]/edge/getServerSideProps.tsx rename to test/e2e/opentelemetry/instrumentation/pages/pages/[param]/edge/getServerSideProps.tsx diff --git a/test/e2e/opentelemetry/pages/pages/[param]/getServerSideProps.tsx b/test/e2e/opentelemetry/instrumentation/pages/pages/[param]/getServerSideProps.tsx similarity index 100% rename from test/e2e/opentelemetry/pages/pages/[param]/getServerSideProps.tsx rename to test/e2e/opentelemetry/instrumentation/pages/pages/[param]/getServerSideProps.tsx diff --git a/test/e2e/opentelemetry/pages/pages/[param]/getStaticProps.tsx b/test/e2e/opentelemetry/instrumentation/pages/pages/[param]/getStaticProps.tsx similarity index 100% rename from test/e2e/opentelemetry/pages/pages/[param]/getStaticProps.tsx rename to test/e2e/opentelemetry/instrumentation/pages/pages/[param]/getStaticProps.tsx diff --git a/test/e2e/opentelemetry/pages/pages/[param]/getStaticProps2.tsx b/test/e2e/opentelemetry/instrumentation/pages/pages/[param]/getStaticProps2.tsx similarity index 100% rename from test/e2e/opentelemetry/pages/pages/[param]/getStaticProps2.tsx rename to test/e2e/opentelemetry/instrumentation/pages/pages/[param]/getStaticProps2.tsx