@photon-ai/otel 1.1.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -19
- package/dist/index.d.ts +111 -58
- package/dist/index.js +516 -497
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/instrument-fetch.ts","../src/sanitize.ts","../src/version.ts","../src/logger.ts","../src/setup.ts","../src/with-span.ts"],"sourcesContent":["import {\n type Attributes,\n context,\n propagation,\n SpanKind,\n SpanStatusCode,\n type Tracer,\n trace,\n} from \"@opentelemetry/api\";\nimport {\n ATTR_ERROR_TYPE,\n ATTR_HTTP_REQUEST_METHOD,\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_SERVER_ADDRESS,\n ATTR_SERVER_PORT,\n ATTR_URL_FULL,\n} from \"@opentelemetry/semantic-conventions\";\nimport { sanitizeErrorMessage } from \"./sanitize\";\nimport { PHOTON_OTEL_VERSION } from \"./version\";\n\nexport interface InstrumentFetchOptions {\n /**\n * Return `true` to skip instrumenting a request whose absolute URL is passed\n * in. Useful to drop noisy endpoints or URLs that carry secrets in their\n * query string. The request is still performed — only the span is skipped.\n */\n ignore?: (url: string) => boolean;\n}\n\nexport interface FetchInstrumentation {\n /** Restore the original `globalThis.fetch`. Safe to call more than once. */\n unpatch(): void;\n}\n\ntype FetchInput = Parameters<typeof fetch>[0];\ntype FetchInit = Parameters<typeof fetch>[1];\ntype FetchFn = (input: FetchInput, init?: FetchInit) => Promise<Response>;\n\n/**\n * Stored on the wrapper via the global symbol registry (`Symbol.for`) so the\n * double-wrap guard holds even when two copies of this module load — which can\n * happen because the `bun` export condition serves `src/` while `default`\n * serves `dist/`.\n */\nconst PATCH_MARKER = Symbol.for(\"@photon-ai/otel.fetch.original\");\n\nconst HTTP_ERROR_STATUS_MIN = 400;\nconst DEFAULT_PORTS: Record<string, number> = { \"https:\": 443, \"http:\": 80 };\n\nlet scopedTracer: Tracer | undefined;\n\nfunction getTracer(): Tracer {\n if (!scopedTracer) {\n scopedTracer = trace.getTracer(\"@photon-ai/otel\", PHOTON_OTEL_VERSION);\n }\n return scopedTracer;\n}\n\nfunction setGlobalFetch(fn: FetchFn): void {\n // `preconnect` (Bun) is copied onto wrappers by preserveProps; the cast just\n // tells TypeScript the runtime object satisfies the full `fetch` type.\n globalThis.fetch = fn as typeof fetch;\n}\n\nfunction getPatchOriginal(fn: FetchFn): FetchFn | undefined {\n return (fn as unknown as Record<symbol, FetchFn | undefined>)[PATCH_MARKER];\n}\n\nfunction setPatchOriginal(fn: FetchFn, original: FetchFn): void {\n (fn as unknown as Record<symbol, FetchFn>)[PATCH_MARKER] = original;\n}\n\n/** Copy extra own properties (e.g. Bun's `fetch.preconnect`) onto the wrapper. */\nfunction preserveProps(from: FetchFn, to: FetchFn): void {\n for (const key of Object.getOwnPropertyNames(from)) {\n if (key in to) {\n continue;\n }\n const descriptor = Object.getOwnPropertyDescriptor(from, key);\n if (descriptor) {\n Object.defineProperty(to, key, descriptor);\n }\n }\n}\n\nfunction resolveRequestMeta(\n input: FetchInput,\n init: FetchInit\n): { method: string; url: string } {\n if (input instanceof Request) {\n return { method: input.method, url: input.url };\n }\n const url = typeof input === \"string\" ? input : input.toString();\n return { method: init?.method ?? \"GET\", url };\n}\n\nfunction resolvePort(parsed: URL): number | undefined {\n if (parsed.port) {\n return Number(parsed.port);\n }\n return DEFAULT_PORTS[parsed.protocol];\n}\n\nfunction toAttributes(attrs: Attributes): Attributes {\n const out: Attributes = {};\n for (const [key, value] of Object.entries(attrs)) {\n if (value !== undefined) {\n out[key] = value;\n }\n }\n return out;\n}\n\nfunction fetchAttributes(method: string, url: string): Attributes {\n const attrs: Attributes = {\n [ATTR_HTTP_REQUEST_METHOD]: method,\n [ATTR_URL_FULL]: url,\n };\n try {\n const parsed = new URL(url);\n attrs[ATTR_SERVER_ADDRESS] = parsed.hostname || undefined;\n attrs[ATTR_SERVER_PORT] = resolvePort(parsed);\n } catch {\n // Unparseable URL: leave server.* unset rather than failing the request.\n }\n return toAttributes(attrs);\n}\n\n/** Build the outgoing headers and inject the active trace context into them. */\nfunction buildPropagatedHeaders(input: FetchInput, init: FetchInit): Headers {\n const headers = new Headers(\n input instanceof Request ? input.headers : undefined\n );\n if (init?.headers) {\n for (const [key, value] of new Headers(init.headers).entries()) {\n headers.set(key, value);\n }\n }\n propagation.inject(context.active(), headers, {\n set: (carrier, key, value) => {\n carrier.set(key, value);\n },\n });\n return headers;\n}\n\nfunction callOriginal(\n original: FetchFn,\n input: FetchInput,\n init: FetchInit,\n headers: Headers\n): Promise<Response> {\n if (input instanceof Request) {\n // A consumed Request's body can't be rebuilt, but its headers stay mutable\n // (verified on both Bun and Node) — inject the propagated context in place\n // so trace headers still flow without reconstructing the unusable Request.\n if (input.bodyUsed) {\n for (const [key, value] of headers.entries()) {\n input.headers.set(key, value);\n }\n return original(input, init);\n }\n return original(new Request(input, { ...init, headers }));\n }\n return original(input, { ...init, headers });\n}\n\nfunction buildWrappedFetch(\n original: FetchFn,\n options?: InstrumentFetchOptions\n): FetchFn {\n return (input, init) => {\n const { method, url } = resolveRequestMeta(input, init);\n if (options?.ignore?.(url)) {\n return original(input, init);\n }\n const name = method.toUpperCase();\n return getTracer().startActiveSpan(\n name,\n { kind: SpanKind.CLIENT },\n async (span) => {\n span.setAttributes(fetchAttributes(name, url));\n try {\n const headers = buildPropagatedHeaders(input, init);\n const response = await callOriginal(original, input, init, headers);\n span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, response.status);\n span.setStatus({\n code:\n response.status >= HTTP_ERROR_STATUS_MIN\n ? SpanStatusCode.ERROR\n : SpanStatusCode.OK,\n });\n return response;\n } catch (err) {\n span.recordException(err as Error);\n const errorObj = err instanceof Error ? err : undefined;\n span.setAttribute(\n ATTR_ERROR_TYPE,\n errorObj?.constructor.name ?? typeof err\n );\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: errorObj\n ? sanitizeErrorMessage(errorObj.message)\n : sanitizeErrorMessage(String(err)),\n });\n throw err;\n } finally {\n span.end();\n }\n }\n );\n };\n}\n\n/**\n * Wrap `globalThis.fetch` so every outbound request produces a CLIENT span and\n * carries W3C trace context to the downstream service.\n *\n * On Bun this is the only fetch instrumentation that works: Bun's native fetch\n * emits no `diagnostics_channel` events, so the standard `instrumentation-undici`\n * / `instrumentation-http` (and `opentelemetry-instrumentation-fetch-node`,\n * which is itself diagnostics_channel-based) produce no spans. It works\n * identically on Node, where `globalThis.fetch` is undici-backed.\n *\n * Idempotent: a second call does not stack another wrapper. Returns a handle\n * whose `unpatch()` restores the original fetch.\n */\nexport function instrumentFetch(\n options?: InstrumentFetchOptions\n): FetchInstrumentation {\n const current: FetchFn = globalThis.fetch;\n const existingOriginal = getPatchOriginal(current);\n if (existingOriginal) {\n return {\n unpatch() {\n if (globalThis.fetch === current) {\n setGlobalFetch(existingOriginal);\n }\n },\n };\n }\n\n const original = current;\n const wrapped = buildWrappedFetch(original, options);\n preserveProps(original, wrapped);\n setPatchOriginal(wrapped, original);\n setGlobalFetch(wrapped);\n\n return {\n unpatch() {\n if (globalThis.fetch === wrapped) {\n setGlobalFetch(original);\n }\n },\n };\n}\n","// E.164-ish phone match: optional `+`, 7–15 digits with optional separators.\nconst PHONE_PATTERN = /\\+?\\d[\\d\\s()\\-.]{6,18}\\d/g;\nconst EMAIL_PATTERN = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g;\n\n/**\n * Mask a phone number, keeping the leading `+` (if any) plus the first 3 digits\n * and the last 4 digits visible. Example: `+13315553374` -> `+133xxxxx3374`.\n *\n * Inputs that don't have enough digits to safely mask are returned as\n * `xxxx` to avoid leaking the entire short value.\n */\nexport function sanitizePhone(input: string): string {\n const hasPlus = input.startsWith(\"+\");\n const digits = input.replace(/\\D/g, \"\");\n if (digits.length < 8) {\n return hasPlus ? \"+xxxx\" : \"xxxx\";\n }\n const head = digits.slice(0, 3);\n const tail = digits.slice(-4);\n const middleLength = digits.length - head.length - tail.length;\n return `${hasPlus ? \"+\" : \"\"}${head}${\"x\".repeat(middleLength)}${tail}`;\n}\n\n/**\n * Mask an email address, keeping the first 2 chars of the local part, the\n * first char of the domain, and the TLD. Example:\n * `foo.bar@example.com` -> `fo***@e***.com`.\n */\nexport function sanitizeEmail(input: string): string {\n const atIndex = input.lastIndexOf(\"@\");\n if (atIndex < 1) {\n return \"***\";\n }\n const local = input.slice(0, atIndex);\n const domain = input.slice(atIndex + 1);\n const dotIndex = domain.lastIndexOf(\".\");\n if (dotIndex < 1) {\n return \"***\";\n }\n const localHead = local.slice(0, 2);\n const domainHead = domain.slice(0, 1);\n const tld = domain.slice(dotIndex);\n return `${localHead}***@${domainHead}***${tld}`;\n}\n\n/**\n * Replace every phone number and email address inside a free-form string with\n * its sanitized form. Used to scrub `Error.message` values before attaching\n * them to span status.\n */\nexport function sanitizeErrorMessage(input: string): string {\n return input\n .replace(EMAIL_PATTERN, (match) => sanitizeEmail(match))\n .replace(PHONE_PATTERN, (match) => sanitizePhone(match));\n}\n","export const PHOTON_OTEL_VERSION = \"0.1.0\";\n","import { context as otelContext } from \"@opentelemetry/api\";\nimport { type Logger, logs, SeverityNumber } from \"@opentelemetry/api-logs\";\nimport { PHOTON_OTEL_VERSION } from \"./version\";\n\nexport type LogAttrs = Record<string, string | number | boolean | undefined>;\n\n/**\n * Minimum severity that gets emitted (to both the OTLP record and the console).\n * `\"silent\"` suppresses everything, including errors.\n */\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\" | \"silent\";\n\nconst LEVEL_SEVERITY: Record<LogLevel, number> = {\n debug: SeverityNumber.DEBUG, // 5\n info: SeverityNumber.INFO, // 9\n warn: SeverityNumber.WARN, // 13\n error: SeverityNumber.ERROR, // 17\n silent: Number.POSITIVE_INFINITY,\n};\n\nlet levelOverride: LogLevel | undefined;\n\nfunction envLevel(): LogLevel | undefined {\n const raw = process.env.LOG_LEVEL?.toLowerCase();\n if (raw && raw in LEVEL_SEVERITY) {\n return raw as LogLevel;\n }\n return;\n}\n\nfunction defaultLevel(): LogLevel {\n return (process.env.DEPLOYMENT_ENV ?? \"development\") === \"development\"\n ? \"debug\"\n : \"info\";\n}\n\n/**\n * Resolve the active level fresh on each call so that `LOG_LEVEL` changes and\n * `setLogLevel()` both take effect immediately. Resolution order (env wins, to\n * match the rest of the package's config story):\n * 1. `LOG_LEVEL` env var\n * 2. `setLogLevel()` / `setupOtel({ logLevel })`\n * 3. environment-driven default (`debug` in development, `info` otherwise)\n */\nfunction resolveLevel(): LogLevel {\n return envLevel() ?? levelOverride ?? defaultLevel();\n}\n\n/**\n * Programmatically set the minimum log level. Takes effect immediately for\n * subsequent logs. `LOG_LEVEL` env var still wins if set.\n */\nexport function setLogLevel(level: LogLevel): void {\n levelOverride = level;\n}\n\n/** Current effective log level, after env / override / default resolution. */\nexport function getLogLevel(): LogLevel {\n return resolveLevel();\n}\n\nlet scopedLogger: Logger | undefined;\n\nfunction getLogger(): Logger {\n if (!scopedLogger) {\n scopedLogger = logs.getLogger(\"@photon-ai/otel\", PHOTON_OTEL_VERSION);\n }\n return scopedLogger;\n}\n\nfunction filterUndefined(\n attrs?: LogAttrs\n): Record<string, string | number | boolean> {\n if (!attrs) {\n return {};\n }\n const out: Record<string, string | number | boolean> = {};\n for (const [k, v] of Object.entries(attrs)) {\n if (v !== undefined) {\n out[k] = v;\n }\n }\n return out;\n}\n\nfunction consoleFor(\n severityNumber: SeverityNumber\n): (...args: unknown[]) => void {\n if (severityNumber >= SeverityNumber.ERROR) {\n return console.error;\n }\n if (severityNumber >= SeverityNumber.WARN) {\n return console.warn;\n }\n if (severityNumber >= SeverityNumber.INFO) {\n return console.info;\n }\n return console.debug;\n}\n\nfunction emit(\n severityNumber: SeverityNumber,\n severityText: string,\n module: string,\n message: string,\n attrs?: LogAttrs,\n error?: unknown\n): void {\n // Single gate: drop sub-threshold logs before they reach OTLP or the console.\n if (severityNumber < LEVEL_SEVERITY[resolveLevel()]) {\n return;\n }\n\n const userAttrs = filterUndefined(attrs);\n const attributes: Record<string, string | number | boolean> = {\n \"log.module\": module,\n ...userAttrs,\n };\n\n if (error instanceof Error) {\n attributes[\"exception.type\"] = error.name;\n attributes[\"exception.message\"] = error.message;\n if (error.stack) {\n attributes[\"exception.stacktrace\"] = error.stack;\n }\n } else if (error !== undefined) {\n // Don't silently drop non-Error throws (strings, plain objects, etc.).\n attributes[\"exception.type\"] = typeof error;\n attributes[\"exception.message\"] = String(error);\n }\n\n getLogger().emit({\n severityNumber,\n severityText,\n body: message,\n attributes,\n context: otelContext.active(),\n });\n\n // Console: `[module] LEVEL message { ...attrs }` plus the raw error so the\n // runtime renders the full stack and pretty-prints the attribute bag.\n const extras: unknown[] = [];\n if (Object.keys(userAttrs).length > 0) {\n extras.push(userAttrs);\n }\n if (error !== undefined) {\n extras.push(error);\n }\n consoleFor(severityNumber)(`[${module}]`, severityText, message, ...extras);\n}\n\nexport interface PhotonLogger {\n debug(message: string, attrs?: LogAttrs, error?: unknown): void;\n error(message: string, attrs?: LogAttrs, error?: unknown): void;\n info(message: string, attrs?: LogAttrs, error?: unknown): void;\n warn(message: string, attrs?: LogAttrs, error?: unknown): void;\n}\n\nexport function createLogger(module: string): PhotonLogger {\n return {\n debug: (message, attrs, error) =>\n emit(SeverityNumber.DEBUG, \"DEBUG\", module, message, attrs, error),\n info: (message, attrs, error) =>\n emit(SeverityNumber.INFO, \"INFO\", module, message, attrs, error),\n warn: (message, attrs, error) =>\n emit(SeverityNumber.WARN, \"WARN\", module, message, attrs, error),\n error: (message, attrs, error) =>\n emit(SeverityNumber.ERROR, \"ERROR\", module, message, attrs, error),\n };\n}\n","import { context, propagation, trace } from \"@opentelemetry/api\";\nimport { logs } from \"@opentelemetry/api-logs\";\nimport { AsyncLocalStorageContextManager } from \"@opentelemetry/context-async-hooks\";\nimport {\n CompositePropagator,\n W3CBaggagePropagator,\n W3CTraceContextPropagator,\n} from \"@opentelemetry/core\";\nimport { OTLPLogExporter } from \"@opentelemetry/exporter-logs-otlp-http\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport {\n BatchLogRecordProcessor,\n LoggerProvider,\n} from \"@opentelemetry/sdk-logs\";\nimport {\n BasicTracerProvider,\n BatchSpanProcessor,\n} from \"@opentelemetry/sdk-trace-base\";\nimport {\n type FetchInstrumentation,\n type InstrumentFetchOptions,\n instrumentFetch,\n} from \"./instrument-fetch\";\nimport { type LogLevel, setLogLevel } from \"./logger\";\n\nexport interface SetupOtelOptions {\n /**\n * Default OTLP/HTTP base endpoint (e.g. `https://otel.example.com`). The\n * `/v1/traces` and `/v1/logs` paths are appended automatically. Standard\n * `OTEL_EXPORTER_OTLP_*` env vars always take precedence.\n */\n endpoint?: string;\n /**\n * Default OTLP headers (e.g. `{ Authorization: \"Basic ...\" }`). Merged with\n * any headers parsed from `OTEL_EXPORTER_OTLP_HEADERS`; env values win on\n * conflicts.\n */\n headers?: Record<string, string>;\n /**\n * Auto-instrument outbound `globalThis.fetch` with CLIENT spans and W3C\n * trace-context propagation. On Bun this is the only fetch instrumentation\n * that works (diagnostics_channel-based instrumentations emit nothing on\n * Bun's native fetch); it works identically on Node.\n *\n * `true` enables with defaults; pass an object to filter URLs via `ignore`.\n * Defaults to enabled when a traces endpoint is configured. Pass `false` to\n * disable.\n */\n instrumentFetch?: boolean | InstrumentFetchOptions;\n /**\n * Minimum log level emitted by `createLogger()` (to both OTLP and console).\n * The `LOG_LEVEL` env var still takes precedence. Defaults to `debug` in\n * development and `info` otherwise.\n */\n logLevel?: LogLevel;\n /**\n * Extra resource attributes attached to every span/log alongside\n * `service.name` / `service.version`.\n */\n resourceAttributes?: Record<string, string | number | boolean>;\n serviceName: string;\n serviceVersion?: string;\n}\n\nexport interface OtelHandle {\n shutdown(): Promise<void>;\n}\n\nlet activeHandle: OtelHandle | undefined;\n\nconst TRAILING_SLASH = /\\/$/;\n\nfunction parseEnvHeaders(raw: string | undefined): Record<string, string> {\n if (!raw) {\n return {};\n }\n const out: Record<string, string> = {};\n for (const pair of raw.split(\",\")) {\n const eq = pair.indexOf(\"=\");\n if (eq <= 0) {\n continue;\n }\n const key = pair.slice(0, eq).trim();\n const value = pair.slice(eq + 1).trim();\n if (key) {\n out[key] = value;\n }\n }\n return out;\n}\n\nfunction resolveTracesEndpoint(base: string | undefined): string | undefined {\n const traces = process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT;\n if (traces) {\n return traces;\n }\n const generic = process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? base;\n return generic\n ? `${generic.replace(TRAILING_SLASH, \"\")}/v1/traces`\n : undefined;\n}\n\nfunction resolveLogsEndpoint(base: string | undefined): string | undefined {\n const logsEndpoint = process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT;\n if (logsEndpoint) {\n return logsEndpoint;\n }\n const generic = process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? base;\n return generic ? `${generic.replace(TRAILING_SLASH, \"\")}/v1/logs` : undefined;\n}\n\n/**\n * Normalize a URL to an `origin + path` key (trailing slash stripped) for exact\n * self-trace matching. Returns `undefined` for unparseable URLs.\n */\nfunction otlpEndpointKey(url: string): string | undefined {\n try {\n const parsed = new URL(url);\n return `${parsed.origin}${parsed.pathname.replace(TRAILING_SLASH, \"\")}`;\n } catch {\n return;\n }\n}\n\nfunction otlpEndpointKeysOf(\n tracesEndpoint: string | undefined,\n logsEndpoint: string | undefined\n): string[] {\n const keys: string[] = [];\n for (const endpoint of [tracesEndpoint, logsEndpoint]) {\n if (!endpoint) {\n continue;\n }\n const key = otlpEndpointKey(endpoint);\n if (key) {\n keys.push(key);\n }\n }\n return keys;\n}\n\n/**\n * Patch `globalThis.fetch` unless disabled. Defaults to on when a traces\n * pipeline is configured. Always excludes our own OTLP endpoints so the\n * exporter's traffic is never self-traced (matters on Node, where the OTLP\n * exporter can use fetch).\n */\nfunction startFetchInstrumentation(\n option: boolean | InstrumentFetchOptions | undefined,\n hasTraces: boolean,\n tracesEndpoint: string | undefined,\n logsEndpoint: string | undefined\n): FetchInstrumentation | undefined {\n const want = option ?? hasTraces;\n if (!want) {\n return;\n }\n const userOptions = typeof option === \"object\" ? option : undefined;\n const otlpEndpointKeys = otlpEndpointKeysOf(tracesEndpoint, logsEndpoint);\n return instrumentFetch({\n ignore: (url) => {\n const key = otlpEndpointKey(url);\n const isOtlpEndpoint =\n key !== undefined && otlpEndpointKeys.includes(key);\n return isOtlpEndpoint || (userOptions?.ignore?.(url) ?? false);\n },\n });\n}\n\n/**\n * Boot an OTLP/HTTP-based OpenTelemetry pipeline (traces + logs).\n *\n * Idempotent: calling twice in the same process is a no-op on the second\n * call, so libraries can safely invoke this without clobbering an app-level\n * OTel setup that ran earlier.\n *\n * Standard `OTEL_EXPORTER_OTLP_*` env vars override the `endpoint` and\n * `headers` arguments — this matches the OpenTelemetry SDK config spec.\n */\nexport function setupOtel(options: SetupOtelOptions): OtelHandle {\n if (activeHandle) {\n return activeHandle;\n }\n\n if (options.logLevel) {\n setLogLevel(options.logLevel);\n }\n\n const tracesEndpoint = resolveTracesEndpoint(options.endpoint);\n const logsEndpoint = resolveLogsEndpoint(options.endpoint);\n const mergedHeaders = {\n ...options.headers,\n ...parseEnvHeaders(process.env.OTEL_EXPORTER_OTLP_HEADERS),\n };\n const hasHeaders = Object.keys(mergedHeaders).length > 0;\n\n const resource = resourceFromAttributes({\n \"service.name\": options.serviceName,\n ...(options.serviceVersion\n ? { \"service.version\": options.serviceVersion }\n : {}),\n \"deployment.environment\": process.env.DEPLOYMENT_ENV ?? \"development\",\n ...options.resourceAttributes,\n });\n\n context.setGlobalContextManager(new AsyncLocalStorageContextManager());\n propagation.setGlobalPropagator(\n new CompositePropagator({\n propagators: [\n new W3CTraceContextPropagator(),\n new W3CBaggagePropagator(),\n ],\n })\n );\n\n const traceProcessors = tracesEndpoint\n ? [\n new BatchSpanProcessor(\n new OTLPTraceExporter({\n url: tracesEndpoint,\n headers: hasHeaders ? mergedHeaders : undefined,\n })\n ),\n ]\n : [];\n\n const tracerProvider = new BasicTracerProvider({\n resource,\n spanProcessors: traceProcessors,\n });\n trace.setGlobalTracerProvider(tracerProvider);\n\n const fetchInstrumentation = startFetchInstrumentation(\n options.instrumentFetch,\n traceProcessors.length > 0,\n tracesEndpoint,\n logsEndpoint\n );\n\n const logProcessors = logsEndpoint\n ? [\n new BatchLogRecordProcessor(\n new OTLPLogExporter({\n url: logsEndpoint,\n headers: hasHeaders ? mergedHeaders : undefined,\n })\n ),\n ]\n : [];\n\n const loggerProvider = new LoggerProvider({\n resource,\n processors: logProcessors,\n });\n logs.setGlobalLoggerProvider(loggerProvider);\n\n const handle: OtelHandle = {\n async shutdown() {\n fetchInstrumentation?.unpatch();\n await Promise.allSettled([\n tracerProvider.shutdown(),\n loggerProvider.shutdown(),\n ]);\n activeHandle = undefined;\n },\n };\n\n activeHandle = handle;\n return handle;\n}\n\n/**\n * Read-only accessor for tests / debug paths that need to know whether\n * `setupOtel` has already run in this process.\n */\nexport function isOtelActive(): boolean {\n return activeHandle !== undefined;\n}\n","import {\n type Attributes,\n SpanStatusCode,\n type Tracer,\n trace,\n} from \"@opentelemetry/api\";\nimport type { LogAttrs } from \"./logger\";\nimport { sanitizeErrorMessage } from \"./sanitize\";\nimport { PHOTON_OTEL_VERSION } from \"./version\";\n\nlet scopedTracer: Tracer | undefined;\n\nfunction getTracer(): Tracer {\n if (!scopedTracer) {\n scopedTracer = trace.getTracer(\"@photon-ai/otel\", PHOTON_OTEL_VERSION);\n }\n return scopedTracer;\n}\n\nfunction toAttributes(attrs: LogAttrs): Attributes {\n const out: Attributes = {};\n for (const [k, v] of Object.entries(attrs)) {\n if (v !== undefined) {\n out[k] = v;\n }\n }\n return out;\n}\n\nexport function withSpan<T>(name: string, fn: () => Promise<T> | T): Promise<T>;\nexport function withSpan<T>(\n name: string,\n attrs: LogAttrs,\n fn: () => Promise<T> | T\n): Promise<T>;\nexport function withSpan<T>(\n name: string,\n attrsOrFn: LogAttrs | (() => Promise<T> | T),\n maybeFn?: () => Promise<T> | T\n): Promise<T> {\n const fn = typeof attrsOrFn === \"function\" ? attrsOrFn : maybeFn;\n if (!fn) {\n throw new Error(\"withSpan: function argument is required\");\n }\n const attrs = typeof attrsOrFn === \"function\" ? undefined : attrsOrFn;\n\n return getTracer().startActiveSpan(name, async (span) => {\n if (attrs) {\n span.setAttributes(toAttributes(attrs));\n }\n try {\n const result = await fn();\n span.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (err) {\n span.recordException(err as Error);\n const errorObj = err instanceof Error ? err : undefined;\n span.setAttribute(\"error.type\", errorObj?.constructor.name ?? typeof err);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: errorObj\n ? sanitizeErrorMessage(errorObj.message)\n : sanitizeErrorMessage(String(err)),\n });\n throw err;\n } finally {\n span.end();\n }\n });\n}\n"],"mappings":";AAAA;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACfP,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AASf,SAAS,cAAc,OAAuB;AACnD,QAAM,UAAU,MAAM,WAAW,GAAG;AACpC,QAAM,SAAS,MAAM,QAAQ,OAAO,EAAE;AACtC,MAAI,OAAO,SAAS,GAAG;AACrB,WAAO,UAAU,UAAU;AAAA,EAC7B;AACA,QAAM,OAAO,OAAO,MAAM,GAAG,CAAC;AAC9B,QAAM,OAAO,OAAO,MAAM,EAAE;AAC5B,QAAM,eAAe,OAAO,SAAS,KAAK,SAAS,KAAK;AACxD,SAAO,GAAG,UAAU,MAAM,EAAE,GAAG,IAAI,GAAG,IAAI,OAAO,YAAY,CAAC,GAAG,IAAI;AACvE;AAOO,SAAS,cAAc,OAAuB;AACnD,QAAM,UAAU,MAAM,YAAY,GAAG;AACrC,MAAI,UAAU,GAAG;AACf,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,MAAM,MAAM,GAAG,OAAO;AACpC,QAAM,SAAS,MAAM,MAAM,UAAU,CAAC;AACtC,QAAM,WAAW,OAAO,YAAY,GAAG;AACvC,MAAI,WAAW,GAAG;AAChB,WAAO;AAAA,EACT;AACA,QAAM,YAAY,MAAM,MAAM,GAAG,CAAC;AAClC,QAAM,aAAa,OAAO,MAAM,GAAG,CAAC;AACpC,QAAM,MAAM,OAAO,MAAM,QAAQ;AACjC,SAAO,GAAG,SAAS,OAAO,UAAU,MAAM,GAAG;AAC/C;AAOO,SAAS,qBAAqB,OAAuB;AAC1D,SAAO,MACJ,QAAQ,eAAe,CAAC,UAAU,cAAc,KAAK,CAAC,EACtD,QAAQ,eAAe,CAAC,UAAU,cAAc,KAAK,CAAC;AAC3D;;;ACtDO,IAAM,sBAAsB;;;AF4CnC,IAAM,eAAe,uBAAO,IAAI,gCAAgC;AAEhE,IAAM,wBAAwB;AAC9B,IAAM,gBAAwC,EAAE,UAAU,KAAK,SAAS,GAAG;AAE3E,IAAI;AAEJ,SAAS,YAAoB;AAC3B,MAAI,CAAC,cAAc;AACjB,mBAAe,MAAM,UAAU,mBAAmB,mBAAmB;AAAA,EACvE;AACA,SAAO;AACT;AAEA,SAAS,eAAe,IAAmB;AAGzC,aAAW,QAAQ;AACrB;AAEA,SAAS,iBAAiB,IAAkC;AAC1D,SAAQ,GAAsD,YAAY;AAC5E;AAEA,SAAS,iBAAiB,IAAa,UAAyB;AAC9D,EAAC,GAA0C,YAAY,IAAI;AAC7D;AAGA,SAAS,cAAc,MAAe,IAAmB;AACvD,aAAW,OAAO,OAAO,oBAAoB,IAAI,GAAG;AAClD,QAAI,OAAO,IAAI;AACb;AAAA,IACF;AACA,UAAM,aAAa,OAAO,yBAAyB,MAAM,GAAG;AAC5D,QAAI,YAAY;AACd,aAAO,eAAe,IAAI,KAAK,UAAU;AAAA,IAC3C;AAAA,EACF;AACF;AAEA,SAAS,mBACP,OACA,MACiC;AACjC,MAAI,iBAAiB,SAAS;AAC5B,WAAO,EAAE,QAAQ,MAAM,QAAQ,KAAK,MAAM,IAAI;AAAA,EAChD;AACA,QAAM,MAAM,OAAO,UAAU,WAAW,QAAQ,MAAM,SAAS;AAC/D,SAAO,EAAE,QAAQ,MAAM,UAAU,OAAO,IAAI;AAC9C;AAEA,SAAS,YAAY,QAAiC;AACpD,MAAI,OAAO,MAAM;AACf,WAAO,OAAO,OAAO,IAAI;AAAA,EAC3B;AACA,SAAO,cAAc,OAAO,QAAQ;AACtC;AAEA,SAAS,aAAa,OAA+B;AACnD,QAAM,MAAkB,CAAC;AACzB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,UAAU,QAAW;AACvB,UAAI,GAAG,IAAI;AAAA,IACb;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,QAAgB,KAAyB;AAChE,QAAM,QAAoB;AAAA,IACxB,CAAC,wBAAwB,GAAG;AAAA,IAC5B,CAAC,aAAa,GAAG;AAAA,EACnB;AACA,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,UAAM,mBAAmB,IAAI,OAAO,YAAY;AAChD,UAAM,gBAAgB,IAAI,YAAY,MAAM;AAAA,EAC9C,QAAQ;AAAA,EAER;AACA,SAAO,aAAa,KAAK;AAC3B;AAGA,SAAS,uBAAuB,OAAmB,MAA0B;AAC3E,QAAM,UAAU,IAAI;AAAA,IAClB,iBAAiB,UAAU,MAAM,UAAU;AAAA,EAC7C;AACA,MAAI,MAAM,SAAS;AACjB,eAAW,CAAC,KAAK,KAAK,KAAK,IAAI,QAAQ,KAAK,OAAO,EAAE,QAAQ,GAAG;AAC9D,cAAQ,IAAI,KAAK,KAAK;AAAA,IACxB;AAAA,EACF;AACA,cAAY,OAAO,QAAQ,OAAO,GAAG,SAAS;AAAA,IAC5C,KAAK,CAAC,SAAS,KAAK,UAAU;AAC5B,cAAQ,IAAI,KAAK,KAAK;AAAA,IACxB;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEA,SAAS,aACP,UACA,OACA,MACA,SACmB;AACnB,MAAI,iBAAiB,SAAS;AAI5B,QAAI,MAAM,UAAU;AAClB,iBAAW,CAAC,KAAK,KAAK,KAAK,QAAQ,QAAQ,GAAG;AAC5C,cAAM,QAAQ,IAAI,KAAK,KAAK;AAAA,MAC9B;AACA,aAAO,SAAS,OAAO,IAAI;AAAA,IAC7B;AACA,WAAO,SAAS,IAAI,QAAQ,OAAO,EAAE,GAAG,MAAM,QAAQ,CAAC,CAAC;AAAA,EAC1D;AACA,SAAO,SAAS,OAAO,EAAE,GAAG,MAAM,QAAQ,CAAC;AAC7C;AAEA,SAAS,kBACP,UACA,SACS;AACT,SAAO,CAAC,OAAO,SAAS;AACtB,UAAM,EAAE,QAAQ,IAAI,IAAI,mBAAmB,OAAO,IAAI;AACtD,QAAI,SAAS,SAAS,GAAG,GAAG;AAC1B,aAAO,SAAS,OAAO,IAAI;AAAA,IAC7B;AACA,UAAM,OAAO,OAAO,YAAY;AAChC,WAAO,UAAU,EAAE;AAAA,MACjB;AAAA,MACA,EAAE,MAAM,SAAS,OAAO;AAAA,MACxB,OAAO,SAAS;AACd,aAAK,cAAc,gBAAgB,MAAM,GAAG,CAAC;AAC7C,YAAI;AACF,gBAAM,UAAU,uBAAuB,OAAO,IAAI;AAClD,gBAAM,WAAW,MAAM,aAAa,UAAU,OAAO,MAAM,OAAO;AAClE,eAAK,aAAa,gCAAgC,SAAS,MAAM;AACjE,eAAK,UAAU;AAAA,YACb,MACE,SAAS,UAAU,wBACf,eAAe,QACf,eAAe;AAAA,UACvB,CAAC;AACD,iBAAO;AAAA,QACT,SAAS,KAAK;AACZ,eAAK,gBAAgB,GAAY;AACjC,gBAAM,WAAW,eAAe,QAAQ,MAAM;AAC9C,eAAK;AAAA,YACH;AAAA,YACA,UAAU,YAAY,QAAQ,OAAO;AAAA,UACvC;AACA,eAAK,UAAU;AAAA,YACb,MAAM,eAAe;AAAA,YACrB,SAAS,WACL,qBAAqB,SAAS,OAAO,IACrC,qBAAqB,OAAO,GAAG,CAAC;AAAA,UACtC,CAAC;AACD,gBAAM;AAAA,QACR,UAAE;AACA,eAAK,IAAI;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAeO,SAAS,gBACd,SACsB;AACtB,QAAM,UAAmB,WAAW;AACpC,QAAM,mBAAmB,iBAAiB,OAAO;AACjD,MAAI,kBAAkB;AACpB,WAAO;AAAA,MACL,UAAU;AACR,YAAI,WAAW,UAAU,SAAS;AAChC,yBAAe,gBAAgB;AAAA,QACjC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW;AACjB,QAAM,UAAU,kBAAkB,UAAU,OAAO;AACnD,gBAAc,UAAU,OAAO;AAC/B,mBAAiB,SAAS,QAAQ;AAClC,iBAAe,OAAO;AAEtB,SAAO;AAAA,IACL,UAAU;AACR,UAAI,WAAW,UAAU,SAAS;AAChC,uBAAe,QAAQ;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AACF;;;AGhQA,SAAS,WAAW,mBAAmB;AACvC,SAAsB,MAAM,sBAAsB;AAWlD,IAAM,iBAA2C;AAAA,EAC/C,OAAO,eAAe;AAAA;AAAA,EACtB,MAAM,eAAe;AAAA;AAAA,EACrB,MAAM,eAAe;AAAA;AAAA,EACrB,OAAO,eAAe;AAAA;AAAA,EACtB,QAAQ,OAAO;AACjB;AAEA,IAAI;AAEJ,SAAS,WAAiC;AACxC,QAAM,MAAM,QAAQ,IAAI,WAAW,YAAY;AAC/C,MAAI,OAAO,OAAO,gBAAgB;AAChC,WAAO;AAAA,EACT;AACA;AACF;AAEA,SAAS,eAAyB;AAChC,UAAQ,QAAQ,IAAI,kBAAkB,mBAAmB,gBACrD,UACA;AACN;AAUA,SAAS,eAAyB;AAChC,SAAO,SAAS,KAAK,iBAAiB,aAAa;AACrD;AAMO,SAAS,YAAY,OAAuB;AACjD,kBAAgB;AAClB;AAGO,SAAS,cAAwB;AACtC,SAAO,aAAa;AACtB;AAEA,IAAI;AAEJ,SAAS,YAAoB;AAC3B,MAAI,CAAC,cAAc;AACjB,mBAAe,KAAK,UAAU,mBAAmB,mBAAmB;AAAA,EACtE;AACA,SAAO;AACT;AAEA,SAAS,gBACP,OAC2C;AAC3C,MAAI,CAAC,OAAO;AACV,WAAO,CAAC;AAAA,EACV;AACA,QAAM,MAAiD,CAAC;AACxD,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC1C,QAAI,MAAM,QAAW;AACnB,UAAI,CAAC,IAAI;AAAA,IACX;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,WACP,gBAC8B;AAC9B,MAAI,kBAAkB,eAAe,OAAO;AAC1C,WAAO,QAAQ;AAAA,EACjB;AACA,MAAI,kBAAkB,eAAe,MAAM;AACzC,WAAO,QAAQ;AAAA,EACjB;AACA,MAAI,kBAAkB,eAAe,MAAM;AACzC,WAAO,QAAQ;AAAA,EACjB;AACA,SAAO,QAAQ;AACjB;AAEA,SAAS,KACP,gBACA,cACA,QACA,SACA,OACA,OACM;AAEN,MAAI,iBAAiB,eAAe,aAAa,CAAC,GAAG;AACnD;AAAA,EACF;AAEA,QAAM,YAAY,gBAAgB,KAAK;AACvC,QAAM,aAAwD;AAAA,IAC5D,cAAc;AAAA,IACd,GAAG;AAAA,EACL;AAEA,MAAI,iBAAiB,OAAO;AAC1B,eAAW,gBAAgB,IAAI,MAAM;AACrC,eAAW,mBAAmB,IAAI,MAAM;AACxC,QAAI,MAAM,OAAO;AACf,iBAAW,sBAAsB,IAAI,MAAM;AAAA,IAC7C;AAAA,EACF,WAAW,UAAU,QAAW;AAE9B,eAAW,gBAAgB,IAAI,OAAO;AACtC,eAAW,mBAAmB,IAAI,OAAO,KAAK;AAAA,EAChD;AAEA,YAAU,EAAE,KAAK;AAAA,IACf;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA,SAAS,YAAY,OAAO;AAAA,EAC9B,CAAC;AAID,QAAM,SAAoB,CAAC;AAC3B,MAAI,OAAO,KAAK,SAAS,EAAE,SAAS,GAAG;AACrC,WAAO,KAAK,SAAS;AAAA,EACvB;AACA,MAAI,UAAU,QAAW;AACvB,WAAO,KAAK,KAAK;AAAA,EACnB;AACA,aAAW,cAAc,EAAE,IAAI,MAAM,KAAK,cAAc,SAAS,GAAG,MAAM;AAC5E;AASO,SAAS,aAAa,QAA8B;AACzD,SAAO;AAAA,IACL,OAAO,CAAC,SAAS,OAAO,UACtB,KAAK,eAAe,OAAO,SAAS,QAAQ,SAAS,OAAO,KAAK;AAAA,IACnE,MAAM,CAAC,SAAS,OAAO,UACrB,KAAK,eAAe,MAAM,QAAQ,QAAQ,SAAS,OAAO,KAAK;AAAA,IACjE,MAAM,CAAC,SAAS,OAAO,UACrB,KAAK,eAAe,MAAM,QAAQ,QAAQ,SAAS,OAAO,KAAK;AAAA,IACjE,OAAO,CAAC,SAAS,OAAO,UACtB,KAAK,eAAe,OAAO,SAAS,QAAQ,SAAS,OAAO,KAAK;AAAA,EACrE;AACF;;;ACzKA,SAAS,WAAAA,UAAS,eAAAC,cAAa,SAAAC,cAAa;AAC5C,SAAS,QAAAC,aAAY;AACrB,SAAS,uCAAuC;AAChD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,uBAAuB;AAChC,SAAS,yBAAyB;AAClC,SAAS,8BAA8B;AACvC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAmDP,IAAI;AAEJ,IAAM,iBAAiB;AAEvB,SAAS,gBAAgB,KAAiD;AACxE,MAAI,CAAC,KAAK;AACR,WAAO,CAAC;AAAA,EACV;AACA,QAAM,MAA8B,CAAC;AACrC,aAAW,QAAQ,IAAI,MAAM,GAAG,GAAG;AACjC,UAAM,KAAK,KAAK,QAAQ,GAAG;AAC3B,QAAI,MAAM,GAAG;AACX;AAAA,IACF;AACA,UAAM,MAAM,KAAK,MAAM,GAAG,EAAE,EAAE,KAAK;AACnC,UAAM,QAAQ,KAAK,MAAM,KAAK,CAAC,EAAE,KAAK;AACtC,QAAI,KAAK;AACP,UAAI,GAAG,IAAI;AAAA,IACb;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,sBAAsB,MAA8C;AAC3E,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,QAAQ;AACV,WAAO;AAAA,EACT;AACA,QAAM,UAAU,QAAQ,IAAI,+BAA+B;AAC3D,SAAO,UACH,GAAG,QAAQ,QAAQ,gBAAgB,EAAE,CAAC,eACtC;AACN;AAEA,SAAS,oBAAoB,MAA8C;AACzE,QAAM,eAAe,QAAQ,IAAI;AACjC,MAAI,cAAc;AAChB,WAAO;AAAA,EACT;AACA,QAAM,UAAU,QAAQ,IAAI,+BAA+B;AAC3D,SAAO,UAAU,GAAG,QAAQ,QAAQ,gBAAgB,EAAE,CAAC,aAAa;AACtE;AAMA,SAAS,gBAAgB,KAAiC;AACxD,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,WAAO,GAAG,OAAO,MAAM,GAAG,OAAO,SAAS,QAAQ,gBAAgB,EAAE,CAAC;AAAA,EACvE,QAAQ;AACN;AAAA,EACF;AACF;AAEA,SAAS,mBACP,gBACA,cACU;AACV,QAAM,OAAiB,CAAC;AACxB,aAAW,YAAY,CAAC,gBAAgB,YAAY,GAAG;AACrD,QAAI,CAAC,UAAU;AACb;AAAA,IACF;AACA,UAAM,MAAM,gBAAgB,QAAQ;AACpC,QAAI,KAAK;AACP,WAAK,KAAK,GAAG;AAAA,IACf;AAAA,EACF;AACA,SAAO;AACT;AAQA,SAAS,0BACP,QACA,WACA,gBACA,cACkC;AAClC,QAAM,OAAO,UAAU;AACvB,MAAI,CAAC,MAAM;AACT;AAAA,EACF;AACA,QAAM,cAAc,OAAO,WAAW,WAAW,SAAS;AAC1D,QAAM,mBAAmB,mBAAmB,gBAAgB,YAAY;AACxE,SAAO,gBAAgB;AAAA,IACrB,QAAQ,CAAC,QAAQ;AACf,YAAM,MAAM,gBAAgB,GAAG;AAC/B,YAAM,iBACJ,QAAQ,UAAa,iBAAiB,SAAS,GAAG;AACpD,aAAO,mBAAmB,aAAa,SAAS,GAAG,KAAK;AAAA,IAC1D;AAAA,EACF,CAAC;AACH;AAYO,SAAS,UAAU,SAAuC;AAC/D,MAAI,cAAc;AAChB,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,UAAU;AACpB,gBAAY,QAAQ,QAAQ;AAAA,EAC9B;AAEA,QAAM,iBAAiB,sBAAsB,QAAQ,QAAQ;AAC7D,QAAM,eAAe,oBAAoB,QAAQ,QAAQ;AACzD,QAAM,gBAAgB;AAAA,IACpB,GAAG,QAAQ;AAAA,IACX,GAAG,gBAAgB,QAAQ,IAAI,0BAA0B;AAAA,EAC3D;AACA,QAAM,aAAa,OAAO,KAAK,aAAa,EAAE,SAAS;AAEvD,QAAM,WAAW,uBAAuB;AAAA,IACtC,gBAAgB,QAAQ;AAAA,IACxB,GAAI,QAAQ,iBACR,EAAE,mBAAmB,QAAQ,eAAe,IAC5C,CAAC;AAAA,IACL,0BAA0B,QAAQ,IAAI,kBAAkB;AAAA,IACxD,GAAG,QAAQ;AAAA,EACb,CAAC;AAED,EAAAC,SAAQ,wBAAwB,IAAI,gCAAgC,CAAC;AACrE,EAAAC,aAAY;AAAA,IACV,IAAI,oBAAoB;AAAA,MACtB,aAAa;AAAA,QACX,IAAI,0BAA0B;AAAA,QAC9B,IAAI,qBAAqB;AAAA,MAC3B;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,kBAAkB,iBACpB;AAAA,IACE,IAAI;AAAA,MACF,IAAI,kBAAkB;AAAA,QACpB,KAAK;AAAA,QACL,SAAS,aAAa,gBAAgB;AAAA,MACxC,CAAC;AAAA,IACH;AAAA,EACF,IACA,CAAC;AAEL,QAAM,iBAAiB,IAAI,oBAAoB;AAAA,IAC7C;AAAA,IACA,gBAAgB;AAAA,EAClB,CAAC;AACD,EAAAC,OAAM,wBAAwB,cAAc;AAE5C,QAAM,uBAAuB;AAAA,IAC3B,QAAQ;AAAA,IACR,gBAAgB,SAAS;AAAA,IACzB;AAAA,IACA;AAAA,EACF;AAEA,QAAM,gBAAgB,eAClB;AAAA,IACE,IAAI;AAAA,MACF,IAAI,gBAAgB;AAAA,QAClB,KAAK;AAAA,QACL,SAAS,aAAa,gBAAgB;AAAA,MACxC,CAAC;AAAA,IACH;AAAA,EACF,IACA,CAAC;AAEL,QAAM,iBAAiB,IAAI,eAAe;AAAA,IACxC;AAAA,IACA,YAAY;AAAA,EACd,CAAC;AACD,EAAAC,MAAK,wBAAwB,cAAc;AAE3C,QAAM,SAAqB;AAAA,IACzB,MAAM,WAAW;AACf,4BAAsB,QAAQ;AAC9B,YAAM,QAAQ,WAAW;AAAA,QACvB,eAAe,SAAS;AAAA,QACxB,eAAe,SAAS;AAAA,MAC1B,CAAC;AACD,qBAAe;AAAA,IACjB;AAAA,EACF;AAEA,iBAAe;AACf,SAAO;AACT;AAMO,SAAS,eAAwB;AACtC,SAAO,iBAAiB;AAC1B;;;ACtRA;AAAA,EAEE,kBAAAC;AAAA,EAEA,SAAAC;AAAA,OACK;AAKP,IAAIC;AAEJ,SAASC,aAAoB;AAC3B,MAAI,CAACD,eAAc;AACjB,IAAAA,gBAAeE,OAAM,UAAU,mBAAmB,mBAAmB;AAAA,EACvE;AACA,SAAOF;AACT;AAEA,SAASG,cAAa,OAA6B;AACjD,QAAM,MAAkB,CAAC;AACzB,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC1C,QAAI,MAAM,QAAW;AACnB,UAAI,CAAC,IAAI;AAAA,IACX;AAAA,EACF;AACA,SAAO;AACT;AAQO,SAAS,SACd,MACA,WACA,SACY;AACZ,QAAM,KAAK,OAAO,cAAc,aAAa,YAAY;AACzD,MAAI,CAAC,IAAI;AACP,UAAM,IAAI,MAAM,yCAAyC;AAAA,EAC3D;AACA,QAAM,QAAQ,OAAO,cAAc,aAAa,SAAY;AAE5D,SAAOF,WAAU,EAAE,gBAAgB,MAAM,OAAO,SAAS;AACvD,QAAI,OAAO;AACT,WAAK,cAAcE,cAAa,KAAK,CAAC;AAAA,IACxC;AACA,QAAI;AACF,YAAM,SAAS,MAAM,GAAG;AACxB,WAAK,UAAU,EAAE,MAAMC,gBAAe,GAAG,CAAC;AAC1C,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,WAAK,gBAAgB,GAAY;AACjC,YAAM,WAAW,eAAe,QAAQ,MAAM;AAC9C,WAAK,aAAa,cAAc,UAAU,YAAY,QAAQ,OAAO,GAAG;AACxE,WAAK,UAAU;AAAA,QACb,MAAMA,gBAAe;AAAA,QACrB,SAAS,WACL,qBAAqB,SAAS,OAAO,IACrC,qBAAqB,OAAO,GAAG,CAAC;AAAA,MACtC,CAAC;AACD,YAAM;AAAA,IACR,UAAE;AACA,WAAK,IAAI;AAAA,IACX;AAAA,EACF,CAAC;AACH;","names":["context","propagation","trace","logs","context","propagation","trace","logs","SpanStatusCode","trace","scopedTracer","getTracer","trace","toAttributes","SpanStatusCode"]}
|
|
1
|
+
{"version":3,"file":"index.js","names":["scopedTracer","getTracer","toAttributes","otelContext"],"sources":["../src/sanitize.ts","../src/version.ts","../src/instrument-fetch.ts","../src/logger.ts","../src/instrument-fetch-native.ts","../src/runtime.ts","../src/setup.ts","../src/with-span.ts"],"sourcesContent":["// E.164-ish phone match: optional `+`, 7–15 digits with optional separators.\nconst PHONE_PATTERN = /\\+?\\d[\\d\\s()\\-.]{6,18}\\d/g;\nconst EMAIL_PATTERN = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g;\n\n/**\n * Mask a phone number, keeping the leading `+` (if any) plus the first 3 digits\n * and the last 4 digits visible. Example: `+13315553374` -> `+133xxxxx3374`.\n *\n * Inputs that don't have enough digits to safely mask are returned as\n * `xxxx` to avoid leaking the entire short value.\n */\nexport function sanitizePhone(input: string): string {\n const hasPlus = input.startsWith(\"+\");\n const digits = input.replace(/\\D/g, \"\");\n if (digits.length < 8) {\n return hasPlus ? \"+xxxx\" : \"xxxx\";\n }\n const head = digits.slice(0, 3);\n const tail = digits.slice(-4);\n const middleLength = digits.length - head.length - tail.length;\n return `${hasPlus ? \"+\" : \"\"}${head}${\"x\".repeat(middleLength)}${tail}`;\n}\n\n/**\n * Mask an email address, keeping the first 2 chars of the local part, the\n * first char of the domain, and the TLD. Example:\n * `foo.bar@example.com` -> `fo***@e***.com`.\n */\nexport function sanitizeEmail(input: string): string {\n const atIndex = input.lastIndexOf(\"@\");\n if (atIndex < 1) {\n return \"***\";\n }\n const local = input.slice(0, atIndex);\n const domain = input.slice(atIndex + 1);\n const dotIndex = domain.lastIndexOf(\".\");\n if (dotIndex < 1) {\n return \"***\";\n }\n const localHead = local.slice(0, 2);\n const domainHead = domain.slice(0, 1);\n const tld = domain.slice(dotIndex);\n return `${localHead}***@${domainHead}***${tld}`;\n}\n\n/**\n * Replace every phone number and email address inside a free-form string with\n * its sanitized form. Used to scrub `Error.message` values before attaching\n * them to span status.\n */\nexport function sanitizeErrorMessage(input: string): string {\n return input\n .replace(EMAIL_PATTERN, (match) => sanitizeEmail(match))\n .replace(PHONE_PATTERN, (match) => sanitizePhone(match));\n}\n","export const PHOTON_OTEL_VERSION = \"1.1.0\";\n","import {\n type Attributes,\n context,\n propagation,\n SpanKind,\n SpanStatusCode,\n type Tracer,\n trace,\n} from \"@opentelemetry/api\";\nimport {\n ATTR_ERROR_TYPE,\n ATTR_HTTP_REQUEST_METHOD,\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_SERVER_ADDRESS,\n ATTR_SERVER_PORT,\n ATTR_URL_FULL,\n} from \"@opentelemetry/semantic-conventions\";\nimport { sanitizeErrorMessage } from \"./sanitize\";\nimport { PHOTON_OTEL_VERSION } from \"./version\";\n\nexport interface FetchSpanOptions {\n /**\n * Static attributes merged into every CLIENT span this instrumentation\n * produces. Handy for tagging an SDK's traffic, e.g. `{ \"peer.service\":\n * \"openai\" }`, so spans from different instrumented fetches stay\n * distinguishable.\n */\n attributes?: Attributes;\n /**\n * Return `true` to skip instrumenting a request whose absolute URL is passed\n * in. Useful to drop noisy endpoints or URLs that carry secrets in their\n * query string. The request is still performed — only the span is skipped.\n */\n ignore?: (url: string) => boolean;\n}\n\nexport interface InstrumentFetchOptions extends FetchSpanOptions {\n /**\n * Which fetch-instrumentation strategy `setupOtel()` should use:\n * - `\"auto\"` (default): the official `@opentelemetry/instrumentation-undici`\n * on Node (richer HTTP-client semantic conventions, captures all undici\n * traffic, no global monkey-patch), and the `globalThis.fetch` wrap on Bun\n * (the only thing that works there).\n * - `\"global\"`: always wrap `globalThis.fetch` on both runtimes. Produces\n * identical spans everywhere and keeps the built-in PII scrubbing of error\n * messages, at the cost of the richer Node attributes. Use this when you\n * want Bun and Node telemetry to match exactly.\n *\n * Only consulted by `setupOtel()`. Calling `instrumentFetch()` directly always\n * performs a global wrap regardless of this field.\n */\n mode?: \"auto\" | \"global\";\n}\n\nexport interface FetchInstrumentation {\n /** Restore the original `globalThis.fetch`. Safe to call more than once. */\n unpatch(): void;\n}\n\ntype FetchInput = Parameters<typeof fetch>[0];\ntype FetchInit = Parameters<typeof fetch>[1];\ntype FetchFn = (input: FetchInput, init?: FetchInit) => Promise<Response>;\n\n/**\n * Stored on the wrapper via the global symbol registry (`Symbol.for`) so the\n * double-wrap guard holds even when two copies of this module load — which can\n * happen because the `bun` export condition serves `src/` while `default`\n * serves `dist/`.\n */\nconst PATCH_MARKER = Symbol.for(\"@photon-ai/otel.fetch.original\");\n\nconst HTTP_ERROR_STATUS_MIN = 400;\nconst DEFAULT_PORTS: Record<string, number> = { \"https:\": 443, \"http:\": 80 };\n\nlet scopedTracer: Tracer | undefined;\n\nfunction getTracer(): Tracer {\n if (!scopedTracer) {\n scopedTracer = trace.getTracer(\"@photon-ai/otel\", PHOTON_OTEL_VERSION);\n }\n return scopedTracer;\n}\n\nfunction setGlobalFetch(fn: FetchFn): void {\n // `preconnect` (Bun) is copied onto wrappers by preserveProps; the cast just\n // tells TypeScript the runtime object satisfies the full `fetch` type.\n globalThis.fetch = fn as typeof fetch;\n}\n\nfunction getPatchOriginal(fn: FetchFn): FetchFn | undefined {\n return (fn as unknown as Record<symbol, FetchFn | undefined>)[PATCH_MARKER];\n}\n\nfunction setPatchOriginal(fn: FetchFn, original: FetchFn): void {\n (fn as unknown as Record<symbol, FetchFn>)[PATCH_MARKER] = original;\n}\n\n/** Copy extra own properties (e.g. Bun's `fetch.preconnect`) onto the wrapper. */\nfunction preserveProps(from: FetchFn, to: FetchFn): void {\n for (const key of Object.getOwnPropertyNames(from)) {\n if (key in to) {\n continue;\n }\n const descriptor = Object.getOwnPropertyDescriptor(from, key);\n if (descriptor) {\n Object.defineProperty(to, key, descriptor);\n }\n }\n}\n\nfunction resolveRequestMeta(\n input: FetchInput,\n init: FetchInit\n): { method: string; url: string } {\n if (input instanceof Request) {\n return { method: input.method, url: input.url };\n }\n const url = typeof input === \"string\" ? input : input.toString();\n return { method: init?.method ?? \"GET\", url };\n}\n\nfunction resolvePort(parsed: URL): number | undefined {\n if (parsed.port) {\n return Number(parsed.port);\n }\n return DEFAULT_PORTS[parsed.protocol];\n}\n\nfunction toAttributes(attrs: Attributes): Attributes {\n const out: Attributes = {};\n for (const [key, value] of Object.entries(attrs)) {\n if (value !== undefined) {\n out[key] = value;\n }\n }\n return out;\n}\n\nfunction fetchAttributes(method: string, url: string): Attributes {\n const attrs: Attributes = {\n [ATTR_HTTP_REQUEST_METHOD]: method,\n [ATTR_URL_FULL]: url,\n };\n try {\n const parsed = new URL(url);\n attrs[ATTR_SERVER_ADDRESS] = parsed.hostname || undefined;\n attrs[ATTR_SERVER_PORT] = resolvePort(parsed);\n } catch {\n // Unparseable URL: leave server.* unset rather than failing the request.\n }\n return toAttributes(attrs);\n}\n\n/** Build the outgoing headers and inject the active trace context into them. */\nfunction buildPropagatedHeaders(input: FetchInput, init: FetchInit): Headers {\n const headers = new Headers(\n input instanceof Request ? input.headers : undefined\n );\n if (init?.headers) {\n for (const [key, value] of new Headers(init.headers).entries()) {\n headers.set(key, value);\n }\n }\n propagation.inject(context.active(), headers, {\n set: (carrier, key, value) => {\n carrier.set(key, value);\n },\n });\n return headers;\n}\n\nfunction callOriginal(\n original: FetchFn,\n input: FetchInput,\n init: FetchInit,\n headers: Headers\n): Promise<Response> {\n if (input instanceof Request) {\n // A consumed Request's body can't be rebuilt, but its headers stay mutable\n // (verified on both Bun and Node) — inject the propagated context in place\n // so trace headers still flow without reconstructing the unusable Request.\n if (input.bodyUsed) {\n for (const [key, value] of headers.entries()) {\n input.headers.set(key, value);\n }\n return original(input, init);\n }\n return original(new Request(input, { ...init, headers }));\n }\n return original(input, { ...init, headers });\n}\n\nfunction buildWrappedFetch(\n original: FetchFn,\n options?: FetchSpanOptions\n): FetchFn {\n const staticAttributes = options?.attributes;\n return (input, init) => {\n const { method, url } = resolveRequestMeta(input, init);\n if (options?.ignore?.(url)) {\n return original(input, init);\n }\n const name = method.toUpperCase();\n return getTracer().startActiveSpan(\n name,\n { kind: SpanKind.CLIENT },\n async (span) => {\n if (staticAttributes) {\n span.setAttributes(staticAttributes);\n }\n span.setAttributes(fetchAttributes(name, url));\n try {\n const headers = buildPropagatedHeaders(input, init);\n const response = await callOriginal(original, input, init, headers);\n span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, response.status);\n span.setStatus({\n code:\n response.status >= HTTP_ERROR_STATUS_MIN\n ? SpanStatusCode.ERROR\n : SpanStatusCode.OK,\n });\n return response;\n } catch (err) {\n span.recordException(err as Error);\n const errorObj = err instanceof Error ? err : undefined;\n span.setAttribute(\n ATTR_ERROR_TYPE,\n errorObj?.constructor.name ?? typeof err\n );\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: errorObj\n ? sanitizeErrorMessage(errorObj.message)\n : sanitizeErrorMessage(String(err)),\n });\n throw err;\n } finally {\n span.end();\n }\n }\n );\n };\n}\n\n/**\n * Wrap a single fetch function — not `globalThis.fetch` — so requests made\n * through the RETURNED fetch produce a CLIENT span and carry W3C trace context\n * to the downstream service.\n *\n * Built for SDKs that accept a `fetch` option, e.g.\n * `new OpenAI({ fetch: createInstrumentedFetch() })`. Unlike `instrumentFetch`,\n * it never mutates the global and has no lifecycle to unpatch — it just returns\n * a new fetch you pass where you need it.\n *\n * `baseFetch` defaults to the current `globalThis.fetch`, read at call time.\n * Idempotent: passing an already-instrumented fetch returns it unchanged.\n *\n * Always uses the global-wrap technique (the native undici instrumentation\n * cannot target a single instance), so it behaves identically on Bun and Node.\n * On Node, if `setupOtel`'s global fetch instrumentation is also active, the\n * SDK's request is captured twice — disable it (`instrumentFetch: false`) for\n * paths you instrument per-instance.\n */\nexport function createInstrumentedFetch(\n baseFetch: typeof fetch = globalThis.fetch,\n options?: FetchSpanOptions\n): typeof fetch {\n if (getPatchOriginal(baseFetch)) {\n return baseFetch;\n }\n const wrapped = buildWrappedFetch(baseFetch, options);\n preserveProps(baseFetch, wrapped);\n setPatchOriginal(wrapped, baseFetch);\n return wrapped as typeof fetch;\n}\n\n/**\n * Wrap `globalThis.fetch` so every outbound request produces a CLIENT span and\n * carries W3C trace context to the downstream service.\n *\n * On Bun this is the only fetch instrumentation that works: Bun's native fetch\n * emits no `diagnostics_channel` events, so the standard `instrumentation-undici`\n * / `instrumentation-http` (and `opentelemetry-instrumentation-fetch-node`,\n * which is itself diagnostics_channel-based) produce no spans. It works\n * identically on Node, where `globalThis.fetch` is undici-backed.\n *\n * Idempotent: a second call does not stack another wrapper. Returns a handle\n * whose `unpatch()` restores the original fetch.\n */\nexport function instrumentFetch(\n options?: InstrumentFetchOptions\n): FetchInstrumentation {\n const current = globalThis.fetch;\n const existingOriginal = getPatchOriginal(current);\n if (existingOriginal) {\n return {\n unpatch() {\n if (globalThis.fetch === current) {\n setGlobalFetch(existingOriginal);\n }\n },\n };\n }\n\n const original = current;\n const wrapped = createInstrumentedFetch(original, options);\n setGlobalFetch(wrapped);\n\n return {\n unpatch() {\n if (globalThis.fetch === wrapped) {\n setGlobalFetch(original);\n }\n },\n };\n}\n","import { context as otelContext } from \"@opentelemetry/api\";\nimport { type Logger, logs, SeverityNumber } from \"@opentelemetry/api-logs\";\nimport { PHOTON_OTEL_VERSION } from \"./version\";\n\nexport type LogAttrs = Record<string, string | number | boolean | undefined>;\n\n/**\n * Minimum severity that gets emitted (to both the OTLP record and the console).\n * `\"silent\"` suppresses everything, including errors.\n */\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\" | \"silent\";\n\nconst LEVEL_SEVERITY: Record<LogLevel, number> = {\n debug: SeverityNumber.DEBUG, // 5\n info: SeverityNumber.INFO, // 9\n warn: SeverityNumber.WARN, // 13\n error: SeverityNumber.ERROR, // 17\n silent: Number.POSITIVE_INFINITY,\n};\n\nlet levelOverride: LogLevel | undefined;\n\nfunction envLevel(): LogLevel | undefined {\n const raw = process.env.LOG_LEVEL?.toLowerCase();\n if (raw && raw in LEVEL_SEVERITY) {\n return raw as LogLevel;\n }\n return;\n}\n\nfunction defaultLevel(): LogLevel {\n return (process.env.DEPLOYMENT_ENV ?? \"development\") === \"development\"\n ? \"debug\"\n : \"info\";\n}\n\n/**\n * Resolve the active level fresh on each call so that `LOG_LEVEL` changes and\n * `setLogLevel()` both take effect immediately. Resolution order (env wins, to\n * match the rest of the package's config story):\n * 1. `LOG_LEVEL` env var\n * 2. `setLogLevel()` / `setupOtel({ logLevel })`\n * 3. environment-driven default (`debug` in development, `info` otherwise)\n */\nfunction resolveLevel(): LogLevel {\n return envLevel() ?? levelOverride ?? defaultLevel();\n}\n\n/**\n * Programmatically set the minimum log level. Takes effect immediately for\n * subsequent logs. `LOG_LEVEL` env var still wins if set.\n */\nexport function setLogLevel(level: LogLevel): void {\n levelOverride = level;\n}\n\n/** Current effective log level, after env / override / default resolution. */\nexport function getLogLevel(): LogLevel {\n return resolveLevel();\n}\n\nlet scopedLogger: Logger | undefined;\n\nfunction getLogger(): Logger {\n if (!scopedLogger) {\n scopedLogger = logs.getLogger(\"@photon-ai/otel\", PHOTON_OTEL_VERSION);\n }\n return scopedLogger;\n}\n\nfunction filterUndefined(\n attrs?: LogAttrs\n): Record<string, string | number | boolean> {\n if (!attrs) {\n return {};\n }\n const out: Record<string, string | number | boolean> = {};\n for (const [k, v] of Object.entries(attrs)) {\n if (v !== undefined) {\n out[k] = v;\n }\n }\n return out;\n}\n\nfunction consoleFor(\n severityNumber: SeverityNumber\n): (...args: unknown[]) => void {\n if (severityNumber >= SeverityNumber.ERROR) {\n return console.error;\n }\n if (severityNumber >= SeverityNumber.WARN) {\n return console.warn;\n }\n if (severityNumber >= SeverityNumber.INFO) {\n return console.info;\n }\n return console.debug;\n}\n\nfunction emit(\n severityNumber: SeverityNumber,\n severityText: string,\n module: string,\n message: string,\n attrs?: LogAttrs,\n error?: unknown\n): void {\n // Single gate: drop sub-threshold logs before they reach OTLP or the console.\n if (severityNumber < LEVEL_SEVERITY[resolveLevel()]) {\n return;\n }\n\n const userAttrs = filterUndefined(attrs);\n const attributes: Record<string, string | number | boolean> = {\n \"log.module\": module,\n ...userAttrs,\n };\n\n if (error instanceof Error) {\n attributes[\"exception.type\"] = error.name;\n attributes[\"exception.message\"] = error.message;\n if (error.stack) {\n attributes[\"exception.stacktrace\"] = error.stack;\n }\n } else if (error !== undefined) {\n // Don't silently drop non-Error throws (strings, plain objects, etc.).\n attributes[\"exception.type\"] = typeof error;\n attributes[\"exception.message\"] = String(error);\n }\n\n getLogger().emit({\n severityNumber,\n severityText,\n body: message,\n attributes,\n context: otelContext.active(),\n });\n\n // Console: `[module] LEVEL message { ...attrs }` plus the raw error so the\n // runtime renders the full stack and pretty-prints the attribute bag.\n const extras: unknown[] = [];\n if (Object.keys(userAttrs).length > 0) {\n extras.push(userAttrs);\n }\n if (error !== undefined) {\n extras.push(error);\n }\n consoleFor(severityNumber)(`[${module}]`, severityText, message, ...extras);\n}\n\nexport interface PhotonLogger {\n debug(message: string, attrs?: LogAttrs, error?: unknown): void;\n error(message: string, attrs?: LogAttrs, error?: unknown): void;\n info(message: string, attrs?: LogAttrs, error?: unknown): void;\n warn(message: string, attrs?: LogAttrs, error?: unknown): void;\n}\n\nexport function createLogger(module: string): PhotonLogger {\n return {\n debug: (message, attrs, error) =>\n emit(SeverityNumber.DEBUG, \"DEBUG\", module, message, attrs, error),\n info: (message, attrs, error) =>\n emit(SeverityNumber.INFO, \"INFO\", module, message, attrs, error),\n warn: (message, attrs, error) =>\n emit(SeverityNumber.WARN, \"WARN\", module, message, attrs, error),\n error: (message, attrs, error) =>\n emit(SeverityNumber.ERROR, \"ERROR\", module, message, attrs, error),\n };\n}\n","import type {\n FetchInstrumentation,\n InstrumentFetchOptions,\n} from \"./instrument-fetch\";\n\n/**\n * A `require`-like loader. Injected (rather than calling `createRequire` in this\n * module) so the native path is unit-testable on any runtime — including Bun,\n * where the real packages would never be loaded — with a fake implementation.\n */\nexport type RequireFn = (id: string) => unknown;\n\n/** Minimal shape of the request object passed to undici's `ignoreRequestHook`. */\ninterface UndiciRequestLike {\n origin: string;\n path: string;\n}\n\ninterface UndiciInstrumentationConfig {\n ignoreRequestHook?: (request: UndiciRequestLike) => boolean;\n}\n\ninterface UndiciInstrumentationInstance {\n disable(): void;\n}\n\ntype UndiciInstrumentationCtor = new (\n config?: UndiciInstrumentationConfig\n) => UndiciInstrumentationInstance;\n\ntype RegisterInstrumentationsFn = (config: {\n instrumentations: unknown[];\n}) => unknown;\n\n/**\n * Reconstruct the absolute URL undici describes from `origin` + `path`, matching\n * how the instrumentation builds `url.full`. This lets the caller's `ignore(url)`\n * predicate (and the OTLP self-trace exclusion) behave identically to the\n * global-wrap path.\n */\nfunction toAbsoluteUrl(request: UndiciRequestLike): string {\n try {\n return new URL(request.path, request.origin).toString();\n } catch {\n return `${request.origin}${request.path}`;\n }\n}\n\n/** Node's \"module isn't installed\" errors carry one of these messages. */\nconst MODULE_NOT_FOUND_MESSAGE = /Cannot find (module|package)/;\n\n/**\n * True only when `error` signals an optional package being absent, so the caller\n * can safely fall back to the `globalThis.fetch` wrap. A version mismatch or a\n * throw from the package's own initialization is a real failure and must be\n * rethrown rather than masked as \"not installed\".\n */\nfunction isModuleNotFoundError(error: unknown): boolean {\n if (typeof error !== \"object\" || error === null) {\n return false;\n }\n const { code, message } = error as { code?: unknown; message?: unknown };\n if (code === \"MODULE_NOT_FOUND\" || code === \"ERR_MODULE_NOT_FOUND\") {\n return true;\n }\n return typeof message === \"string\" && MODULE_NOT_FOUND_MESSAGE.test(message);\n}\n\n/**\n * Register `@opentelemetry/instrumentation-undici` — Node's native fetch\n * instrumentation, which reads the global tracer provider and propagator that\n * `setupOtel()` installs. Returns `undefined` when the optional packages aren't\n * installed, or when static `attributes` are requested (the undici path has no\n * hook to stamp them on every span), so the caller can fall back to the\n * `globalThis.fetch` wrap.\n *\n * The packages are referenced only through `requireFn(...)` string calls (never\n * a static `import`), so esbuild can't bundle them and Bun never loads them.\n */\nexport function instrumentFetchNative(\n options: InstrumentFetchOptions | undefined,\n requireFn: RequireFn\n): FetchInstrumentation | undefined {\n // The undici instrumentation has no hook to stamp caller-supplied static\n // attributes onto every span, so when they're requested, decline the native\n // path and let the caller fall back to the globalThis.fetch wrap (which does\n // apply them).\n if (options?.attributes && Object.keys(options.attributes).length > 0) {\n return;\n }\n\n let UndiciInstrumentation: UndiciInstrumentationCtor;\n let registerInstrumentations: RegisterInstrumentationsFn;\n try {\n const undiciModule = requireFn(\"@opentelemetry/instrumentation-undici\") as {\n UndiciInstrumentation: UndiciInstrumentationCtor;\n };\n const instrumentationModule = requireFn(\n \"@opentelemetry/instrumentation\"\n ) as {\n registerInstrumentations: RegisterInstrumentationsFn;\n };\n UndiciInstrumentation = undiciModule.UndiciInstrumentation;\n registerInstrumentations = instrumentationModule.registerInstrumentations;\n } catch (error) {\n // Only \"package not installed\" is a valid fall-back-to-wrap signal; a\n // version mismatch or a broken install must surface, not be swallowed.\n if (isModuleNotFoundError(error)) {\n return;\n }\n throw error;\n }\n\n const userIgnore = options?.ignore;\n const instrumentation = new UndiciInstrumentation({\n ignoreRequestHook: userIgnore\n ? (request) => userIgnore(toAbsoluteUrl(request))\n : undefined,\n });\n registerInstrumentations({ instrumentations: [instrumentation] });\n\n return {\n unpatch() {\n instrumentation.disable();\n },\n };\n}\n","/**\n * `true` when running on Bun, `false` on Node (or any other runtime).\n *\n * This is the one place the library detects its runtime. Bun's native `fetch`\n * emits no `diagnostics_channel` events, so the official OpenTelemetry\n * instrumentations (`instrumentation-undici` / `-http`) produce no spans there\n * — we must wrap `globalThis.fetch` instead. On Node we prefer the native\n * undici instrumentation. `setupOtel()` branches on this constant.\n *\n * `process.versions.bun` is the canonical signal: it survives deletion of the\n * `Bun` global and matches the codebase's `process.*` convention. Evaluated\n * once at module load — the runtime never changes mid-process.\n */\nexport const IS_BUN: boolean =\n typeof process !== \"undefined\" && process.versions?.bun !== undefined;\n","import { createRequire } from \"node:module\";\nimport { context, propagation, trace } from \"@opentelemetry/api\";\nimport { logs } from \"@opentelemetry/api-logs\";\nimport { AsyncLocalStorageContextManager } from \"@opentelemetry/context-async-hooks\";\nimport {\n CompositePropagator,\n W3CBaggagePropagator,\n W3CTraceContextPropagator,\n} from \"@opentelemetry/core\";\nimport { OTLPLogExporter } from \"@opentelemetry/exporter-logs-otlp-http\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport {\n BatchLogRecordProcessor,\n LoggerProvider,\n} from \"@opentelemetry/sdk-logs\";\nimport {\n BasicTracerProvider,\n BatchSpanProcessor,\n} from \"@opentelemetry/sdk-trace-base\";\nimport {\n type FetchInstrumentation,\n type InstrumentFetchOptions,\n instrumentFetch,\n} from \"./instrument-fetch\";\nimport { instrumentFetchNative } from \"./instrument-fetch-native\";\nimport { type LogLevel, setLogLevel } from \"./logger\";\nimport { IS_BUN } from \"./runtime\";\n\nexport interface SetupOtelOptions {\n /**\n * Default OTLP/HTTP base endpoint (e.g. `https://otel.example.com`). The\n * `/v1/traces` and `/v1/logs` paths are appended automatically. Standard\n * `OTEL_EXPORTER_OTLP_*` env vars always take precedence.\n */\n endpoint?: string;\n /**\n * Default OTLP headers (e.g. `{ Authorization: \"Basic ...\" }`). Merged with\n * any headers parsed from `OTEL_EXPORTER_OTLP_HEADERS`; env values win on\n * conflicts.\n */\n headers?: Record<string, string>;\n /**\n * Auto-instrument outbound `globalThis.fetch` with CLIENT spans and W3C\n * trace-context propagation. On Bun this is the only fetch instrumentation\n * that works (diagnostics_channel-based instrumentations emit nothing on\n * Bun's native fetch); it works identically on Node.\n *\n * `true` enables with defaults; pass an object to filter URLs via `ignore`.\n * Defaults to enabled when a traces endpoint is configured. Pass `false` to\n * disable.\n */\n instrumentFetch?: boolean | InstrumentFetchOptions;\n /**\n * Minimum log level emitted by `createLogger()` (to both OTLP and console).\n * The `LOG_LEVEL` env var still takes precedence. Defaults to `debug` in\n * development and `info` otherwise.\n */\n logLevel?: LogLevel;\n /**\n * Extra resource attributes attached to every span/log alongside\n * `service.name` / `service.version`.\n */\n resourceAttributes?: Record<string, string | number | boolean>;\n serviceName: string;\n serviceVersion?: string;\n}\n\nexport interface OtelHandle {\n shutdown(): Promise<void>;\n}\n\nlet activeHandle: OtelHandle | undefined;\n\nconst TRAILING_SLASH = /\\/$/;\n\nfunction parseEnvHeaders(raw: string | undefined): Record<string, string> {\n if (!raw) {\n return {};\n }\n const out: Record<string, string> = {};\n for (const pair of raw.split(\",\")) {\n const eq = pair.indexOf(\"=\");\n if (eq <= 0) {\n continue;\n }\n const key = pair.slice(0, eq).trim();\n const value = pair.slice(eq + 1).trim();\n if (key) {\n out[key] = value;\n }\n }\n return out;\n}\n\nfunction resolveTracesEndpoint(base: string | undefined): string | undefined {\n const traces = process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT;\n if (traces) {\n return traces;\n }\n const generic = process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? base;\n return generic\n ? `${generic.replace(TRAILING_SLASH, \"\")}/v1/traces`\n : undefined;\n}\n\nfunction resolveLogsEndpoint(base: string | undefined): string | undefined {\n const logsEndpoint = process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT;\n if (logsEndpoint) {\n return logsEndpoint;\n }\n const generic = process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? base;\n return generic ? `${generic.replace(TRAILING_SLASH, \"\")}/v1/logs` : undefined;\n}\n\n/**\n * Normalize a URL to an `origin + path` key (trailing slash stripped) for exact\n * self-trace matching. Returns `undefined` for unparseable URLs.\n */\nfunction otlpEndpointKey(url: string): string | undefined {\n try {\n const parsed = new URL(url);\n return `${parsed.origin}${parsed.pathname.replace(TRAILING_SLASH, \"\")}`;\n } catch {\n return;\n }\n}\n\nfunction otlpEndpointKeysOf(\n tracesEndpoint: string | undefined,\n logsEndpoint: string | undefined\n): string[] {\n const keys: string[] = [];\n for (const endpoint of [tracesEndpoint, logsEndpoint]) {\n if (!endpoint) {\n continue;\n }\n const key = otlpEndpointKey(endpoint);\n if (key) {\n keys.push(key);\n }\n }\n return keys;\n}\n\n/**\n * Start fetch instrumentation unless disabled. Defaults to on when a traces\n * pipeline is configured. On Node (mode `\"auto\"`) this registers the native\n * `@opentelemetry/instrumentation-undici`; on Bun, or with mode `\"global\"`, it\n * wraps `globalThis.fetch`. Always excludes our own OTLP endpoints so the\n * exporter's traffic is never self-traced (matters on Node, where the OTLP\n * exporter can use fetch).\n */\nfunction startFetchInstrumentation(\n option: boolean | InstrumentFetchOptions | undefined,\n hasTraces: boolean,\n tracesEndpoint: string | undefined,\n logsEndpoint: string | undefined\n): FetchInstrumentation | undefined {\n const want = option ?? hasTraces;\n if (!want) {\n return;\n }\n const userOptions = typeof option === \"object\" ? option : undefined;\n const otlpEndpointKeys = otlpEndpointKeysOf(tracesEndpoint, logsEndpoint);\n const ignore = (url: string): boolean => {\n const key = otlpEndpointKey(url);\n const isOtlpEndpoint = key !== undefined && otlpEndpointKeys.includes(key);\n return isOtlpEndpoint || (userOptions?.ignore?.(url) ?? false);\n };\n\n // \"auto\" (default) prefers Node's native undici instrumentation; \"global\"\n // forces the globalThis.fetch wrap. Native never applies on Bun, whose fetch\n // emits no diagnostics_channel events. Fall back to the wrap when the optional\n // undici packages aren't installed, so Node still gets fetch spans.\n if ((userOptions?.mode ?? \"auto\") === \"auto\" && !IS_BUN) {\n const native = instrumentFetchNative(\n { ...userOptions, ignore },\n createRequire(import.meta.url)\n );\n if (native) {\n return native;\n }\n }\n // Forward the user's options (e.g. static `attributes`) to the wrap too; the\n // composed `ignore` overrides any user-supplied one so OTLP self-traces stay\n // excluded.\n return instrumentFetch({ ...userOptions, ignore });\n}\n\n/**\n * Boot an OTLP/HTTP-based OpenTelemetry pipeline (traces + logs).\n *\n * Idempotent: calling twice in the same process is a no-op on the second\n * call, so libraries can safely invoke this without clobbering an app-level\n * OTel setup that ran earlier.\n *\n * Standard `OTEL_EXPORTER_OTLP_*` env vars override the `endpoint` and\n * `headers` arguments — this matches the OpenTelemetry SDK config spec.\n */\nexport function setupOtel(options: SetupOtelOptions): OtelHandle {\n if (activeHandle) {\n return activeHandle;\n }\n\n if (options.logLevel) {\n setLogLevel(options.logLevel);\n }\n\n const tracesEndpoint = resolveTracesEndpoint(options.endpoint);\n const logsEndpoint = resolveLogsEndpoint(options.endpoint);\n const mergedHeaders = {\n ...options.headers,\n ...parseEnvHeaders(process.env.OTEL_EXPORTER_OTLP_HEADERS),\n };\n const hasHeaders = Object.keys(mergedHeaders).length > 0;\n\n const resource = resourceFromAttributes({\n \"service.name\": options.serviceName,\n ...(options.serviceVersion\n ? { \"service.version\": options.serviceVersion }\n : {}),\n \"deployment.environment\": process.env.DEPLOYMENT_ENV ?? \"development\",\n ...options.resourceAttributes,\n });\n\n context.setGlobalContextManager(new AsyncLocalStorageContextManager());\n propagation.setGlobalPropagator(\n new CompositePropagator({\n propagators: [\n new W3CTraceContextPropagator(),\n new W3CBaggagePropagator(),\n ],\n })\n );\n\n const traceProcessors = tracesEndpoint\n ? [\n new BatchSpanProcessor(\n new OTLPTraceExporter({\n url: tracesEndpoint,\n headers: hasHeaders ? mergedHeaders : undefined,\n })\n ),\n ]\n : [];\n\n const tracerProvider = new BasicTracerProvider({\n resource,\n spanProcessors: traceProcessors,\n });\n trace.setGlobalTracerProvider(tracerProvider);\n\n const fetchInstrumentation = startFetchInstrumentation(\n options.instrumentFetch,\n traceProcessors.length > 0,\n tracesEndpoint,\n logsEndpoint\n );\n\n const logProcessors = logsEndpoint\n ? [\n new BatchLogRecordProcessor(\n new OTLPLogExporter({\n url: logsEndpoint,\n headers: hasHeaders ? mergedHeaders : undefined,\n })\n ),\n ]\n : [];\n\n const loggerProvider = new LoggerProvider({\n resource,\n processors: logProcessors,\n });\n logs.setGlobalLoggerProvider(loggerProvider);\n\n const handle: OtelHandle = {\n async shutdown() {\n fetchInstrumentation?.unpatch();\n await Promise.allSettled([\n tracerProvider.shutdown(),\n loggerProvider.shutdown(),\n ]);\n activeHandle = undefined;\n },\n };\n\n activeHandle = handle;\n return handle;\n}\n\n/**\n * Read-only accessor for tests / debug paths that need to know whether\n * `setupOtel` has already run in this process.\n */\nexport function isOtelActive(): boolean {\n return activeHandle !== undefined;\n}\n","import {\n type Attributes,\n SpanStatusCode,\n type Tracer,\n trace,\n} from \"@opentelemetry/api\";\nimport type { LogAttrs } from \"./logger\";\nimport { sanitizeErrorMessage } from \"./sanitize\";\nimport { PHOTON_OTEL_VERSION } from \"./version\";\n\nlet scopedTracer: Tracer | undefined;\n\nfunction getTracer(): Tracer {\n if (!scopedTracer) {\n scopedTracer = trace.getTracer(\"@photon-ai/otel\", PHOTON_OTEL_VERSION);\n }\n return scopedTracer;\n}\n\nfunction toAttributes(attrs: LogAttrs): Attributes {\n const out: Attributes = {};\n for (const [k, v] of Object.entries(attrs)) {\n if (v !== undefined) {\n out[k] = v;\n }\n }\n return out;\n}\n\nexport function withSpan<T>(name: string, fn: () => Promise<T> | T): Promise<T>;\nexport function withSpan<T>(\n name: string,\n attrs: LogAttrs,\n fn: () => Promise<T> | T\n): Promise<T>;\nexport function withSpan<T>(\n name: string,\n attrsOrFn: LogAttrs | (() => Promise<T> | T),\n maybeFn?: () => Promise<T> | T\n): Promise<T> {\n const fn = typeof attrsOrFn === \"function\" ? attrsOrFn : maybeFn;\n if (!fn) {\n throw new Error(\"withSpan: function argument is required\");\n }\n const attrs = typeof attrsOrFn === \"function\" ? undefined : attrsOrFn;\n\n return getTracer().startActiveSpan(name, async (span) => {\n if (attrs) {\n span.setAttributes(toAttributes(attrs));\n }\n try {\n const result = await fn();\n span.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (err) {\n span.recordException(err as Error);\n const errorObj = err instanceof Error ? err : undefined;\n span.setAttribute(\"error.type\", errorObj?.constructor.name ?? typeof err);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: errorObj\n ? sanitizeErrorMessage(errorObj.message)\n : sanitizeErrorMessage(String(err)),\n });\n throw err;\n } finally {\n span.end();\n }\n });\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@photon-ai/otel",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "DX-focused OpenTelemetry wrapper for Bun and Node.js: one-call setup, structured logging, span helper, and built-in PII scrubbing.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"typescript": "^5 || ^6.0.0"
|
|
33
33
|
},
|
|
34
|
+
"optionalDependencies": {
|
|
35
|
+
"@opentelemetry/instrumentation": "^0.219.0",
|
|
36
|
+
"@opentelemetry/instrumentation-undici": "^0.29.0"
|
|
37
|
+
},
|
|
34
38
|
"engines": {
|
|
35
39
|
"node": ">=20"
|
|
36
40
|
},
|