autotel-hono 0.4.41 → 0.4.42

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["HTTPAttributes","ServiceAttributes","SpanStatusCode","propagation","otelContext","SpanKind","URLAttributes"],"sources":["../src/metrics.ts","../src/index.ts"],"sourcesContent":["/**\n * HTTP server metrics per OTel semantic conventions.\n *\n * Lazy-initialized request duration histogram and active-requests up/down counter.\n */\n\ntype AttributeValue = string | number | boolean | undefined;\nexport type Attributes = Record<string, AttributeValue>;\n\ntype Histogram = {\n record: (value: number, attrs?: Attributes) => void;\n};\n\ntype UpDownCounter = {\n add: (value: number, attrs?: Attributes) => void;\n};\n\nexport type Meter = {\n createHistogram: (\n name: string,\n options?: {\n description?: string;\n unit?: string;\n advice?: { explicitBucketBoundaries?: number[] };\n },\n ) => Histogram;\n createUpDownCounter: (\n name: string,\n options?: {\n description?: string;\n },\n ) => UpDownCounter;\n};\n\n/** OTel HTTP server metric names (stable convention names) */\nconst METRIC_HTTP_SERVER_REQUEST_DURATION = 'http.server.request.duration';\nconst METRIC_HTTP_SERVER_ACTIVE_REQUESTS = 'http.server.active_requests';\n\n/** Recommended bucket boundaries for request duration (seconds) */\nconst HTTP_DURATION_BUCKETS = [\n 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,\n];\n\nexport type HttpMetricsConfig = {\n meter: Meter;\n captureRequestDuration?: boolean;\n captureActiveRequests?: boolean;\n};\n\nexport function createRequestDurationTracker(config: HttpMetricsConfig): {\n record: (durationSeconds: number, attrs: Attributes) => void;\n} {\n if (config.captureRequestDuration === false) {\n return { record: () => {} };\n }\n const histogram = config.meter.createHistogram(\n METRIC_HTTP_SERVER_REQUEST_DURATION,\n {\n description: 'Duration of HTTP server requests in seconds',\n unit: 's',\n advice: { explicitBucketBoundaries: HTTP_DURATION_BUCKETS },\n },\n );\n return {\n record(durationSeconds: number, attrs: Attributes) {\n histogram.record(durationSeconds, attrs);\n },\n };\n}\n\nexport function createActiveRequestsTracker(config: HttpMetricsConfig): {\n increment: (attrs: Attributes) => void;\n decrement: (attrs: Attributes) => void;\n} | undefined {\n if (config.captureActiveRequests === false) {\n return undefined;\n }\n const counter = config.meter.createUpDownCounter(\n METRIC_HTTP_SERVER_ACTIVE_REQUESTS,\n {\n description: 'Number of active (in-flight) HTTP server requests',\n },\n );\n return {\n increment(attrs: Attributes) {\n counter.add(1, attrs);\n },\n decrement(attrs: Attributes) {\n counter.add(-1, attrs);\n },\n };\n}\n","import type { Span, Tracer } from 'autotel';\nimport {\n getTracer,\n getMeter,\n context as otelContext,\n propagation,\n SpanKind,\n SpanStatusCode,\n HTTPAttributes,\n URLAttributes,\n ServiceAttributes,\n httpRequestHeaderAttribute,\n httpResponseHeaderAttribute,\n} from 'autotel';\nimport type { MiddlewareHandler, Context } from 'hono';\nimport { createMiddleware } from 'hono/factory';\nimport { routePath } from 'hono/route';\nimport {\n createRequestDurationTracker,\n createActiveRequestsTracker,\n type HttpMetricsConfig,\n} from './metrics';\n\nconst INSTRUMENTATION_SCOPE_NAME = 'autotel-hono';\n\ntype TimeInput = number | [number, number];\ntype TracerProvider = { getTracer(name: string, version?: string): Tracer };\ntype Meter = HttpMetricsConfig['meter'];\ntype MeterProvider = { getMeter(name: string, version?: string): Meter };\n\nfunction now(): number {\n const p = (globalThis as unknown as { performance?: { now(): number } })\n .performance;\n return p?.now() ?? Date.now();\n}\n\nexport type OtelConfig = {\n tracer?: Tracer;\n tracerProvider?: TracerProvider;\n meter?: Meter;\n meterProvider?: MeterProvider;\n tracerName?: string;\n spanNameFactory?: (c: Context) => string;\n captureRequestHeaders?: string[];\n captureResponseHeaders?: string[];\n captureActiveRequests?: boolean;\n captureRequestDuration?: boolean;\n serviceName?: string;\n serviceVersion?: string;\n disableTracing?: boolean;\n getTime?(): TimeInput;\n};\n\ntype NormalizedOtelConfig = OtelConfig & {\n requestHeaderSet: Set<string>;\n responseHeaderSet: Set<string>;\n captureActiveRequests: boolean;\n captureRequestDuration: boolean;\n};\n\nfunction normalizeConfig(config: OtelConfig = {}): NormalizedOtelConfig {\n const reqHeadersSrc = [...(config.captureRequestHeaders ?? [])];\n const resHeadersSrc = [...(config.captureResponseHeaders ?? [])];\n const requestHeaderSet = new Set(reqHeadersSrc.map((h) => h.toLowerCase()));\n const responseHeaderSet = new Set(resHeadersSrc.map((h) => h.toLowerCase()));\n return {\n ...config,\n requestHeaderSet,\n responseHeaderSet,\n captureActiveRequests: config.captureActiveRequests ?? true,\n captureRequestDuration: config.captureRequestDuration ?? true,\n };\n}\n\nfunction resolveTracer(config: NormalizedOtelConfig): Tracer | undefined {\n if (config.disableTracing) return undefined;\n if (config.tracer) return config.tracer;\n if (config.tracerProvider) {\n return config.tracerProvider.getTracer(\n config.tracerName ?? INSTRUMENTATION_SCOPE_NAME,\n config.serviceVersion,\n );\n }\n return getTracer(config.tracerName ?? INSTRUMENTATION_SCOPE_NAME, config.serviceVersion);\n}\n\nfunction resolveMeter(config: NormalizedOtelConfig): Meter {\n if (config.meter) return config.meter;\n if (config.meterProvider) {\n return config.meterProvider.getMeter(\n INSTRUMENTATION_SCOPE_NAME,\n config.serviceVersion,\n );\n }\n return getMeter();\n}\n\nexport function otel(userConfig: OtelConfig = {}): MiddlewareHandler {\n const config = normalizeConfig(userConfig);\n const tracer = resolveTracer(config);\n const meter = resolveMeter(config);\n\n const metricsConfig: HttpMetricsConfig = {\n meter,\n captureRequestDuration: config.captureRequestDuration,\n captureActiveRequests: config.captureActiveRequests,\n };\n const requestDuration = createRequestDurationTracker(metricsConfig);\n const activeReqs = createActiveRequestsTracker(metricsConfig);\n\n const spanName = (c: Context) =>\n config.spanNameFactory?.(c) ?? `${c.req.method} ${routePath(c)}`;\n\n return createMiddleware(async (c, next) => {\n const method = c.req.method;\n\n const stableAttrs: Record<string, string | number | undefined> = {\n [HTTPAttributes.requestMethod]: method,\n [ServiceAttributes.name]: config.serviceName,\n [ServiceAttributes.version]: config.serviceVersion,\n };\n\n activeReqs?.increment(stableAttrs);\n const startTime = now();\n\n const deferredRequestHeaderAttributes: Record<string, string> = {};\n const reqHeaders = c.req.raw.headers;\n for (const [rawName, value] of reqHeaders.entries()) {\n const name = rawName.toLowerCase();\n if (config.requestHeaderSet.has(name)) {\n deferredRequestHeaderAttributes[httpRequestHeaderAttribute(name)] =\n typeof value === 'string' ? value : value[0] ?? '';\n }\n }\n\n const finalize = (span?: Span, error?: unknown) => {\n try {\n const status = c.res.status;\n\n if (span) {\n for (const [name, value] of c.res.headers.entries()) {\n const lower = name.toLowerCase();\n if (config.responseHeaderSet.has(lower)) {\n span.setAttribute(httpResponseHeaderAttribute(lower), value);\n }\n }\n span.setAttribute(HTTPAttributes.responseStatusCode, status);\n if (status >= 500) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n if (error) {\n try {\n span.recordException(error as Error);\n } catch {\n // Ignore errors when recording exception\n }\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n }\n } finally {\n activeReqs?.decrement(stableAttrs);\n span?.setAttribute(HTTPAttributes.route, routePath(c));\n span?.updateName(spanName(c));\n const durationSeconds = (now() - startTime) / 1000;\n requestDuration.record(durationSeconds, {\n ...stableAttrs,\n [HTTPAttributes.route]: routePath(c),\n [HTTPAttributes.responseStatusCode]: c.res.status,\n });\n }\n };\n\n if (!tracer) {\n try {\n await next();\n finalize();\n } catch (error) {\n finalize(undefined, error);\n throw error;\n }\n return;\n }\n\n const parent = propagation.extract(otelContext.active(), c.req.header());\n return tracer.startActiveSpan(\n spanName(c),\n {\n kind: SpanKind.SERVER,\n startTime: config.getTime?.(),\n attributes: {\n ...stableAttrs,\n [URLAttributes.full]: c.req.url,\n [HTTPAttributes.route]: routePath(c),\n },\n },\n parent,\n async (span) => {\n try {\n for (const [k, v] of Object.entries(deferredRequestHeaderAttributes)) {\n span.setAttribute(k, v);\n }\n await next();\n finalize(span, c.error);\n } catch (error) {\n finalize(span, error);\n throw error;\n } finally {\n span.end(config.getTime?.());\n }\n },\n );\n });\n}\n"],"mappings":";;;;;;AAmCA,MAAM,sCAAsC;AAC5C,MAAM,qCAAqC;;AAG3C,MAAM,wBAAwB;CAC5B;CAAO;CAAM;CAAO;CAAM;CAAO;CAAK;CAAM;CAAK;CAAM;CAAG;CAAK;CAAG;CAAK;AACzE;AAQA,SAAgB,6BAA6B,QAE3C;CACA,IAAI,OAAO,2BAA2B,OACpC,OAAO,EAAE,cAAc,CAAC,EAAE;CAE5B,MAAM,YAAY,OAAO,MAAM,gBAC7B,qCACA;EACE,aAAa;EACb,MAAM;EACN,QAAQ,EAAE,0BAA0B,sBAAsB;CAC5D,CACF;CACA,OAAO,EACL,OAAO,iBAAyB,OAAmB;EACjD,UAAU,OAAO,iBAAiB,KAAK;CACzC,EACF;AACF;AAEA,SAAgB,4BAA4B,QAG9B;CACZ,IAAI,OAAO,0BAA0B,OACnC;CAEF,MAAM,UAAU,OAAO,MAAM,oBAC3B,oCACA,EACE,aAAa,oDACf,CACF;CACA,OAAO;EACL,UAAU,OAAmB;GAC3B,QAAQ,IAAI,GAAG,KAAK;EACtB;EACA,UAAU,OAAmB;GAC3B,QAAQ,IAAI,IAAI,KAAK;EACvB;CACF;AACF;;;ACpEA,MAAM,6BAA6B;AAOnC,SAAS,MAAc;CAGrB,OAFW,WACR,aACO,IAAI,KAAK,KAAK,IAAI;AAC9B;AA0BA,SAAS,gBAAgB,SAAqB,CAAC,GAAyB;CACtE,MAAM,gBAAgB,CAAC,GAAI,OAAO,yBAAyB,CAAC,CAAE;CAC9D,MAAM,gBAAgB,CAAC,GAAI,OAAO,0BAA0B,CAAC,CAAE;CAC/D,MAAM,mBAAmB,IAAI,IAAI,cAAc,KAAK,MAAM,EAAE,YAAY,CAAC,CAAC;CAC1E,MAAM,oBAAoB,IAAI,IAAI,cAAc,KAAK,MAAM,EAAE,YAAY,CAAC,CAAC;CAC3E,OAAO;EACL,GAAG;EACH;EACA;EACA,uBAAuB,OAAO,yBAAyB;EACvD,wBAAwB,OAAO,0BAA0B;CAC3D;AACF;AAEA,SAAS,cAAc,QAAkD;CACvE,IAAI,OAAO,gBAAgB,OAAO,KAAA;CAClC,IAAI,OAAO,QAAQ,OAAO,OAAO;CACjC,IAAI,OAAO,gBACT,OAAO,OAAO,eAAe,UAC3B,OAAO,cAAc,4BACrB,OAAO,cACT;CAEF,QAAA,GAAA,QAAA,UAAA,CAAiB,OAAO,cAAc,4BAA4B,OAAO,cAAc;AACzF;AAEA,SAAS,aAAa,QAAqC;CACzD,IAAI,OAAO,OAAO,OAAO,OAAO;CAChC,IAAI,OAAO,eACT,OAAO,OAAO,cAAc,SAC1B,4BACA,OAAO,cACT;CAEF,QAAA,GAAA,QAAA,SAAA,CAAgB;AAClB;AAEA,SAAgB,KAAK,aAAyB,CAAC,GAAsB;CACnE,MAAM,SAAS,gBAAgB,UAAU;CACzC,MAAM,SAAS,cAAc,MAAM;CAGnC,MAAM,gBAAmC;EACvC,OAHY,aAAa,MAGrB;EACJ,wBAAwB,OAAO;EAC/B,uBAAuB,OAAO;CAChC;CACA,MAAM,kBAAkB,6BAA6B,aAAa;CAClE,MAAM,aAAa,4BAA4B,aAAa;CAE5D,MAAM,YAAY,MAChB,OAAO,kBAAkB,CAAC,KAAK,GAAG,EAAE,IAAI,OAAO,IAAA,GAAA,WAAA,UAAA,CAAa,CAAC;CAE/D,QAAA,GAAA,aAAA,iBAAA,CAAwB,OAAO,GAAG,SAAS;EACzC,MAAM,SAAS,EAAE,IAAI;EAErB,MAAM,cAA2D;IAC9DA,QAAAA,eAAe,gBAAgB;IAC/BC,QAAAA,kBAAkB,OAAO,OAAO;IAChCA,QAAAA,kBAAkB,UAAU,OAAO;EACtC;EAEA,YAAY,UAAU,WAAW;EACjC,MAAM,YAAY,IAAI;EAEtB,MAAM,kCAA0D,CAAC;EACjE,MAAM,aAAa,EAAE,IAAI,IAAI;EAC7B,KAAK,MAAM,CAAC,SAAS,UAAU,WAAW,QAAQ,GAAG;GACnD,MAAM,OAAO,QAAQ,YAAY;GACjC,IAAI,OAAO,iBAAiB,IAAI,IAAI,GAClC,iCAAA,GAAA,QAAA,2BAAA,CAA2D,IAAI,KAC7D,OAAO,UAAU,WAAW,QAAQ,MAAM,MAAM;EAEtD;EAEA,MAAM,YAAY,MAAa,UAAoB;GACjD,IAAI;IACF,MAAM,SAAS,EAAE,IAAI;IAErB,IAAI,MAAM;KACR,KAAK,MAAM,CAAC,MAAM,UAAU,EAAE,IAAI,QAAQ,QAAQ,GAAG;MACnD,MAAM,QAAQ,KAAK,YAAY;MAC/B,IAAI,OAAO,kBAAkB,IAAI,KAAK,GACpC,KAAK,cAAA,GAAA,QAAA,4BAAA,CAAyC,KAAK,GAAG,KAAK;KAE/D;KACA,KAAK,aAAaD,QAAAA,eAAe,oBAAoB,MAAM;KAC3D,IAAI,UAAU,KACZ,KAAK,UAAU,EAAE,MAAME,QAAAA,eAAe,MAAM,CAAC;KAE/C,IAAI,OAAO;MACT,IAAI;OACF,KAAK,gBAAgB,KAAc;MACrC,QAAQ,CAER;MACA,KAAK,UAAU,EAAE,MAAMA,QAAAA,eAAe,MAAM,CAAC;KAC/C;IACF;GACF,UAAU;IACR,YAAY,UAAU,WAAW;IACjC,MAAM,aAAaF,QAAAA,eAAe,QAAA,GAAA,WAAA,UAAA,CAAiB,CAAC,CAAC;IACrD,MAAM,WAAW,SAAS,CAAC,CAAC;IAC5B,MAAM,mBAAmB,IAAI,IAAI,aAAa;IAC9C,gBAAgB,OAAO,iBAAiB;KACtC,GAAG;MACFA,QAAAA,eAAe,SAAA,GAAA,WAAA,UAAA,CAAkB,CAAC;MAClCA,QAAAA,eAAe,qBAAqB,EAAE,IAAI;IAC7C,CAAC;GACH;EACF;EAEA,IAAI,CAAC,QAAQ;GACX,IAAI;IACF,MAAM,KAAK;IACX,SAAS;GACX,SAAS,OAAO;IACd,SAAS,KAAA,GAAW,KAAK;IACzB,MAAM;GACR;GACA;EACF;EAEA,MAAM,SAASG,QAAAA,YAAY,QAAQC,QAAAA,QAAY,OAAO,GAAG,EAAE,IAAI,OAAO,CAAC;EACvE,OAAO,OAAO,gBACZ,SAAS,CAAC,GACV;GACE,MAAMC,QAAAA,SAAS;GACf,WAAW,OAAO,UAAU;GAC5B,YAAY;IACV,GAAG;KACFC,QAAAA,cAAc,OAAO,EAAE,IAAI;KAC3BN,QAAAA,eAAe,SAAA,GAAA,WAAA,UAAA,CAAkB,CAAC;GACrC;EACF,GACA,QACA,OAAO,SAAS;GACd,IAAI;IACF,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,+BAA+B,GACjE,KAAK,aAAa,GAAG,CAAC;IAExB,MAAM,KAAK;IACX,SAAS,MAAM,EAAE,KAAK;GACxB,SAAS,OAAO;IACd,SAAS,MAAM,KAAK;IACpB,MAAM;GACR,UAAU;IACR,KAAK,IAAI,OAAO,UAAU,CAAC;GAC7B;EACF,CACF;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"index.cjs","names":["HTTPAttributes","ServiceAttributes","SpanStatusCode","propagation","otelContext","SpanKind","URLAttributes"],"sources":["../src/metrics.ts","../src/index.ts"],"sourcesContent":["/**\n * HTTP server metrics per OTel semantic conventions.\n *\n * Lazy-initialized request duration histogram and active-requests up/down counter.\n */\n\ntype AttributeValue = string | number | boolean | undefined;\nexport type Attributes = Record<string, AttributeValue>;\n\ntype Histogram = {\n record: (value: number, attrs?: Attributes) => void;\n};\n\ntype UpDownCounter = {\n add: (value: number, attrs?: Attributes) => void;\n};\n\nexport type Meter = {\n createHistogram: (\n name: string,\n options?: {\n description?: string;\n unit?: string;\n advice?: { explicitBucketBoundaries?: number[] };\n },\n ) => Histogram;\n createUpDownCounter: (\n name: string,\n options?: {\n description?: string;\n },\n ) => UpDownCounter;\n};\n\n/** OTel HTTP server metric names (stable convention names) */\nconst METRIC_HTTP_SERVER_REQUEST_DURATION = 'http.server.request.duration';\nconst METRIC_HTTP_SERVER_ACTIVE_REQUESTS = 'http.server.active_requests';\n\n/** Recommended bucket boundaries for request duration (seconds) */\nconst HTTP_DURATION_BUCKETS = [\n 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,\n];\n\nexport type HttpMetricsConfig = {\n meter: Meter;\n captureRequestDuration?: boolean;\n captureActiveRequests?: boolean;\n};\n\nexport function createRequestDurationTracker(config: HttpMetricsConfig): {\n record: (durationSeconds: number, attrs: Attributes) => void;\n} {\n if (config.captureRequestDuration === false) {\n return { record: () => {} };\n }\n const histogram = config.meter.createHistogram(\n METRIC_HTTP_SERVER_REQUEST_DURATION,\n {\n description: 'Duration of HTTP server requests in seconds',\n unit: 's',\n advice: { explicitBucketBoundaries: HTTP_DURATION_BUCKETS },\n },\n );\n return {\n record(durationSeconds: number, attrs: Attributes) {\n histogram.record(durationSeconds, attrs);\n },\n };\n}\n\nexport function createActiveRequestsTracker(config: HttpMetricsConfig):\n | {\n increment: (attrs: Attributes) => void;\n decrement: (attrs: Attributes) => void;\n }\n | undefined {\n if (config.captureActiveRequests === false) {\n return undefined;\n }\n const counter = config.meter.createUpDownCounter(\n METRIC_HTTP_SERVER_ACTIVE_REQUESTS,\n {\n description: 'Number of active (in-flight) HTTP server requests',\n },\n );\n return {\n increment(attrs: Attributes) {\n counter.add(1, attrs);\n },\n decrement(attrs: Attributes) {\n counter.add(-1, attrs);\n },\n };\n}\n","import type { Span, Tracer } from 'autotel';\nimport {\n getTracer,\n getMeter,\n context as otelContext,\n propagation,\n SpanKind,\n SpanStatusCode,\n HTTPAttributes,\n URLAttributes,\n ServiceAttributes,\n httpRequestHeaderAttribute,\n httpResponseHeaderAttribute,\n} from 'autotel';\nimport type { MiddlewareHandler, Context } from 'hono';\nimport { createMiddleware } from 'hono/factory';\nimport { routePath } from 'hono/route';\nimport {\n createRequestDurationTracker,\n createActiveRequestsTracker,\n type HttpMetricsConfig,\n} from './metrics';\n\nconst INSTRUMENTATION_SCOPE_NAME = 'autotel-hono';\n\ntype TimeInput = number | [number, number];\ntype TracerProvider = { getTracer(name: string, version?: string): Tracer };\ntype Meter = HttpMetricsConfig['meter'];\ntype MeterProvider = { getMeter(name: string, version?: string): Meter };\n\nfunction now(): number {\n const p = (globalThis as unknown as { performance?: { now(): number } })\n .performance;\n return p?.now() ?? Date.now();\n}\n\nexport type OtelConfig = {\n tracer?: Tracer;\n tracerProvider?: TracerProvider;\n meter?: Meter;\n meterProvider?: MeterProvider;\n tracerName?: string;\n spanNameFactory?: (c: Context) => string;\n captureRequestHeaders?: string[];\n captureResponseHeaders?: string[];\n captureActiveRequests?: boolean;\n captureRequestDuration?: boolean;\n serviceName?: string;\n serviceVersion?: string;\n disableTracing?: boolean;\n getTime?(): TimeInput;\n};\n\ntype NormalizedOtelConfig = OtelConfig & {\n requestHeaderSet: Set<string>;\n responseHeaderSet: Set<string>;\n captureActiveRequests: boolean;\n captureRequestDuration: boolean;\n};\n\nfunction normalizeConfig(config: OtelConfig = {}): NormalizedOtelConfig {\n const reqHeadersSrc = [...(config.captureRequestHeaders ?? [])];\n const resHeadersSrc = [...(config.captureResponseHeaders ?? [])];\n const requestHeaderSet = new Set(reqHeadersSrc.map((h) => h.toLowerCase()));\n const responseHeaderSet = new Set(resHeadersSrc.map((h) => h.toLowerCase()));\n return {\n ...config,\n requestHeaderSet,\n responseHeaderSet,\n captureActiveRequests: config.captureActiveRequests ?? true,\n captureRequestDuration: config.captureRequestDuration ?? true,\n };\n}\n\nfunction resolveTracer(config: NormalizedOtelConfig): Tracer | undefined {\n if (config.disableTracing) return undefined;\n if (config.tracer) return config.tracer;\n if (config.tracerProvider) {\n return config.tracerProvider.getTracer(\n config.tracerName ?? INSTRUMENTATION_SCOPE_NAME,\n config.serviceVersion,\n );\n }\n return getTracer(\n config.tracerName ?? INSTRUMENTATION_SCOPE_NAME,\n config.serviceVersion,\n );\n}\n\nfunction resolveMeter(config: NormalizedOtelConfig): Meter {\n if (config.meter) return config.meter;\n if (config.meterProvider) {\n return config.meterProvider.getMeter(\n INSTRUMENTATION_SCOPE_NAME,\n config.serviceVersion,\n );\n }\n return getMeter();\n}\n\nexport function otel(userConfig: OtelConfig = {}): MiddlewareHandler {\n const config = normalizeConfig(userConfig);\n const tracer = resolveTracer(config);\n const meter = resolveMeter(config);\n\n const metricsConfig: HttpMetricsConfig = {\n meter,\n captureRequestDuration: config.captureRequestDuration,\n captureActiveRequests: config.captureActiveRequests,\n };\n const requestDuration = createRequestDurationTracker(metricsConfig);\n const activeReqs = createActiveRequestsTracker(metricsConfig);\n\n const spanName = (c: Context) =>\n config.spanNameFactory?.(c) ?? `${c.req.method} ${routePath(c)}`;\n\n return createMiddleware(async (c, next) => {\n const method = c.req.method;\n\n const stableAttrs: Record<string, string | number | undefined> = {\n [HTTPAttributes.requestMethod]: method,\n [ServiceAttributes.name]: config.serviceName,\n [ServiceAttributes.version]: config.serviceVersion,\n };\n\n activeReqs?.increment(stableAttrs);\n const startTime = now();\n\n const deferredRequestHeaderAttributes: Record<string, string> = {};\n const reqHeaders = c.req.raw.headers;\n for (const [rawName, value] of reqHeaders.entries()) {\n const name = rawName.toLowerCase();\n if (config.requestHeaderSet.has(name)) {\n deferredRequestHeaderAttributes[httpRequestHeaderAttribute(name)] =\n typeof value === 'string' ? value : (value[0] ?? '');\n }\n }\n\n const finalize = (span?: Span, error?: unknown) => {\n try {\n const status = c.res.status;\n\n if (span) {\n for (const [name, value] of c.res.headers.entries()) {\n const lower = name.toLowerCase();\n if (config.responseHeaderSet.has(lower)) {\n span.setAttribute(httpResponseHeaderAttribute(lower), value);\n }\n }\n span.setAttribute(HTTPAttributes.responseStatusCode, status);\n if (status >= 500) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n if (error) {\n try {\n span.recordException(error as Error);\n } catch {\n // Ignore errors when recording exception\n }\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n }\n } finally {\n activeReqs?.decrement(stableAttrs);\n span?.setAttribute(HTTPAttributes.route, routePath(c));\n span?.updateName(spanName(c));\n const durationSeconds = (now() - startTime) / 1000;\n requestDuration.record(durationSeconds, {\n ...stableAttrs,\n [HTTPAttributes.route]: routePath(c),\n [HTTPAttributes.responseStatusCode]: c.res.status,\n });\n }\n };\n\n if (!tracer) {\n try {\n await next();\n finalize();\n } catch (error) {\n finalize(undefined, error);\n throw error;\n }\n return;\n }\n\n const parent = propagation.extract(otelContext.active(), c.req.header());\n return tracer.startActiveSpan(\n spanName(c),\n {\n kind: SpanKind.SERVER,\n startTime: config.getTime?.(),\n attributes: {\n ...stableAttrs,\n [URLAttributes.full]: c.req.url,\n [HTTPAttributes.route]: routePath(c),\n },\n },\n parent,\n async (span) => {\n try {\n for (const [k, v] of Object.entries(\n deferredRequestHeaderAttributes,\n )) {\n span.setAttribute(k, v);\n }\n await next();\n finalize(span, c.error);\n } catch (error) {\n finalize(span, error);\n throw error;\n } finally {\n span.end(config.getTime?.());\n }\n },\n );\n });\n}\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { Tracer } from "autotel";
2
2
  import { Context, MiddlewareHandler } from "hono";
3
-
4
3
  //#region src/metrics.d.ts
5
4
  /**
6
5
  * HTTP server metrics per OTel semantic conventions.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/metrics.ts","../src/index.ts"],"mappings":";;;;;;;;;KAMK,cAAA;AAAA,KACO,UAAA,GAAa,MAAM,SAAS,cAAA;AAAA,KAEnC,SAAA;EACH,MAAA,GAAS,KAAA,UAAe,KAAA,GAAQ,UAAU;AAAA;AAAA,KAGvC,aAAA;EACH,GAAA,GAAM,KAAA,UAAe,KAAA,GAAQ,UAAU;AAAA;AAAA,KAG7B,OAAA;EACV,eAAA,GACE,IAAA,UACA,OAAA;IACE,WAAA;IACA,IAAA;IACA,MAAA;MAAW,wBAAA;IAAA;EAAA,MAEV,SAAA;EACL,mBAAA,GACE,IAAA,UACA,OAAA;IACE,WAAA;EAAA,MAEC,aAAa;AAAA;AAAA,KAYR,iBAAA;EACV,KAAA,EAAO,OAAK;EACZ,sBAAA;EACA,qBAAA;AAAA;;;KCrBG,SAAA;AAAA,KACA,cAAA;EAAmB,SAAA,CAAU,IAAA,UAAc,OAAA,YAAmB,MAAM;AAAA;AAAA,KACpE,KAAA,GAAQ,iBAAiB;AAAA,KACzB,aAAA;EAAkB,QAAA,CAAS,IAAA,UAAc,OAAA,YAAmB,KAAK;AAAA;AAAA,KAQ1D,UAAA;EACV,MAAA,GAAS,MAAA;EACT,cAAA,GAAiB,cAAA;EACjB,KAAA,GAAQ,KAAA;EACR,aAAA,GAAgB,aAAA;EAChB,UAAA;EACA,eAAA,IAAmB,CAAA,EAAG,OAAA;EACtB,qBAAA;EACA,sBAAA;EACA,qBAAA;EACA,sBAAA;EACA,WAAA;EACA,cAAA;EACA,cAAA;EACA,OAAA,KAAY,SAAA;AAAA;AAAA,iBA+CE,IAAA,CAAK,UAAA,GAAY,UAAA,GAAkB,iBAAiB"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/metrics.ts","../src/index.ts"],"mappings":";;;;;;;;KAMK;KACO,aAAa,eAAe;KAEnC;EACH,SAAS,eAAe,QAAQ;;KAG7B;EACH,MAAM,eAAe,QAAQ;;KAGnB;EACV,kBACE,cACA;IACE;IACA;IACA;MAAW;;QAEV;EACL,sBACE,cACA;IACE;QAEC;;KAYK;EACV,OAAO;EACP;EACA;;;;KCrBG;KACA;EAAmB,UAAU,cAAc,mBAAmB;;KAC9D,QAAQ;KACR;EAAkB,SAAS,cAAc,mBAAmB;;KAQrD;EACV,SAAS;EACT,iBAAiB;EACjB,QAAQ;EACR,gBAAgB;EAChB;EACA,mBAAmB,GAAG;EACtB;EACA;EACA;EACA;EACA;EACA;EACA;EACA,YAAY;;iBAkDE,KAAK,aAAY,aAAkB"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { Tracer } from "autotel";
2
2
  import { Context, MiddlewareHandler } from "hono";
3
-
4
3
  //#region src/metrics.d.ts
5
4
  /**
6
5
  * HTTP server metrics per OTel semantic conventions.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/metrics.ts","../src/index.ts"],"mappings":";;;;;;;;;KAMK,cAAA;AAAA,KACO,UAAA,GAAa,MAAM,SAAS,cAAA;AAAA,KAEnC,SAAA;EACH,MAAA,GAAS,KAAA,UAAe,KAAA,GAAQ,UAAU;AAAA;AAAA,KAGvC,aAAA;EACH,GAAA,GAAM,KAAA,UAAe,KAAA,GAAQ,UAAU;AAAA;AAAA,KAG7B,OAAA;EACV,eAAA,GACE,IAAA,UACA,OAAA;IACE,WAAA;IACA,IAAA;IACA,MAAA;MAAW,wBAAA;IAAA;EAAA,MAEV,SAAA;EACL,mBAAA,GACE,IAAA,UACA,OAAA;IACE,WAAA;EAAA,MAEC,aAAa;AAAA;AAAA,KAYR,iBAAA;EACV,KAAA,EAAO,OAAK;EACZ,sBAAA;EACA,qBAAA;AAAA;;;KCrBG,SAAA;AAAA,KACA,cAAA;EAAmB,SAAA,CAAU,IAAA,UAAc,OAAA,YAAmB,MAAM;AAAA;AAAA,KACpE,KAAA,GAAQ,iBAAiB;AAAA,KACzB,aAAA;EAAkB,QAAA,CAAS,IAAA,UAAc,OAAA,YAAmB,KAAK;AAAA;AAAA,KAQ1D,UAAA;EACV,MAAA,GAAS,MAAA;EACT,cAAA,GAAiB,cAAA;EACjB,KAAA,GAAQ,KAAA;EACR,aAAA,GAAgB,aAAA;EAChB,UAAA;EACA,eAAA,IAAmB,CAAA,EAAG,OAAA;EACtB,qBAAA;EACA,sBAAA;EACA,qBAAA;EACA,sBAAA;EACA,WAAA;EACA,cAAA;EACA,cAAA;EACA,OAAA,KAAY,SAAA;AAAA;AAAA,iBA+CE,IAAA,CAAK,UAAA,GAAY,UAAA,GAAkB,iBAAiB"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/metrics.ts","../src/index.ts"],"mappings":";;;;;;;;KAMK;KACO,aAAa,eAAe;KAEnC;EACH,SAAS,eAAe,QAAQ;;KAG7B;EACH,MAAM,eAAe,QAAQ;;KAGnB;EACV,kBACE,cACA;IACE;IACA;IACA;MAAW;;QAEV;EACL,sBACE,cACA;IACE;QAEC;;KAYK;EACV,OAAO;EACP;EACA;;;;KCrBG;KACA;EAAmB,UAAU,cAAc,mBAAmB;;KAC9D,QAAQ;KACR;EAAkB,SAAS,cAAc,mBAAmB;;KAQrD;EACV,SAAS;EACT,iBAAiB;EACjB,QAAQ;EACR,gBAAgB;EAChB;EACA,mBAAmB,GAAG;EACtB;EACA;EACA;EACA;EACA;EACA;EACA;EACA,YAAY;;iBAkDE,KAAK,aAAY,aAAkB"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["otelContext"],"sources":["../src/metrics.ts","../src/index.ts"],"sourcesContent":["/**\n * HTTP server metrics per OTel semantic conventions.\n *\n * Lazy-initialized request duration histogram and active-requests up/down counter.\n */\n\ntype AttributeValue = string | number | boolean | undefined;\nexport type Attributes = Record<string, AttributeValue>;\n\ntype Histogram = {\n record: (value: number, attrs?: Attributes) => void;\n};\n\ntype UpDownCounter = {\n add: (value: number, attrs?: Attributes) => void;\n};\n\nexport type Meter = {\n createHistogram: (\n name: string,\n options?: {\n description?: string;\n unit?: string;\n advice?: { explicitBucketBoundaries?: number[] };\n },\n ) => Histogram;\n createUpDownCounter: (\n name: string,\n options?: {\n description?: string;\n },\n ) => UpDownCounter;\n};\n\n/** OTel HTTP server metric names (stable convention names) */\nconst METRIC_HTTP_SERVER_REQUEST_DURATION = 'http.server.request.duration';\nconst METRIC_HTTP_SERVER_ACTIVE_REQUESTS = 'http.server.active_requests';\n\n/** Recommended bucket boundaries for request duration (seconds) */\nconst HTTP_DURATION_BUCKETS = [\n 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,\n];\n\nexport type HttpMetricsConfig = {\n meter: Meter;\n captureRequestDuration?: boolean;\n captureActiveRequests?: boolean;\n};\n\nexport function createRequestDurationTracker(config: HttpMetricsConfig): {\n record: (durationSeconds: number, attrs: Attributes) => void;\n} {\n if (config.captureRequestDuration === false) {\n return { record: () => {} };\n }\n const histogram = config.meter.createHistogram(\n METRIC_HTTP_SERVER_REQUEST_DURATION,\n {\n description: 'Duration of HTTP server requests in seconds',\n unit: 's',\n advice: { explicitBucketBoundaries: HTTP_DURATION_BUCKETS },\n },\n );\n return {\n record(durationSeconds: number, attrs: Attributes) {\n histogram.record(durationSeconds, attrs);\n },\n };\n}\n\nexport function createActiveRequestsTracker(config: HttpMetricsConfig): {\n increment: (attrs: Attributes) => void;\n decrement: (attrs: Attributes) => void;\n} | undefined {\n if (config.captureActiveRequests === false) {\n return undefined;\n }\n const counter = config.meter.createUpDownCounter(\n METRIC_HTTP_SERVER_ACTIVE_REQUESTS,\n {\n description: 'Number of active (in-flight) HTTP server requests',\n },\n );\n return {\n increment(attrs: Attributes) {\n counter.add(1, attrs);\n },\n decrement(attrs: Attributes) {\n counter.add(-1, attrs);\n },\n };\n}\n","import type { Span, Tracer } from 'autotel';\nimport {\n getTracer,\n getMeter,\n context as otelContext,\n propagation,\n SpanKind,\n SpanStatusCode,\n HTTPAttributes,\n URLAttributes,\n ServiceAttributes,\n httpRequestHeaderAttribute,\n httpResponseHeaderAttribute,\n} from 'autotel';\nimport type { MiddlewareHandler, Context } from 'hono';\nimport { createMiddleware } from 'hono/factory';\nimport { routePath } from 'hono/route';\nimport {\n createRequestDurationTracker,\n createActiveRequestsTracker,\n type HttpMetricsConfig,\n} from './metrics';\n\nconst INSTRUMENTATION_SCOPE_NAME = 'autotel-hono';\n\ntype TimeInput = number | [number, number];\ntype TracerProvider = { getTracer(name: string, version?: string): Tracer };\ntype Meter = HttpMetricsConfig['meter'];\ntype MeterProvider = { getMeter(name: string, version?: string): Meter };\n\nfunction now(): number {\n const p = (globalThis as unknown as { performance?: { now(): number } })\n .performance;\n return p?.now() ?? Date.now();\n}\n\nexport type OtelConfig = {\n tracer?: Tracer;\n tracerProvider?: TracerProvider;\n meter?: Meter;\n meterProvider?: MeterProvider;\n tracerName?: string;\n spanNameFactory?: (c: Context) => string;\n captureRequestHeaders?: string[];\n captureResponseHeaders?: string[];\n captureActiveRequests?: boolean;\n captureRequestDuration?: boolean;\n serviceName?: string;\n serviceVersion?: string;\n disableTracing?: boolean;\n getTime?(): TimeInput;\n};\n\ntype NormalizedOtelConfig = OtelConfig & {\n requestHeaderSet: Set<string>;\n responseHeaderSet: Set<string>;\n captureActiveRequests: boolean;\n captureRequestDuration: boolean;\n};\n\nfunction normalizeConfig(config: OtelConfig = {}): NormalizedOtelConfig {\n const reqHeadersSrc = [...(config.captureRequestHeaders ?? [])];\n const resHeadersSrc = [...(config.captureResponseHeaders ?? [])];\n const requestHeaderSet = new Set(reqHeadersSrc.map((h) => h.toLowerCase()));\n const responseHeaderSet = new Set(resHeadersSrc.map((h) => h.toLowerCase()));\n return {\n ...config,\n requestHeaderSet,\n responseHeaderSet,\n captureActiveRequests: config.captureActiveRequests ?? true,\n captureRequestDuration: config.captureRequestDuration ?? true,\n };\n}\n\nfunction resolveTracer(config: NormalizedOtelConfig): Tracer | undefined {\n if (config.disableTracing) return undefined;\n if (config.tracer) return config.tracer;\n if (config.tracerProvider) {\n return config.tracerProvider.getTracer(\n config.tracerName ?? INSTRUMENTATION_SCOPE_NAME,\n config.serviceVersion,\n );\n }\n return getTracer(config.tracerName ?? INSTRUMENTATION_SCOPE_NAME, config.serviceVersion);\n}\n\nfunction resolveMeter(config: NormalizedOtelConfig): Meter {\n if (config.meter) return config.meter;\n if (config.meterProvider) {\n return config.meterProvider.getMeter(\n INSTRUMENTATION_SCOPE_NAME,\n config.serviceVersion,\n );\n }\n return getMeter();\n}\n\nexport function otel(userConfig: OtelConfig = {}): MiddlewareHandler {\n const config = normalizeConfig(userConfig);\n const tracer = resolveTracer(config);\n const meter = resolveMeter(config);\n\n const metricsConfig: HttpMetricsConfig = {\n meter,\n captureRequestDuration: config.captureRequestDuration,\n captureActiveRequests: config.captureActiveRequests,\n };\n const requestDuration = createRequestDurationTracker(metricsConfig);\n const activeReqs = createActiveRequestsTracker(metricsConfig);\n\n const spanName = (c: Context) =>\n config.spanNameFactory?.(c) ?? `${c.req.method} ${routePath(c)}`;\n\n return createMiddleware(async (c, next) => {\n const method = c.req.method;\n\n const stableAttrs: Record<string, string | number | undefined> = {\n [HTTPAttributes.requestMethod]: method,\n [ServiceAttributes.name]: config.serviceName,\n [ServiceAttributes.version]: config.serviceVersion,\n };\n\n activeReqs?.increment(stableAttrs);\n const startTime = now();\n\n const deferredRequestHeaderAttributes: Record<string, string> = {};\n const reqHeaders = c.req.raw.headers;\n for (const [rawName, value] of reqHeaders.entries()) {\n const name = rawName.toLowerCase();\n if (config.requestHeaderSet.has(name)) {\n deferredRequestHeaderAttributes[httpRequestHeaderAttribute(name)] =\n typeof value === 'string' ? value : value[0] ?? '';\n }\n }\n\n const finalize = (span?: Span, error?: unknown) => {\n try {\n const status = c.res.status;\n\n if (span) {\n for (const [name, value] of c.res.headers.entries()) {\n const lower = name.toLowerCase();\n if (config.responseHeaderSet.has(lower)) {\n span.setAttribute(httpResponseHeaderAttribute(lower), value);\n }\n }\n span.setAttribute(HTTPAttributes.responseStatusCode, status);\n if (status >= 500) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n if (error) {\n try {\n span.recordException(error as Error);\n } catch {\n // Ignore errors when recording exception\n }\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n }\n } finally {\n activeReqs?.decrement(stableAttrs);\n span?.setAttribute(HTTPAttributes.route, routePath(c));\n span?.updateName(spanName(c));\n const durationSeconds = (now() - startTime) / 1000;\n requestDuration.record(durationSeconds, {\n ...stableAttrs,\n [HTTPAttributes.route]: routePath(c),\n [HTTPAttributes.responseStatusCode]: c.res.status,\n });\n }\n };\n\n if (!tracer) {\n try {\n await next();\n finalize();\n } catch (error) {\n finalize(undefined, error);\n throw error;\n }\n return;\n }\n\n const parent = propagation.extract(otelContext.active(), c.req.header());\n return tracer.startActiveSpan(\n spanName(c),\n {\n kind: SpanKind.SERVER,\n startTime: config.getTime?.(),\n attributes: {\n ...stableAttrs,\n [URLAttributes.full]: c.req.url,\n [HTTPAttributes.route]: routePath(c),\n },\n },\n parent,\n async (span) => {\n try {\n for (const [k, v] of Object.entries(deferredRequestHeaderAttributes)) {\n span.setAttribute(k, v);\n }\n await next();\n finalize(span, c.error);\n } catch (error) {\n finalize(span, error);\n throw error;\n } finally {\n span.end(config.getTime?.());\n }\n },\n );\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["otelContext"],"sources":["../src/metrics.ts","../src/index.ts"],"sourcesContent":["/**\n * HTTP server metrics per OTel semantic conventions.\n *\n * Lazy-initialized request duration histogram and active-requests up/down counter.\n */\n\ntype AttributeValue = string | number | boolean | undefined;\nexport type Attributes = Record<string, AttributeValue>;\n\ntype Histogram = {\n record: (value: number, attrs?: Attributes) => void;\n};\n\ntype UpDownCounter = {\n add: (value: number, attrs?: Attributes) => void;\n};\n\nexport type Meter = {\n createHistogram: (\n name: string,\n options?: {\n description?: string;\n unit?: string;\n advice?: { explicitBucketBoundaries?: number[] };\n },\n ) => Histogram;\n createUpDownCounter: (\n name: string,\n options?: {\n description?: string;\n },\n ) => UpDownCounter;\n};\n\n/** OTel HTTP server metric names (stable convention names) */\nconst METRIC_HTTP_SERVER_REQUEST_DURATION = 'http.server.request.duration';\nconst METRIC_HTTP_SERVER_ACTIVE_REQUESTS = 'http.server.active_requests';\n\n/** Recommended bucket boundaries for request duration (seconds) */\nconst HTTP_DURATION_BUCKETS = [\n 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,\n];\n\nexport type HttpMetricsConfig = {\n meter: Meter;\n captureRequestDuration?: boolean;\n captureActiveRequests?: boolean;\n};\n\nexport function createRequestDurationTracker(config: HttpMetricsConfig): {\n record: (durationSeconds: number, attrs: Attributes) => void;\n} {\n if (config.captureRequestDuration === false) {\n return { record: () => {} };\n }\n const histogram = config.meter.createHistogram(\n METRIC_HTTP_SERVER_REQUEST_DURATION,\n {\n description: 'Duration of HTTP server requests in seconds',\n unit: 's',\n advice: { explicitBucketBoundaries: HTTP_DURATION_BUCKETS },\n },\n );\n return {\n record(durationSeconds: number, attrs: Attributes) {\n histogram.record(durationSeconds, attrs);\n },\n };\n}\n\nexport function createActiveRequestsTracker(config: HttpMetricsConfig):\n | {\n increment: (attrs: Attributes) => void;\n decrement: (attrs: Attributes) => void;\n }\n | undefined {\n if (config.captureActiveRequests === false) {\n return undefined;\n }\n const counter = config.meter.createUpDownCounter(\n METRIC_HTTP_SERVER_ACTIVE_REQUESTS,\n {\n description: 'Number of active (in-flight) HTTP server requests',\n },\n );\n return {\n increment(attrs: Attributes) {\n counter.add(1, attrs);\n },\n decrement(attrs: Attributes) {\n counter.add(-1, attrs);\n },\n };\n}\n","import type { Span, Tracer } from 'autotel';\nimport {\n getTracer,\n getMeter,\n context as otelContext,\n propagation,\n SpanKind,\n SpanStatusCode,\n HTTPAttributes,\n URLAttributes,\n ServiceAttributes,\n httpRequestHeaderAttribute,\n httpResponseHeaderAttribute,\n} from 'autotel';\nimport type { MiddlewareHandler, Context } from 'hono';\nimport { createMiddleware } from 'hono/factory';\nimport { routePath } from 'hono/route';\nimport {\n createRequestDurationTracker,\n createActiveRequestsTracker,\n type HttpMetricsConfig,\n} from './metrics';\n\nconst INSTRUMENTATION_SCOPE_NAME = 'autotel-hono';\n\ntype TimeInput = number | [number, number];\ntype TracerProvider = { getTracer(name: string, version?: string): Tracer };\ntype Meter = HttpMetricsConfig['meter'];\ntype MeterProvider = { getMeter(name: string, version?: string): Meter };\n\nfunction now(): number {\n const p = (globalThis as unknown as { performance?: { now(): number } })\n .performance;\n return p?.now() ?? Date.now();\n}\n\nexport type OtelConfig = {\n tracer?: Tracer;\n tracerProvider?: TracerProvider;\n meter?: Meter;\n meterProvider?: MeterProvider;\n tracerName?: string;\n spanNameFactory?: (c: Context) => string;\n captureRequestHeaders?: string[];\n captureResponseHeaders?: string[];\n captureActiveRequests?: boolean;\n captureRequestDuration?: boolean;\n serviceName?: string;\n serviceVersion?: string;\n disableTracing?: boolean;\n getTime?(): TimeInput;\n};\n\ntype NormalizedOtelConfig = OtelConfig & {\n requestHeaderSet: Set<string>;\n responseHeaderSet: Set<string>;\n captureActiveRequests: boolean;\n captureRequestDuration: boolean;\n};\n\nfunction normalizeConfig(config: OtelConfig = {}): NormalizedOtelConfig {\n const reqHeadersSrc = [...(config.captureRequestHeaders ?? [])];\n const resHeadersSrc = [...(config.captureResponseHeaders ?? [])];\n const requestHeaderSet = new Set(reqHeadersSrc.map((h) => h.toLowerCase()));\n const responseHeaderSet = new Set(resHeadersSrc.map((h) => h.toLowerCase()));\n return {\n ...config,\n requestHeaderSet,\n responseHeaderSet,\n captureActiveRequests: config.captureActiveRequests ?? true,\n captureRequestDuration: config.captureRequestDuration ?? true,\n };\n}\n\nfunction resolveTracer(config: NormalizedOtelConfig): Tracer | undefined {\n if (config.disableTracing) return undefined;\n if (config.tracer) return config.tracer;\n if (config.tracerProvider) {\n return config.tracerProvider.getTracer(\n config.tracerName ?? INSTRUMENTATION_SCOPE_NAME,\n config.serviceVersion,\n );\n }\n return getTracer(\n config.tracerName ?? INSTRUMENTATION_SCOPE_NAME,\n config.serviceVersion,\n );\n}\n\nfunction resolveMeter(config: NormalizedOtelConfig): Meter {\n if (config.meter) return config.meter;\n if (config.meterProvider) {\n return config.meterProvider.getMeter(\n INSTRUMENTATION_SCOPE_NAME,\n config.serviceVersion,\n );\n }\n return getMeter();\n}\n\nexport function otel(userConfig: OtelConfig = {}): MiddlewareHandler {\n const config = normalizeConfig(userConfig);\n const tracer = resolveTracer(config);\n const meter = resolveMeter(config);\n\n const metricsConfig: HttpMetricsConfig = {\n meter,\n captureRequestDuration: config.captureRequestDuration,\n captureActiveRequests: config.captureActiveRequests,\n };\n const requestDuration = createRequestDurationTracker(metricsConfig);\n const activeReqs = createActiveRequestsTracker(metricsConfig);\n\n const spanName = (c: Context) =>\n config.spanNameFactory?.(c) ?? `${c.req.method} ${routePath(c)}`;\n\n return createMiddleware(async (c, next) => {\n const method = c.req.method;\n\n const stableAttrs: Record<string, string | number | undefined> = {\n [HTTPAttributes.requestMethod]: method,\n [ServiceAttributes.name]: config.serviceName,\n [ServiceAttributes.version]: config.serviceVersion,\n };\n\n activeReqs?.increment(stableAttrs);\n const startTime = now();\n\n const deferredRequestHeaderAttributes: Record<string, string> = {};\n const reqHeaders = c.req.raw.headers;\n for (const [rawName, value] of reqHeaders.entries()) {\n const name = rawName.toLowerCase();\n if (config.requestHeaderSet.has(name)) {\n deferredRequestHeaderAttributes[httpRequestHeaderAttribute(name)] =\n typeof value === 'string' ? value : (value[0] ?? '');\n }\n }\n\n const finalize = (span?: Span, error?: unknown) => {\n try {\n const status = c.res.status;\n\n if (span) {\n for (const [name, value] of c.res.headers.entries()) {\n const lower = name.toLowerCase();\n if (config.responseHeaderSet.has(lower)) {\n span.setAttribute(httpResponseHeaderAttribute(lower), value);\n }\n }\n span.setAttribute(HTTPAttributes.responseStatusCode, status);\n if (status >= 500) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n if (error) {\n try {\n span.recordException(error as Error);\n } catch {\n // Ignore errors when recording exception\n }\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n }\n } finally {\n activeReqs?.decrement(stableAttrs);\n span?.setAttribute(HTTPAttributes.route, routePath(c));\n span?.updateName(spanName(c));\n const durationSeconds = (now() - startTime) / 1000;\n requestDuration.record(durationSeconds, {\n ...stableAttrs,\n [HTTPAttributes.route]: routePath(c),\n [HTTPAttributes.responseStatusCode]: c.res.status,\n });\n }\n };\n\n if (!tracer) {\n try {\n await next();\n finalize();\n } catch (error) {\n finalize(undefined, error);\n throw error;\n }\n return;\n }\n\n const parent = propagation.extract(otelContext.active(), c.req.header());\n return tracer.startActiveSpan(\n spanName(c),\n {\n kind: SpanKind.SERVER,\n startTime: config.getTime?.(),\n attributes: {\n ...stableAttrs,\n [URLAttributes.full]: c.req.url,\n [HTTPAttributes.route]: routePath(c),\n },\n },\n parent,\n async (span) => {\n try {\n for (const [k, v] of Object.entries(\n deferredRequestHeaderAttributes,\n )) {\n span.setAttribute(k, v);\n }\n await next();\n finalize(span, c.error);\n } catch (error) {\n finalize(span, error);\n throw error;\n } finally {\n span.end(config.getTime?.());\n }\n },\n );\n });\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autotel-hono",
3
- "version": "0.4.41",
3
+ "version": "0.4.42",
4
4
  "description": "OpenTelemetry Hono middleware powered by autotel",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -20,11 +20,11 @@
20
20
  "skills"
21
21
  ],
22
22
  "dependencies": {
23
- "autotel": "4.3.0",
24
- "autotel-adapters": "1.0.0"
23
+ "autotel": "5.0.0",
24
+ "autotel-adapters": "2.0.0"
25
25
  },
26
26
  "peerDependencies": {
27
- "hono": ">=4.12.28"
27
+ "hono": ">=4.12.31"
28
28
  },
29
29
  "peerDependenciesMeta": {
30
30
  "hono": {
@@ -32,9 +32,9 @@
32
32
  }
33
33
  },
34
34
  "devDependencies": {
35
- "@types/node": "^26.1.0",
35
+ "@types/node": "^26.1.1",
36
36
  "hono": "^4.12.28",
37
- "tsdown": "^0.22.3",
37
+ "tsdown": "^0.22.12",
38
38
  "typescript": "^6.0.3",
39
39
  "vitest": "^4.1.10"
40
40
  },
@@ -21,14 +21,16 @@ app.use(otel({ serviceName: 'my-api' }));
21
21
  ## Configuration
22
22
 
23
23
  ```typescript
24
- app.use(otel({
25
- serviceName: 'my-api',
26
- captureRequestHeaders: ['x-request-id'],
27
- captureResponseHeaders: ['x-response-time'],
28
- captureActiveRequests: true,
29
- captureRequestDuration: true,
30
- spanNameFactory: (c) => `${c.req.method} ${c.req.routePath}`,
31
- }));
24
+ app.use(
25
+ otel({
26
+ serviceName: 'my-api',
27
+ captureRequestHeaders: ['x-request-id'],
28
+ captureResponseHeaders: ['x-response-time'],
29
+ captureActiveRequests: true,
30
+ captureRequestDuration: true,
31
+ spanNameFactory: (c) => `${c.req.method} ${c.req.routePath}`,
32
+ }),
33
+ );
32
34
  ```
33
35
 
34
36
  ## What Gets Traced
@@ -39,6 +41,6 @@ app.use(otel({
39
41
 
40
42
  ## Common Mistakes
41
43
 
42
- - Do NOT call `otel()` per-route — register once with `app.use(otel(...))`.
43
- - Do NOT pass a tracer unless you need a custom one — the middleware creates one from the global provider.
44
+ - Do NOT call `otel()` per-route: register once with `app.use(otel(...))`.
45
+ - Do NOT pass a tracer unless you need a custom one: the middleware creates one from the global provider.
44
46
  - Header capture requires listing header names upfront in config, not at request time.