@cleverbrush/otel 4.3.0 → 4.3.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/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{metrics as
|
|
1
|
+
import{metrics as q,trace as Q}from"@opentelemetry/api";var I=Symbol.for("ITracer"),M=Symbol.for("IMeter");function W(e,n){let r=n?.tracerName??"@cleverbrush/otel",o=n?.meterName??"@cleverbrush/otel",u=n?.version;typeof e?.addSingleton=="function"&&(e.addSingleton(I,()=>Q.getTracer(r,u)),e.addSingleton(M,()=>q.getMeter(o,u)))}import{trace as $}from"@opentelemetry/api";function B(){return e=>{let n=$.getActiveSpan();if(!n)return e;let r=n.spanContext();return r?.traceId?{...e,properties:{...e.properties,TraceId:r.traceId,SpanId:r.spanId,TraceFlags:r.traceFlags}}:e}}import{SpanKind as z,SpanStatusCode as L,trace as Y}from"@opentelemetry/api";import{ATTR_DB_NAMESPACE as F,ATTR_DB_OPERATION_NAME as G,ATTR_DB_QUERY_TEXT as j,ATTR_DB_SYSTEM_NAME as X,ATTR_SERVER_ADDRESS as Z,ATTR_SERVER_PORT as J}from"@opentelemetry/semantic-conventions/incubating";var ee=/^\s*([A-Za-z]+)/,te=new Set(["SELECT","INSERT","UPDATE","DELETE","MERGE","REPLACE","CALL","CREATE","ALTER","DROP","TRUNCATE","EXPLAIN","ANALYZE","SHOW","WITH","EXECUTE","UPSERT"]);function re(e,n){if(n)return n.toUpperCase();let o=ee.exec(e)?.[1]?.toUpperCase();return o&&te.has(o)?o:void 0}function ne(e){let n=e?.dialect??e?.config?.client;if(!n)return;let r=String(n).toLowerCase();return r.startsWith("pg")||r.includes("postgres")?"postgresql":r.includes("mysql")||r==="mysql2"?"mysql":r.includes("sqlite")||r==="better-sqlite3"?"sqlite":r.includes("mssql")?"mssql":r.includes("oracle")?"oracle":r}function oe(e,n){let r=n?.tracerName??"@cleverbrush/otel/knex",o=n?.recordStatement??!0,u=n?.sanitizeStatement,l,T=()=>(l||(l=Y.getTracer(r)),l),m=e.client,s=n?.dbSystem??ne(m)??"other_sql",t=m?.config?.connection??{},p=typeof t=="object"?t.database:void 0,c=typeof t=="object"?t.host:void 0,E=typeof t=="object"&&typeof t.port=="number"?t.port:void 0,h=new Map,i={[X]:s};return p&&(i[F]=p),c&&(i[Z]=c),typeof E=="number"&&(i[J]=E),e.on?.("query",d=>{let a=d.__knexQueryUid;if(!a)return;let f=re(d.sql,d.method),g=f?p?`${f} ${p}`:f:"db.query",y={...i};f&&(y[G]=f),o&&d.sql&&(y[j]=u?u(d.sql):d.sql);let x=T().startSpan(g,{kind:z.CLIENT,attributes:y});h.set(a,x)}),e.on?.("query-response",(d,a)=>{let f=a?.__knexQueryUid;if(!f)return;let g=h.get(f);g&&(h.delete(f),g.setStatus({code:L.OK}),g.end())}),e.on?.("query-error",(d,a)=>{let f=a?.__knexQueryUid;if(!f)return;let g=h.get(f);g&&(h.delete(f),d instanceof Error?(g.recordException(d),g.setStatus({code:L.ERROR,message:d.message})):g.setStatus({code:L.ERROR,message:String(d)}),g.end())}),e}import{context as C,propagation as se,SpanKind as ie,SpanStatusCode as N,trace as k}from"@opentelemetry/api";import{ATTR_HTTP_REQUEST_METHOD as ae,ATTR_HTTP_RESPONSE_STATUS_CODE as D,ATTR_HTTP_ROUTE as ce,ATTR_SERVER_ADDRESS as de,ATTR_SERVER_PORT as ue,ATTR_URL_PATH as pe,ATTR_URL_QUERY as le,ATTR_URL_SCHEME as fe,ATTR_USER_AGENT_ORIGINAL as me}from"@opentelemetry/semantic-conventions";var U="otel.span";function ge(e){return e?.items?.get("__endpoint_meta")}function Se(e){let r=e?.items?.get("__batch_subrequest");if(!(typeof r?.index!="number"||typeof r.size!="number"||typeof r.path!="string"))return{index:r.index,size:r.size,path:r.path}}function Te(e){return/^00-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$/i.test(e.traceparent??"")}function Ee(e){if(!e)return;let n=e.basePath??"",r=e.pathTemplate;if(typeof r=="string")return`${n}${r}`;let o=r?.template??r?.pattern??r?.toString?.()??null;return typeof o=="string"?`${n}${o}`:n||void 0}function Re(e){let n=e?.tracerName??"@cleverbrush/otel",r=e?.tracerVersion,o=new Set((e?.excludePaths??[]).map(t=>typeof t=="string"?t:t.path)),u=e?.enrichSpan,l=e?.recordQuery??!1,T=e?.responseTraceHeader===!1?!1:e?.responseTraceHeader??"X-Trace-Id",m,s=()=>(m||(m=k.getTracer(n,r)),m);return async(t,p)=>{let c=t.url,E=c?.pathname??t.path??"";if(o.has(E)){await p();return}let h=s(),i=t.headers??{},d=se.extract(C.active(),i),a=Se(t),f=a?C.active():d,g=a&&Te(i)?k.getSpanContext(d):void 0,y=ge(t),x=Ee(y),v=(t.method??"GET").toUpperCase(),V=y?.operationId||(x?`${v} ${x}`:`${v} ${E}`),R={[ae]:v,[pe]:E};if(c?.protocol&&(R[fe]=c.protocol.replace(/:$/,"")),c?.hostname&&(R[de]=c.hostname),c?.port){let S=Number(c.port);Number.isFinite(S)&&(R[ue]=S)}x&&(R[ce]=x),i["user-agent"]&&(R[me]=i["user-agent"]),l&&c?.search&&(R[le]=c.search.replace(/^\?/,"")),y?.tags?.length&&(R["cleverbrush.endpoint.tags"]=y.tags.join(",")),y?.operationId&&(R["cleverbrush.endpoint.operationId"]=y.operationId),a&&(R["cleverbrush.batch.index"]=a.index,R["cleverbrush.batch.size"]=a.size,R["cleverbrush.batch.path"]=a.path);let K={kind:ie.SERVER,attributes:R,...g?{links:[{context:g}]}:{}};await h.startActiveSpan(V,K,f,async S=>{if(t.items?.set?.(U,S),T){let{traceId:b}=S.spanContext();if(b){let A=t.response;A?.setHeader?A.setHeader(T,b):t.setHeader&&t.setHeader(T,b)}}if(u)try{u(S,t)}catch{}try{await p();let b=t.response?.statusCode??t.statusCode??200;S.setAttribute(D,b),b>=500?S.setStatus({code:N.ERROR}):S.setStatus({code:N.OK})}catch(b){let A=t.response?.statusCode??t.statusCode??500;throw S.setAttribute(D,A),b instanceof Error?(S.recordException(b),S.setStatus({code:N.ERROR,message:b.message})):S.setStatus({code:N.ERROR,message:String(b)}),b}finally{S.end()}})}}import{DiagConsoleLogger as be,DiagLogLevel as ye,diag as he}from"@opentelemetry/api";import{OTLPLogExporter as xe}from"@opentelemetry/exporter-logs-otlp-http";import{OTLPMetricExporter as _e}from"@opentelemetry/exporter-metrics-otlp-http";import{OTLPTraceExporter as Oe}from"@opentelemetry/exporter-trace-otlp-http";import{resourceFromAttributes as Ae}from"@opentelemetry/resources";import{BatchLogRecordProcessor as Ne}from"@opentelemetry/sdk-logs";import{PeriodicExportingMetricReader as ve}from"@opentelemetry/sdk-metrics";import{NodeSDK as Le}from"@opentelemetry/sdk-node";import{BatchSpanProcessor as we}from"@opentelemetry/sdk-trace-node";import{ATTR_DEPLOYMENT_ENVIRONMENT_NAME as Pe,ATTR_SERVICE_NAME as Ie,ATTR_SERVICE_VERSION as Me}from"@opentelemetry/semantic-conventions/incubating";function w(e,n,r){if(n)return n;if(e)return`${e.replace(/\/$/,"")}${r}`}function Ce(e){if(!e.serviceName)throw new Error("setupOtel: `serviceName` is required");e.debug&&he.setLogger(new be,ye.DEBUG);let n={[Ie]:e.serviceName,...e.serviceVersion?{[Me]:e.serviceVersion}:{},...e.environment?{[Pe]:e.environment}:{},...e.resourceAttributes??{}},r=Ae(n),o=e.headers,u;if(!e.disableTraces){let t=w(e.otlpEndpoint,e.tracesEndpoint,"/v1/traces"),p=new Oe({...t?{url:t}:{},...o?{headers:o}:{}});u=[new we(p)]}let l;if(!e.disableLogs){let t=w(e.otlpEndpoint,e.logsEndpoint,"/v1/logs"),p=new xe({...t?{url:t}:{},...o?{headers:o}:{}});l=[new Ne(p)]}let T;if(!e.disableMetrics){let t=w(e.otlpEndpoint,e.metricsEndpoint,"/v1/metrics"),p=new _e({...t?{url:t}:{},...o?{headers:o}:{}});T=new ve({exporter:p,exportIntervalMillis:e.metricsExportIntervalMs??6e4})}let m=new Le({resource:r,...u?{spanProcessors:u}:{},...l?{logRecordProcessors:l}:{},...T?{metricReaders:[T]}:{},instrumentations:e.instrumentations??[]});m.start();let s=null;return{sdk:m,shutdown(){return s||(s=m.shutdown().catch(t=>{console.error("[otel] shutdown error:",t)})),s}}}import{LogLevel as _,levelToString as ke}from"@cleverbrush/log";import{logs as De,SeverityNumber as O}from"@opentelemetry/api-logs";var Ue={[_.Trace]:O.TRACE,[_.Debug]:O.DEBUG,[_.Information]:O.INFO,[_.Warning]:O.WARN,[_.Error]:O.ERROR,[_.Fatal]:O.FATAL},He=new Set(["string","number","boolean","bigint"]);function H(e){if(e==null)return e;if(!(typeof e=="function"||typeof e=="symbol")){if(He.has(typeof e))return e;if(Array.isArray(e))return e.map(H).filter(n=>n!==void 0);try{return JSON.stringify(e)}catch{return String(e)}}}function Ve(e){let n=e?.loggerName??"@cleverbrush/otel",r=e?.loggerVersion,o=e?.sanitizeAttribute,u,l=()=>(u||(u=De.getLogger(n,r)),u);return{async emit(T){let m=l();for(let s of T){let t={};for(let[p,c]of Object.entries(s.properties)){let E=o?o(p,c):H(c);E!==void 0&&(t[p]=E)}s.messageTemplate&&(t["cleverbrush.message_template"]=s.messageTemplate),s.eventId&&(t["cleverbrush.event_id"]=s.eventId),s.exception&&(t["exception.type"]=s.exception.name,t["exception.message"]=s.exception.message,s.exception.stack&&(t["exception.stacktrace"]=s.exception.stack)),m.emit({timestamp:s.timestamp.getTime(),severityNumber:Ue[s.level],severityText:ke(s.level).toUpperCase(),body:s.renderedMessage,attributes:t})}},async[Symbol.asyncDispose](){}}}import{SpanKind as Ke,SpanStatusCode as P,trace as qe}from"@opentelemetry/api";var Qe="@cleverbrush/otel";function We(e,n,r){let o=typeof n=="function",u=o?n:void 0,l=o?r:n,T=l?.tracerName??Qe,m=l?.tracerVersion,s=l?.kind??Ke.INTERNAL,t=l?.attributes,p=qe.getTracer(T,m);if(u!==void 0)return p.startActiveSpan(e,{kind:s,attributes:t},i=>{let d;try{d=u(i)}catch(a){throw i.recordException(a),i.setStatus({code:P.ERROR}),i.end(),a}return d instanceof Promise?d.then(a=>(i.end(),a),a=>{throw i.recordException(a),i.setStatus({code:P.ERROR}),i.end(),a}):(i.end(),d)});let c=p.startSpan(e,{kind:s,attributes:t}),E=!1;return{span:c,fail(i){E||(E=!0,c.recordException(i),c.setStatus({code:P.ERROR}))},[Symbol.asyncDispose](){return c.end(),Promise.resolve()}}}export{M as IMeter,I as ITracer,U as OTEL_SPAN_ITEM_KEY,W as configureOtel,oe as instrumentKnex,Ve as otelLogSink,Ce as setupOtel,B as traceEnricher,Re as tracingMiddleware,We as withSpan};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/di.ts","../src/enrichers/trace.ts","../src/knex/instrumentKnex.ts","../src/middleware/tracing.ts","../src/setupOtel.ts","../src/sinks/OtelLogSink.ts","../src/span.ts"],"sourcesContent":["import { type Meter, metrics, type Tracer, trace } from '@opentelemetry/api';\n\n/**\n * DI service key for an OpenTelemetry `Tracer`.\n *\n * Resolved from the global `TracerProvider` set by\n * {@link import('./setupOtel.js').setupOtel}. Components that prefer\n * dependency injection over the global API can inject this token.\n */\nexport const ITracer = Symbol.for('ITracer') as unknown as {\n __brand: 'ITracer';\n};\n\n/**\n * DI service key for an OpenTelemetry `Meter`.\n *\n * Resolved from the global `MeterProvider` set by\n * {@link import('./setupOtel.js').setupOtel}.\n */\nexport const IMeter = Symbol.for('IMeter') as unknown as {\n __brand: 'IMeter';\n};\n\n/**\n * Configuration for {@link configureOtel}.\n */\nexport interface ConfigureOtelOptions {\n /**\n * Tracer name.\n *\n * @default '@cleverbrush/otel'\n */\n tracerName?: string;\n\n /**\n * Meter name.\n *\n * @default '@cleverbrush/otel'\n */\n meterName?: string;\n\n /** Optional version string used for both tracer and meter. */\n version?: string;\n}\n\n/**\n * Registers OTel `Tracer` and `Meter` instances in the DI container.\n *\n * Both are resolved lazily from the global providers configured by\n * {@link import('./setupOtel.js').setupOtel}, so this helper can be\n * called at DI setup time even before the SDK has fully started.\n *\n * @param services - the `ServiceCollection` to register with\n * @param options - tracer / meter naming overrides\n *\n * @example\n * ```ts\n * import { configureOtel, ITracer } from '@cleverbrush/otel';\n *\n * configureOtel(services, { tracerName: 'todo-backend' });\n *\n * const tracer = provider.get(ITracer);\n * tracer.startActiveSpan('custom-work', span => {\n * // …\n * span.end();\n * });\n * ```\n */\nexport function configureOtel(\n services: any,\n options?: ConfigureOtelOptions\n): void {\n const tracerName = options?.tracerName ?? '@cleverbrush/otel';\n const meterName = options?.meterName ?? '@cleverbrush/otel';\n const version = options?.version;\n\n if (typeof services?.addSingleton === 'function') {\n services.addSingleton(\n ITracer,\n (): Tracer => trace.getTracer(tracerName, version)\n );\n services.addSingleton(\n IMeter,\n (): Meter => metrics.getMeter(meterName, version)\n );\n }\n}\n","import type { Enricher } from '@cleverbrush/log';\nimport { trace } from '@opentelemetry/api';\n\n/**\n * Creates a log enricher that attaches the active span's\n * `TraceId`, `SpanId`, and `TraceFlags` to every log event.\n *\n * Reads from the OpenTelemetry context via `@opentelemetry/api`,\n * so it works with any tracer provider — including the one\n * configured by {@link import('../setupOtel.js').setupOtel}.\n *\n * No-op when no span is active.\n *\n * @returns an enricher that adds `{ TraceId, SpanId, TraceFlags }` if a span is active\n *\n * @example\n * ```ts\n * import { createLogger, consoleSink } from '@cleverbrush/log';\n * import { traceEnricher } from '@cleverbrush/otel';\n *\n * const logger = createLogger({\n * sinks: [consoleSink()],\n * enrichers: [traceEnricher()],\n * });\n * ```\n */\nexport function traceEnricher(): Enricher {\n return event => {\n const span = trace.getActiveSpan();\n if (!span) return event;\n const ctx = span.spanContext();\n if (!ctx?.traceId) return event;\n return {\n ...event,\n properties: {\n ...event.properties,\n TraceId: ctx.traceId,\n SpanId: ctx.spanId,\n TraceFlags: ctx.traceFlags\n }\n };\n };\n}\n","import {\n type Span,\n SpanKind,\n SpanStatusCode,\n type Tracer,\n trace\n} from '@opentelemetry/api';\nimport {\n ATTR_DB_NAMESPACE,\n ATTR_DB_OPERATION_NAME,\n ATTR_DB_QUERY_TEXT,\n ATTR_DB_SYSTEM_NAME,\n ATTR_SERVER_ADDRESS,\n ATTR_SERVER_PORT\n} from '@opentelemetry/semantic-conventions/incubating';\nimport type { Knex } from 'knex';\n\n/**\n * Configuration for {@link instrumentKnex}.\n */\nexport interface InstrumentKnexOptions {\n /**\n * Value to record as `db.system.name` (e.g. `postgresql`, `mysql`,\n * `sqlite`). When omitted, inferred from the knex client.\n */\n dbSystem?: string;\n\n /**\n * Tracer name used when resolving the OTel tracer.\n *\n * @default '@cleverbrush/otel/knex'\n */\n tracerName?: string;\n\n /**\n * Whether to include the SQL statement as `db.query.text`.\n *\n * The statement is taken verbatim from knex (parameter placeholders\n * are kept; bound values are **not** included). Disable if your\n * SQL itself may contain sensitive identifiers.\n *\n * @default true\n */\n recordStatement?: boolean;\n\n /**\n * Optional hook to redact / rewrite the SQL before it is recorded.\n * Called only when {@link recordStatement} is enabled.\n */\n sanitizeStatement?: (sql: string) => string;\n}\n\ninterface KnexQueryEvent {\n sql: string;\n method?: string;\n bindings?: unknown[];\n __knexQueryUid?: string;\n}\n\nconst FIRST_KEYWORD_RE = /^\\s*([A-Za-z]+)/;\n\n// Recognised SQL operation keywords — anything outside this set is ignored\n// to prevent arbitrary SQL words becoming low-cardinality span names.\nconst KNOWN_SQL_OPS = new Set([\n 'SELECT',\n 'INSERT',\n 'UPDATE',\n 'DELETE',\n 'MERGE',\n 'REPLACE',\n 'CALL',\n 'CREATE',\n 'ALTER',\n 'DROP',\n 'TRUNCATE',\n 'EXPLAIN',\n 'ANALYZE',\n 'SHOW',\n 'WITH',\n 'EXECUTE',\n 'UPSERT'\n]);\n\nfunction inferOperation(sql: string, method?: string): string | undefined {\n if (method) return method.toUpperCase();\n const m = FIRST_KEYWORD_RE.exec(sql);\n const word = m?.[1]?.toUpperCase();\n return word && KNOWN_SQL_OPS.has(word) ? word : undefined;\n}\n\nfunction inferDbSystem(client: any): string | undefined {\n const dialect: string | undefined =\n client?.dialect ?? client?.config?.client;\n if (!dialect) return undefined;\n const norm = String(dialect).toLowerCase();\n if (norm.startsWith('pg') || norm.includes('postgres')) {\n return 'postgresql';\n }\n if (norm.includes('mysql') || norm === 'mysql2') return 'mysql';\n if (norm.includes('sqlite') || norm === 'better-sqlite3') return 'sqlite';\n if (norm.includes('mssql')) return 'mssql';\n if (norm.includes('oracle')) return 'oracle';\n return norm;\n}\n\n/**\n * Instruments a Knex instance to emit an OpenTelemetry `CLIENT` span\n * for every executed query.\n *\n * Hooks knex's built-in `query`, `query-response`, and `query-error`\n * events — every dbset read, change-tracker write, save-graph, and raw\n * `knex(...)` call is captured uniformly because they all flow through\n * the same knex instance.\n *\n * Spans automatically nest under any ambient OTel context, so DB spans\n * become children of the enclosing HTTP server span produced by\n * `tracingMiddleware`.\n *\n * Returns the same instance for fluent chaining:\n * `instrumentKnex(knex({...}))`.\n *\n * @param k - the knex instance to instrument (mutated in place)\n * @param options - optional overrides for db system, tracer name, redaction\n * @returns the same knex instance\n *\n * @example\n * ```ts\n * import knex from 'knex';\n * import { instrumentKnex } from '@cleverbrush/otel';\n *\n * services.addSingleton(KnexToken, () =>\n * instrumentKnex(\n * knex({ client: 'pg', connection: dbUrl }),\n * { dbSystem: 'postgresql' }\n * )\n * );\n * ```\n */\nexport function instrumentKnex<T extends Knex>(\n k: T,\n options?: InstrumentKnexOptions\n): T {\n const tracerName = options?.tracerName ?? '@cleverbrush/otel/knex';\n const recordStatement = options?.recordStatement ?? true;\n const sanitize = options?.sanitizeStatement;\n\n let cachedTracer: Tracer | undefined;\n const getTracer = (): Tracer => {\n if (!cachedTracer) cachedTracer = trace.getTracer(tracerName);\n return cachedTracer;\n };\n\n const client: any = (k as any).client;\n const dbSystem = options?.dbSystem ?? inferDbSystem(client) ?? 'other_sql';\n const connection: any = client?.config?.connection ?? {};\n const dbName: string | undefined =\n typeof connection === 'object' ? connection.database : undefined;\n const host: string | undefined =\n typeof connection === 'object' ? connection.host : undefined;\n const port: number | undefined =\n typeof connection === 'object' && typeof connection.port === 'number'\n ? connection.port\n : undefined;\n\n const active = new Map<string, Span>();\n\n const baseAttrs: Record<string, string | number> = {\n [ATTR_DB_SYSTEM_NAME]: dbSystem\n };\n if (dbName) baseAttrs[ATTR_DB_NAMESPACE] = dbName;\n if (host) baseAttrs[ATTR_SERVER_ADDRESS] = host;\n if (typeof port === 'number') baseAttrs[ATTR_SERVER_PORT] = port;\n\n (k as any).on?.('query', (q: KnexQueryEvent) => {\n const uid = q.__knexQueryUid;\n if (!uid) return;\n const operation = inferOperation(q.sql, q.method);\n const spanName = operation\n ? dbName\n ? `${operation} ${dbName}`\n : operation\n : 'db.query';\n\n const attrs: Record<string, string | number> = { ...baseAttrs };\n if (operation) attrs[ATTR_DB_OPERATION_NAME] = operation;\n if (recordStatement && q.sql) {\n attrs[ATTR_DB_QUERY_TEXT] = sanitize ? sanitize(q.sql) : q.sql;\n }\n\n const span = getTracer().startSpan(spanName, {\n kind: SpanKind.CLIENT,\n attributes: attrs\n });\n active.set(uid, span);\n });\n\n (k as any).on?.('query-response', (_resp: unknown, q: KnexQueryEvent) => {\n const uid = q?.__knexQueryUid;\n if (!uid) return;\n const span = active.get(uid);\n if (!span) return;\n active.delete(uid);\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n });\n\n (k as any).on?.('query-error', (err: unknown, q: KnexQueryEvent) => {\n const uid = q?.__knexQueryUid;\n if (!uid) return;\n const span = active.get(uid);\n if (!span) return;\n active.delete(uid);\n if (err instanceof Error) {\n span.recordException(err);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: err.message\n });\n } else {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: String(err)\n });\n }\n span.end();\n });\n\n return k;\n}\n","import {\n context,\n propagation,\n type Span,\n SpanKind,\n SpanStatusCode,\n type Tracer,\n trace\n} from '@opentelemetry/api';\nimport {\n ATTR_HTTP_REQUEST_METHOD,\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_HTTP_ROUTE,\n ATTR_SERVER_ADDRESS,\n ATTR_SERVER_PORT,\n ATTR_URL_PATH,\n ATTR_URL_QUERY,\n ATTR_URL_SCHEME,\n ATTR_USER_AGENT_ORIGINAL\n} from '@opentelemetry/semantic-conventions';\n\n/**\n * Attribute key under which the OTel server span is stashed on the\n * per-request `RequestContext.items` map.\n *\n * Downstream middleware/handlers can read this with\n * `ctx.items.get(OTEL_SPAN_ITEM_KEY)` to attach custom attributes\n * or events to the active server span.\n */\nexport const OTEL_SPAN_ITEM_KEY = 'otel.span';\n\n/**\n * Configuration for {@link tracingMiddleware}.\n */\nexport interface TracingMiddlewareOptions {\n /**\n * Tracer name used when resolving the OTel tracer.\n *\n * @default '@cleverbrush/otel'\n */\n tracerName?: string;\n\n /** Tracer version. */\n tracerVersion?: string;\n\n /**\n * Paths to exclude from tracing entirely (no span created).\n *\n * Accepts plain strings or objects with a `path` property\n * (e.g. an `EndpointBuilder`). Useful for `/health` and other\n * high-frequency, low-value endpoints.\n */\n excludePaths?: (string | { readonly path: string })[];\n\n /**\n * Hook for adding custom attributes to the server span just before\n * the inner pipeline runs. Errors thrown here are swallowed.\n */\n enrichSpan?: (span: Span, ctx: any) => void;\n\n /**\n * Whether to record the URL query string as `url.query`.\n *\n * Disabled by default because query strings frequently contain PII\n * (search terms, tokens). Enable explicitly only when you have\n * verified your URLs are safe to record.\n *\n * @default false\n */\n recordQuery?: boolean;\n\n /**\n * Name of the response header that carries the W3C trace ID for the\n * current request span.\n *\n * Expose this so API consumers can include it in bug reports and you\n * can look up the exact trace in SigNoz / Jaeger / etc.\n *\n * Set to `false` to disable the header entirely.\n *\n * @default 'X-Trace-Id'\n */\n responseTraceHeader?: string | false;\n}\n\nfunction getEndpointMeta(ctx: any): any | undefined {\n const items: Map<string, unknown> | undefined = ctx?.items;\n return items?.get('__endpoint_meta');\n}\n\nfunction getRouteTemplate(meta: any): string | undefined {\n if (!meta) return undefined;\n const base: string = meta.basePath ?? '';\n const tpl = meta.pathTemplate;\n if (typeof tpl === 'string') return `${base}${tpl}`;\n // ParseStringSchemaBuilder — try its `template` getter, otherwise toString\n const candidate =\n tpl?.template ?? tpl?.pattern ?? tpl?.toString?.() ?? null;\n if (typeof candidate === 'string') return `${base}${candidate}`;\n return base || undefined;\n}\n\n/**\n * Creates a `@cleverbrush/server` middleware that opens an OpenTelemetry\n * `SERVER` span for every incoming request.\n *\n * Should be registered as the **first** middleware so that the span\n * wraps CORS, auth, request logging, and the handler — capturing the\n * full request lifetime.\n *\n * Behavior:\n * - Extracts inbound trace context from request headers\n * (W3C `traceparent`, `baggage`).\n * - Names the span `${operationId}` if available, otherwise\n * `${method} ${http.route}`, otherwise `${method} ${url.path}`.\n * - Sets HTTP semantic-convention attributes\n * (`http.request.method`, `url.path`, `url.scheme`,\n * `server.address`, `user_agent.original`, `http.route`).\n * - Records `http.response.status_code` after `next()` completes.\n * - Marks the span `ERROR` and records the exception on uncaught errors.\n * - Stashes the span at `ctx.items.get(OTEL_SPAN_ITEM_KEY)` for\n * downstream code to enrich.\n *\n * @param options - tracing configuration\n * @returns a `Middleware` compatible with `@cleverbrush/server`\n *\n * @example\n * ```ts\n * import { tracingMiddleware } from '@cleverbrush/otel';\n *\n * createServer()\n * .use(tracingMiddleware({ excludePaths: ['/health'] }))\n * .use(corsMiddleware)\n * .use(authMiddleware)\n * .listen(3000);\n * ```\n */\nexport function tracingMiddleware(options?: TracingMiddlewareOptions) {\n const tracerName = options?.tracerName ?? '@cleverbrush/otel';\n const tracerVersion = options?.tracerVersion;\n const excludePaths = new Set(\n (options?.excludePaths ?? []).map(p =>\n typeof p === 'string' ? p : p.path\n )\n );\n const enrichSpan = options?.enrichSpan;\n const recordQuery = options?.recordQuery ?? false;\n const responseTraceHeader =\n options?.responseTraceHeader === false\n ? false\n : (options?.responseTraceHeader ?? 'X-Trace-Id');\n\n let cachedTracer: Tracer | undefined;\n const getTracer = (): Tracer => {\n if (!cachedTracer) {\n cachedTracer = trace.getTracer(tracerName, tracerVersion);\n }\n return cachedTracer;\n };\n\n return async (ctx: any, next: () => Promise<void>): Promise<void> => {\n const url: URL | undefined = ctx.url;\n const pathname = url?.pathname ?? ctx.path ?? '';\n\n if (excludePaths.has(pathname)) {\n await next();\n return;\n }\n\n const tracer = getTracer();\n const headers: Record<string, string> = ctx.headers ?? {};\n\n // Extract inbound trace context (W3C traceparent + baggage).\n const parentCtx = propagation.extract(context.active(), headers);\n\n const meta = getEndpointMeta(ctx);\n const route = getRouteTemplate(meta);\n const method: string = (ctx.method ?? 'GET').toUpperCase();\n // Span name follows OTel HTTP semconv (low-cardinality, descending priority):\n // 1. operationId — e.g. \"getTodoById\"\n // 2. METHOD ROUTE — e.g. \"GET /todos/{id}\"\n // 3. METHOD pathname — fallback when no route template is available\n const spanName: string =\n meta?.operationId ||\n (route ? `${method} ${route}` : `${method} ${pathname}`);\n\n const attributes: Record<string, string | number> = {\n [ATTR_HTTP_REQUEST_METHOD]: method,\n [ATTR_URL_PATH]: pathname\n };\n if (url?.protocol) {\n attributes[ATTR_URL_SCHEME] = url.protocol.replace(/:$/, '');\n }\n if (url?.hostname) {\n attributes[ATTR_SERVER_ADDRESS] = url.hostname;\n }\n if (url?.port) {\n const port = Number(url.port);\n if (Number.isFinite(port)) attributes[ATTR_SERVER_PORT] = port;\n }\n if (route) attributes[ATTR_HTTP_ROUTE] = route;\n if (headers['user-agent']) {\n attributes[ATTR_USER_AGENT_ORIGINAL] = headers['user-agent'];\n }\n if (recordQuery && url?.search) {\n attributes[ATTR_URL_QUERY] = url.search.replace(/^\\?/, '');\n }\n if (meta?.tags?.length) {\n attributes['cleverbrush.endpoint.tags'] = meta.tags.join(',');\n }\n if (meta?.operationId) {\n attributes['cleverbrush.endpoint.operationId'] = meta.operationId;\n }\n\n await tracer.startActiveSpan(\n spanName,\n { kind: SpanKind.SERVER, attributes },\n parentCtx,\n async (span: Span) => {\n ctx.items?.set?.(OTEL_SPAN_ITEM_KEY, span);\n\n // Write the trace ID to the response so consumers can look\n // up the exact trace in SigNoz / Jaeger / any backend.\n if (responseTraceHeader) {\n const { traceId } = span.spanContext();\n if (traceId) {\n const res = ctx.response;\n if (res?.setHeader) {\n res.setHeader(responseTraceHeader, traceId);\n } else if (ctx.setHeader) {\n ctx.setHeader(responseTraceHeader, traceId);\n }\n }\n }\n\n if (enrichSpan) {\n try {\n enrichSpan(span, ctx);\n } catch {\n // ignore enrichment errors\n }\n }\n\n try {\n await next();\n const status: number =\n ctx.response?.statusCode ?? ctx.statusCode ?? 200;\n span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, status);\n if (status >= 500) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n } catch (err) {\n const status: number =\n ctx.response?.statusCode ?? ctx.statusCode ?? 500;\n span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, status);\n if (err instanceof Error) {\n span.recordException(err);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: err.message\n });\n } else {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: String(err)\n });\n }\n throw err;\n } finally {\n span.end();\n }\n }\n );\n };\n}\n","import { DiagConsoleLogger, DiagLogLevel, diag } from '@opentelemetry/api';\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\nimport { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';\nimport { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport {\n BatchSpanProcessor,\n type SpanProcessor\n} from '@opentelemetry/sdk-trace-node';\nimport {\n ATTR_DEPLOYMENT_ENVIRONMENT_NAME,\n ATTR_SERVICE_NAME,\n ATTR_SERVICE_VERSION\n} from '@opentelemetry/semantic-conventions/incubating';\n\n/**\n * Configuration for {@link setupOtel}.\n */\nexport interface OtelConfig {\n /**\n * Logical name of the service emitting telemetry.\n * Becomes the `service.name` resource attribute and is the primary\n * identifier in observability backends.\n */\n serviceName: string;\n\n /** Optional service version → `service.version` resource attribute. */\n serviceVersion?: string;\n\n /**\n * Deployment environment name (e.g. `production`, `staging`, `dev`).\n * Becomes the `deployment.environment.name` resource attribute.\n */\n environment?: string;\n\n /**\n * Additional resource attributes merged onto the default resource.\n * Useful for `host.name`, `cloud.region`, custom team tags, etc.\n */\n resourceAttributes?: Record<string, string | number | boolean>;\n\n /**\n * Base OTLP/HTTP endpoint for traces, logs, and metrics.\n *\n * If not provided, falls back to the standard OTel environment\n * variables (`OTEL_EXPORTER_OTLP_ENDPOINT`,\n * `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`, etc.).\n *\n * Per-signal endpoints below take precedence over this value.\n *\n * @default process.env.OTEL_EXPORTER_OTLP_ENDPOINT\n */\n otlpEndpoint?: string;\n\n /** Override the OTLP traces endpoint (`/v1/traces` is appended). */\n tracesEndpoint?: string;\n\n /** Override the OTLP logs endpoint (`/v1/logs` is appended). */\n logsEndpoint?: string;\n\n /** Override the OTLP metrics endpoint (`/v1/metrics` is appended). */\n metricsEndpoint?: string;\n\n /**\n * Optional headers to send with every OTLP export request\n * (e.g. authentication tokens for hosted backends).\n */\n headers?: Record<string, string>;\n\n /** Disable trace export. @default false */\n disableTraces?: boolean;\n\n /** Disable log export. @default false */\n disableLogs?: boolean;\n\n /** Disable metrics export. @default false */\n disableMetrics?: boolean;\n\n /**\n * Metric export interval in milliseconds.\n * @default 60000\n */\n metricsExportIntervalMs?: number;\n\n /**\n * Auto-instrumentations to register at SDK startup.\n *\n * Use the helpers from `@cleverbrush/otel/instrumentations`\n * (e.g. `outboundHttpInstrumentations()`, `runtimeMetrics()`).\n */\n instrumentations?: unknown[];\n\n /**\n * Enable verbose OTel SDK diagnostics (sets the global `diag` logger).\n *\n * @default false\n */\n debug?: boolean;\n}\n\n/**\n * Handle returned by {@link setupOtel} for lifecycle management.\n */\nexport interface OtelHandle {\n /**\n * Gracefully flushes and shuts down all exporters.\n *\n * Idempotent — safe to call multiple times.\n * Should be invoked from your process's shutdown hook\n * (`SIGTERM`/`SIGINT`) before `process.exit`.\n */\n shutdown(): Promise<void>;\n\n /**\n * The underlying `NodeSDK` instance.\n *\n * Exposed for advanced use cases (custom span processors, runtime\n * configuration). Most consumers do not need to touch this directly.\n */\n sdk: NodeSDK;\n}\n\nfunction buildExporterUrl(\n base: string | undefined,\n perSignal: string | undefined,\n suffix: string\n): string | undefined {\n if (perSignal) return perSignal;\n if (!base) return undefined;\n return `${base.replace(/\\/$/, '')}${suffix}`;\n}\n\n/**\n * Initializes the OpenTelemetry Node SDK with sensible defaults for\n * the Cleverbrush framework.\n *\n * This must be called **before** any instrumented modules are imported\n * — typically via `node --import ./telemetry.js entrypoint.js`.\n *\n * Configures W3C Trace Context propagation, OTLP/HTTP exporters for\n * traces, logs, and metrics, and the resource attributes that identify\n * the service in observability backends.\n *\n * @param config - service identity and exporter configuration\n * @returns a handle exposing `shutdown()` and the underlying SDK\n *\n * @example\n * ```ts\n * // telemetry.ts — loaded via `node --import ./telemetry.js`\n * import { setupOtel } from '@cleverbrush/otel';\n * import { outboundHttpInstrumentations, runtimeMetrics } from '@cleverbrush/otel/instrumentations';\n *\n * export const otel = setupOtel({\n * serviceName: 'todo-backend',\n * serviceVersion: '1.0.0',\n * environment: process.env.NODE_ENV,\n * otlpEndpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,\n * instrumentations: [...outboundHttpInstrumentations(), runtimeMetrics()],\n * });\n *\n * process.on('SIGTERM', () => otel.shutdown());\n * ```\n */\nexport function setupOtel(config: OtelConfig): OtelHandle {\n if (!config.serviceName) {\n throw new Error('setupOtel: `serviceName` is required');\n }\n\n if (config.debug) {\n diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);\n }\n\n const attrs: Record<string, string | number | boolean> = {\n [ATTR_SERVICE_NAME]: config.serviceName,\n ...(config.serviceVersion\n ? { [ATTR_SERVICE_VERSION]: config.serviceVersion }\n : {}),\n ...(config.environment\n ? { [ATTR_DEPLOYMENT_ENVIRONMENT_NAME]: config.environment }\n : {}),\n ...(config.resourceAttributes ?? {})\n };\n const resource = resourceFromAttributes(attrs);\n\n const headers = config.headers;\n\n let spanProcessors: SpanProcessor[] | undefined;\n if (!config.disableTraces) {\n const url = buildExporterUrl(\n config.otlpEndpoint,\n config.tracesEndpoint,\n '/v1/traces'\n );\n const traceExporter = new OTLPTraceExporter({\n ...(url ? { url } : {}),\n ...(headers ? { headers } : {})\n });\n spanProcessors = [new BatchSpanProcessor(traceExporter)];\n }\n\n let logRecordProcessors: BatchLogRecordProcessor[] | undefined;\n if (!config.disableLogs) {\n const url = buildExporterUrl(\n config.otlpEndpoint,\n config.logsEndpoint,\n '/v1/logs'\n );\n const logExporter = new OTLPLogExporter({\n ...(url ? { url } : {}),\n ...(headers ? { headers } : {})\n });\n logRecordProcessors = [new BatchLogRecordProcessor(logExporter)];\n }\n\n let metricReader: PeriodicExportingMetricReader | undefined;\n if (!config.disableMetrics) {\n const url = buildExporterUrl(\n config.otlpEndpoint,\n config.metricsEndpoint,\n '/v1/metrics'\n );\n const metricExporter = new OTLPMetricExporter({\n ...(url ? { url } : {}),\n ...(headers ? { headers } : {})\n });\n metricReader = new PeriodicExportingMetricReader({\n exporter: metricExporter,\n exportIntervalMillis: config.metricsExportIntervalMs ?? 60_000\n });\n }\n\n const sdk = new NodeSDK({\n resource,\n ...(spanProcessors ? { spanProcessors } : {}),\n ...(logRecordProcessors ? { logRecordProcessors } : {}),\n ...(metricReader ? { metricReaders: [metricReader] } : {}),\n instrumentations: (config.instrumentations ?? []) as any\n });\n\n sdk.start();\n\n let shuttingDown: Promise<void> | null = null;\n\n return {\n sdk,\n shutdown(): Promise<void> {\n if (!shuttingDown) {\n shuttingDown = sdk.shutdown().catch(err => {\n // Best-effort shutdown — log but don't throw\n console.error('[otel] shutdown error:', err);\n });\n }\n return shuttingDown;\n }\n };\n}\n","import type { LogEvent, LogSink } from '@cleverbrush/log';\nimport { LogLevel, levelToString } from '@cleverbrush/log';\nimport {\n logs,\n type Logger as OtelLogger,\n SeverityNumber\n} from '@opentelemetry/api-logs';\n\n/**\n * Configuration for {@link otelLogSink}.\n */\nexport interface OtelLogSinkOptions {\n /**\n * Logger name (`InstrumentationScope`) under which records are\n * emitted via the OTel Logs API.\n *\n * @default '@cleverbrush/otel'\n */\n loggerName?: string;\n\n /** Optional logger version. */\n loggerVersion?: string;\n\n /**\n * Hook for redacting / dropping properties before they become\n * OTel log record attributes. Return `undefined` to drop the\n * attribute entirely.\n */\n sanitizeAttribute?: (key: string, value: unknown) => unknown | undefined;\n}\n\nconst SEVERITY_NUMBER: Record<LogLevel, SeverityNumber> = {\n [LogLevel.Trace]: SeverityNumber.TRACE,\n [LogLevel.Debug]: SeverityNumber.DEBUG,\n [LogLevel.Information]: SeverityNumber.INFO,\n [LogLevel.Warning]: SeverityNumber.WARN,\n [LogLevel.Error]: SeverityNumber.ERROR,\n [LogLevel.Fatal]: SeverityNumber.FATAL\n};\n\nconst SCALAR_TYPES = new Set(['string', 'number', 'boolean', 'bigint']);\n\nfunction toAttributeValue(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n if (typeof value === 'function' || typeof value === 'symbol') {\n return undefined;\n }\n if (SCALAR_TYPES.has(typeof value)) return value;\n if (Array.isArray(value)) {\n return value.map(toAttributeValue).filter(v => v !== undefined);\n }\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n/**\n * Creates a {@link LogSink} that forwards every event to the\n * OpenTelemetry Logs API.\n *\n * Maps each `LogEvent` to an OTel `LogRecord`:\n * - `timestamp` → nanoseconds via `Date.getTime()` × 1e6\n * - `level` → `severityNumber` and `severityText`\n * - `renderedMessage` → `body`\n * - `properties` → flat attribute map (functions / symbols dropped,\n * nested objects JSON-stringified)\n * - `messageTemplate` → `cleverbrush.message_template` attribute\n * - `eventId` → `cleverbrush.event_id` attribute\n * - `exception.*` attributes when the event carries an `Error`\n *\n * Trace correlation (`traceId`, `spanId`) is filled in automatically\n * by the OTel SDK from the active context — typically established by\n * `tracingMiddleware`.\n *\n * The sink itself is per-event; for high-throughput services wrap it\n * with `BatchingSink` from `@cleverbrush/log`.\n *\n * Requires that `setupOtel({ ... })` has been called so the global\n * `LoggerProvider` is set; otherwise emissions become no-ops.\n *\n * @param options - logger name and attribute sanitization\n * @returns a `LogSink` that emits to the OTel Logs pipeline\n *\n * @example\n * ```ts\n * import { createLogger, consoleSink } from '@cleverbrush/log';\n * import { otelLogSink } from '@cleverbrush/otel';\n *\n * const logger = createLogger({\n * minimumLevel: 'information',\n * sinks: [consoleSink(), otelLogSink()],\n * });\n * ```\n */\nexport function otelLogSink(options?: OtelLogSinkOptions): LogSink {\n const loggerName = options?.loggerName ?? '@cleverbrush/otel';\n const loggerVersion = options?.loggerVersion;\n const sanitize = options?.sanitizeAttribute;\n\n let cached: OtelLogger | undefined;\n const getLogger = (): OtelLogger => {\n if (!cached) cached = logs.getLogger(loggerName, loggerVersion);\n return cached;\n };\n\n return {\n async emit(events: LogEvent[]): Promise<void> {\n const otelLogger = getLogger();\n for (const event of events) {\n const attributes: Record<string, unknown> = {};\n for (const [key, raw] of Object.entries(event.properties)) {\n const value = sanitize\n ? sanitize(key, raw)\n : toAttributeValue(raw);\n if (value !== undefined) attributes[key] = value;\n }\n\n if (event.messageTemplate) {\n attributes['cleverbrush.message_template'] =\n event.messageTemplate;\n }\n if (event.eventId) {\n attributes['cleverbrush.event_id'] = event.eventId;\n }\n if (event.exception) {\n attributes['exception.type'] = event.exception.name;\n attributes['exception.message'] = event.exception.message;\n if (event.exception.stack) {\n attributes['exception.stacktrace'] =\n event.exception.stack;\n }\n }\n\n otelLogger.emit({\n timestamp: event.timestamp.getTime(), // milliseconds; OTel SDK converts to ns internally\n severityNumber: SEVERITY_NUMBER[event.level],\n severityText: levelToString(event.level).toUpperCase(),\n body: event.renderedMessage,\n attributes: attributes as any\n });\n }\n },\n\n async [Symbol.asyncDispose](): Promise<void> {\n // The OTel SDK owns the LoggerProvider lifecycle —\n // it is shut down via `OtelHandle.shutdown()`.\n }\n };\n}\n","import {\n type Span,\n type SpanAttributes,\n SpanKind,\n SpanStatusCode,\n trace\n} from '@opentelemetry/api';\n\nconst DEFAULT_TRACER_NAME = '@cleverbrush/otel';\n\n/**\n * Options shared by both calling conventions of {@link withSpan}.\n */\nexport interface WithSpanOptions {\n /**\n * OTel span kind.\n * @default SpanKind.INTERNAL\n */\n kind?: SpanKind;\n\n /** Initial attributes to set on the span. */\n attributes?: SpanAttributes;\n\n /**\n * Tracer name used when resolving the OTel tracer.\n * @default '@cleverbrush/otel'\n */\n tracerName?: string;\n\n /** Tracer version. */\n tracerVersion?: string;\n}\n\n/**\n * Handle returned by the disposable form of {@link withSpan}.\n *\n * Implements `AsyncDisposable` so it can be used with `await using`:\n * ```ts\n * await using handle = withSpan('my.operation');\n * handle.span.setAttribute('key', value);\n * try {\n * // ... do work ...\n * } catch (err) {\n * handle.fail(err);\n * throw err;\n * }\n * // span.end() is called automatically when the block exits\n * ```\n *\n * **Note:** The span created by the disposable form is NOT activated as\n * the current context span (OTel's public API requires a callback scope\n * for context propagation). DB / outbound-HTTP child spans created\n * inside the `await using` block will be siblings of this span (both\n * children of the enclosing HTTP span) rather than nested under it.\n * Use the callback form of `withSpan` when proper nesting is required.\n */\nexport interface SpanHandle {\n /** The underlying OTel span. Use it to set attributes or add events. */\n readonly span: Span;\n\n /**\n * Records an exception and marks the span as errored.\n *\n * Idempotent — safe to call in a `catch` block even if the same\n * error might be reported elsewhere.\n */\n fail(err: unknown): void;\n\n /** Ends the span. Called automatically when exiting an `await using` block. */\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/**\n * Wraps `fn` in a custom OTel span and returns whatever `fn` returns.\n *\n * The span is activated as the current context span via\n * `startActiveSpan`, so any child spans created inside `fn` (e.g. DB\n * queries from `instrumentKnex`) will be correctly nested under it.\n * Error handling is automatic — exceptions are recorded and the span is\n * marked `ERROR` before re-throwing.\n *\n * @example\n * ```ts\n * const result = await withSpan('order.process', async span => {\n * span.setAttribute('order.id', orderId);\n * return processOrder(orderId); // DB spans nest under this span\n * });\n * ```\n */\nexport function withSpan<T>(\n name: string,\n fn: (span: Span) => T,\n options?: WithSpanOptions\n): T;\n\n/**\n * Creates a custom OTel span and returns a {@link SpanHandle} that\n * implements `AsyncDisposable`.\n *\n * Use with `await using` for an ergonomic resource-management syntax.\n * Note that the span is **not** the active context span (see\n * {@link SpanHandle} for details). Call {@link SpanHandle.fail} in a\n * `catch` block to record errors before re-throwing.\n *\n * @example\n * ```ts\n * await using handle = withSpan('todo.create', { attributes: { 'todo.user_id': userId } });\n * try {\n * const todo = await db.todos.insert({...});\n * handle.span.setAttribute('todo.id', todo.id);\n * } catch (err) {\n * handle.fail(err);\n * throw err;\n * }\n * ```\n */\nexport function withSpan(name: string, options?: WithSpanOptions): SpanHandle;\n\nexport function withSpan<T>(\n name: string,\n fnOrOptions?: ((span: Span) => T) | WithSpanOptions,\n maybeOptions?: WithSpanOptions\n): T | SpanHandle {\n const isCallback = typeof fnOrOptions === 'function';\n const fn = isCallback ? (fnOrOptions as (span: Span) => T) : undefined;\n const opts: WithSpanOptions | undefined = isCallback\n ? maybeOptions\n : (fnOrOptions as WithSpanOptions | undefined);\n\n const tracerName = opts?.tracerName ?? DEFAULT_TRACER_NAME;\n const tracerVersion = opts?.tracerVersion;\n const kind = opts?.kind ?? SpanKind.INTERNAL;\n const attributes = opts?.attributes;\n\n const tracer = trace.getTracer(tracerName, tracerVersion);\n\n if (fn !== undefined) {\n // Callback form — startActiveSpan propagates the span into context so\n // child spans (e.g. Knex DB spans) nest under it automatically.\n return tracer.startActiveSpan(name, { kind, attributes }, span => {\n let result: T;\n try {\n result = fn(span);\n } catch (err) {\n span.recordException(err as Error);\n span.setStatus({ code: SpanStatusCode.ERROR });\n span.end();\n throw err;\n }\n if (result instanceof Promise) {\n return result.then(\n v => {\n span.end();\n return v;\n },\n err => {\n span.recordException(err);\n span.setStatus({ code: SpanStatusCode.ERROR });\n span.end();\n throw err;\n }\n ) as T;\n }\n span.end();\n return result;\n });\n }\n\n // Disposable form — span is not activated in context (no callback scope\n // available in the OTel public API for context.with).\n const span = tracer.startSpan(name, { kind, attributes });\n let failed = false;\n\n const handle: SpanHandle = {\n span,\n fail(err: unknown): void {\n if (failed) return;\n failed = true;\n span.recordException(err as Error);\n span.setStatus({ code: SpanStatusCode.ERROR });\n },\n [Symbol.asyncDispose](): Promise<void> {\n span.end();\n return Promise.resolve();\n }\n };\n\n return handle;\n}\n"],"mappings":"AAAA,OAAqB,WAAAA,EAAsB,SAAAC,MAAa,qBASjD,IAAMC,EAAU,OAAO,IAAI,SAAS,EAU9BC,EAAS,OAAO,IAAI,QAAQ,EAiDlC,SAASC,EACZC,EACAC,EACI,CACJ,IAAMC,EAAaD,GAAS,YAAc,oBACpCE,EAAYF,GAAS,WAAa,oBAClCG,EAAUH,GAAS,QAErB,OAAOD,GAAU,cAAiB,aAClCA,EAAS,aACLH,EACA,IAAcD,EAAM,UAAUM,EAAYE,CAAO,CACrD,EACAJ,EAAS,aACLF,EACA,IAAaH,EAAQ,SAASQ,EAAWC,CAAO,CACpD,EAER,CCrFA,OAAS,SAAAC,MAAa,qBAyBf,SAASC,GAA0B,CACtC,OAAOC,GAAS,CACZ,IAAMC,EAAOH,EAAM,cAAc,EACjC,GAAI,CAACG,EAAM,OAAOD,EAClB,IAAME,EAAMD,EAAK,YAAY,EAC7B,OAAKC,GAAK,QACH,CACH,GAAGF,EACH,WAAY,CACR,GAAGA,EAAM,WACT,QAASE,EAAI,QACb,OAAQA,EAAI,OACZ,WAAYA,EAAI,UACpB,CACJ,EAT0BF,CAU9B,CACJ,CC1CA,OAEI,YAAAG,EACA,kBAAAC,EAEA,SAAAC,MACG,qBACP,OACI,qBAAAC,EACA,0BAAAC,EACA,sBAAAC,EACA,uBAAAC,EACA,uBAAAC,EACA,oBAAAC,MACG,iDA6CP,IAAMC,EAAmB,kBAInBC,EAAgB,IAAI,IAAI,CAC1B,SACA,SACA,SACA,SACA,QACA,UACA,OACA,SACA,QACA,OACA,WACA,UACA,UACA,OACA,OACA,UACA,QACJ,CAAC,EAED,SAASC,EAAeC,EAAaC,EAAqC,CACtE,GAAIA,EAAQ,OAAOA,EAAO,YAAY,EAEtC,IAAMC,EADIL,EAAiB,KAAKG,CAAG,IAClB,CAAC,GAAG,YAAY,EACjC,OAAOE,GAAQJ,EAAc,IAAII,CAAI,EAAIA,EAAO,MACpD,CAEA,SAASC,EAAcC,EAAiC,CACpD,IAAMC,EACFD,GAAQ,SAAWA,GAAQ,QAAQ,OACvC,GAAI,CAACC,EAAS,OACd,IAAMC,EAAO,OAAOD,CAAO,EAAE,YAAY,EACzC,OAAIC,EAAK,WAAW,IAAI,GAAKA,EAAK,SAAS,UAAU,EAC1C,aAEPA,EAAK,SAAS,OAAO,GAAKA,IAAS,SAAiB,QACpDA,EAAK,SAAS,QAAQ,GAAKA,IAAS,iBAAyB,SAC7DA,EAAK,SAAS,OAAO,EAAU,QAC/BA,EAAK,SAAS,QAAQ,EAAU,SAC7BA,CACX,CAmCO,SAASC,EACZC,EACAC,EACC,CACD,IAAMC,EAAaD,GAAS,YAAc,yBACpCE,EAAkBF,GAAS,iBAAmB,GAC9CG,EAAWH,GAAS,kBAEtBI,EACEC,EAAY,KACTD,IAAcA,EAAevB,EAAM,UAAUoB,CAAU,GACrDG,GAGLT,EAAeI,EAAU,OACzBO,EAAWN,GAAS,UAAYN,EAAcC,CAAM,GAAK,YACzDY,EAAkBZ,GAAQ,QAAQ,YAAc,CAAC,EACjDa,EACF,OAAOD,GAAe,SAAWA,EAAW,SAAW,OACrDE,EACF,OAAOF,GAAe,SAAWA,EAAW,KAAO,OACjDG,EACF,OAAOH,GAAe,UAAY,OAAOA,EAAW,MAAS,SACvDA,EAAW,KACX,OAEJI,EAAS,IAAI,IAEbC,EAA6C,CAC/C,CAAC3B,CAAmB,EAAGqB,CAC3B,EACA,OAAIE,IAAQI,EAAU9B,CAAiB,EAAI0B,GACvCC,IAAMG,EAAU1B,CAAmB,EAAIuB,GACvC,OAAOC,GAAS,WAAUE,EAAUzB,CAAgB,EAAIuB,GAE3DX,EAAU,KAAK,QAAUc,GAAsB,CAC5C,IAAMC,EAAMD,EAAE,eACd,GAAI,CAACC,EAAK,OACV,IAAMC,EAAYzB,EAAeuB,EAAE,IAAKA,EAAE,MAAM,EAC1CG,EAAWD,EACXP,EACI,GAAGO,CAAS,IAAIP,CAAM,GACtBO,EACJ,WAEAE,EAAyC,CAAE,GAAGL,CAAU,EAC1DG,IAAWE,EAAMlC,CAAsB,EAAIgC,GAC3Cb,GAAmBW,EAAE,MACrBI,EAAMjC,CAAkB,EAAImB,EAAWA,EAASU,EAAE,GAAG,EAAIA,EAAE,KAG/D,IAAMK,EAAOb,EAAU,EAAE,UAAUW,EAAU,CACzC,KAAMrC,EAAS,OACf,WAAYsC,CAChB,CAAC,EACDN,EAAO,IAAIG,EAAKI,CAAI,CACxB,CAAC,EAEAnB,EAAU,KAAK,iBAAkB,CAACoB,EAAgBN,IAAsB,CACrE,IAAMC,EAAMD,GAAG,eACf,GAAI,CAACC,EAAK,OACV,IAAMI,EAAOP,EAAO,IAAIG,CAAG,EACtBI,IACLP,EAAO,OAAOG,CAAG,EACjBI,EAAK,UAAU,CAAE,KAAMtC,EAAe,EAAG,CAAC,EAC1CsC,EAAK,IAAI,EACb,CAAC,EAEAnB,EAAU,KAAK,cAAe,CAACqB,EAAcP,IAAsB,CAChE,IAAMC,EAAMD,GAAG,eACf,GAAI,CAACC,EAAK,OACV,IAAMI,EAAOP,EAAO,IAAIG,CAAG,EACtBI,IACLP,EAAO,OAAOG,CAAG,EACbM,aAAe,OACfF,EAAK,gBAAgBE,CAAG,EACxBF,EAAK,UAAU,CACX,KAAMtC,EAAe,MACrB,QAASwC,EAAI,OACjB,CAAC,GAEDF,EAAK,UAAU,CACX,KAAMtC,EAAe,MACrB,QAAS,OAAOwC,CAAG,CACvB,CAAC,EAELF,EAAK,IAAI,EACb,CAAC,EAEMnB,CACX,CCpOA,OACI,WAAAsB,EACA,eAAAC,GAEA,YAAAC,GACA,kBAAAC,EAEA,SAAAC,OACG,qBACP,OACI,4BAAAC,GACA,kCAAAC,EACA,mBAAAC,GACA,uBAAAC,GACA,oBAAAC,GACA,iBAAAC,GACA,kBAAAC,GACA,mBAAAC,GACA,4BAAAC,OACG,sCAUA,IAAMC,EAAqB,YAwDlC,SAASC,GAAgBC,EAA2B,CAEhD,OADgDA,GAAK,OACvC,IAAI,iBAAiB,CACvC,CAEA,SAASC,GAAiBC,EAA+B,CACrD,GAAI,CAACA,EAAM,OACX,IAAMC,EAAeD,EAAK,UAAY,GAChCE,EAAMF,EAAK,aACjB,GAAI,OAAOE,GAAQ,SAAU,MAAO,GAAGD,CAAI,GAAGC,CAAG,GAEjD,IAAMC,EACFD,GAAK,UAAYA,GAAK,SAAWA,GAAK,WAAW,GAAK,KAC1D,OAAI,OAAOC,GAAc,SAAiB,GAAGF,CAAI,GAAGE,CAAS,GACtDF,GAAQ,MACnB,CAqCO,SAASG,GAAkBC,EAAoC,CAClE,IAAMC,EAAaD,GAAS,YAAc,oBACpCE,EAAgBF,GAAS,cACzBG,EAAe,IAAI,KACpBH,GAAS,cAAgB,CAAC,GAAG,IAAII,GAC9B,OAAOA,GAAM,SAAWA,EAAIA,EAAE,IAClC,CACJ,EACMC,EAAaL,GAAS,WACtBM,EAAcN,GAAS,aAAe,GACtCO,EACFP,GAAS,sBAAwB,GAC3B,GACCA,GAAS,qBAAuB,aAEvCQ,EACEC,EAAY,KACTD,IACDA,EAAe3B,GAAM,UAAUoB,EAAYC,CAAa,GAErDM,GAGX,MAAO,OAAOf,EAAUiB,IAA6C,CACjE,IAAMC,EAAuBlB,EAAI,IAC3BmB,EAAWD,GAAK,UAAYlB,EAAI,MAAQ,GAE9C,GAAIU,EAAa,IAAIS,CAAQ,EAAG,CAC5B,MAAMF,EAAK,EACX,MACJ,CAEA,IAAMG,EAASJ,EAAU,EACnBK,EAAkCrB,EAAI,SAAW,CAAC,EAGlDsB,EAAYrC,GAAY,QAAQD,EAAQ,OAAO,EAAGqC,CAAO,EAEzDnB,EAAOH,GAAgBC,CAAG,EAC1BuB,EAAQtB,GAAiBC,CAAI,EAC7BsB,GAAkBxB,EAAI,QAAU,OAAO,YAAY,EAKnDyB,EACFvB,GAAM,cACLqB,EAAQ,GAAGC,CAAM,IAAID,CAAK,GAAK,GAAGC,CAAM,IAAIL,CAAQ,IAEnDO,EAA8C,CAChD,CAACrC,EAAwB,EAAGmC,EAC5B,CAAC9B,EAAa,EAAGyB,CACrB,EAOA,GANID,GAAK,WACLQ,EAAW9B,EAAe,EAAIsB,EAAI,SAAS,QAAQ,KAAM,EAAE,GAE3DA,GAAK,WACLQ,EAAWlC,EAAmB,EAAI0B,EAAI,UAEtCA,GAAK,KAAM,CACX,IAAMS,EAAO,OAAOT,EAAI,IAAI,EACxB,OAAO,SAASS,CAAI,IAAGD,EAAWjC,EAAgB,EAAIkC,EAC9D,CACIJ,IAAOG,EAAWnC,EAAe,EAAIgC,GACrCF,EAAQ,YAAY,IACpBK,EAAW7B,EAAwB,EAAIwB,EAAQ,YAAY,GAE3DR,GAAeK,GAAK,SACpBQ,EAAW/B,EAAc,EAAIuB,EAAI,OAAO,QAAQ,MAAO,EAAE,GAEzDhB,GAAM,MAAM,SACZwB,EAAW,2BAA2B,EAAIxB,EAAK,KAAK,KAAK,GAAG,GAE5DA,GAAM,cACNwB,EAAW,kCAAkC,EAAIxB,EAAK,aAG1D,MAAMkB,EAAO,gBACTK,EACA,CAAE,KAAMvC,GAAS,OAAQ,WAAAwC,CAAW,EACpCJ,EACA,MAAOM,GAAe,CAKlB,GAJA5B,EAAI,OAAO,MAAMF,EAAoB8B,CAAI,EAIrCd,EAAqB,CACrB,GAAM,CAAE,QAAAe,CAAQ,EAAID,EAAK,YAAY,EACrC,GAAIC,EAAS,CACT,IAAMC,EAAM9B,EAAI,SACZ8B,GAAK,UACLA,EAAI,UAAUhB,EAAqBe,CAAO,EACnC7B,EAAI,WACXA,EAAI,UAAUc,EAAqBe,CAAO,CAElD,CACJ,CAEA,GAAIjB,EACA,GAAI,CACAA,EAAWgB,EAAM5B,CAAG,CACxB,MAAQ,CAER,CAGJ,GAAI,CACA,MAAMiB,EAAK,EACX,IAAMc,EACF/B,EAAI,UAAU,YAAcA,EAAI,YAAc,IAClD4B,EAAK,aAAatC,EAAgCyC,CAAM,EACpDA,GAAU,IACVH,EAAK,UAAU,CAAE,KAAMzC,EAAe,KAAM,CAAC,EAE7CyC,EAAK,UAAU,CAAE,KAAMzC,EAAe,EAAG,CAAC,CAElD,OAAS6C,EAAK,CACV,IAAMD,EACF/B,EAAI,UAAU,YAAcA,EAAI,YAAc,IAClD,MAAA4B,EAAK,aAAatC,EAAgCyC,CAAM,EACpDC,aAAe,OACfJ,EAAK,gBAAgBI,CAAG,EACxBJ,EAAK,UAAU,CACX,KAAMzC,EAAe,MACrB,QAAS6C,EAAI,OACjB,CAAC,GAEDJ,EAAK,UAAU,CACX,KAAMzC,EAAe,MACrB,QAAS,OAAO6C,CAAG,CACvB,CAAC,EAECA,CACV,QAAE,CACEJ,EAAK,IAAI,CACb,CACJ,CACJ,CACJ,CACJ,CCpRA,OAAS,qBAAAK,GAAmB,gBAAAC,GAAc,QAAAC,OAAY,qBACtD,OAAS,mBAAAC,OAAuB,yCAChC,OAAS,sBAAAC,OAA0B,4CACnC,OAAS,qBAAAC,OAAyB,0CAClC,OAAS,0BAAAC,OAA8B,2BACvC,OAAS,2BAAAC,OAA+B,0BACxC,OAAS,iCAAAC,OAAqC,6BAC9C,OAAS,WAAAC,OAAe,0BACxB,OACI,sBAAAC,OAEG,gCACP,OACI,oCAAAC,GACA,qBAAAC,GACA,wBAAAC,OACG,iDA6GP,SAASC,EACLC,EACAC,EACAC,EACkB,CAClB,GAAID,EAAW,OAAOA,EACtB,GAAKD,EACL,MAAO,GAAGA,EAAK,QAAQ,MAAO,EAAE,CAAC,GAAGE,CAAM,EAC9C,CAiCO,SAASC,GAAUC,EAAgC,CACtD,GAAI,CAACA,EAAO,YACR,MAAM,IAAI,MAAM,sCAAsC,EAGtDA,EAAO,OACPjB,GAAK,UAAU,IAAIF,GAAqBC,GAAa,KAAK,EAG9D,IAAMmB,EAAmD,CACrD,CAACR,EAAiB,EAAGO,EAAO,YAC5B,GAAIA,EAAO,eACL,CAAE,CAACN,EAAoB,EAAGM,EAAO,cAAe,EAChD,CAAC,EACP,GAAIA,EAAO,YACL,CAAE,CAACR,EAAgC,EAAGQ,EAAO,WAAY,EACzD,CAAC,EACP,GAAIA,EAAO,oBAAsB,CAAC,CACtC,EACME,EAAWf,GAAuBc,CAAK,EAEvCE,EAAUH,EAAO,QAEnBI,EACJ,GAAI,CAACJ,EAAO,cAAe,CACvB,IAAMK,EAAMV,EACRK,EAAO,aACPA,EAAO,eACP,YACJ,EACMM,EAAgB,IAAIpB,GAAkB,CACxC,GAAImB,EAAM,CAAE,IAAAA,CAAI,EAAI,CAAC,EACrB,GAAIF,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,CACjC,CAAC,EACDC,EAAiB,CAAC,IAAIb,GAAmBe,CAAa,CAAC,CAC3D,CAEA,IAAIC,EACJ,GAAI,CAACP,EAAO,YAAa,CACrB,IAAMK,EAAMV,EACRK,EAAO,aACPA,EAAO,aACP,UACJ,EACMQ,EAAc,IAAIxB,GAAgB,CACpC,GAAIqB,EAAM,CAAE,IAAAA,CAAI,EAAI,CAAC,EACrB,GAAIF,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,CACjC,CAAC,EACDI,EAAsB,CAAC,IAAInB,GAAwBoB,CAAW,CAAC,CACnE,CAEA,IAAIC,EACJ,GAAI,CAACT,EAAO,eAAgB,CACxB,IAAMK,EAAMV,EACRK,EAAO,aACPA,EAAO,gBACP,aACJ,EACMU,EAAiB,IAAIzB,GAAmB,CAC1C,GAAIoB,EAAM,CAAE,IAAAA,CAAI,EAAI,CAAC,EACrB,GAAIF,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,CACjC,CAAC,EACDM,EAAe,IAAIpB,GAA8B,CAC7C,SAAUqB,EACV,qBAAsBV,EAAO,yBAA2B,GAC5D,CAAC,CACL,CAEA,IAAMW,EAAM,IAAIrB,GAAQ,CACpB,SAAAY,EACA,GAAIE,EAAiB,CAAE,eAAAA,CAAe,EAAI,CAAC,EAC3C,GAAIG,EAAsB,CAAE,oBAAAA,CAAoB,EAAI,CAAC,EACrD,GAAIE,EAAe,CAAE,cAAe,CAACA,CAAY,CAAE,EAAI,CAAC,EACxD,iBAAmBT,EAAO,kBAAoB,CAAC,CACnD,CAAC,EAEDW,EAAI,MAAM,EAEV,IAAIC,EAAqC,KAEzC,MAAO,CACH,IAAAD,EACA,UAA0B,CACtB,OAAKC,IACDA,EAAeD,EAAI,SAAS,EAAE,MAAME,GAAO,CAEvC,QAAQ,MAAM,yBAA0BA,CAAG,CAC/C,CAAC,GAEED,CACX,CACJ,CACJ,CCjQA,OAAS,YAAAE,EAAU,iBAAAC,OAAqB,mBACxC,OACI,QAAAC,GAEA,kBAAAC,MACG,0BAyBP,IAAMC,GAAoD,CACtD,CAACJ,EAAS,KAAK,EAAGG,EAAe,MACjC,CAACH,EAAS,KAAK,EAAGG,EAAe,MACjC,CAACH,EAAS,WAAW,EAAGG,EAAe,KACvC,CAACH,EAAS,OAAO,EAAGG,EAAe,KACnC,CAACH,EAAS,KAAK,EAAGG,EAAe,MACjC,CAACH,EAAS,KAAK,EAAGG,EAAe,KACrC,EAEME,GAAe,IAAI,IAAI,CAAC,SAAU,SAAU,UAAW,QAAQ,CAAC,EAEtE,SAASC,EAAiBC,EAAyB,CAC/C,GAAIA,GAAU,KAA6B,OAAOA,EAClD,GAAI,SAAOA,GAAU,YAAc,OAAOA,GAAU,UAGpD,IAAIF,GAAa,IAAI,OAAOE,CAAK,EAAG,OAAOA,EAC3C,GAAI,MAAM,QAAQA,CAAK,EACnB,OAAOA,EAAM,IAAID,CAAgB,EAAE,OAAOE,GAAKA,IAAM,MAAS,EAElE,GAAI,CACA,OAAO,KAAK,UAAUD,CAAK,CAC/B,MAAQ,CACJ,OAAO,OAAOA,CAAK,CACvB,EACJ,CAwCO,SAASE,GAAYC,EAAuC,CAC/D,IAAMC,EAAaD,GAAS,YAAc,oBACpCE,EAAgBF,GAAS,cACzBG,EAAWH,GAAS,kBAEtBI,EACEC,EAAY,KACTD,IAAQA,EAASZ,GAAK,UAAUS,EAAYC,CAAa,GACvDE,GAGX,MAAO,CACH,MAAM,KAAKE,EAAmC,CAC1C,IAAMC,EAAaF,EAAU,EAC7B,QAAWG,KAASF,EAAQ,CACxB,IAAMG,EAAsC,CAAC,EAC7C,OAAW,CAACC,EAAKC,CAAG,IAAK,OAAO,QAAQH,EAAM,UAAU,EAAG,CACvD,IAAMX,EAAQM,EACRA,EAASO,EAAKC,CAAG,EACjBf,EAAiBe,CAAG,EACtBd,IAAU,SAAWY,EAAWC,CAAG,EAAIb,EAC/C,CAEIW,EAAM,kBACNC,EAAW,8BAA8B,EACrCD,EAAM,iBAEVA,EAAM,UACNC,EAAW,sBAAsB,EAAID,EAAM,SAE3CA,EAAM,YACNC,EAAW,gBAAgB,EAAID,EAAM,UAAU,KAC/CC,EAAW,mBAAmB,EAAID,EAAM,UAAU,QAC9CA,EAAM,UAAU,QAChBC,EAAW,sBAAsB,EAC7BD,EAAM,UAAU,QAI5BD,EAAW,KAAK,CACZ,UAAWC,EAAM,UAAU,QAAQ,EACnC,eAAgBd,GAAgBc,EAAM,KAAK,EAC3C,aAAcjB,GAAciB,EAAM,KAAK,EAAE,YAAY,EACrD,KAAMA,EAAM,gBACZ,WAAYC,CAChB,CAAC,CACL,CACJ,EAEA,MAAO,OAAO,YAAY,GAAmB,CAG7C,CACJ,CACJ,CCtJA,OAGI,YAAAG,GACA,kBAAAC,EACA,SAAAC,OACG,qBAEP,IAAMC,GAAsB,oBA8GrB,SAASC,GACZC,EACAC,EACAC,EACc,CACd,IAAMC,EAAa,OAAOF,GAAgB,WACpCG,EAAKD,EAAcF,EAAoC,OACvDI,EAAoCF,EACpCD,EACCD,EAEDK,EAAaD,GAAM,YAAcP,GACjCS,EAAgBF,GAAM,cACtBG,EAAOH,GAAM,MAAQV,GAAS,SAC9Bc,EAAaJ,GAAM,WAEnBK,EAASb,GAAM,UAAUS,EAAYC,CAAa,EAExD,GAAIH,IAAO,OAGP,OAAOM,EAAO,gBAAgBV,EAAM,CAAE,KAAAQ,EAAM,WAAAC,CAAW,EAAGE,GAAQ,CAC9D,IAAIC,EACJ,GAAI,CACAA,EAASR,EAAGO,CAAI,CACpB,OAASE,EAAK,CACV,MAAAF,EAAK,gBAAgBE,CAAY,EACjCF,EAAK,UAAU,CAAE,KAAMf,EAAe,KAAM,CAAC,EAC7Ce,EAAK,IAAI,EACHE,CACV,CACA,OAAID,aAAkB,QACXA,EAAO,KACVE,IACIH,EAAK,IAAI,EACFG,GAEXD,GAAO,CACH,MAAAF,EAAK,gBAAgBE,CAAG,EACxBF,EAAK,UAAU,CAAE,KAAMf,EAAe,KAAM,CAAC,EAC7Ce,EAAK,IAAI,EACHE,CACV,CACJ,GAEJF,EAAK,IAAI,EACFC,EACX,CAAC,EAKL,IAAMD,EAAOD,EAAO,UAAUV,EAAM,CAAE,KAAAQ,EAAM,WAAAC,CAAW,CAAC,EACpDM,EAAS,GAgBb,MAd2B,CACvB,KAAAJ,EACA,KAAKE,EAAoB,CACjBE,IACJA,EAAS,GACTJ,EAAK,gBAAgBE,CAAY,EACjCF,EAAK,UAAU,CAAE,KAAMf,EAAe,KAAM,CAAC,EACjD,EACA,CAAC,OAAO,YAAY,GAAmB,CACnC,OAAAe,EAAK,IAAI,EACF,QAAQ,QAAQ,CAC3B,CACJ,CAGJ","names":["metrics","trace","ITracer","IMeter","configureOtel","services","options","tracerName","meterName","version","trace","traceEnricher","event","span","ctx","SpanKind","SpanStatusCode","trace","ATTR_DB_NAMESPACE","ATTR_DB_OPERATION_NAME","ATTR_DB_QUERY_TEXT","ATTR_DB_SYSTEM_NAME","ATTR_SERVER_ADDRESS","ATTR_SERVER_PORT","FIRST_KEYWORD_RE","KNOWN_SQL_OPS","inferOperation","sql","method","word","inferDbSystem","client","dialect","norm","instrumentKnex","k","options","tracerName","recordStatement","sanitize","cachedTracer","getTracer","dbSystem","connection","dbName","host","port","active","baseAttrs","q","uid","operation","spanName","attrs","span","_resp","err","context","propagation","SpanKind","SpanStatusCode","trace","ATTR_HTTP_REQUEST_METHOD","ATTR_HTTP_RESPONSE_STATUS_CODE","ATTR_HTTP_ROUTE","ATTR_SERVER_ADDRESS","ATTR_SERVER_PORT","ATTR_URL_PATH","ATTR_URL_QUERY","ATTR_URL_SCHEME","ATTR_USER_AGENT_ORIGINAL","OTEL_SPAN_ITEM_KEY","getEndpointMeta","ctx","getRouteTemplate","meta","base","tpl","candidate","tracingMiddleware","options","tracerName","tracerVersion","excludePaths","p","enrichSpan","recordQuery","responseTraceHeader","cachedTracer","getTracer","next","url","pathname","tracer","headers","parentCtx","route","method","spanName","attributes","port","span","traceId","res","status","err","DiagConsoleLogger","DiagLogLevel","diag","OTLPLogExporter","OTLPMetricExporter","OTLPTraceExporter","resourceFromAttributes","BatchLogRecordProcessor","PeriodicExportingMetricReader","NodeSDK","BatchSpanProcessor","ATTR_DEPLOYMENT_ENVIRONMENT_NAME","ATTR_SERVICE_NAME","ATTR_SERVICE_VERSION","buildExporterUrl","base","perSignal","suffix","setupOtel","config","attrs","resource","headers","spanProcessors","url","traceExporter","logRecordProcessors","logExporter","metricReader","metricExporter","sdk","shuttingDown","err","LogLevel","levelToString","logs","SeverityNumber","SEVERITY_NUMBER","SCALAR_TYPES","toAttributeValue","value","v","otelLogSink","options","loggerName","loggerVersion","sanitize","cached","getLogger","events","otelLogger","event","attributes","key","raw","SpanKind","SpanStatusCode","trace","DEFAULT_TRACER_NAME","withSpan","name","fnOrOptions","maybeOptions","isCallback","fn","opts","tracerName","tracerVersion","kind","attributes","tracer","span","result","err","v","failed"]}
|
|
1
|
+
{"version":3,"sources":["../src/di.ts","../src/enrichers/trace.ts","../src/knex/instrumentKnex.ts","../src/middleware/tracing.ts","../src/setupOtel.ts","../src/sinks/OtelLogSink.ts","../src/span.ts"],"sourcesContent":["import { type Meter, metrics, type Tracer, trace } from '@opentelemetry/api';\n\n/**\n * DI service key for an OpenTelemetry `Tracer`.\n *\n * Resolved from the global `TracerProvider` set by\n * {@link import('./setupOtel.js').setupOtel}. Components that prefer\n * dependency injection over the global API can inject this token.\n */\nexport const ITracer = Symbol.for('ITracer') as unknown as {\n __brand: 'ITracer';\n};\n\n/**\n * DI service key for an OpenTelemetry `Meter`.\n *\n * Resolved from the global `MeterProvider` set by\n * {@link import('./setupOtel.js').setupOtel}.\n */\nexport const IMeter = Symbol.for('IMeter') as unknown as {\n __brand: 'IMeter';\n};\n\n/**\n * Configuration for {@link configureOtel}.\n */\nexport interface ConfigureOtelOptions {\n /**\n * Tracer name.\n *\n * @default '@cleverbrush/otel'\n */\n tracerName?: string;\n\n /**\n * Meter name.\n *\n * @default '@cleverbrush/otel'\n */\n meterName?: string;\n\n /** Optional version string used for both tracer and meter. */\n version?: string;\n}\n\n/**\n * Registers OTel `Tracer` and `Meter` instances in the DI container.\n *\n * Both are resolved lazily from the global providers configured by\n * {@link import('./setupOtel.js').setupOtel}, so this helper can be\n * called at DI setup time even before the SDK has fully started.\n *\n * @param services - the `ServiceCollection` to register with\n * @param options - tracer / meter naming overrides\n *\n * @example\n * ```ts\n * import { configureOtel, ITracer } from '@cleverbrush/otel';\n *\n * configureOtel(services, { tracerName: 'todo-backend' });\n *\n * const tracer = provider.get(ITracer);\n * tracer.startActiveSpan('custom-work', span => {\n * // …\n * span.end();\n * });\n * ```\n */\nexport function configureOtel(\n services: any,\n options?: ConfigureOtelOptions\n): void {\n const tracerName = options?.tracerName ?? '@cleverbrush/otel';\n const meterName = options?.meterName ?? '@cleverbrush/otel';\n const version = options?.version;\n\n if (typeof services?.addSingleton === 'function') {\n services.addSingleton(\n ITracer,\n (): Tracer => trace.getTracer(tracerName, version)\n );\n services.addSingleton(\n IMeter,\n (): Meter => metrics.getMeter(meterName, version)\n );\n }\n}\n","import type { Enricher } from '@cleverbrush/log';\nimport { trace } from '@opentelemetry/api';\n\n/**\n * Creates a log enricher that attaches the active span's\n * `TraceId`, `SpanId`, and `TraceFlags` to every log event.\n *\n * Reads from the OpenTelemetry context via `@opentelemetry/api`,\n * so it works with any tracer provider — including the one\n * configured by {@link import('../setupOtel.js').setupOtel}.\n *\n * No-op when no span is active.\n *\n * @returns an enricher that adds `{ TraceId, SpanId, TraceFlags }` if a span is active\n *\n * @example\n * ```ts\n * import { createLogger, consoleSink } from '@cleverbrush/log';\n * import { traceEnricher } from '@cleverbrush/otel';\n *\n * const logger = createLogger({\n * sinks: [consoleSink()],\n * enrichers: [traceEnricher()],\n * });\n * ```\n */\nexport function traceEnricher(): Enricher {\n return event => {\n const span = trace.getActiveSpan();\n if (!span) return event;\n const ctx = span.spanContext();\n if (!ctx?.traceId) return event;\n return {\n ...event,\n properties: {\n ...event.properties,\n TraceId: ctx.traceId,\n SpanId: ctx.spanId,\n TraceFlags: ctx.traceFlags\n }\n };\n };\n}\n","import {\n type Span,\n SpanKind,\n SpanStatusCode,\n type Tracer,\n trace\n} from '@opentelemetry/api';\nimport {\n ATTR_DB_NAMESPACE,\n ATTR_DB_OPERATION_NAME,\n ATTR_DB_QUERY_TEXT,\n ATTR_DB_SYSTEM_NAME,\n ATTR_SERVER_ADDRESS,\n ATTR_SERVER_PORT\n} from '@opentelemetry/semantic-conventions/incubating';\nimport type { Knex } from 'knex';\n\n/**\n * Configuration for {@link instrumentKnex}.\n */\nexport interface InstrumentKnexOptions {\n /**\n * Value to record as `db.system.name` (e.g. `postgresql`, `mysql`,\n * `sqlite`). When omitted, inferred from the knex client.\n */\n dbSystem?: string;\n\n /**\n * Tracer name used when resolving the OTel tracer.\n *\n * @default '@cleverbrush/otel/knex'\n */\n tracerName?: string;\n\n /**\n * Whether to include the SQL statement as `db.query.text`.\n *\n * The statement is taken verbatim from knex (parameter placeholders\n * are kept; bound values are **not** included). Disable if your\n * SQL itself may contain sensitive identifiers.\n *\n * @default true\n */\n recordStatement?: boolean;\n\n /**\n * Optional hook to redact / rewrite the SQL before it is recorded.\n * Called only when {@link recordStatement} is enabled.\n */\n sanitizeStatement?: (sql: string) => string;\n}\n\ninterface KnexQueryEvent {\n sql: string;\n method?: string;\n bindings?: unknown[];\n __knexQueryUid?: string;\n}\n\nconst FIRST_KEYWORD_RE = /^\\s*([A-Za-z]+)/;\n\n// Recognised SQL operation keywords — anything outside this set is ignored\n// to prevent arbitrary SQL words becoming low-cardinality span names.\nconst KNOWN_SQL_OPS = new Set([\n 'SELECT',\n 'INSERT',\n 'UPDATE',\n 'DELETE',\n 'MERGE',\n 'REPLACE',\n 'CALL',\n 'CREATE',\n 'ALTER',\n 'DROP',\n 'TRUNCATE',\n 'EXPLAIN',\n 'ANALYZE',\n 'SHOW',\n 'WITH',\n 'EXECUTE',\n 'UPSERT'\n]);\n\nfunction inferOperation(sql: string, method?: string): string | undefined {\n if (method) return method.toUpperCase();\n const m = FIRST_KEYWORD_RE.exec(sql);\n const word = m?.[1]?.toUpperCase();\n return word && KNOWN_SQL_OPS.has(word) ? word : undefined;\n}\n\nfunction inferDbSystem(client: any): string | undefined {\n const dialect: string | undefined =\n client?.dialect ?? client?.config?.client;\n if (!dialect) return undefined;\n const norm = String(dialect).toLowerCase();\n if (norm.startsWith('pg') || norm.includes('postgres')) {\n return 'postgresql';\n }\n if (norm.includes('mysql') || norm === 'mysql2') return 'mysql';\n if (norm.includes('sqlite') || norm === 'better-sqlite3') return 'sqlite';\n if (norm.includes('mssql')) return 'mssql';\n if (norm.includes('oracle')) return 'oracle';\n return norm;\n}\n\n/**\n * Instruments a Knex instance to emit an OpenTelemetry `CLIENT` span\n * for every executed query.\n *\n * Hooks knex's built-in `query`, `query-response`, and `query-error`\n * events — every dbset read, change-tracker write, save-graph, and raw\n * `knex(...)` call is captured uniformly because they all flow through\n * the same knex instance.\n *\n * Spans automatically nest under any ambient OTel context, so DB spans\n * become children of the enclosing HTTP server span produced by\n * `tracingMiddleware`.\n *\n * Returns the same instance for fluent chaining:\n * `instrumentKnex(knex({...}))`.\n *\n * @param k - the knex instance to instrument (mutated in place)\n * @param options - optional overrides for db system, tracer name, redaction\n * @returns the same knex instance\n *\n * @example\n * ```ts\n * import knex from 'knex';\n * import { instrumentKnex } from '@cleverbrush/otel';\n *\n * services.addSingleton(KnexToken, () =>\n * instrumentKnex(\n * knex({ client: 'pg', connection: dbUrl }),\n * { dbSystem: 'postgresql' }\n * )\n * );\n * ```\n */\nexport function instrumentKnex<T extends Knex>(\n k: T,\n options?: InstrumentKnexOptions\n): T {\n const tracerName = options?.tracerName ?? '@cleverbrush/otel/knex';\n const recordStatement = options?.recordStatement ?? true;\n const sanitize = options?.sanitizeStatement;\n\n let cachedTracer: Tracer | undefined;\n const getTracer = (): Tracer => {\n if (!cachedTracer) cachedTracer = trace.getTracer(tracerName);\n return cachedTracer;\n };\n\n const client: any = (k as any).client;\n const dbSystem = options?.dbSystem ?? inferDbSystem(client) ?? 'other_sql';\n const connection: any = client?.config?.connection ?? {};\n const dbName: string | undefined =\n typeof connection === 'object' ? connection.database : undefined;\n const host: string | undefined =\n typeof connection === 'object' ? connection.host : undefined;\n const port: number | undefined =\n typeof connection === 'object' && typeof connection.port === 'number'\n ? connection.port\n : undefined;\n\n const active = new Map<string, Span>();\n\n const baseAttrs: Record<string, string | number> = {\n [ATTR_DB_SYSTEM_NAME]: dbSystem\n };\n if (dbName) baseAttrs[ATTR_DB_NAMESPACE] = dbName;\n if (host) baseAttrs[ATTR_SERVER_ADDRESS] = host;\n if (typeof port === 'number') baseAttrs[ATTR_SERVER_PORT] = port;\n\n (k as any).on?.('query', (q: KnexQueryEvent) => {\n const uid = q.__knexQueryUid;\n if (!uid) return;\n const operation = inferOperation(q.sql, q.method);\n const spanName = operation\n ? dbName\n ? `${operation} ${dbName}`\n : operation\n : 'db.query';\n\n const attrs: Record<string, string | number> = { ...baseAttrs };\n if (operation) attrs[ATTR_DB_OPERATION_NAME] = operation;\n if (recordStatement && q.sql) {\n attrs[ATTR_DB_QUERY_TEXT] = sanitize ? sanitize(q.sql) : q.sql;\n }\n\n const span = getTracer().startSpan(spanName, {\n kind: SpanKind.CLIENT,\n attributes: attrs\n });\n active.set(uid, span);\n });\n\n (k as any).on?.('query-response', (_resp: unknown, q: KnexQueryEvent) => {\n const uid = q?.__knexQueryUid;\n if (!uid) return;\n const span = active.get(uid);\n if (!span) return;\n active.delete(uid);\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n });\n\n (k as any).on?.('query-error', (err: unknown, q: KnexQueryEvent) => {\n const uid = q?.__knexQueryUid;\n if (!uid) return;\n const span = active.get(uid);\n if (!span) return;\n active.delete(uid);\n if (err instanceof Error) {\n span.recordException(err);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: err.message\n });\n } else {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: String(err)\n });\n }\n span.end();\n });\n\n return k;\n}\n","import {\n context,\n propagation,\n type Span,\n type SpanContext,\n SpanKind,\n type SpanOptions,\n SpanStatusCode,\n type Tracer,\n trace\n} from '@opentelemetry/api';\nimport {\n ATTR_HTTP_REQUEST_METHOD,\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_HTTP_ROUTE,\n ATTR_SERVER_ADDRESS,\n ATTR_SERVER_PORT,\n ATTR_URL_PATH,\n ATTR_URL_QUERY,\n ATTR_URL_SCHEME,\n ATTR_USER_AGENT_ORIGINAL\n} from '@opentelemetry/semantic-conventions';\n\n/**\n * Attribute key under which the OTel server span is stashed on the\n * per-request `RequestContext.items` map.\n *\n * Downstream middleware/handlers can read this with\n * `ctx.items.get(OTEL_SPAN_ITEM_KEY)` to attach custom attributes\n * or events to the active server span.\n */\nexport const OTEL_SPAN_ITEM_KEY = 'otel.span';\n\n/**\n * Configuration for {@link tracingMiddleware}.\n */\nexport interface TracingMiddlewareOptions {\n /**\n * Tracer name used when resolving the OTel tracer.\n *\n * @default '@cleverbrush/otel'\n */\n tracerName?: string;\n\n /** Tracer version. */\n tracerVersion?: string;\n\n /**\n * Paths to exclude from tracing entirely (no span created).\n *\n * Accepts plain strings or objects with a `path` property\n * (e.g. an `EndpointBuilder`). Useful for `/health` and other\n * high-frequency, low-value endpoints.\n */\n excludePaths?: (string | { readonly path: string })[];\n\n /**\n * Hook for adding custom attributes to the server span just before\n * the inner pipeline runs. Errors thrown here are swallowed.\n */\n enrichSpan?: (span: Span, ctx: any) => void;\n\n /**\n * Whether to record the URL query string as `url.query`.\n *\n * Disabled by default because query strings frequently contain PII\n * (search terms, tokens). Enable explicitly only when you have\n * verified your URLs are safe to record.\n *\n * @default false\n */\n recordQuery?: boolean;\n\n /**\n * Name of the response header that carries the W3C trace ID for the\n * current request span.\n *\n * Expose this so API consumers can include it in bug reports and you\n * can look up the exact trace in SigNoz / Jaeger / etc.\n *\n * Set to `false` to disable the header entirely.\n *\n * @default 'X-Trace-Id'\n */\n responseTraceHeader?: string | false;\n}\n\nfunction getEndpointMeta(ctx: any): any | undefined {\n const items: Map<string, unknown> | undefined = ctx?.items;\n return items?.get('__endpoint_meta');\n}\n\ninterface BatchSubrequestMeta {\n index: number;\n size: number;\n path: string;\n}\n\nfunction getBatchSubrequestMeta(ctx: any): BatchSubrequestMeta | undefined {\n const items: Map<string, unknown> | undefined = ctx?.items;\n const meta = items?.get('__batch_subrequest') as\n | Partial<BatchSubrequestMeta>\n | undefined;\n if (\n typeof meta?.index !== 'number' ||\n typeof meta.size !== 'number' ||\n typeof meta.path !== 'string'\n ) {\n return undefined;\n }\n return {\n index: meta.index,\n size: meta.size,\n path: meta.path\n };\n}\n\nfunction hasValidTraceparent(headers: Record<string, string>): boolean {\n return /^00-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$/i.test(\n headers.traceparent ?? ''\n );\n}\n\nfunction getRouteTemplate(meta: any): string | undefined {\n if (!meta) return undefined;\n const base: string = meta.basePath ?? '';\n const tpl = meta.pathTemplate;\n if (typeof tpl === 'string') return `${base}${tpl}`;\n // ParseStringSchemaBuilder — try its `template` getter, otherwise toString\n const candidate =\n tpl?.template ?? tpl?.pattern ?? tpl?.toString?.() ?? null;\n if (typeof candidate === 'string') return `${base}${candidate}`;\n return base || undefined;\n}\n\n/**\n * Creates a `@cleverbrush/server` middleware that opens an OpenTelemetry\n * `SERVER` span for every incoming request.\n *\n * Should be registered as the **first** middleware so that the span\n * wraps CORS, auth, request logging, and the handler — capturing the\n * full request lifetime.\n *\n * Behavior:\n * - Extracts inbound trace context from request headers\n * (W3C `traceparent`, `baggage`).\n * - Names the span `${operationId}` if available, otherwise\n * `${method} ${http.route}`, otherwise `${method} ${url.path}`.\n * - Sets HTTP semantic-convention attributes\n * (`http.request.method`, `url.path`, `url.scheme`,\n * `server.address`, `user_agent.original`, `http.route`).\n * - Records `http.response.status_code` after `next()` completes.\n * - Marks the span `ERROR` and records the exception on uncaught errors.\n * - Stashes the span at `ctx.items.get(OTEL_SPAN_ITEM_KEY)` for\n * downstream code to enrich.\n *\n * @param options - tracing configuration\n * @returns a `Middleware` compatible with `@cleverbrush/server`\n *\n * @example\n * ```ts\n * import { tracingMiddleware } from '@cleverbrush/otel';\n *\n * createServer()\n * .use(tracingMiddleware({ excludePaths: ['/health'] }))\n * .use(corsMiddleware)\n * .use(authMiddleware)\n * .listen(3000);\n * ```\n */\nexport function tracingMiddleware(options?: TracingMiddlewareOptions) {\n const tracerName = options?.tracerName ?? '@cleverbrush/otel';\n const tracerVersion = options?.tracerVersion;\n const excludePaths = new Set(\n (options?.excludePaths ?? []).map(p =>\n typeof p === 'string' ? p : p.path\n )\n );\n const enrichSpan = options?.enrichSpan;\n const recordQuery = options?.recordQuery ?? false;\n const responseTraceHeader =\n options?.responseTraceHeader === false\n ? false\n : (options?.responseTraceHeader ?? 'X-Trace-Id');\n\n let cachedTracer: Tracer | undefined;\n const getTracer = (): Tracer => {\n if (!cachedTracer) {\n cachedTracer = trace.getTracer(tracerName, tracerVersion);\n }\n return cachedTracer;\n };\n\n return async (ctx: any, next: () => Promise<void>): Promise<void> => {\n const url: URL | undefined = ctx.url;\n const pathname = url?.pathname ?? ctx.path ?? '';\n\n if (excludePaths.has(pathname)) {\n await next();\n return;\n }\n\n const tracer = getTracer();\n const headers: Record<string, string> = ctx.headers ?? {};\n\n // Extract inbound trace context (W3C traceparent + baggage).\n const extractedCtx = propagation.extract(context.active(), headers);\n const batchSubrequest = getBatchSubrequestMeta(ctx);\n const parentCtx = batchSubrequest ? context.active() : extractedCtx;\n const linkedSpanContext: SpanContext | undefined =\n batchSubrequest && hasValidTraceparent(headers)\n ? trace.getSpanContext(extractedCtx)\n : undefined;\n\n const meta = getEndpointMeta(ctx);\n const route = getRouteTemplate(meta);\n const method: string = (ctx.method ?? 'GET').toUpperCase();\n // Span name follows OTel HTTP semconv (low-cardinality, descending priority):\n // 1. operationId — e.g. \"getTodoById\"\n // 2. METHOD ROUTE — e.g. \"GET /todos/{id}\"\n // 3. METHOD pathname — fallback when no route template is available\n const spanName: string =\n meta?.operationId ||\n (route ? `${method} ${route}` : `${method} ${pathname}`);\n\n const attributes: Record<string, string | number> = {\n [ATTR_HTTP_REQUEST_METHOD]: method,\n [ATTR_URL_PATH]: pathname\n };\n if (url?.protocol) {\n attributes[ATTR_URL_SCHEME] = url.protocol.replace(/:$/, '');\n }\n if (url?.hostname) {\n attributes[ATTR_SERVER_ADDRESS] = url.hostname;\n }\n if (url?.port) {\n const port = Number(url.port);\n if (Number.isFinite(port)) attributes[ATTR_SERVER_PORT] = port;\n }\n if (route) attributes[ATTR_HTTP_ROUTE] = route;\n if (headers['user-agent']) {\n attributes[ATTR_USER_AGENT_ORIGINAL] = headers['user-agent'];\n }\n if (recordQuery && url?.search) {\n attributes[ATTR_URL_QUERY] = url.search.replace(/^\\?/, '');\n }\n if (meta?.tags?.length) {\n attributes['cleverbrush.endpoint.tags'] = meta.tags.join(',');\n }\n if (meta?.operationId) {\n attributes['cleverbrush.endpoint.operationId'] = meta.operationId;\n }\n if (batchSubrequest) {\n attributes['cleverbrush.batch.index'] = batchSubrequest.index;\n attributes['cleverbrush.batch.size'] = batchSubrequest.size;\n attributes['cleverbrush.batch.path'] = batchSubrequest.path;\n }\n\n const spanOptions: SpanOptions = {\n kind: SpanKind.SERVER,\n attributes,\n ...(linkedSpanContext\n ? { links: [{ context: linkedSpanContext }] }\n : {})\n };\n\n await tracer.startActiveSpan(\n spanName,\n spanOptions,\n parentCtx,\n async (span: Span) => {\n ctx.items?.set?.(OTEL_SPAN_ITEM_KEY, span);\n\n // Write the trace ID to the response so consumers can look\n // up the exact trace in SigNoz / Jaeger / any backend.\n if (responseTraceHeader) {\n const { traceId } = span.spanContext();\n if (traceId) {\n const res = ctx.response;\n if (res?.setHeader) {\n res.setHeader(responseTraceHeader, traceId);\n } else if (ctx.setHeader) {\n ctx.setHeader(responseTraceHeader, traceId);\n }\n }\n }\n\n if (enrichSpan) {\n try {\n enrichSpan(span, ctx);\n } catch {\n // ignore enrichment errors\n }\n }\n\n try {\n await next();\n const status: number =\n ctx.response?.statusCode ?? ctx.statusCode ?? 200;\n span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, status);\n if (status >= 500) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n } catch (err) {\n const status: number =\n ctx.response?.statusCode ?? ctx.statusCode ?? 500;\n span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, status);\n if (err instanceof Error) {\n span.recordException(err);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: err.message\n });\n } else {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: String(err)\n });\n }\n throw err;\n } finally {\n span.end();\n }\n }\n );\n };\n}\n","import { DiagConsoleLogger, DiagLogLevel, diag } from '@opentelemetry/api';\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\nimport { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';\nimport { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport {\n BatchSpanProcessor,\n type SpanProcessor\n} from '@opentelemetry/sdk-trace-node';\nimport {\n ATTR_DEPLOYMENT_ENVIRONMENT_NAME,\n ATTR_SERVICE_NAME,\n ATTR_SERVICE_VERSION\n} from '@opentelemetry/semantic-conventions/incubating';\n\n/**\n * Configuration for {@link setupOtel}.\n */\nexport interface OtelConfig {\n /**\n * Logical name of the service emitting telemetry.\n * Becomes the `service.name` resource attribute and is the primary\n * identifier in observability backends.\n */\n serviceName: string;\n\n /** Optional service version → `service.version` resource attribute. */\n serviceVersion?: string;\n\n /**\n * Deployment environment name (e.g. `production`, `staging`, `dev`).\n * Becomes the `deployment.environment.name` resource attribute.\n */\n environment?: string;\n\n /**\n * Additional resource attributes merged onto the default resource.\n * Useful for `host.name`, `cloud.region`, custom team tags, etc.\n */\n resourceAttributes?: Record<string, string | number | boolean>;\n\n /**\n * Base OTLP/HTTP endpoint for traces, logs, and metrics.\n *\n * If not provided, falls back to the standard OTel environment\n * variables (`OTEL_EXPORTER_OTLP_ENDPOINT`,\n * `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`, etc.).\n *\n * Per-signal endpoints below take precedence over this value.\n *\n * @default process.env.OTEL_EXPORTER_OTLP_ENDPOINT\n */\n otlpEndpoint?: string;\n\n /** Override the OTLP traces endpoint (`/v1/traces` is appended). */\n tracesEndpoint?: string;\n\n /** Override the OTLP logs endpoint (`/v1/logs` is appended). */\n logsEndpoint?: string;\n\n /** Override the OTLP metrics endpoint (`/v1/metrics` is appended). */\n metricsEndpoint?: string;\n\n /**\n * Optional headers to send with every OTLP export request\n * (e.g. authentication tokens for hosted backends).\n */\n headers?: Record<string, string>;\n\n /** Disable trace export. @default false */\n disableTraces?: boolean;\n\n /** Disable log export. @default false */\n disableLogs?: boolean;\n\n /** Disable metrics export. @default false */\n disableMetrics?: boolean;\n\n /**\n * Metric export interval in milliseconds.\n * @default 60000\n */\n metricsExportIntervalMs?: number;\n\n /**\n * Auto-instrumentations to register at SDK startup.\n *\n * Use the helpers from `@cleverbrush/otel/instrumentations`\n * (e.g. `outboundHttpInstrumentations()`, `runtimeMetrics()`).\n */\n instrumentations?: unknown[];\n\n /**\n * Enable verbose OTel SDK diagnostics (sets the global `diag` logger).\n *\n * @default false\n */\n debug?: boolean;\n}\n\n/**\n * Handle returned by {@link setupOtel} for lifecycle management.\n */\nexport interface OtelHandle {\n /**\n * Gracefully flushes and shuts down all exporters.\n *\n * Idempotent — safe to call multiple times.\n * Should be invoked from your process's shutdown hook\n * (`SIGTERM`/`SIGINT`) before `process.exit`.\n */\n shutdown(): Promise<void>;\n\n /**\n * The underlying `NodeSDK` instance.\n *\n * Exposed for advanced use cases (custom span processors, runtime\n * configuration). Most consumers do not need to touch this directly.\n */\n sdk: NodeSDK;\n}\n\nfunction buildExporterUrl(\n base: string | undefined,\n perSignal: string | undefined,\n suffix: string\n): string | undefined {\n if (perSignal) return perSignal;\n if (!base) return undefined;\n return `${base.replace(/\\/$/, '')}${suffix}`;\n}\n\n/**\n * Initializes the OpenTelemetry Node SDK with sensible defaults for\n * the Cleverbrush framework.\n *\n * This must be called **before** any instrumented modules are imported\n * — typically via `node --import ./telemetry.js entrypoint.js`.\n *\n * Configures W3C Trace Context propagation, OTLP/HTTP exporters for\n * traces, logs, and metrics, and the resource attributes that identify\n * the service in observability backends.\n *\n * @param config - service identity and exporter configuration\n * @returns a handle exposing `shutdown()` and the underlying SDK\n *\n * @example\n * ```ts\n * // telemetry.ts — loaded via `node --import ./telemetry.js`\n * import { setupOtel } from '@cleverbrush/otel';\n * import { outboundHttpInstrumentations, runtimeMetrics } from '@cleverbrush/otel/instrumentations';\n *\n * export const otel = setupOtel({\n * serviceName: 'todo-backend',\n * serviceVersion: '1.0.0',\n * environment: process.env.NODE_ENV,\n * otlpEndpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,\n * instrumentations: [...outboundHttpInstrumentations(), runtimeMetrics()],\n * });\n *\n * process.on('SIGTERM', () => otel.shutdown());\n * ```\n */\nexport function setupOtel(config: OtelConfig): OtelHandle {\n if (!config.serviceName) {\n throw new Error('setupOtel: `serviceName` is required');\n }\n\n if (config.debug) {\n diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);\n }\n\n const attrs: Record<string, string | number | boolean> = {\n [ATTR_SERVICE_NAME]: config.serviceName,\n ...(config.serviceVersion\n ? { [ATTR_SERVICE_VERSION]: config.serviceVersion }\n : {}),\n ...(config.environment\n ? { [ATTR_DEPLOYMENT_ENVIRONMENT_NAME]: config.environment }\n : {}),\n ...(config.resourceAttributes ?? {})\n };\n const resource = resourceFromAttributes(attrs);\n\n const headers = config.headers;\n\n let spanProcessors: SpanProcessor[] | undefined;\n if (!config.disableTraces) {\n const url = buildExporterUrl(\n config.otlpEndpoint,\n config.tracesEndpoint,\n '/v1/traces'\n );\n const traceExporter = new OTLPTraceExporter({\n ...(url ? { url } : {}),\n ...(headers ? { headers } : {})\n });\n spanProcessors = [new BatchSpanProcessor(traceExporter)];\n }\n\n let logRecordProcessors: BatchLogRecordProcessor[] | undefined;\n if (!config.disableLogs) {\n const url = buildExporterUrl(\n config.otlpEndpoint,\n config.logsEndpoint,\n '/v1/logs'\n );\n const logExporter = new OTLPLogExporter({\n ...(url ? { url } : {}),\n ...(headers ? { headers } : {})\n });\n logRecordProcessors = [new BatchLogRecordProcessor(logExporter)];\n }\n\n let metricReader: PeriodicExportingMetricReader | undefined;\n if (!config.disableMetrics) {\n const url = buildExporterUrl(\n config.otlpEndpoint,\n config.metricsEndpoint,\n '/v1/metrics'\n );\n const metricExporter = new OTLPMetricExporter({\n ...(url ? { url } : {}),\n ...(headers ? { headers } : {})\n });\n metricReader = new PeriodicExportingMetricReader({\n exporter: metricExporter,\n exportIntervalMillis: config.metricsExportIntervalMs ?? 60_000\n });\n }\n\n const sdk = new NodeSDK({\n resource,\n ...(spanProcessors ? { spanProcessors } : {}),\n ...(logRecordProcessors ? { logRecordProcessors } : {}),\n ...(metricReader ? { metricReaders: [metricReader] } : {}),\n instrumentations: (config.instrumentations ?? []) as any\n });\n\n sdk.start();\n\n let shuttingDown: Promise<void> | null = null;\n\n return {\n sdk,\n shutdown(): Promise<void> {\n if (!shuttingDown) {\n shuttingDown = sdk.shutdown().catch(err => {\n // Best-effort shutdown — log but don't throw\n console.error('[otel] shutdown error:', err);\n });\n }\n return shuttingDown;\n }\n };\n}\n","import type { LogEvent, LogSink } from '@cleverbrush/log';\nimport { LogLevel, levelToString } from '@cleverbrush/log';\nimport {\n logs,\n type Logger as OtelLogger,\n SeverityNumber\n} from '@opentelemetry/api-logs';\n\n/**\n * Configuration for {@link otelLogSink}.\n */\nexport interface OtelLogSinkOptions {\n /**\n * Logger name (`InstrumentationScope`) under which records are\n * emitted via the OTel Logs API.\n *\n * @default '@cleverbrush/otel'\n */\n loggerName?: string;\n\n /** Optional logger version. */\n loggerVersion?: string;\n\n /**\n * Hook for redacting / dropping properties before they become\n * OTel log record attributes. Return `undefined` to drop the\n * attribute entirely.\n */\n sanitizeAttribute?: (key: string, value: unknown) => unknown | undefined;\n}\n\nconst SEVERITY_NUMBER: Record<LogLevel, SeverityNumber> = {\n [LogLevel.Trace]: SeverityNumber.TRACE,\n [LogLevel.Debug]: SeverityNumber.DEBUG,\n [LogLevel.Information]: SeverityNumber.INFO,\n [LogLevel.Warning]: SeverityNumber.WARN,\n [LogLevel.Error]: SeverityNumber.ERROR,\n [LogLevel.Fatal]: SeverityNumber.FATAL\n};\n\nconst SCALAR_TYPES = new Set(['string', 'number', 'boolean', 'bigint']);\n\nfunction toAttributeValue(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n if (typeof value === 'function' || typeof value === 'symbol') {\n return undefined;\n }\n if (SCALAR_TYPES.has(typeof value)) return value;\n if (Array.isArray(value)) {\n return value.map(toAttributeValue).filter(v => v !== undefined);\n }\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n/**\n * Creates a {@link LogSink} that forwards every event to the\n * OpenTelemetry Logs API.\n *\n * Maps each `LogEvent` to an OTel `LogRecord`:\n * - `timestamp` → nanoseconds via `Date.getTime()` × 1e6\n * - `level` → `severityNumber` and `severityText`\n * - `renderedMessage` → `body`\n * - `properties` → flat attribute map (functions / symbols dropped,\n * nested objects JSON-stringified)\n * - `messageTemplate` → `cleverbrush.message_template` attribute\n * - `eventId` → `cleverbrush.event_id` attribute\n * - `exception.*` attributes when the event carries an `Error`\n *\n * Trace correlation (`traceId`, `spanId`) is filled in automatically\n * by the OTel SDK from the active context — typically established by\n * `tracingMiddleware`.\n *\n * The sink itself is per-event; for high-throughput services wrap it\n * with `BatchingSink` from `@cleverbrush/log`.\n *\n * Requires that `setupOtel({ ... })` has been called so the global\n * `LoggerProvider` is set; otherwise emissions become no-ops.\n *\n * @param options - logger name and attribute sanitization\n * @returns a `LogSink` that emits to the OTel Logs pipeline\n *\n * @example\n * ```ts\n * import { createLogger, consoleSink } from '@cleverbrush/log';\n * import { otelLogSink } from '@cleverbrush/otel';\n *\n * const logger = createLogger({\n * minimumLevel: 'information',\n * sinks: [consoleSink(), otelLogSink()],\n * });\n * ```\n */\nexport function otelLogSink(options?: OtelLogSinkOptions): LogSink {\n const loggerName = options?.loggerName ?? '@cleverbrush/otel';\n const loggerVersion = options?.loggerVersion;\n const sanitize = options?.sanitizeAttribute;\n\n let cached: OtelLogger | undefined;\n const getLogger = (): OtelLogger => {\n if (!cached) cached = logs.getLogger(loggerName, loggerVersion);\n return cached;\n };\n\n return {\n async emit(events: LogEvent[]): Promise<void> {\n const otelLogger = getLogger();\n for (const event of events) {\n const attributes: Record<string, unknown> = {};\n for (const [key, raw] of Object.entries(event.properties)) {\n const value = sanitize\n ? sanitize(key, raw)\n : toAttributeValue(raw);\n if (value !== undefined) attributes[key] = value;\n }\n\n if (event.messageTemplate) {\n attributes['cleverbrush.message_template'] =\n event.messageTemplate;\n }\n if (event.eventId) {\n attributes['cleverbrush.event_id'] = event.eventId;\n }\n if (event.exception) {\n attributes['exception.type'] = event.exception.name;\n attributes['exception.message'] = event.exception.message;\n if (event.exception.stack) {\n attributes['exception.stacktrace'] =\n event.exception.stack;\n }\n }\n\n otelLogger.emit({\n timestamp: event.timestamp.getTime(), // milliseconds; OTel SDK converts to ns internally\n severityNumber: SEVERITY_NUMBER[event.level],\n severityText: levelToString(event.level).toUpperCase(),\n body: event.renderedMessage,\n attributes: attributes as any\n });\n }\n },\n\n async [Symbol.asyncDispose](): Promise<void> {\n // The OTel SDK owns the LoggerProvider lifecycle —\n // it is shut down via `OtelHandle.shutdown()`.\n }\n };\n}\n","import {\n type Span,\n type SpanAttributes,\n SpanKind,\n SpanStatusCode,\n trace\n} from '@opentelemetry/api';\n\nconst DEFAULT_TRACER_NAME = '@cleverbrush/otel';\n\n/**\n * Options shared by both calling conventions of {@link withSpan}.\n */\nexport interface WithSpanOptions {\n /**\n * OTel span kind.\n * @default SpanKind.INTERNAL\n */\n kind?: SpanKind;\n\n /** Initial attributes to set on the span. */\n attributes?: SpanAttributes;\n\n /**\n * Tracer name used when resolving the OTel tracer.\n * @default '@cleverbrush/otel'\n */\n tracerName?: string;\n\n /** Tracer version. */\n tracerVersion?: string;\n}\n\n/**\n * Handle returned by the disposable form of {@link withSpan}.\n *\n * Implements `AsyncDisposable` so it can be used with `await using`:\n * ```ts\n * await using handle = withSpan('my.operation');\n * handle.span.setAttribute('key', value);\n * try {\n * // ... do work ...\n * } catch (err) {\n * handle.fail(err);\n * throw err;\n * }\n * // span.end() is called automatically when the block exits\n * ```\n *\n * **Note:** The span created by the disposable form is NOT activated as\n * the current context span (OTel's public API requires a callback scope\n * for context propagation). DB / outbound-HTTP child spans created\n * inside the `await using` block will be siblings of this span (both\n * children of the enclosing HTTP span) rather than nested under it.\n * Use the callback form of `withSpan` when proper nesting is required.\n */\nexport interface SpanHandle {\n /** The underlying OTel span. Use it to set attributes or add events. */\n readonly span: Span;\n\n /**\n * Records an exception and marks the span as errored.\n *\n * Idempotent — safe to call in a `catch` block even if the same\n * error might be reported elsewhere.\n */\n fail(err: unknown): void;\n\n /** Ends the span. Called automatically when exiting an `await using` block. */\n [Symbol.asyncDispose](): Promise<void>;\n}\n\n/**\n * Wraps `fn` in a custom OTel span and returns whatever `fn` returns.\n *\n * The span is activated as the current context span via\n * `startActiveSpan`, so any child spans created inside `fn` (e.g. DB\n * queries from `instrumentKnex`) will be correctly nested under it.\n * Error handling is automatic — exceptions are recorded and the span is\n * marked `ERROR` before re-throwing.\n *\n * @example\n * ```ts\n * const result = await withSpan('order.process', async span => {\n * span.setAttribute('order.id', orderId);\n * return processOrder(orderId); // DB spans nest under this span\n * });\n * ```\n */\nexport function withSpan<T>(\n name: string,\n fn: (span: Span) => T,\n options?: WithSpanOptions\n): T;\n\n/**\n * Creates a custom OTel span and returns a {@link SpanHandle} that\n * implements `AsyncDisposable`.\n *\n * Use with `await using` for an ergonomic resource-management syntax.\n * Note that the span is **not** the active context span (see\n * {@link SpanHandle} for details). Call {@link SpanHandle.fail} in a\n * `catch` block to record errors before re-throwing.\n *\n * @example\n * ```ts\n * await using handle = withSpan('todo.create', { attributes: { 'todo.user_id': userId } });\n * try {\n * const todo = await db.todos.insert({...});\n * handle.span.setAttribute('todo.id', todo.id);\n * } catch (err) {\n * handle.fail(err);\n * throw err;\n * }\n * ```\n */\nexport function withSpan(name: string, options?: WithSpanOptions): SpanHandle;\n\nexport function withSpan<T>(\n name: string,\n fnOrOptions?: ((span: Span) => T) | WithSpanOptions,\n maybeOptions?: WithSpanOptions\n): T | SpanHandle {\n const isCallback = typeof fnOrOptions === 'function';\n const fn = isCallback ? (fnOrOptions as (span: Span) => T) : undefined;\n const opts: WithSpanOptions | undefined = isCallback\n ? maybeOptions\n : (fnOrOptions as WithSpanOptions | undefined);\n\n const tracerName = opts?.tracerName ?? DEFAULT_TRACER_NAME;\n const tracerVersion = opts?.tracerVersion;\n const kind = opts?.kind ?? SpanKind.INTERNAL;\n const attributes = opts?.attributes;\n\n const tracer = trace.getTracer(tracerName, tracerVersion);\n\n if (fn !== undefined) {\n // Callback form — startActiveSpan propagates the span into context so\n // child spans (e.g. Knex DB spans) nest under it automatically.\n return tracer.startActiveSpan(name, { kind, attributes }, span => {\n let result: T;\n try {\n result = fn(span);\n } catch (err) {\n span.recordException(err as Error);\n span.setStatus({ code: SpanStatusCode.ERROR });\n span.end();\n throw err;\n }\n if (result instanceof Promise) {\n return result.then(\n v => {\n span.end();\n return v;\n },\n err => {\n span.recordException(err);\n span.setStatus({ code: SpanStatusCode.ERROR });\n span.end();\n throw err;\n }\n ) as T;\n }\n span.end();\n return result;\n });\n }\n\n // Disposable form — span is not activated in context (no callback scope\n // available in the OTel public API for context.with).\n const span = tracer.startSpan(name, { kind, attributes });\n let failed = false;\n\n const handle: SpanHandle = {\n span,\n fail(err: unknown): void {\n if (failed) return;\n failed = true;\n span.recordException(err as Error);\n span.setStatus({ code: SpanStatusCode.ERROR });\n },\n [Symbol.asyncDispose](): Promise<void> {\n span.end();\n return Promise.resolve();\n }\n };\n\n return handle;\n}\n"],"mappings":"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","names":["metrics","trace","ITracer","IMeter","configureOtel","services","options","tracerName","meterName","version","trace","traceEnricher","event","span","ctx","SpanKind","SpanStatusCode","trace","ATTR_DB_NAMESPACE","ATTR_DB_OPERATION_NAME","ATTR_DB_QUERY_TEXT","ATTR_DB_SYSTEM_NAME","ATTR_SERVER_ADDRESS","ATTR_SERVER_PORT","FIRST_KEYWORD_RE","KNOWN_SQL_OPS","inferOperation","sql","method","word","inferDbSystem","client","dialect","norm","instrumentKnex","k","options","tracerName","recordStatement","sanitize","cachedTracer","getTracer","dbSystem","connection","dbName","host","port","active","baseAttrs","q","uid","operation","spanName","attrs","span","_resp","err","context","propagation","SpanKind","SpanStatusCode","trace","ATTR_HTTP_REQUEST_METHOD","ATTR_HTTP_RESPONSE_STATUS_CODE","ATTR_HTTP_ROUTE","ATTR_SERVER_ADDRESS","ATTR_SERVER_PORT","ATTR_URL_PATH","ATTR_URL_QUERY","ATTR_URL_SCHEME","ATTR_USER_AGENT_ORIGINAL","OTEL_SPAN_ITEM_KEY","getEndpointMeta","ctx","getBatchSubrequestMeta","meta","hasValidTraceparent","headers","getRouteTemplate","base","tpl","candidate","tracingMiddleware","options","tracerName","tracerVersion","excludePaths","p","enrichSpan","recordQuery","responseTraceHeader","cachedTracer","getTracer","next","url","pathname","tracer","extractedCtx","batchSubrequest","parentCtx","linkedSpanContext","route","method","spanName","attributes","port","spanOptions","span","traceId","res","status","err","DiagConsoleLogger","DiagLogLevel","diag","OTLPLogExporter","OTLPMetricExporter","OTLPTraceExporter","resourceFromAttributes","BatchLogRecordProcessor","PeriodicExportingMetricReader","NodeSDK","BatchSpanProcessor","ATTR_DEPLOYMENT_ENVIRONMENT_NAME","ATTR_SERVICE_NAME","ATTR_SERVICE_VERSION","buildExporterUrl","base","perSignal","suffix","setupOtel","config","attrs","resource","headers","spanProcessors","url","traceExporter","logRecordProcessors","logExporter","metricReader","metricExporter","sdk","shuttingDown","err","LogLevel","levelToString","logs","SeverityNumber","SEVERITY_NUMBER","SCALAR_TYPES","toAttributeValue","value","v","otelLogSink","options","loggerName","loggerVersion","sanitize","cached","getLogger","events","otelLogger","event","attributes","key","raw","SpanKind","SpanStatusCode","trace","DEFAULT_TRACER_NAME","withSpan","name","fnOrOptions","maybeOptions","isCallback","fn","opts","tracerName","tracerVersion","kind","attributes","tracer","span","result","err","v","failed"]}
|
package/package.json
CHANGED