Spaces:
Running
Running
| ; | |
| Object.defineProperty(exports, "__esModule", { | |
| value: true | |
| }); | |
| 0 && (module.exports = { | |
| BubbledError: null, | |
| SpanKind: null, | |
| SpanStatusCode: null, | |
| getTracer: null, | |
| isBubbledError: null | |
| }); | |
| function _export(target, all) { | |
| for(var name in all)Object.defineProperty(target, name, { | |
| enumerable: true, | |
| get: all[name] | |
| }); | |
| } | |
| _export(exports, { | |
| BubbledError: function() { | |
| return BubbledError; | |
| }, | |
| SpanKind: function() { | |
| return SpanKind; | |
| }, | |
| SpanStatusCode: function() { | |
| return SpanStatusCode; | |
| }, | |
| getTracer: function() { | |
| return getTracer; | |
| }, | |
| isBubbledError: function() { | |
| return isBubbledError; | |
| } | |
| }); | |
| const _constants = require("./constants"); | |
| const _isthenable = require("../../../shared/lib/is-thenable"); | |
| const NEXT_OTEL_PERFORMANCE_PREFIX = process.env.NEXT_OTEL_PERFORMANCE_PREFIX; | |
| let api; | |
| // we want to allow users to use their own version of @opentelemetry/api if they | |
| // want to, so we try to require it first, and if it fails we fall back to the | |
| // version that is bundled with Next.js | |
| // this is because @opentelemetry/api has to be synced with the version of | |
| // @opentelemetry/tracing that is used, and we don't want to force users to use | |
| // the version that is bundled with Next.js. | |
| // the API is ~stable, so this should be fine | |
| if (process.env.NEXT_RUNTIME === 'edge') { | |
| api = require('@opentelemetry/api'); | |
| } else { | |
| try { | |
| api = require('@opentelemetry/api'); | |
| } catch (err) { | |
| api = require('next/dist/compiled/@opentelemetry/api'); | |
| } | |
| } | |
| const { context, propagation, trace, SpanStatusCode, SpanKind, ROOT_CONTEXT } = api; | |
| class BubbledError extends Error { | |
| constructor(bubble, result){ | |
| super(), this.bubble = bubble, this.result = result; | |
| } | |
| } | |
| function isBubbledError(error) { | |
| if (typeof error !== 'object' || error === null) return false; | |
| return error instanceof BubbledError; | |
| } | |
| const closeSpanWithError = (span, error)=>{ | |
| if (isBubbledError(error) && error.bubble) { | |
| span.setAttribute('next.bubble', true); | |
| } else { | |
| if (error) { | |
| span.recordException(error); | |
| span.setAttribute('error.type', error.name); | |
| } | |
| span.setStatus({ | |
| code: SpanStatusCode.ERROR, | |
| message: error == null ? void 0 : error.message | |
| }); | |
| } | |
| span.end(); | |
| }; | |
| /** we use this map to propagate attributes from nested spans to the top span */ const rootSpanAttributesStore = new Map(); | |
| const rootSpanIdKey = api.createContextKey('next.rootSpanId'); | |
| let lastSpanId = 0; | |
| const getSpanId = ()=>lastSpanId++; | |
| const clientTraceDataSetter = { | |
| set (carrier, key, value) { | |
| carrier.push({ | |
| key, | |
| value | |
| }); | |
| } | |
| }; | |
| class NextTracerImpl { | |
| /** | |
| * Returns an instance to the trace with configured name. | |
| * Since wrap / trace can be defined in any place prior to actual trace subscriber initialization, | |
| * This should be lazily evaluated. | |
| */ getTracerInstance() { | |
| return trace.getTracer('next.js', '0.0.1'); | |
| } | |
| getContext() { | |
| return context; | |
| } | |
| getTracePropagationData() { | |
| const activeContext = context.active(); | |
| const entries = []; | |
| propagation.inject(activeContext, entries, clientTraceDataSetter); | |
| return entries; | |
| } | |
| getActiveScopeSpan() { | |
| return trace.getSpan(context == null ? void 0 : context.active()); | |
| } | |
| withPropagatedContext(carrier, fn, getter) { | |
| const activeContext = context.active(); | |
| if (trace.getSpanContext(activeContext)) { | |
| // Active span is already set, too late to propagate. | |
| return fn(); | |
| } | |
| const remoteContext = propagation.extract(activeContext, carrier, getter); | |
| return context.with(remoteContext, fn); | |
| } | |
| trace(...args) { | |
| const [type, fnOrOptions, fnOrEmpty] = args; | |
| // coerce options form overload | |
| const { fn, options } = typeof fnOrOptions === 'function' ? { | |
| fn: fnOrOptions, | |
| options: {} | |
| } : { | |
| fn: fnOrEmpty, | |
| options: { | |
| ...fnOrOptions | |
| } | |
| }; | |
| const spanName = options.spanName ?? type; | |
| if (!_constants.NextVanillaSpanAllowlist.has(type) && process.env.NEXT_OTEL_VERBOSE !== '1' || options.hideSpan) { | |
| return fn(); | |
| } | |
| // Trying to get active scoped span to assign parent. If option specifies parent span manually, will try to use it. | |
| let spanContext = this.getSpanContext((options == null ? void 0 : options.parentSpan) ?? this.getActiveScopeSpan()); | |
| if (!spanContext) { | |
| spanContext = (context == null ? void 0 : context.active()) ?? ROOT_CONTEXT; | |
| } | |
| // Check if there's already a root span in the store for this trace | |
| // We are intentionally not checking whether there is an active context | |
| // from outside of nextjs to ensure that we can provide the same level | |
| // of telemetry when using a custom server | |
| const existingRootSpanId = spanContext.getValue(rootSpanIdKey); | |
| const isRootSpan = typeof existingRootSpanId !== 'number' || !rootSpanAttributesStore.has(existingRootSpanId); | |
| const spanId = getSpanId(); | |
| options.attributes = { | |
| 'next.span_name': spanName, | |
| 'next.span_type': type, | |
| ...options.attributes | |
| }; | |
| return context.with(spanContext.setValue(rootSpanIdKey, spanId), ()=>this.getTracerInstance().startActiveSpan(spanName, options, (span)=>{ | |
| let startTime; | |
| if (NEXT_OTEL_PERFORMANCE_PREFIX && type && _constants.LogSpanAllowList.has(type)) { | |
| startTime = 'performance' in globalThis && 'measure' in performance ? globalThis.performance.now() : undefined; | |
| } | |
| let cleanedUp = false; | |
| const onCleanup = ()=>{ | |
| if (cleanedUp) return; | |
| cleanedUp = true; | |
| rootSpanAttributesStore.delete(spanId); | |
| if (startTime) { | |
| performance.measure(`${NEXT_OTEL_PERFORMANCE_PREFIX}:next-${(type.split('.').pop() || '').replace(/[A-Z]/g, (match)=>'-' + match.toLowerCase())}`, { | |
| start: startTime, | |
| end: performance.now() | |
| }); | |
| } | |
| }; | |
| if (isRootSpan) { | |
| rootSpanAttributesStore.set(spanId, new Map(Object.entries(options.attributes ?? {}))); | |
| } | |
| if (fn.length > 1) { | |
| try { | |
| return fn(span, (err)=>closeSpanWithError(span, err)); | |
| } catch (err) { | |
| closeSpanWithError(span, err); | |
| throw err; | |
| } finally{ | |
| onCleanup(); | |
| } | |
| } | |
| try { | |
| const result = fn(span); | |
| if ((0, _isthenable.isThenable)(result)) { | |
| // If there's error make sure it throws | |
| return result.then((res)=>{ | |
| span.end(); | |
| // Need to pass down the promise result, | |
| // it could be react stream response with error { error, stream } | |
| return res; | |
| }).catch((err)=>{ | |
| closeSpanWithError(span, err); | |
| throw err; | |
| }).finally(onCleanup); | |
| } else { | |
| span.end(); | |
| onCleanup(); | |
| } | |
| return result; | |
| } catch (err) { | |
| closeSpanWithError(span, err); | |
| onCleanup(); | |
| throw err; | |
| } | |
| })); | |
| } | |
| wrap(...args) { | |
| const tracer = this; | |
| const [name, options, fn] = args.length === 3 ? args : [ | |
| args[0], | |
| {}, | |
| args[1] | |
| ]; | |
| if (!_constants.NextVanillaSpanAllowlist.has(name) && process.env.NEXT_OTEL_VERBOSE !== '1') { | |
| return fn; | |
| } | |
| return function() { | |
| let optionsObj = options; | |
| if (typeof optionsObj === 'function' && typeof fn === 'function') { | |
| optionsObj = optionsObj.apply(this, arguments); | |
| } | |
| const lastArgId = arguments.length - 1; | |
| const cb = arguments[lastArgId]; | |
| if (typeof cb === 'function') { | |
| const scopeBoundCb = tracer.getContext().bind(context.active(), cb); | |
| return tracer.trace(name, optionsObj, (_span, done)=>{ | |
| arguments[lastArgId] = function(err) { | |
| done == null ? void 0 : done(err); | |
| return scopeBoundCb.apply(this, arguments); | |
| }; | |
| return fn.apply(this, arguments); | |
| }); | |
| } else { | |
| return tracer.trace(name, optionsObj, ()=>fn.apply(this, arguments)); | |
| } | |
| }; | |
| } | |
| startSpan(...args) { | |
| const [type, options] = args; | |
| const spanContext = this.getSpanContext((options == null ? void 0 : options.parentSpan) ?? this.getActiveScopeSpan()); | |
| return this.getTracerInstance().startSpan(type, options, spanContext); | |
| } | |
| getSpanContext(parentSpan) { | |
| const spanContext = parentSpan ? trace.setSpan(context.active(), parentSpan) : undefined; | |
| return spanContext; | |
| } | |
| getRootSpanAttributes() { | |
| const spanId = context.active().getValue(rootSpanIdKey); | |
| return rootSpanAttributesStore.get(spanId); | |
| } | |
| setRootSpanAttribute(key, value) { | |
| const spanId = context.active().getValue(rootSpanIdKey); | |
| const attributes = rootSpanAttributesStore.get(spanId); | |
| if (attributes && !attributes.has(key)) { | |
| attributes.set(key, value); | |
| } | |
| } | |
| withSpan(span, fn) { | |
| const spanContext = trace.setSpan(context.active(), span); | |
| return context.with(spanContext, fn); | |
| } | |
| } | |
| const getTracer = (()=>{ | |
| const tracer = new NextTracerImpl(); | |
| return ()=>tracer; | |
| })(); | |
| //# sourceMappingURL=tracer.js.map |