From 4a5b4db17f1e6bc9fbe4217d8e07de6af829d388 Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Thu, 9 Oct 2025 17:55:45 -0400 Subject: [PATCH 01/11] fix: replace otel references with @netlify/otel references --- src/run/handlers/cache.cts | 29 ++++---- src/run/handlers/server.ts | 9 +-- src/run/handlers/tracer.cts | 96 +++++++++++++-------------- src/run/handlers/use-cache-handler.ts | 35 +++++----- src/run/headers.ts | 4 +- src/run/next.cts | 4 +- src/run/storage/storage.cts | 11 +-- 7 files changed, 98 insertions(+), 90 deletions(-) diff --git a/src/run/handlers/cache.cts b/src/run/handlers/cache.cts index 1baa0c3957..64f5042da2 100644 --- a/src/run/handlers/cache.cts +++ b/src/run/handlers/cache.cts @@ -5,7 +5,8 @@ import { Buffer } from 'node:buffer' import { join } from 'node:path' import { join as posixJoin } from 'node:path/posix' -import { type Span } from '@opentelemetry/api' +import { withActiveSpan } from '@netlify/otel' +import type { Span } from '@netlify/otel/opentelemetry' import type { PrerenderManifest } from 'next/dist/build/index.js' import { NEXT_CACHE_TAGS_HEADER } from 'next/dist/lib/constants.js' @@ -68,7 +69,7 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { private captureResponseCacheLastModified( cacheValue: NetlifyCacheHandlerValue, key: string, - getCacheKeySpan: Span, + getCacheKeySpan?: Span, ) { if (cacheValue.value?.kind === 'FETCH') { return @@ -256,17 +257,17 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { async get( ...args: Parameters ): ReturnType { - return this.tracer.withActiveSpan('get cache key', async (span) => { + return withActiveSpan(this.tracer, 'get cache key', async (span) => { const [key, context = {}] = args getLogger().debug(`[NetlifyCacheHandler.get]: ${key}`) - span.setAttributes({ key }) + span?.setAttributes({ key }) const blob = await this.cacheStore.get(key, 'blobStore.get') // if blob is null then we don't have a cache entry if (!blob) { - span.addEvent('Cache miss', { key }) + span?.addEvent('Cache miss', { key }) return null } @@ -275,7 +276,7 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { if (getRequestContext()?.isBackgroundRevalidation && typeof ttl === 'number' && ttl < 0) { // background revalidation request should allow data that is not yet stale, // but opt to discard STALE data, so that Next.js generate fresh response - span.addEvent('Discarding stale entry due to SWR background revalidation request', { + span?.addEvent('Discarding stale entry due to SWR background revalidation request', { key, ttl, }) @@ -297,7 +298,7 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { ) if (staleByTags) { - span.addEvent('Stale', { staleByTags, key, ttl }) + span?.addEvent('Stale', { staleByTags, key, ttl }) return null } @@ -311,7 +312,7 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { switch (blob.value?.kind) { case 'FETCH': - span.addEvent('FETCH', { + span?.addEvent('FETCH', { lastModified: blob.lastModified, revalidate: context.revalidate, ttl, @@ -323,7 +324,7 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { case 'ROUTE': case 'APP_ROUTE': { - span.addEvent(blob.value?.kind, { + span?.addEvent(blob.value?.kind, { lastModified: blob.lastModified, status: blob.value.status, revalidate: blob.value.revalidate, @@ -349,7 +350,7 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { requestContext.pageHandlerRevalidate = revalidate } - span.addEvent(blob.value?.kind, { lastModified: blob.lastModified, revalidate, ttl }) + span?.addEvent(blob.value?.kind, { lastModified: blob.lastModified, revalidate, ttl }) await this.injectEntryToPrerenderManifest(key, blob.value) @@ -366,7 +367,7 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { const { revalidate, rscData, segmentData, ...restOfPageValue } = blob.value - span.addEvent(blob.value?.kind, { lastModified: blob.lastModified, revalidate, ttl }) + span?.addEvent(blob.value?.kind, { lastModified: blob.lastModified, revalidate, ttl }) await this.injectEntryToPrerenderManifest(key, blob.value) @@ -387,7 +388,7 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { } } default: - span.recordException(new Error(`Unknown cache entry kind: ${blob.value?.kind}`)) + span?.recordException(new Error(`Unknown cache entry kind: ${blob.value?.kind}`)) } return null }) @@ -439,10 +440,10 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { } async set(...args: Parameters) { - return this.tracer.withActiveSpan('set cache key', async (span) => { + return this.tracer?.withActiveSpan('set cache key', async (span: Span) => { const [key, data, context] = args const lastModified = Date.now() - span.setAttributes({ key, lastModified }) + span?.setAttributes({ key, lastModified }) getLogger().debug(`[NetlifyCacheHandler.set]: ${key}`) diff --git a/src/run/handlers/server.ts b/src/run/handlers/server.ts index 6434a571d1..c38d0f3350 100644 --- a/src/run/handlers/server.ts +++ b/src/run/handlers/server.ts @@ -2,7 +2,8 @@ import type { OutgoingHttpHeaders } from 'http' import { ComputeJsOutgoingMessage, toComputeResponse, toReqRes } from '@fastly/http-compute-js' import type { Context } from '@netlify/functions' -import { Span } from '@opentelemetry/api' +import { withActiveSpan } from '@netlify/otel' +import type { Span } from '@netlify/otel/opentelemetry' import type { WorkerRequestHandler } from 'next/dist/server/lib/types.js' import { getRunConfig, setRunConfig } from '../config.js' @@ -67,7 +68,7 @@ export default async ( const tracer = getTracer() if (!nextHandler) { - await tracer.withActiveSpan('initialize next server', async () => { + await withActiveSpan(tracer, 'initialize next server', async () => { const { getMockedRequestHandler } = await nextImportPromise const url = new URL(request.url) @@ -80,7 +81,7 @@ export default async ( }) } - return await tracer.withActiveSpan('generate response', async (span) => { + return await withActiveSpan(tracer, 'generate response', async (span) => { const { req, res } = toReqRes(request) // Work around a bug in http-proxy in next@<14.0.2 @@ -104,7 +105,7 @@ export default async ( getLogger().withError(error).error('next handler error') console.error(error) resProxy.statusCode = 500 - span.setAttribute('http.status_code', 500) + span?.setAttribute('http.status_code', 500) resProxy.end('Internal Server Error') }) diff --git a/src/run/handlers/tracer.cts b/src/run/handlers/tracer.cts index 9147fce5d9..f11963feda 100644 --- a/src/run/handlers/tracer.cts +++ b/src/run/handlers/tracer.cts @@ -1,8 +1,7 @@ +import { getTracer as otelGetTracer, withActiveSpan as otelWithActiveSpan } from '@netlify/otel' // Here we need to actually import `trace` from @opentelemetry/api to add extra wrappers // other places should import `getTracer` from this module -// eslint-disable-next-line no-restricted-imports -import { type Span, trace, type Tracer } from '@opentelemetry/api' -import { type SugaredTracer, wrapTracer } from '@opentelemetry/api/experimental' +import { trace, type Span, type Tracer, type SugaredTracer } from '@netlify/otel/opentelemetry' import { getRequestContext, type RequestContext } from './request-context.cjs' @@ -36,57 +35,58 @@ function spanHook(span: Span): Span { return span } -// startSpan and startActiveSpan don't automatically handle span ending and error handling -// so this typing just tries to enforce not using those methods in our code -// we should be using withActiveSpan (and optionally withSpan) instead -export type RuntimeTracer = Omit - -let tracer: RuntimeTracer | undefined - -export function getTracer(): RuntimeTracer { - if (!tracer) { - const baseTracer = trace.getTracer('Next.js Runtime') - - // we add hooks to capture span start and end events to be able to add server-timings - // while preserving OTEL api - const startSpan = baseTracer.startSpan.bind(baseTracer) - baseTracer.startSpan = ( - ...args: Parameters - ): ReturnType => { - const span = startSpan(...args) - spanMeta.set(span, { start: performance.now(), name: args[0] }) - return spanHook(span) - } +// // startSpan and startActiveSpan don't automatically handle span ending and error handling +// // so this typing just tries to enforce not using those methods in our code +// // we should be using withActiveSpan (and optionally withSpan) instead +// export type RuntimeTracer = Omit - const startActiveSpan = baseTracer.startActiveSpan.bind(baseTracer) - - // @ts-expect-error Target signature provides too few arguments. Expected 4 or more, but got 2. - baseTracer.startActiveSpan = ( - ...args: Parameters - ): ReturnType => { - const [name, ...restOfArgs] = args - - const augmentedArgs = restOfArgs.map((arg) => { - // callback might be 2nd, 3rd or 4th argument depending on used signature - // only callback can be a function so target that and keep rest arguments as-is - if (typeof arg === 'function') { - return (span: Span) => { - spanMeta.set(span, { start: performance.now(), name: args[0] }) - spanHook(span) - return arg(span) - } - } +let tracer: SugaredTracer | undefined - return arg - }) as typeof restOfArgs +export function getTracer(): SugaredTracer | undefined { + if (tracer) return; - return startActiveSpan(name, ...augmentedArgs) - } + const baseTracer = otelGetTracer('Next.js Runtime') - // finally use SugaredTracer - tracer = wrapTracer(baseTracer) + if (!baseTracer) return; + + // we add hooks to capture span start and end events to be able to add server-timings + // while preserving OTEL api + const startSpan = baseTracer.startSpan.bind(baseTracer) + baseTracer.startSpan = ( + ...args: Parameters + ): ReturnType => { + const span = startSpan(...args) + spanMeta.set(span, { start: performance.now(), name: args[0] }) + return spanHook(span) } + const startActiveSpan = baseTracer.startActiveSpan.bind(baseTracer) + + // @ts-expect-error Target signature provides too few arguments. Expected 4 or more, but got 2. + baseTracer.startActiveSpan = ( + ...args: Parameters + ): ReturnType => { + const [name, ...restOfArgs] = args + + const augmentedArgs = restOfArgs.map((arg) => { + // callback might be 2nd, 3rd or 4th argument depending on used signature + // only callback can be a function so target that and keep rest arguments as-is + if (typeof arg === 'function') { + return (span: Span) => { + spanMeta.set(span, { start: performance.now(), name: args[0] }) + spanHook(span) + return arg(span) + } + } + + return arg + }) as typeof restOfArgs + + return startActiveSpan(name, ...augmentedArgs) + } + + tracer = baseTracer + return tracer } diff --git a/src/run/handlers/use-cache-handler.ts b/src/run/handlers/use-cache-handler.ts index b2f6f5f2b9..9d3b68d444 100644 --- a/src/run/handlers/use-cache-handler.ts +++ b/src/run/handlers/use-cache-handler.ts @@ -1,6 +1,7 @@ import { Buffer } from 'node:buffer' import { LRUCache } from 'lru-cache' +import { withActiveSpan } from '@netlify/otel' import type { CacheEntry, // only supporting latest variant (https://github.com/vercel/next.js/pull/76687) @@ -87,11 +88,12 @@ const tmpResolvePendingBeforeCreatingAPromise = () => {} export const NetlifyDefaultUseCacheHandler = { get(cacheKey: string): ReturnType { - return getTracer().withActiveSpan( + return withActiveSpan( + getTracer(), 'DefaultUseCacheHandler.get', async (span): ReturnType => { getLogger().withFields({ cacheKey }).debug(`[NetlifyDefaultUseCacheHandler] get`) - span.setAttributes({ + span?.setAttributes({ cacheKey, }) @@ -105,7 +107,7 @@ export const NetlifyDefaultUseCacheHandler = { getLogger() .withFields({ cacheKey, status: 'MISS' }) .debug(`[NetlifyDefaultUseCacheHandler] get result`) - span.setAttributes({ + span?.setAttributes({ cacheStatus: 'miss', }) return undefined @@ -120,7 +122,7 @@ export const NetlifyDefaultUseCacheHandler = { getLogger() .withFields({ cacheKey, ttl, status: 'STALE' }) .debug(`[NetlifyDefaultUseCacheHandler] get result`) - span.setAttributes({ + span?.setAttributes({ cacheStatus: 'expired, discarded', ttl, }) @@ -132,7 +134,7 @@ export const NetlifyDefaultUseCacheHandler = { .withFields({ cacheKey, ttl, status: 'STALE BY TAG' }) .debug(`[NetlifyDefaultUseCacheHandler] get result`) - span.setAttributes({ + span?.setAttributes({ cacheStatus: 'stale tag, discarded', ttl, }) @@ -148,7 +150,7 @@ export const NetlifyDefaultUseCacheHandler = { getLogger() .withFields({ cacheKey, ttl, status: 'HIT' }) .debug(`[NetlifyDefaultUseCacheHandler] get result`) - span.setAttributes({ + span?.setAttributes({ cacheStatus: 'hit', ttl, }) @@ -161,11 +163,12 @@ export const NetlifyDefaultUseCacheHandler = { ) }, set(cacheKey: string, pendingEntry: Promise): ReturnType { - return getTracer().withActiveSpan( + return withActiveSpan( + getTracer(), 'DefaultUseCacheHandler.set', async (span): ReturnType => { getLogger().withFields({ cacheKey }).debug(`[NetlifyDefaultUseCacheHandler]: set`) - span.setAttributes({ + span?.setAttributes({ cacheKey, }) @@ -180,7 +183,7 @@ export const NetlifyDefaultUseCacheHandler = { const entry = await pendingEntry - span.setAttributes({ + span?.setAttributes({ cacheKey, }) @@ -194,7 +197,7 @@ export const NetlifyDefaultUseCacheHandler = { size += Buffer.from(chunk.value).byteLength } - span.setAttributes({ + span?.setAttributes({ tags: entry.tags, timestamp: entry.timestamp, revalidate: entry.revalidate, @@ -222,10 +225,11 @@ export const NetlifyDefaultUseCacheHandler = { // while blocking pipeline }, getExpiration: function (...tags: string[]): ReturnType { - return getTracer().withActiveSpan( + return withActiveSpan( + getTracer(), 'DefaultUseCacheHandler.getExpiration', async (span): ReturnType => { - span.setAttributes({ + span?.setAttributes({ tags, }) @@ -234,7 +238,7 @@ export const NetlifyDefaultUseCacheHandler = { getLogger() .withFields({ tags, expiration }) .debug(`[NetlifyDefaultUseCacheHandler] getExpiration`) - span.setAttributes({ + span?.setAttributes({ expiration, }) @@ -243,11 +247,12 @@ export const NetlifyDefaultUseCacheHandler = { ) }, expireTags(...tags: string[]): ReturnType { - return getTracer().withActiveSpan( + return withActiveSpan( + getTracer(), 'DefaultUseCacheHandler.expireTags', async (span): ReturnType => { getLogger().withFields({ tags }).debug(`[NetlifyDefaultUseCacheHandler] expireTags`) - span.setAttributes({ + span?.setAttributes({ tags, }) diff --git a/src/run/headers.ts b/src/run/headers.ts index a4b66b4734..0ed7adbc6e 100644 --- a/src/run/headers.ts +++ b/src/run/headers.ts @@ -1,4 +1,4 @@ -import type { Span } from '@opentelemetry/api' +import type { Span } from '@netlify/otel/opentelemetry' import type { NextConfigComplete } from 'next/dist/server/config-shared.js' import type { NetlifyCachedRouteValue, NetlifyCacheHandlerValue } from '../shared/cache-types.cjs' @@ -152,7 +152,7 @@ export const adjustDateHeader = async ({ }: { headers: Headers request: Request - span: Span + span?: Span requestContext: RequestContext }) => { const key = new URL(request.url).pathname diff --git a/src/run/next.cts b/src/run/next.cts index 779b995e2b..f49afe3da1 100644 --- a/src/run/next.cts +++ b/src/run/next.cts @@ -2,6 +2,7 @@ import { AsyncLocalStorage } from 'node:async_hooks' import fs from 'node:fs/promises' import { relative, resolve } from 'node:path' +import { withActiveSpan } from '@netlify/otel' // @ts-expect-error no types installed import { patchFs } from 'fs-monkey' @@ -91,8 +92,7 @@ export async function getMockedRequestHandler( */ const initAsyncLocalStorage = new AsyncLocalStorage() - const tracer = getTracer() - return tracer.withActiveSpan('mocked request handler', async () => { + return withActiveSpan(getTracer(), 'mocked request handler', async () => { const ofs = { ...fs } async function readFileFallbackBlobStore(...fsargs: Parameters) { diff --git a/src/run/storage/storage.cts b/src/run/storage/storage.cts index 273c8822de..09ac7c2e55 100644 --- a/src/run/storage/storage.cts +++ b/src/run/storage/storage.cts @@ -2,6 +2,7 @@ // Remaining modules in storage directory are implementation details // and should not be used directly outside of this directory. // There is eslint `no-restricted-imports` rule to enforce this. +import { withActiveSpan } from '@netlify/otel' import { type BlobType } from '../../shared/blob-types.cjs' import { getTracer } from '../handlers/tracer.cjs' @@ -30,11 +31,11 @@ export const getMemoizedKeyValueStoreBackedByRegionalBlobStore = ( } const blobKey = await encodeBlobKey(key) - const getPromise = tracer.withActiveSpan(otelSpanTitle, async (span) => { - span.setAttributes({ key, blobKey }) + const getPromise = withActiveSpan(tracer, otelSpanTitle, async (span) => { + span?.setAttributes({ key, blobKey }) const blob = (await store.get(blobKey, { type: 'json' })) as T | null inMemoryCache.set(key, blob) - span.addEvent(blob ? 'Hit' : 'Miss') + span?.addEvent(blob ? 'Hit' : 'Miss') return blob }) inMemoryCache.set(key, getPromise) @@ -46,8 +47,8 @@ export const getMemoizedKeyValueStoreBackedByRegionalBlobStore = ( inMemoryCache.set(key, value) const blobKey = await encodeBlobKey(key) - return tracer.withActiveSpan(otelSpanTitle, async (span) => { - span.setAttributes({ key, blobKey }) + return withActiveSpan(tracer, otelSpanTitle, async (span) => { + span?.setAttributes({ key, blobKey }) return await store.setJSON(blobKey, value) }) }, From 263804e593335532ef133bf26708749f96505244 Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Thu, 9 Oct 2025 17:56:51 -0400 Subject: [PATCH 02/11] fix: add otel library (temp) --- package-lock.json | 746 ++++++++++++++++++++++++++++++++++++++++------ package.json | 3 +- 2 files changed, 658 insertions(+), 91 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9e11cd5e8a..cdd5e89e2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@netlify/edge-functions-bootstrap": "^2.14.0", "@netlify/eslint-config-node": "^7.0.1", "@netlify/functions": "^4.2.7", + "@netlify/otel": "^4.1.0", "@netlify/serverless-functions-api": "^2.5.0", "@netlify/zip-it-and-ship-it": "^14.1.8", "@opentelemetry/api": "^1.8.0", @@ -4380,6 +4381,165 @@ "@opentelemetry/api": "~1.8.0" } }, + "node_modules/@netlify/otel": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@netlify/otel/-/otel-4.1.0.tgz", + "integrity": "sha512-FkGk3c9VrPdYtL9gyK7awFKmP3lQV7uTKIICyK0GnkQ1x/IxF3IoaX5wet8m817jkf13CkdPw6eALriQhV6+5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@opentelemetry/api": "1.9.0", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/otlp-transformer": "0.57.2", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-trace-node": "1.30.1" + }, + "engines": { + "node": "^18.14.0 || >=20.6.1" + } + }, + "node_modules/@netlify/otel/node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@netlify/otel/node_modules/@opentelemetry/api-logs": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.57.2.tgz", + "integrity": "sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@netlify/otel/node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@netlify/otel/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.57.2.tgz", + "integrity": "sha512-48IIRj49gbQVK52jYsw70+Jv+JbahT8BqT2Th7C4H7RCM9d0gZ5sgNPoMpWldmfjvIsSgiGJtjfk9MeZvjhoig==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.57.2", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-logs": "0.57.2", + "@opentelemetry/sdk-metrics": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@netlify/otel/node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@netlify/otel/node_modules/@opentelemetry/sdk-logs": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.57.2.tgz", + "integrity": "sha512-TXFHJ5c+BKggWbdEQ/inpgIzEmS2BGQowLE9UhsMd7YYlUfBQJ4uax0VF/B5NYigdM/75OoJGhAV3upEhK+3gg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.57.2", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@netlify/otel/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@netlify/otel/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@netlify/otel/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, "node_modules/@netlify/plugins-list": { "version": "6.80.0", "resolved": "https://registry.npmjs.org/@netlify/plugins-list/-/plugins-list-6.80.0.tgz", @@ -4774,6 +4934,19 @@ "node": ">=14" } }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, "node_modules/@opentelemetry/core": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.22.0.tgz", @@ -5058,6 +5231,50 @@ "node": ">=14" } }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", + "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.203.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation/node_modules/@opentelemetry/api-logs": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", + "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation/node_modules/import-in-the-middle": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.14.4.tgz", + "integrity": "sha512-eWjxh735SJLFJJDs5X82JQ2405OdJeAHDBnaoFCfdr5GVc7AWc9xU7KbrF+3Xd5F2ccP1aQFKtY+65X6EfKZ7A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "acorn": "^8.14.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, "node_modules/@opentelemetry/otlp-exporter-base": { "version": "0.49.1", "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.49.1.tgz", @@ -5165,6 +5382,90 @@ "node": ">=14" } }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.30.1.tgz", + "integrity": "sha512-oATwWWDIJzybAZ4pO76ATN5N6FFbOA1otibAVlS8v90B4S1wClnhRUk7K+2CHAwN1JKYuj4jh/lpCEG5BAqFuQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.30.1.tgz", + "integrity": "sha512-Pj/BfnYEKIOImirH76M4hDaBSx6HyZ2CXUqk+Kj02m6BB80c/yo4BdWkn/1gDFfU+YPY+bPR2U0DKBfdxCKwmg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, "node_modules/@opentelemetry/resources": { "version": "1.24.1", "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.24.1.tgz", @@ -5344,6 +5645,88 @@ "node": ">=14" } }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.30.1.tgz", + "integrity": "sha512-cBjYOINt1JxXdpw1e5MlHmFRc5fgj4GW/86vsKFxJCJ8AL4PdVtYH41gWwl4qd4uQjqEL1oJVrXkSy5cnduAnQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.30.1", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/propagator-b3": "1.30.1", + "@opentelemetry/propagator-jaeger": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, "node_modules/@opentelemetry/semantic-conventions": { "version": "1.24.1", "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.24.1.tgz", @@ -5384,41 +5767,31 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@protobufjs/codegen": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" @@ -5428,41 +5801,31 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@rollup/pluginutils": { "version": "5.1.4", @@ -7666,9 +8029,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/clean-package": { "version": "2.2.0", @@ -12808,9 +13169,7 @@ "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/loose-envify": { "version": "1.4.0", @@ -13429,9 +13788,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/moize": { "version": "6.1.6", @@ -29181,8 +29538,6 @@ "integrity": "sha512-RXyHaACeqXeqAKGLDl68rQKbmObRsTIn4TYVUUug1KfS47YWCo5MacGITEryugIgZqORCvJWEk4l449POg5Txg==", "dev": true, "hasInstallScript": true, - "optional": true, - "peer": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -29665,8 +30020,6 @@ "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.3.0.tgz", "integrity": "sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "debug": "^4.1.1", "module-details-from-path": "^1.0.3", @@ -29681,8 +30034,6 @@ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -35614,6 +35965,109 @@ "dev": true, "requires": {} }, + "@netlify/otel": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@netlify/otel/-/otel-4.1.0.tgz", + "integrity": "sha512-FkGk3c9VrPdYtL9gyK7awFKmP3lQV7uTKIICyK0GnkQ1x/IxF3IoaX5wet8m817jkf13CkdPw6eALriQhV6+5Q==", + "dev": true, + "requires": { + "@opentelemetry/api": "1.9.0", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/otlp-transformer": "0.57.2", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-trace-node": "1.30.1" + }, + "dependencies": { + "@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "dev": true + }, + "@opentelemetry/api-logs": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.57.2.tgz", + "integrity": "sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==", + "dev": true, + "requires": { + "@opentelemetry/api": "^1.3.0" + } + }, + "@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dev": true, + "requires": { + "@opentelemetry/semantic-conventions": "1.28.0" + } + }, + "@opentelemetry/otlp-transformer": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.57.2.tgz", + "integrity": "sha512-48IIRj49gbQVK52jYsw70+Jv+JbahT8BqT2Th7C4H7RCM9d0gZ5sgNPoMpWldmfjvIsSgiGJtjfk9MeZvjhoig==", + "dev": true, + "requires": { + "@opentelemetry/api-logs": "0.57.2", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-logs": "0.57.2", + "@opentelemetry/sdk-metrics": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1", + "protobufjs": "^7.3.0" + } + }, + "@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + } + }, + "@opentelemetry/sdk-logs": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.57.2.tgz", + "integrity": "sha512-TXFHJ5c+BKggWbdEQ/inpgIzEmS2BGQowLE9UhsMd7YYlUfBQJ4uax0VF/B5NYigdM/75OoJGhAV3upEhK+3gg==", + "dev": true, + "requires": { + "@opentelemetry/api-logs": "0.57.2", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + } + }, + "@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + } + }, + "@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "dev": true + } + } + }, "@netlify/plugins-list": { "version": "6.80.0", "resolved": "https://registry.npmjs.org/@netlify/plugins-list/-/plugins-list-6.80.0.tgz", @@ -35878,6 +36332,13 @@ "@opentelemetry/api": "^1.0.0" } }, + "@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "dev": true, + "requires": {} + }, "@opentelemetry/core": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.22.0.tgz", @@ -36090,6 +36551,40 @@ } } }, + "@opentelemetry/instrumentation": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", + "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", + "dev": true, + "requires": { + "@opentelemetry/api-logs": "0.203.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1" + }, + "dependencies": { + "@opentelemetry/api-logs": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", + "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", + "dev": true, + "requires": { + "@opentelemetry/api": "^1.3.0" + } + }, + "import-in-the-middle": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.14.4.tgz", + "integrity": "sha512-eWjxh735SJLFJJDs5X82JQ2405OdJeAHDBnaoFCfdr5GVc7AWc9xU7KbrF+3Xd5F2ccP1aQFKtY+65X6EfKZ7A==", + "dev": true, + "requires": { + "acorn": "^8.14.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + } + } + }, "@opentelemetry/otlp-exporter-base": { "version": "0.49.1", "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.49.1.tgz", @@ -36166,6 +36661,58 @@ } } }, + "@opentelemetry/propagator-b3": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.30.1.tgz", + "integrity": "sha512-oATwWWDIJzybAZ4pO76ATN5N6FFbOA1otibAVlS8v90B4S1wClnhRUk7K+2CHAwN1JKYuj4jh/lpCEG5BAqFuQ==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.30.1" + }, + "dependencies": { + "@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dev": true, + "requires": { + "@opentelemetry/semantic-conventions": "1.28.0" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "dev": true + } + } + }, + "@opentelemetry/propagator-jaeger": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.30.1.tgz", + "integrity": "sha512-Pj/BfnYEKIOImirH76M4hDaBSx6HyZ2CXUqk+Kj02m6BB80c/yo4BdWkn/1gDFfU+YPY+bPR2U0DKBfdxCKwmg==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.30.1" + }, + "dependencies": { + "@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dev": true, + "requires": { + "@opentelemetry/semantic-conventions": "1.28.0" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "dev": true + } + } + }, "@opentelemetry/resources": { "version": "1.24.1", "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.24.1.tgz", @@ -36295,6 +36842,58 @@ } } }, + "@opentelemetry/sdk-trace-node": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.30.1.tgz", + "integrity": "sha512-cBjYOINt1JxXdpw1e5MlHmFRc5fgj4GW/86vsKFxJCJ8AL4PdVtYH41gWwl4qd4uQjqEL1oJVrXkSy5cnduAnQ==", + "dev": true, + "requires": { + "@opentelemetry/context-async-hooks": "1.30.1", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/propagator-b3": "1.30.1", + "@opentelemetry/propagator-jaeger": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1", + "semver": "^7.5.2" + }, + "dependencies": { + "@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dev": true, + "requires": { + "@opentelemetry/semantic-conventions": "1.28.0" + } + }, + "@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + } + }, + "@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "dev": true + } + } + }, "@opentelemetry/semantic-conventions": { "version": "1.24.1", "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.24.1.tgz", @@ -36323,41 +36922,31 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "@protobufjs/codegen": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", "dev": true, - "optional": true, - "peer": true, "requires": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" @@ -36367,41 +36956,31 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "@rollup/pluginutils": { "version": "5.1.4", @@ -37255,8 +37834,7 @@ "dev": true }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "version": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { @@ -37927,9 +38505,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "clean-package": { "version": "2.2.0", @@ -41640,9 +42216,7 @@ "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "loose-envify": { "version": "1.4.0", @@ -42095,9 +42669,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "moize": { "version": "6.1.6", @@ -52932,8 +53504,6 @@ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.3.2.tgz", "integrity": "sha512-RXyHaACeqXeqAKGLDl68rQKbmObRsTIn4TYVUUug1KfS47YWCo5MacGITEryugIgZqORCvJWEk4l449POg5Txg==", "dev": true, - "optional": true, - "peer": true, "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -53294,8 +53864,6 @@ "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.3.0.tgz", "integrity": "sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==", "dev": true, - "optional": true, - "peer": true, "requires": { "debug": "^4.1.1", "module-details-from-path": "^1.0.3", @@ -53307,8 +53875,6 @@ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, - "optional": true, - "peer": true, "requires": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", diff --git a/package.json b/package.json index e6bcca3ca9..4731347f5b 100644 --- a/package.json +++ b/package.json @@ -57,10 +57,11 @@ "@netlify/build": "^35.1.7", "@netlify/config": "^24.0.4", "@netlify/edge-bundler": "^14.5.5", - "@netlify/edge-functions-bootstrap": "^2.14.0", "@netlify/edge-functions": "^2.17.1", + "@netlify/edge-functions-bootstrap": "^2.14.0", "@netlify/eslint-config-node": "^7.0.1", "@netlify/functions": "^4.2.7", + "@netlify/otel": "^4.1.0", "@netlify/serverless-functions-api": "^2.5.0", "@netlify/zip-it-and-ship-it": "^14.1.8", "@opentelemetry/api": "^1.8.0", From ebf1c3026cb09de7828e96e4ab7f70679770f35f Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Fri, 10 Oct 2025 10:12:36 -0400 Subject: [PATCH 03/11] fix: replace otel usage in build --- src/build/content/prerendered.ts | 7 +++---- src/build/content/server.ts | 9 ++++----- src/build/content/static.ts | 13 ++++++------- src/build/functions/server.ts | 11 +++++------ src/index.ts | 19 +++++++++---------- 5 files changed, 27 insertions(+), 32 deletions(-) diff --git a/src/build/content/prerendered.ts b/src/build/content/prerendered.ts index cfe6909597..914ad2b5f6 100644 --- a/src/build/content/prerendered.ts +++ b/src/build/content/prerendered.ts @@ -2,8 +2,7 @@ import { existsSync } from 'node:fs' import { mkdir, readFile, writeFile } from 'node:fs/promises' import { join } from 'node:path' -import { trace } from '@opentelemetry/api' -import { wrapTracer } from '@opentelemetry/api/experimental' +import { getTracer, withActiveSpan } from '@netlify/otel' import { glob } from 'fast-glob' import type { RouteMetadata } from 'next-with-cache-handler-v2/dist/export/routes/types.js' import pLimit from 'p-limit' @@ -21,7 +20,7 @@ import type { import type { PluginContext } from '../plugin-context.js' import { verifyNetlifyForms } from '../verification.js' -const tracer = wrapTracer(trace.getTracer('Next runtime')) +const tracer = getTracer('Next runtime') /** * Write a cache entry to the blob upload directory. @@ -158,7 +157,7 @@ const buildFetchCacheValue = async ( * Upload prerendered content to the blob store */ export const copyPrerenderedContent = async (ctx: PluginContext): Promise => { - return tracer.withActiveSpan('copyPrerenderedContent', async () => { + return withActiveSpan(tracer, 'copyPrerenderedContent', async () => { try { // ensure the blob directory exists await mkdir(ctx.blobDir, { recursive: true }) diff --git a/src/build/content/server.ts b/src/build/content/server.ts index ffa5dbbd4c..7f1b094437 100644 --- a/src/build/content/server.ts +++ b/src/build/content/server.ts @@ -13,8 +13,7 @@ import { createRequire } from 'node:module' import { dirname, join, resolve, sep } from 'node:path' import { join as posixJoin, sep as posixSep } from 'node:path/posix' -import { trace } from '@opentelemetry/api' -import { wrapTracer } from '@opentelemetry/api/experimental' +import { getTracer, withActiveSpan } from '@netlify/otel' import glob from 'fast-glob' import type { MiddlewareManifest } from 'next/dist/build/webpack/plugins/middleware-plugin.js' import type { FunctionsConfigManifest } from 'next-with-cache-handler-v2/dist/build/index.js' @@ -24,7 +23,7 @@ import type { RunConfig } from '../../run/config.js' import { RUN_CONFIG_FILE } from '../../run/constants.js' import type { PluginContext, RequiredServerFilesManifest } from '../plugin-context.js' -const tracer = wrapTracer(trace.getTracer('Next runtime')) +const tracer = getTracer('Next runtime') const toPosixPath = (path: string) => path.split(sep).join(posixSep) @@ -36,7 +35,7 @@ function isError(error: unknown): error is NodeJS.ErrnoException { * Copy App/Pages Router Javascript needed by the server handler */ export const copyNextServerCode = async (ctx: PluginContext): Promise => { - await tracer.withActiveSpan('copyNextServerCode', async () => { + await withActiveSpan(tracer, 'copyNextServerCode', async () => { // update the dist directory inside the required-server-files.json to work with // nx monorepos and other setups where the dist directory is modified const reqServerFilesPath = join( @@ -289,7 +288,7 @@ async function patchNextModules( } export const copyNextDependencies = async (ctx: PluginContext): Promise => { - await tracer.withActiveSpan('copyNextDependencies', async () => { + await withActiveSpan(tracer, 'copyNextDependencies', async () => { const entries = await readdir(ctx.standaloneDir) const filter = ctx.constants.IS_LOCAL ? undefined : nodeModulesFilter diff --git a/src/build/content/static.ts b/src/build/content/static.ts index 47dded47bb..2440271150 100644 --- a/src/build/content/static.ts +++ b/src/build/content/static.ts @@ -2,8 +2,7 @@ import { existsSync } from 'node:fs' import { cp, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises' import { basename, join } from 'node:path' -import { trace } from '@opentelemetry/api' -import { wrapTracer } from '@opentelemetry/api/experimental' +import { getTracer, withActiveSpan } from '@netlify/otel' import glob from 'fast-glob' import type { HtmlBlob } from '../../shared/blob-types.cjs' @@ -11,13 +10,13 @@ import { encodeBlobKey } from '../../shared/blobkey.js' import { PluginContext } from '../plugin-context.js' import { verifyNetlifyForms } from '../verification.js' -const tracer = wrapTracer(trace.getTracer('Next runtime')) +const tracer = getTracer('Next runtime') /** * Assemble the static content for being uploaded to the blob storage */ export const copyStaticContent = async (ctx: PluginContext): Promise => { - return tracer.withActiveSpan('copyStaticContent', async () => { + return withActiveSpan(tracer, 'copyStaticContent', async () => { const srcDir = join(ctx.publishDir, 'server/pages') const destDir = ctx.blobDir @@ -58,7 +57,7 @@ export const copyStaticContent = async (ctx: PluginContext): Promise => { * Copy static content to the static dir so it is uploaded to the CDN */ export const copyStaticAssets = async (ctx: PluginContext): Promise => { - return tracer.withActiveSpan('copyStaticAssets', async (span): Promise => { + return withActiveSpan(tracer, 'copyStaticAssets', async (span): Promise => { try { await rm(ctx.staticDir, { recursive: true, force: true }) const { basePath } = await ctx.getRoutesManifest() @@ -73,7 +72,7 @@ export const copyStaticAssets = async (ctx: PluginContext): Promise => { }) } } catch (error) { - span.end() + span?.end() ctx.failBuild('Failed copying static assets', error) } }) @@ -94,7 +93,7 @@ export const setHeadersConfig = async (ctx: PluginContext): Promise => { } export const copyStaticExport = async (ctx: PluginContext): Promise => { - await tracer.withActiveSpan('copyStaticExport', async () => { + await withActiveSpan(tracer, 'copyStaticExport', async () => { if (!ctx.exportDetail?.outDirectory) { ctx.failBuild('Export directory not found') } diff --git a/src/build/functions/server.ts b/src/build/functions/server.ts index bd38a82162..0a94e7b24c 100644 --- a/src/build/functions/server.ts +++ b/src/build/functions/server.ts @@ -2,8 +2,7 @@ import { cp, mkdir, readFile, rm, writeFile } from 'node:fs/promises' import { join, relative } from 'node:path' import { join as posixJoin } from 'node:path/posix' -import { trace } from '@opentelemetry/api' -import { wrapTracer } from '@opentelemetry/api/experimental' +import { getTracer, withActiveSpan } from '@netlify/otel' import { glob } from 'fast-glob' import { @@ -13,11 +12,11 @@ import { } from '../content/server.js' import { PluginContext, SERVER_HANDLER_NAME } from '../plugin-context.js' -const tracer = wrapTracer(trace.getTracer('Next runtime')) +const tracer = getTracer('Next runtime') /** Copies the runtime dist folder to the lambda */ const copyHandlerDependencies = async (ctx: PluginContext) => { - await tracer.withActiveSpan('copyHandlerDependencies', async (span) => { + await withActiveSpan(tracer, 'copyHandlerDependencies', async (span) => { const promises: Promise[] = [] // if the user specified some files to include in the lambda // we need to copy them to the functions-internal folder @@ -31,7 +30,7 @@ const copyHandlerDependencies = async (ctx: PluginContext) => { posixJoin(ctx.relativeAppDir, '.env.production.local'), ) - span.setAttribute('next.includedFiles', includedFiles.join(',')) + span?.setAttribute('next.includedFiles', includedFiles.join(',')) const resolvedFiles = await Promise.all( includedFiles.map((globPattern) => glob(globPattern, { cwd: process.cwd() })), @@ -137,7 +136,7 @@ export const clearStaleServerHandlers = async (ctx: PluginContext) => { * Create a Netlify function to run the Next.js server */ export const createServerHandler = async (ctx: PluginContext) => { - await tracer.withActiveSpan('createServerHandler', async () => { + await withActiveSpan(tracer, 'createServerHandler', async () => { await mkdir(join(ctx.serverHandlerRuntimeModulesDir), { recursive: true }) await copyNextServerCode(ctx) diff --git a/src/index.ts b/src/index.ts index 27d9c1ff7b..817d9d8825 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,7 @@ import { rm } from 'fs/promises' import type { NetlifyPluginOptions } from '@netlify/build' -import { trace } from '@opentelemetry/api' -import { wrapTracer } from '@opentelemetry/api/experimental' +import { getTracer, withActiveSpan } from '@netlify/otel' import { restoreBuildCache, saveBuildCache } from './build/cache.js' import { copyPrerenderedContent } from './build/content/prerendered.js' @@ -27,7 +26,7 @@ import { const skipPlugin = process.env.NETLIFY_NEXT_PLUGIN_SKIP === 'true' || process.env.NETLIFY_NEXT_PLUGIN_SKIP === '1' const skipText = 'Skipping Next.js plugin due to NETLIFY_NEXT_PLUGIN_SKIP environment variable.' -const tracer = wrapTracer(trace.getTracer('Next.js runtime')) +const tracer = getTracer('Next.js runtime') export const onPreDev = async (options: NetlifyPluginOptions) => { if (skipPlugin) { @@ -35,7 +34,7 @@ export const onPreDev = async (options: NetlifyPluginOptions) => { return } - await tracer.withActiveSpan('onPreDev', async () => { + await withActiveSpan(tracer, 'onPreDev', async () => { const context = new PluginContext(options) // Blob files left over from `ntl build` interfere with `ntl dev` when working with regional blobs @@ -49,7 +48,7 @@ export const onPreBuild = async (options: NetlifyPluginOptions) => { return } - await tracer.withActiveSpan('onPreBuild', async () => { + await withActiveSpan(tracer, 'onPreBuild', async () => { // Enable Next.js standalone mode at build time process.env.NEXT_PRIVATE_STANDALONE = 'true' const ctx = new PluginContext(options) @@ -71,12 +70,12 @@ export const onBuild = async (options: NetlifyPluginOptions) => { return } - await tracer.withActiveSpan('onBuild', async (span) => { + await withActiveSpan(tracer, 'onBuild', async (span) => { const ctx = new PluginContext(options) verifyPublishDir(ctx) - span.setAttribute('next.buildConfig', JSON.stringify(ctx.buildConfig)) + span?.setAttribute('next.buildConfig', JSON.stringify(ctx.buildConfig)) // only save the build cache if not run via the CLI if (!options.constants.IS_LOCAL) { @@ -109,7 +108,7 @@ export const onPostBuild = async (options: NetlifyPluginOptions) => { return } - await tracer.withActiveSpan('onPostBuild', async () => { + await withActiveSpan(tracer, 'onPostBuild', async () => { await publishStaticDir(new PluginContext(options)) }) } @@ -120,7 +119,7 @@ export const onSuccess = async () => { return } - await tracer.withActiveSpan('onSuccess', async () => { + await withActiveSpan(tracer, 'onSuccess', async () => { const prewarm = [process.env.DEPLOY_URL, process.env.DEPLOY_PRIME_URL, process.env.URL].filter( // If running locally then the deploy ID is a placeholder value. Filtering for `https://0--` removes it. (url?: string): url is string => Boolean(url && !url.startsWith('https://0--')), @@ -135,7 +134,7 @@ export const onEnd = async (options: NetlifyPluginOptions) => { return } - await tracer.withActiveSpan('onEnd', async () => { + await withActiveSpan(tracer, 'onEnd', async () => { await unpublishStaticDir(new PluginContext(options)) }) } From 8aa5b3eeae99d61d61a6e570d6bbda78c53430f4 Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Fri, 10 Oct 2025 10:13:15 -0400 Subject: [PATCH 04/11] fix: clean up RuntimeTracer and SugaredTracer references in tracer.cts --- src/run/handlers/tracer.cts | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/run/handlers/tracer.cts b/src/run/handlers/tracer.cts index f11963feda..62c43acb27 100644 --- a/src/run/handlers/tracer.cts +++ b/src/run/handlers/tracer.cts @@ -1,7 +1,8 @@ -import { getTracer as otelGetTracer, withActiveSpan as otelWithActiveSpan } from '@netlify/otel' +import { getTracer as otelGetTracer } from '@netlify/otel' // Here we need to actually import `trace` from @opentelemetry/api to add extra wrappers // other places should import `getTracer` from this module -import { trace, type Span, type Tracer, type SugaredTracer } from '@netlify/otel/opentelemetry' +import { trace } from '@netlify/otel/opentelemetry' +import type { Span } from '@netlify/otel/opentelemetry' import { getRequestContext, type RequestContext } from './request-context.cjs' @@ -35,14 +36,11 @@ function spanHook(span: Span): Span { return span } -// // startSpan and startActiveSpan don't automatically handle span ending and error handling -// // so this typing just tries to enforce not using those methods in our code -// // we should be using withActiveSpan (and optionally withSpan) instead -// export type RuntimeTracer = Omit +type NetlifyOtelTracer = NonNullable> -let tracer: SugaredTracer | undefined +let tracer: NetlifyOtelTracer | undefined -export function getTracer(): SugaredTracer | undefined { +export function getTracer(): NetlifyOtelTracer | undefined { if (tracer) return; const baseTracer = otelGetTracer('Next.js Runtime') @@ -53,8 +51,8 @@ export function getTracer(): SugaredTracer | undefined { // while preserving OTEL api const startSpan = baseTracer.startSpan.bind(baseTracer) baseTracer.startSpan = ( - ...args: Parameters - ): ReturnType => { + ...args: Parameters + ): ReturnType => { const span = startSpan(...args) spanMeta.set(span, { start: performance.now(), name: args[0] }) return spanHook(span) @@ -64,8 +62,8 @@ export function getTracer(): SugaredTracer | undefined { // @ts-expect-error Target signature provides too few arguments. Expected 4 or more, but got 2. baseTracer.startActiveSpan = ( - ...args: Parameters - ): ReturnType => { + ...args: Parameters + ): ReturnType => { const [name, ...restOfArgs] = args const augmentedArgs = restOfArgs.map((arg) => { From e8271d536af8981119eae1467842cac3229bdb9c Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Fri, 10 Oct 2025 10:58:48 -0400 Subject: [PATCH 05/11] fix: replace otel libraries with netlify/otel --- package-lock.json | 18 ++++++++---------- package.json | 3 +-- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index cdd5e89e2a..9d8352f65c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,10 +18,9 @@ "@netlify/edge-functions-bootstrap": "^2.14.0", "@netlify/eslint-config-node": "^7.0.1", "@netlify/functions": "^4.2.7", - "@netlify/otel": "^4.1.0", + "@netlify/otel": "^4.3.0", "@netlify/serverless-functions-api": "^2.5.0", "@netlify/zip-it-and-ship-it": "^14.1.8", - "@opentelemetry/api": "^1.8.0", "@playwright/test": "^1.43.1", "@types/node": "^20.12.7", "@types/picomatch": "^3.0.0", @@ -4382,9 +4381,9 @@ } }, "node_modules/@netlify/otel": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@netlify/otel/-/otel-4.1.0.tgz", - "integrity": "sha512-FkGk3c9VrPdYtL9gyK7awFKmP3lQV7uTKIICyK0GnkQ1x/IxF3IoaX5wet8m817jkf13CkdPw6eALriQhV6+5Q==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@netlify/otel/-/otel-4.3.0.tgz", + "integrity": "sha512-a0ABd3XFTUW4phYVQVBQ6hT+BTWyBblaFldGaTckKBJW0s+SEzKF9h22dq/CoVMfJ+zKgyKlU6ECXJmvehOSgg==", "dev": true, "license": "MIT", "dependencies": { @@ -35966,9 +35965,9 @@ "requires": {} }, "@netlify/otel": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@netlify/otel/-/otel-4.1.0.tgz", - "integrity": "sha512-FkGk3c9VrPdYtL9gyK7awFKmP3lQV7uTKIICyK0GnkQ1x/IxF3IoaX5wet8m817jkf13CkdPw6eALriQhV6+5Q==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@netlify/otel/-/otel-4.3.0.tgz", + "integrity": "sha512-a0ABd3XFTUW4phYVQVBQ6hT+BTWyBblaFldGaTckKBJW0s+SEzKF9h22dq/CoVMfJ+zKgyKlU6ECXJmvehOSgg==", "dev": true, "requires": { "@opentelemetry/api": "1.9.0", @@ -39266,8 +39265,7 @@ } }, "dot-prop": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz", + "version": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz", "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==", "dev": true, "requires": { diff --git a/package.json b/package.json index 4731347f5b..dd1dc69ce4 100644 --- a/package.json +++ b/package.json @@ -61,10 +61,9 @@ "@netlify/edge-functions-bootstrap": "^2.14.0", "@netlify/eslint-config-node": "^7.0.1", "@netlify/functions": "^4.2.7", - "@netlify/otel": "^4.1.0", + "@netlify/otel": "^4.3.0", "@netlify/serverless-functions-api": "^2.5.0", "@netlify/zip-it-and-ship-it": "^14.1.8", - "@opentelemetry/api": "^1.8.0", "@playwright/test": "^1.43.1", "@types/node": "^20.12.7", "@types/picomatch": "^3.0.0", From 18530ab7bf689b1c424b9f6df9cf27b0eceff111 Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Fri, 10 Oct 2025 11:07:13 -0400 Subject: [PATCH 06/11] chore: run prettier --- src/run/handlers/tracer.cts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/run/handlers/tracer.cts b/src/run/handlers/tracer.cts index 62c43acb27..2396940e4e 100644 --- a/src/run/handlers/tracer.cts +++ b/src/run/handlers/tracer.cts @@ -41,11 +41,11 @@ type NetlifyOtelTracer = NonNullable> let tracer: NetlifyOtelTracer | undefined export function getTracer(): NetlifyOtelTracer | undefined { - if (tracer) return; + if (tracer) return const baseTracer = otelGetTracer('Next.js Runtime') - if (!baseTracer) return; + if (!baseTracer) return // we add hooks to capture span start and end events to be able to add server-timings // while preserving OTEL api From dbe0696ffa0c1170483962dab2d9c69f9e424fd6 Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Fri, 10 Oct 2025 11:10:46 -0400 Subject: [PATCH 07/11] fix: method return values --- src/run/handlers/tracer.cts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/run/handlers/tracer.cts b/src/run/handlers/tracer.cts index 2396940e4e..8dc5e3da1c 100644 --- a/src/run/handlers/tracer.cts +++ b/src/run/handlers/tracer.cts @@ -41,11 +41,11 @@ type NetlifyOtelTracer = NonNullable> let tracer: NetlifyOtelTracer | undefined export function getTracer(): NetlifyOtelTracer | undefined { - if (tracer) return + if (tracer) return tracer const baseTracer = otelGetTracer('Next.js Runtime') - if (!baseTracer) return + if (!baseTracer) return undefined // we add hooks to capture span start and end events to be able to add server-timings // while preserving OTEL api From b1ffd00e836fec6bb4718279726c21aba2019c07 Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Fri, 10 Oct 2025 11:15:45 -0400 Subject: [PATCH 08/11] chore: import ordering --- src/run/handlers/use-cache-handler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/run/handlers/use-cache-handler.ts b/src/run/handlers/use-cache-handler.ts index 9d3b68d444..ac67899380 100644 --- a/src/run/handlers/use-cache-handler.ts +++ b/src/run/handlers/use-cache-handler.ts @@ -1,7 +1,7 @@ import { Buffer } from 'node:buffer' -import { LRUCache } from 'lru-cache' import { withActiveSpan } from '@netlify/otel' +import { LRUCache } from 'lru-cache' import type { CacheEntry, // only supporting latest variant (https://github.com/vercel/next.js/pull/76687) From 0467e12f5b2e457c1e6b4072fb68aef9c0c0800c Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Fri, 10 Oct 2025 11:38:37 -0400 Subject: [PATCH 09/11] fix: replace withActiveSpan usage in handlers --- src/build/templates/handler-monorepo.tmpl.js | 8 +++++--- src/build/templates/handler.tmpl.js | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/build/templates/handler-monorepo.tmpl.js b/src/build/templates/handler-monorepo.tmpl.js index 82bd13cbf3..b136bbfcb8 100644 --- a/src/build/templates/handler-monorepo.tmpl.js +++ b/src/build/templates/handler-monorepo.tmpl.js @@ -1,3 +1,5 @@ +import { withActiveSpan } from '@netlify/otel' + import { createRequestContext, runWithRequestContext, @@ -15,8 +17,8 @@ export default async function (req, context) { const tracer = getTracer() const handlerResponse = await runWithRequestContext(requestContext, () => { - return tracer.withActiveSpan('Next.js Server Handler', async (span) => { - span.setAttributes({ + return withActiveSpan(tracer, 'Next.js Server Handler', async (span) => { + span?.setAttributes({ 'account.id': context.account.id, 'deploy.id': context.deploy.id, 'request.id': context.requestId, @@ -32,7 +34,7 @@ export default async function (req, context) { cachedHandler = handler } const response = await cachedHandler(req, context, span, requestContext) - span.setAttributes({ + span?.setAttributes({ 'http.status_code': response.status, }) return response diff --git a/src/build/templates/handler.tmpl.js b/src/build/templates/handler.tmpl.js index ccdf332036..b2c9a86ac8 100644 --- a/src/build/templates/handler.tmpl.js +++ b/src/build/templates/handler.tmpl.js @@ -1,3 +1,5 @@ +import { withActiveSpan } from '@netlify/otel' + import { createRequestContext, runWithRequestContext, @@ -13,8 +15,8 @@ export default async function handler(req, context) { const tracer = getTracer() const handlerResponse = await runWithRequestContext(requestContext, () => { - return tracer.withActiveSpan('Next.js Server Handler', async (span) => { - span.setAttributes({ + return withActiveSpan(tracer, 'Next.js Server Handler', async (span) => { + span?.setAttributes({ 'account.id': context.account.id, 'deploy.id': context.deploy.id, 'request.id': context.requestId, @@ -26,7 +28,7 @@ export default async function handler(req, context) { cwd: process.cwd(), }) const response = await serverHandler(req, context, span, requestContext) - span.setAttributes({ + span?.setAttributes({ 'http.status_code': response.status, }) return response From c07e9be0aef73d99bc4c7a9d88c5aff3e65817e9 Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Fri, 10 Oct 2025 12:04:12 -0400 Subject: [PATCH 10/11] fix: update server handler definition --- src/run/handlers/server.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/run/handlers/server.ts b/src/run/handlers/server.ts index c38d0f3350..cf56c5e14b 100644 --- a/src/run/handlers/server.ts +++ b/src/run/handlers/server.ts @@ -62,7 +62,7 @@ const disableFaultyTransferEncodingHandling = (res: ComputeJsOutgoingMessage) => export default async ( request: Request, _context: Context, - topLevelSpan: Span, + topLevelSpan: Span | undefined, requestContext: RequestContext, ) => { const tracer = getTracer() @@ -114,7 +114,7 @@ export default async ( const response = await toComputeResponse(resProxy) if (requestContext.responseCacheKey) { - topLevelSpan.setAttribute('responseCacheKey', requestContext.responseCacheKey) + topLevelSpan?.setAttribute('responseCacheKey', requestContext.responseCacheKey) } const nextCache = response.headers.get('x-nextjs-cache') @@ -136,7 +136,7 @@ export default async ( const netlifyVary = response.headers.get('netlify-vary') ?? undefined const netlifyCdnCacheControl = response.headers.get('netlify-cdn-cache-control') ?? undefined - topLevelSpan.setAttributes({ + topLevelSpan?.setAttributes({ 'x-nextjs-cache': nextCache ?? undefined, isServedFromNextCache, netlifyVary, @@ -152,7 +152,7 @@ export default async ( (!isRSCRequest && contentType?.includes('text/html'))) ?? false - topLevelSpan.setAttributes({ + topLevelSpan?.setAttributes({ isRSCRequest, isCacheableAppPage: true, contentType, From 09b900dbdb9ccf9be824503dc59882e003b8edcc Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Fri, 10 Oct 2025 13:06:09 -0400 Subject: [PATCH 11/11] fix: re-export withActiveSpan for use within the runtime --- src/build/templates/handler-monorepo.tmpl.js | 4 +--- src/build/templates/handler.tmpl.js | 4 +--- src/run/handlers/cache.cts | 3 +-- src/run/handlers/server.ts | 3 +-- src/run/handlers/tracer.cts | 2 ++ src/run/handlers/use-cache-handler.ts | 3 +-- src/run/next.cts | 3 +-- src/run/storage/storage.cts | 3 +-- 8 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/build/templates/handler-monorepo.tmpl.js b/src/build/templates/handler-monorepo.tmpl.js index b136bbfcb8..04a2dc321c 100644 --- a/src/build/templates/handler-monorepo.tmpl.js +++ b/src/build/templates/handler-monorepo.tmpl.js @@ -1,10 +1,8 @@ -import { withActiveSpan } from '@netlify/otel' - import { createRequestContext, runWithRequestContext, } from '{{cwd}}/.netlify/dist/run/handlers/request-context.cjs' -import { getTracer } from '{{cwd}}/.netlify/dist/run/handlers/tracer.cjs' +import { getTracer, withActiveSpan } from '{{cwd}}/.netlify/dist/run/handlers/tracer.cjs' process.chdir('{{cwd}}') diff --git a/src/build/templates/handler.tmpl.js b/src/build/templates/handler.tmpl.js index b2c9a86ac8..f85d0bc9e5 100644 --- a/src/build/templates/handler.tmpl.js +++ b/src/build/templates/handler.tmpl.js @@ -1,11 +1,9 @@ -import { withActiveSpan } from '@netlify/otel' - import { createRequestContext, runWithRequestContext, } from './.netlify/dist/run/handlers/request-context.cjs' import serverHandler from './.netlify/dist/run/handlers/server.js' -import { getTracer } from './.netlify/dist/run/handlers/tracer.cjs' +import { getTracer, withActiveSpan } from './.netlify/dist/run/handlers/tracer.cjs' // Set feature flag for regional blobs process.env.USE_REGIONAL_BLOBS = '{{useRegionalBlobs}}' diff --git a/src/run/handlers/cache.cts b/src/run/handlers/cache.cts index 64f5042da2..56c116aa93 100644 --- a/src/run/handlers/cache.cts +++ b/src/run/handlers/cache.cts @@ -5,7 +5,6 @@ import { Buffer } from 'node:buffer' import { join } from 'node:path' import { join as posixJoin } from 'node:path/posix' -import { withActiveSpan } from '@netlify/otel' import type { Span } from '@netlify/otel/opentelemetry' import type { PrerenderManifest } from 'next/dist/build/index.js' import { NEXT_CACHE_TAGS_HEADER } from 'next/dist/lib/constants.js' @@ -27,7 +26,7 @@ import { import { getLogger, getRequestContext } from './request-context.cjs' import { isAnyTagStale, markTagsAsStaleAndPurgeEdgeCache, purgeEdgeCache } from './tags-handler.cjs' -import { getTracer, recordWarning } from './tracer.cjs' +import { getTracer, recordWarning, withActiveSpan } from './tracer.cjs' let memoizedPrerenderManifest: PrerenderManifest diff --git a/src/run/handlers/server.ts b/src/run/handlers/server.ts index cf56c5e14b..0a51163bb4 100644 --- a/src/run/handlers/server.ts +++ b/src/run/handlers/server.ts @@ -2,7 +2,6 @@ import type { OutgoingHttpHeaders } from 'http' import { ComputeJsOutgoingMessage, toComputeResponse, toReqRes } from '@fastly/http-compute-js' import type { Context } from '@netlify/functions' -import { withActiveSpan } from '@netlify/otel' import type { Span } from '@netlify/otel/opentelemetry' import type { WorkerRequestHandler } from 'next/dist/server/lib/types.js' @@ -18,7 +17,7 @@ import { nextResponseProxy } from '../revalidate.js' import { setFetchBeforeNextPatchedIt } from '../storage/storage.cjs' import { getLogger, type RequestContext } from './request-context.cjs' -import { getTracer, recordWarning } from './tracer.cjs' +import { getTracer, recordWarning, withActiveSpan } from './tracer.cjs' import { configureUseCacheHandlers } from './use-cache-handler.js' import { setupWaitUntil } from './wait-until.cjs' // make use of global fetch before Next.js applies any patching diff --git a/src/run/handlers/tracer.cts b/src/run/handlers/tracer.cts index 8dc5e3da1c..2f141fe2a3 100644 --- a/src/run/handlers/tracer.cts +++ b/src/run/handlers/tracer.cts @@ -100,3 +100,5 @@ export function recordWarning(warning: Error, span?: Span) { warning: true, }) } + +export { withActiveSpan } from '@netlify/otel' diff --git a/src/run/handlers/use-cache-handler.ts b/src/run/handlers/use-cache-handler.ts index ac67899380..de8d2aa005 100644 --- a/src/run/handlers/use-cache-handler.ts +++ b/src/run/handlers/use-cache-handler.ts @@ -1,6 +1,5 @@ import { Buffer } from 'node:buffer' -import { withActiveSpan } from '@netlify/otel' import { LRUCache } from 'lru-cache' import type { CacheEntry, @@ -15,7 +14,7 @@ import { isAnyTagStale, markTagsAsStaleAndPurgeEdgeCache, } from './tags-handler.cjs' -import { getTracer } from './tracer.cjs' +import { getTracer, withActiveSpan } from './tracer.cjs' // Most of this code is copied and adapted from Next.js default 'use cache' handler implementation // https://github.com/vercel/next.js/blob/84fde91e03918344c5d356986914ab68a5083462/packages/next/src/server/lib/cache-handlers/default.ts diff --git a/src/run/next.cts b/src/run/next.cts index f49afe3da1..7be6df5edb 100644 --- a/src/run/next.cts +++ b/src/run/next.cts @@ -2,7 +2,6 @@ import { AsyncLocalStorage } from 'node:async_hooks' import fs from 'node:fs/promises' import { relative, resolve } from 'node:path' -import { withActiveSpan } from '@netlify/otel' // @ts-expect-error no types installed import { patchFs } from 'fs-monkey' @@ -10,7 +9,7 @@ import { HtmlBlob } from '../shared/blob-types.cjs' import type { NextConfigForMultipleVersions } from './config.js' import { getRequestContext } from './handlers/request-context.cjs' -import { getTracer } from './handlers/tracer.cjs' +import { getTracer, withActiveSpan } from './handlers/tracer.cjs' import { getMemoizedKeyValueStoreBackedByRegionalBlobStore } from './storage/storage.cjs' // https://github.com/vercel/next.js/pull/68193/files#diff-37243d614f1f5d3f7ea50bbf2af263f6b1a9a4f70e84427977781e07b02f57f1R49 diff --git a/src/run/storage/storage.cts b/src/run/storage/storage.cts index 09ac7c2e55..98f11268f2 100644 --- a/src/run/storage/storage.cts +++ b/src/run/storage/storage.cts @@ -2,10 +2,9 @@ // Remaining modules in storage directory are implementation details // and should not be used directly outside of this directory. // There is eslint `no-restricted-imports` rule to enforce this. -import { withActiveSpan } from '@netlify/otel' import { type BlobType } from '../../shared/blob-types.cjs' -import { getTracer } from '../handlers/tracer.cjs' +import { getTracer, withActiveSpan } from '../handlers/tracer.cjs' import { getRegionalBlobStore } from './regional-blob-store.cjs' import { getRequestScopedInMemoryCache } from './request-scoped-in-memory-cache.cjs'