@btx-second-context/otel 0.0.0-stage → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ ## 0.0.1 — 2026-11-12
4
+
5
+ - Export Node backend spans through standard OTLP/HTTP Protobuf.
6
+ - Trace operations with labels, attributes, and parent context.
7
+ - Preserve recorded span content with bounded queues and flush deadlines.
8
+ - Suppress exporter tracing and report sanitized delivery outcomes.
package/README.md CHANGED
@@ -1,3 +1,123 @@
1
- # Temporary Holding Version
1
+ # BTX OpenTelemetry
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ `@btx-second-context/otel` exports Node backend traces through standard OTLP/HTTP Protobuf and
4
+ supports customer-defined Labels. It works with your existing OTel provider,
5
+ instrumentation, resources, and sampler. The package does not register a provider
6
+ or make requests when imported.
7
+
8
+ The package contains compiled ESM and TypeScript declarations, with no BTX
9
+ workspace dependencies. Node 20.6 or later is required.
10
+
11
+ ## Install
12
+
13
+ ```sh
14
+ pnpm add @btx-second-context/otel@0.0.1
15
+ ```
16
+
17
+ ## Export traces
18
+
19
+ ```ts
20
+ import * as btx from '@btx-second-context/otel'
21
+
22
+ const processor = btx.createBtxSpanProcessor({
23
+ otlp: { url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT },
24
+ })
25
+
26
+ // Include processor in your provider's spanProcessors when you initialize it.
27
+ // Keep your existing resource, context manager, instrumentation, and sampler.
28
+ ```
29
+
30
+ The official OTel exporter handles encoding, HTTP transport, and retries. Standard
31
+ `OTEL_EXPORTER_OTLP_*` and trace-specific environment settings apply. An explicit
32
+ `otlp.url` is the full traces endpoint. Without explicit configuration, the OTel
33
+ exporter's defaults apply. An existing standard `SpanExporter` can instead be
34
+ supplied through `exporter`; do not supply both options.
35
+
36
+ ## Trace an operation
37
+
38
+ ```ts
39
+ import * as btx from '@btx-second-context/otel'
40
+
41
+ const reply = await btx.trace('Generate reply', () => generateReply())
42
+ ```
43
+
44
+ `btx.trace(name, work)` and `btx.trace(name, options, work)` accept sync or async callbacks
45
+ and return a promise with the original result. Nested calls create child spans.
46
+ The helper records exceptions and ends the span when the callback settles.
47
+
48
+ ## Labels and attributes
49
+
50
+ ```ts
51
+ import * as btx from '@btx-second-context/otel'
52
+
53
+ const reply = await btx.trace(
54
+ 'Generate reply',
55
+ { labels: ['reply-draft', 'customer-support'], attributes: { 'gen_ai.request.model': model } },
56
+ span => {
57
+ span.setAttributes({ 'app.source': 'support' })
58
+ return generateReply()
59
+ },
60
+ )
61
+ ```
62
+
63
+ Options propagate Labels and attributes to the operation and its children,
64
+ including automatic instrumentation. Nested options merge Labels and override
65
+ shared attributes. Authored span attributes take precedence for that span.
66
+ Labels use the OTel string-array attribute `btx.labels`; the package has no fixed
67
+ label vocabulary. Application boundaries own attribution, capture eligibility,
68
+ and flushing. Labels and attributes never establish access.
69
+
70
+ `btx.trace()` preserves the active parent, including remote parents. Without a parent,
71
+ it starts a new trace. `btx.activeBtxTraceReference()` returns the active trace and span
72
+ IDs. Ordinary OTel spans also export; `requireScope: true` limits capture to `btx.trace()`
73
+ operations and their children. `btx.withoutBtxTracing(work)`
74
+ suppresses capture, including around exporter transport.
75
+
76
+ The span handle supports `setAttributes` and ignores writes after completion.
77
+ Returned HTTP 5xx responses and `options.outcome(result)` can mark failures;
78
+ exceptions record their original message, type, and stack. `options.kind` selects
79
+ the OTel span kind. Instrumentation failures never repeat application work.
80
+
81
+ ## Delivery and content
82
+
83
+ ```ts
84
+ const processor = btx.createBtxSpanProcessor({
85
+ maxQueueSize: 512,
86
+ maxExportBatchSize: 128,
87
+ scheduledDelayMillis: 5_000,
88
+ exportTimeoutMillis: 1_000,
89
+ flushTimeoutMillis: 1_500,
90
+ onExport: ({ outcome, spans }) => reportExport(outcome, spans),
91
+ })
92
+
93
+ await processor.forceFlush() // For short-lived request or job completion.
94
+ await processor.shutdown() // Once, during application shutdown.
95
+ ```
96
+
97
+ Delivery is best effort. Sampling, queue overflow, retention decisions, transport
98
+ failure, and deadlines can prevent delivery. Observations report counts and
99
+ outcomes without span content; flush deadlines reject so the application can
100
+ record a sanitized diagnostic. A successful export callback follows the standard
101
+ exporter's result and is not a guarantee that every span was retained downstream.
102
+
103
+ The processor preserves original names, recorded attributes, events, links,
104
+ resource metadata, instrumentation scope, and upstream drop counts. It does not
105
+ truncate content. Your provider controls SDK content limits. The optional
106
+ `BTX_OTEL_SPAN_LIMITS` preset disables those limits; the exporter does not install
107
+ it automatically. Preserve your own capture and sensitive-data policy.
108
+
109
+ `BtxTraceRetention` supports application-owned keep/discard decisions through `options.retention`. Buffered spans share the configured queue budget; recorded errors
110
+ preserve the execution. The application must settle its decision at completion.
111
+ `withBtxClientDependency` and an optional `dependencyRegistry` add dependency
112
+ metadata without replacing authored attributes or creating extra transport spans.
113
+
114
+ ## Development
115
+
116
+ ```sh
117
+ pnpm --dir code/packages/btx-otel build
118
+ pnpm --dir code/packages/btx-otel typecheck
119
+ pnpm --dir code/packages/btx-otel test
120
+ ```
121
+
122
+ Tests import the compiled package entry point and exercise a local OTLP receiver.
123
+ Package publication and backend ingestion configuration are separate operations.
@@ -0,0 +1,19 @@
1
+ import { type Attributes } from '@opentelemetry/api';
2
+ /** Supplied by a trusted client adapter, never by a request payload or span attributes. */
3
+ export interface BtxClientDependency {
4
+ service: string;
5
+ resourceKind: 'table' | 'rpc';
6
+ resource: string;
7
+ method: string;
8
+ url: string;
9
+ }
10
+ export declare const clientDependencyKey: symbol;
11
+ export interface ClientDependencyFrame {
12
+ readonly metadata: Readonly<BtxClientDependency>;
13
+ readonly origin: string;
14
+ readonly pathname: string;
15
+ }
16
+ /** Enrich automatic instrumentation without creating another span or repeating work. */
17
+ export declare function withBtxClientDependency<T>(metadata: BtxClientDependency | null, work: () => T): T;
18
+ /** Enrich matching transport spans only, including real retry attempts. */
19
+ export declare function matchingClientDependency(frame: ClientDependencyFrame | undefined, attributes: Attributes): BtxClientDependency | undefined;
@@ -0,0 +1,39 @@
1
+ import { context, createContextKey } from '@opentelemetry/api';
2
+ export const clientDependencyKey = createContextKey('btx.otel.client-dependency');
3
+ /** Enrich automatic instrumentation without creating another span or repeating work. */
4
+ export function withBtxClientDependency(metadata, work) {
5
+ let frame;
6
+ try {
7
+ if (metadata && /^[a-z][a-z0-9_.]{0,95}$/.test(metadata.service) &&
8
+ ['table', 'rpc'].includes(metadata.resourceKind) && /^[A-Za-z_][A-Za-z0-9_.-]{0,95}$/.test(metadata.resource) &&
9
+ /^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)$/.test(metadata.method)) {
10
+ const url = new URL(metadata.url);
11
+ if (['https:', 'http:'].includes(url.protocol) && !url.username && !url.password) {
12
+ frame = { metadata: Object.freeze({ service: metadata.service, resourceKind: metadata.resourceKind,
13
+ resource: metadata.resource, method: metadata.method, url: url.origin + url.pathname }), origin: url.origin, pathname: url.pathname };
14
+ }
15
+ }
16
+ }
17
+ catch { /* Invalid metadata leaves the transport span generic. */ }
18
+ const active = context.active().deleteValue(clientDependencyKey);
19
+ return context.with(frame ? active.setValue(clientDependencyKey, frame) : active, work);
20
+ }
21
+ /** Enrich matching transport spans only, including real retry attempts. */
22
+ export function matchingClientDependency(frame, attributes) {
23
+ if (!frame)
24
+ return;
25
+ const raw = attributes['url.full'] ?? attributes['http.url'];
26
+ const method = attributes['http.request.method'] ?? attributes['http.method'];
27
+ try {
28
+ if (typeof raw !== 'string' || method !== frame.metadata.method)
29
+ return;
30
+ const url = new URL(raw);
31
+ if (url.origin !== frame.origin || url.pathname !== frame.pathname || url.username || url.password)
32
+ return;
33
+ return frame.metadata;
34
+ }
35
+ catch {
36
+ return;
37
+ }
38
+ }
39
+ //# sourceMappingURL=client-dependency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-dependency.js","sourceRoot":"","sources":["../src/client-dependency.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAmB,MAAM,oBAAoB,CAAA;AAW/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,gBAAgB,CAAC,4BAA4B,CAAC,CAAA;AAOjF,wFAAwF;AACxF,MAAM,UAAU,uBAAuB,CAAI,QAAoC,EAAE,IAAa;IAC5F,IAAI,KAAwC,CAAA;IAC5C,IAAI,CAAC;QACH,IAAI,QAAQ,IAAI,yBAAyB,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YAC5D,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,iCAAiC,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAC7G,4CAA4C,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACvE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;YACjC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;gBACjF,KAAK,GAAG,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,CAAC,YAAY;wBAChG,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAA;YACzI,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAC,yDAAyD,CAAC,CAAC;IACrE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAA;IAChE,OAAO,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;AACzF,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,wBAAwB,CAAC,KAAwC,EAAE,UAAsB;IACvG,IAAI,CAAC,KAAK;QAAE,OAAM;IAClB,MAAM,GAAG,GAAG,UAAU,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,CAAA;IAC5D,MAAM,MAAM,GAAG,UAAU,CAAC,qBAAqB,CAAC,IAAI,UAAU,CAAC,aAAa,CAAC,CAAA;IAC7E,IAAI,CAAC;QACH,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,KAAK,KAAK,CAAC,QAAQ,CAAC,MAAM;YAAE,OAAM;QACvE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;QACxB,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,IAAI,GAAG,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ;YAAE,OAAM;QAC1G,OAAO,KAAK,CAAC,QAAQ,CAAA;IACvB,CAAC;IAAC,MAAM,CAAC;QAAC,OAAM;IAAC,CAAC;AACpB,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { type Attributes, type Context } from '@opentelemetry/api';
2
+ /** Hold a bounded execution's spans until its application outcome is known. */
3
+ export declare class BtxTraceRetention {
4
+ private decision;
5
+ private failed;
6
+ private listeners;
7
+ whenDecided(listener: (retain: boolean) => void): void;
8
+ preserveFailure(): void;
9
+ decide(retain: boolean): void;
10
+ }
11
+ export interface TraceMetadata {
12
+ labels?: readonly string[];
13
+ attributes?: Attributes;
14
+ retention?: BtxTraceRetention;
15
+ }
16
+ export declare const scopeKey: symbol;
17
+ /** Snapshot metadata before starting a span so automatic children inherit it. */
18
+ export declare function traceScopeContext(parent: Context, scope: TraceMetadata): Context;
19
+ /** Release intake evidence once the execution is known to perform actual work. */
20
+ export declare function retainBtxTrace(): void;
21
+ /** Suppress both automatic instrumentation and the BTX export policy. */
22
+ export declare function withoutBtxTracing<T>(work: () => T): T;
23
+ export declare function activeBtxTraceReference(): {
24
+ traceId: string;
25
+ spanId: string;
26
+ } | null;
@@ -0,0 +1,54 @@
1
+ import { context, createContextKey, isSpanContextValid, trace } from '@opentelemetry/api';
2
+ import { suppressTracing } from '@opentelemetry/core';
3
+ /** Hold a bounded execution's spans until its application outcome is known. */
4
+ export class BtxTraceRetention {
5
+ decision;
6
+ failed = false;
7
+ listeners = [];
8
+ whenDecided(listener) {
9
+ if (this.decision !== undefined)
10
+ listener(this.decision);
11
+ else
12
+ this.listeners.push(listener);
13
+ }
14
+ preserveFailure() { this.failed = true; }
15
+ decide(retain) {
16
+ if (this.decision !== undefined)
17
+ return;
18
+ this.decision = retain || this.failed;
19
+ const listeners = this.listeners;
20
+ this.listeners = [];
21
+ for (const listener of listeners) {
22
+ try {
23
+ listener(this.decision);
24
+ }
25
+ catch { /* Telemetry cannot fail application work. */ }
26
+ }
27
+ }
28
+ }
29
+ export const scopeKey = createContextKey('btx.otel.scope');
30
+ /** Snapshot metadata before starting a span so automatic children inherit it. */
31
+ export function traceScopeContext(parent, scope) {
32
+ const inherited = parent.getValue(scopeKey);
33
+ const labels = [...new Set([...(inherited?.labels ?? []), ...(scope.labels ?? [])])];
34
+ const attributes = Object.fromEntries(Object.entries({ ...inherited?.attributes, ...scope.attributes })
35
+ .map(([key, value]) => [key, Array.isArray(value) ? [...value] : value]));
36
+ return parent.setValue(scopeKey, Object.freeze({
37
+ ...scope, attributes: Object.freeze(attributes), labels: Object.freeze(labels),
38
+ retention: scope.retention ?? inherited?.retention,
39
+ }));
40
+ }
41
+ /** Release intake evidence once the execution is known to perform actual work. */
42
+ export function retainBtxTrace() {
43
+ const scope = context.active().getValue(scopeKey);
44
+ scope?.retention?.decide(true);
45
+ }
46
+ /** Suppress both automatic instrumentation and the BTX export policy. */
47
+ export function withoutBtxTracing(work) {
48
+ return context.with(suppressTracing(context.active().deleteValue(scopeKey)), work);
49
+ }
50
+ export function activeBtxTraceReference() {
51
+ const span = trace.getSpanContext(context.active());
52
+ return span && isSpanContextValid(span) ? { traceId: span.traceId, spanId: span.spanId } : null;
53
+ }
54
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Attributes } from '@opentelemetry/api';
2
+ import type { BtxClientDependency } from './client-dependency.js';
3
+ export interface BtxDependencyRegistry {
4
+ readonly services: readonly {
5
+ name: string;
6
+ matchesHost: (hostname: string) => boolean;
7
+ }[];
8
+ /** Keys use the form 'service METHOD /path'. Values are fixed operation names. */
9
+ readonly operations: ReadonlyMap<string, string>;
10
+ }
11
+ export declare function dependencyAttributes(source: Attributes, registry?: BtxDependencyRegistry, client?: BtxClientDependency): Attributes;
@@ -0,0 +1,35 @@
1
+ const identifier = /^[a-z][a-z0-9_.]{0,95}$/;
2
+ const methods = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS', 'CONNECT', 'TRACE']);
3
+ export function dependencyAttributes(source, registry, client) {
4
+ const attrs = {};
5
+ const method = source['http.request.method'] ?? source['http.method'];
6
+ if (typeof method === 'string' && methods.has(method))
7
+ attrs['http.request.method'] = method;
8
+ const status = source['http.response.status_code'] ?? source['http.status_code'];
9
+ if (typeof status === 'number' && Number.isInteger(status) && status >= 100 && status <= 599)
10
+ attrs['http.response.status_code'] = status;
11
+ if (client)
12
+ return {
13
+ ...attrs,
14
+ 'btx.dependency.service': client.service,
15
+ 'btx.dependency.resource': client.resource,
16
+ 'btx.dependency.resource_kind': client.resourceKind,
17
+ };
18
+ if (!registry)
19
+ return attrs;
20
+ const raw = source['url.full'] ?? source['http.url'];
21
+ if (typeof raw !== 'string' || !URL.canParse(raw))
22
+ return attrs;
23
+ const url = new URL(raw);
24
+ if (url.protocol !== 'https:' || url.port)
25
+ return attrs;
26
+ const service = registry.services.find(entry => entry.matchesHost(url.hostname))?.name;
27
+ if (!service || !identifier.test(service))
28
+ return attrs;
29
+ attrs['btx.dependency.service'] = service;
30
+ const operation = registry.operations.get(`${service} ${attrs['http.request.method']} ${url.pathname}`);
31
+ if (operation && identifier.test(operation))
32
+ attrs['btx.dependency.operation'] = operation;
33
+ return attrs;
34
+ }
35
+ //# sourceMappingURL=dependency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependency.js","sourceRoot":"","sources":["../src/dependency.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,GAAG,yBAAyB,CAAA;AAC5C,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAA;AAEzG,MAAM,UAAU,oBAAoB,CAAC,MAAkB,EAAE,QAAgC,EAAE,MAA4B;IACrH,MAAM,KAAK,GAAe,EAAE,CAAA;IAC5B,MAAM,MAAM,GAAG,MAAM,CAAC,qBAAqB,CAAC,IAAI,MAAM,CAAC,aAAa,CAAC,CAAA;IACrE,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,KAAK,CAAC,qBAAqB,CAAC,GAAG,MAAM,CAAA;IAC5F,MAAM,MAAM,GAAG,MAAM,CAAC,2BAA2B,CAAC,IAAI,MAAM,CAAC,kBAAkB,CAAC,CAAA;IAChF,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,IAAI,GAAG;QAC1F,KAAK,CAAC,2BAA2B,CAAC,GAAG,MAAM,CAAA;IAC7C,IAAI,MAAM;QAAE,OAAO;YACjB,GAAG,KAAK;YACR,wBAAwB,EAAE,MAAM,CAAC,OAAO;YACxC,yBAAyB,EAAE,MAAM,CAAC,QAAQ;YAC1C,8BAA8B,EAAE,MAAM,CAAC,YAAY;SACpD,CAAA;IACD,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC3B,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,CAAA;IACpD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAA;IAC/D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;IACxB,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI;QAAE,OAAO,KAAK,CAAA;IACvD,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAA;IACtF,IAAI,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAA;IACvD,KAAK,CAAC,wBAAwB,CAAC,GAAG,OAAO,CAAA;IACzC,MAAM,SAAS,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,OAAO,IAAI,KAAK,CAAC,qBAAqB,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAA;IACvG,IAAI,SAAS,IAAI,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,KAAK,CAAC,0BAA0B,CAAC,GAAG,SAAS,CAAA;IAC1F,OAAO,KAAK,CAAA;AACd,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { retainBtxTrace, BtxTraceRetention, activeBtxTraceReference, withoutBtxTracing } from './context.js';
2
+ export { createBtxSpanProcessor, BTX_OTEL_LIMITS, type BtxExportObservation, type BtxSpanProcessorOptions } from './processor.js';
3
+ export type { BtxDependencyRegistry } from './dependency.js';
4
+ export { withBtxClientDependency, type BtxClientDependency } from './client-dependency.js';
5
+ export { BTX_OTEL_SPAN_LIMITS, trace, type BtxSpan, type BtxSpanOutcome, type TraceOptions } from './span.js';
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { retainBtxTrace, BtxTraceRetention, activeBtxTraceReference, withoutBtxTracing } from './context.js';
2
+ export { createBtxSpanProcessor, BTX_OTEL_LIMITS } from './processor.js';
3
+ export { withBtxClientDependency } from './client-dependency.js';
4
+ export { BTX_OTEL_SPAN_LIMITS, trace } from './span.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAC5G,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAA2D,MAAM,gBAAgB,CAAA;AAEjI,OAAO,EAAE,uBAAuB,EAA4B,MAAM,wBAAwB,CAAA;AAC1F,OAAO,EAAE,oBAAoB,EAAE,KAAK,EAAwD,MAAM,WAAW,CAAA"}
@@ -0,0 +1,29 @@
1
+ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
2
+ import { type SpanExporter, type SpanProcessor } from '@opentelemetry/sdk-trace';
3
+ import type { BtxDependencyRegistry } from './dependency.js';
4
+ export declare const BTX_OTEL_LIMITS: Readonly<{
5
+ queue: 512;
6
+ batch: 128;
7
+ exportMs: 1000;
8
+ flushMs: 1500;
9
+ }>;
10
+ export type BtxExportObservation = {
11
+ outcome: 'accepted' | 'failed' | 'overflow' | 'projection_failed';
12
+ spans: number;
13
+ };
14
+ export interface BtxSpanProcessorOptions {
15
+ /** Standard OTLP/HTTP Protobuf configuration, including standard OTel environment defaults. */
16
+ otlp?: ConstructorParameters<typeof OTLPTraceExporter>[0];
17
+ /** An existing OTel exporter, for applications that already own their transport. */
18
+ exporter?: SpanExporter;
19
+ dependencyRegistry?: BtxDependencyRegistry;
20
+ /** Export only trace() operations and their children. Default: false. */
21
+ requireScope?: boolean;
22
+ maxQueueSize?: number;
23
+ maxExportBatchSize?: number;
24
+ scheduledDelayMillis?: number;
25
+ exportTimeoutMillis?: number;
26
+ flushTimeoutMillis?: number;
27
+ onExport?: (observation: BtxExportObservation) => void;
28
+ }
29
+ export declare function createBtxSpanProcessor(options?: BtxSpanProcessorOptions): SpanProcessor;
@@ -0,0 +1,142 @@
1
+ import { TraceFlags } from '@opentelemetry/api';
2
+ import { ExportResultCode, isTracingSuppressed } from '@opentelemetry/core';
3
+ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
4
+ import { BatchSpanProcessor } from '@opentelemetry/sdk-trace';
5
+ import { scopeKey, withoutBtxTracing } from './context.js';
6
+ import { exportSpan } from './projection.js';
7
+ import { clientDependencyKey, matchingClientDependency } from './client-dependency.js';
8
+ export const BTX_OTEL_LIMITS = Object.freeze({ queue: 512, batch: 128, exportMs: 1_000, flushMs: 1_500 });
9
+ export function createBtxSpanProcessor(options = {}) {
10
+ const positive = (value, fallback) => {
11
+ if (value === undefined)
12
+ return fallback;
13
+ if (!Number.isSafeInteger(value) || value <= 0)
14
+ throw new Error('Exporter limits must be positive integers');
15
+ return value;
16
+ };
17
+ const queue = positive(options.maxQueueSize, BTX_OTEL_LIMITS.queue);
18
+ const batchSize = positive(options.maxExportBatchSize, Math.min(BTX_OTEL_LIMITS.batch, queue));
19
+ if (batchSize > queue)
20
+ throw new Error('Export batch size exceeds queue size');
21
+ const exportMs = positive(options.exportTimeoutMillis ?? options.otlp?.timeoutMillis, BTX_OTEL_LIMITS.exportMs);
22
+ const flushMs = positive(options.flushTimeoutMillis, BTX_OTEL_LIMITS.flushMs);
23
+ const delayMs = positive(options.scheduledDelayMillis, 5_000);
24
+ if (options.exporter && options.otlp)
25
+ throw new Error('Configure either OTLP or an existing exporter');
26
+ const transport = options.exporter ?? new OTLPTraceExporter({ ...options.otlp, timeoutMillis: exportMs });
27
+ const scopes = new WeakMap();
28
+ const dependencies = new WeakMap();
29
+ let pending = 0;
30
+ let stopped = false;
31
+ const observe = (outcome, spans) => {
32
+ try {
33
+ withoutBtxTracing(() => options.onExport?.({ outcome, spans }));
34
+ }
35
+ catch { /* Preserve application work. */ }
36
+ };
37
+ const inFlight = new Set();
38
+ const exporter = {
39
+ export(spans, callback) {
40
+ let done;
41
+ const sending = new Promise(resolve => { done = resolve; });
42
+ inFlight.add(sending);
43
+ let completed = false;
44
+ const timer = setTimeout(() => finish({ code: ExportResultCode.FAILED,
45
+ error: new Error('Trace export deadline exceeded') }), exportMs);
46
+ const finish = result => {
47
+ if (completed)
48
+ return;
49
+ completed = true;
50
+ clearTimeout(timer);
51
+ pending -= spans.length;
52
+ inFlight.delete(sending);
53
+ done();
54
+ observe(result.code === ExportResultCode.SUCCESS ? 'accepted' : 'failed', spans.length);
55
+ callback(result);
56
+ };
57
+ void withoutBtxTracing(async () => {
58
+ await Promise.all(spans.map(span => span.resource.waitForAsyncAttributes?.()));
59
+ if (!completed)
60
+ transport.export(spans, finish);
61
+ }).catch(() => finish({ code: ExportResultCode.FAILED, error: new Error('Trace export failed') }));
62
+ },
63
+ shutdown: () => withoutBtxTracing(() => transport.shutdown()),
64
+ };
65
+ const batch = new BatchSpanProcessor({ exporter, maxQueueSize: queue,
66
+ maxExportBatchSize: batchSize, scheduledDelayMillis: delayMs,
67
+ exportTimeoutMillis: exportMs });
68
+ let flushing;
69
+ const flush = () => flushing ??= bounded((async () => {
70
+ await batch.forceFlush();
71
+ await Promise.all([...inFlight]);
72
+ })(), flushMs).finally(() => { flushing = undefined; });
73
+ return {
74
+ onStart(span, parent) {
75
+ try {
76
+ const scope = parent.getValue(scopeKey);
77
+ if (!stopped && !isTracingSuppressed(parent) && (!options.requireScope || scope)) {
78
+ scopes.set(span, scope ?? {});
79
+ const dependency = parent.getValue(clientDependencyKey);
80
+ if (span.kind === 2 && dependency)
81
+ dependencies.set(span, dependency);
82
+ }
83
+ }
84
+ catch { /* Ignore instrumentation failures. */ }
85
+ },
86
+ onEnd(span) {
87
+ try {
88
+ const scope = scopes.get(span);
89
+ scopes.delete(span);
90
+ const dependency = dependencies.get(span);
91
+ dependencies.delete(span);
92
+ if (!scope || stopped || !(span.spanContext().traceFlags & TraceFlags.SAMPLED))
93
+ return;
94
+ if (span.status.code === 2)
95
+ scope.retention?.preserveFailure();
96
+ if (pending >= queue) {
97
+ observe('overflow', 1);
98
+ return;
99
+ }
100
+ const copy = exportSpan(span, scope, options.dependencyRegistry, matchingClientDependency(dependency, span.attributes));
101
+ pending++;
102
+ const enqueue = (retain) => {
103
+ if (retain && !stopped)
104
+ batch.onEnd(copy);
105
+ else
106
+ pending--;
107
+ };
108
+ if (scope.retention)
109
+ scope.retention.whenDecided(enqueue);
110
+ else
111
+ enqueue(true);
112
+ }
113
+ catch {
114
+ observe('projection_failed', 1);
115
+ }
116
+ },
117
+ forceFlush: flush,
118
+ async shutdown() {
119
+ if (stopped)
120
+ return;
121
+ stopped = true;
122
+ try {
123
+ await flush();
124
+ }
125
+ finally {
126
+ await bounded(batch.shutdown(), flushMs);
127
+ }
128
+ },
129
+ };
130
+ }
131
+ async function bounded(work, timeoutMs) {
132
+ let timer;
133
+ try {
134
+ await Promise.race([work, new Promise((_, reject) => {
135
+ timer = setTimeout(() => reject(new Error('Trace flush deadline exceeded')), timeoutMs);
136
+ })]);
137
+ }
138
+ finally {
139
+ clearTimeout(timer);
140
+ }
141
+ }
142
+ //# sourceMappingURL=processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processor.js","sourceRoot":"","sources":["../src/processor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { ReadableSpan } from '@opentelemetry/sdk-trace';
2
+ import { type BtxDependencyRegistry } from './dependency.js';
3
+ import type { TraceMetadata } from './context.js';
4
+ import type { BtxClientDependency } from './client-dependency.js';
5
+ /** Copy at span end without filtering or rewriting the instrumented evidence. */
6
+ export declare function exportSpan(span: ReadableSpan, scope: TraceMetadata, registry?: BtxDependencyRegistry, client?: BtxClientDependency): ReadableSpan;
@@ -0,0 +1,48 @@
1
+ import { dependencyAttributes } from './dependency.js';
2
+ /** Copy at span end without filtering or rewriting the instrumented evidence. */
3
+ export function exportSpan(span, scope, registry, client) {
4
+ const attributes = {
5
+ ...(span.kind === 2 ? dependencyAttributes(span.attributes, registry, client) : {}),
6
+ ...scope.attributes,
7
+ ...span.attributes
8
+ };
9
+ // Trusted correlation is additive; authored values remain intact.
10
+ const labels = attributes['btx.labels'];
11
+ if (labels === undefined)
12
+ attributes['btx.labels'] = [...(scope.labels ?? [])];
13
+ else if (Array.isArray(labels) && labels.every((label) => typeof label === 'string'))
14
+ attributes['btx.labels'] = [
15
+ ...labels,
16
+ ...(scope.labels ?? []).filter((label) => !labels.includes(label))
17
+ ];
18
+ const copyAttributes = (source) => Object.fromEntries(Object.entries(source).map(([key, value]) => [key, Array.isArray(value) ? [...value] : value]));
19
+ const spanContext = { ...span.spanContext() };
20
+ return {
21
+ name: span.name,
22
+ kind: span.kind,
23
+ spanContext: () => spanContext,
24
+ parentSpanContext: span.parentSpanContext && { ...span.parentSpanContext },
25
+ startTime: [...span.startTime],
26
+ endTime: [...span.endTime],
27
+ duration: [...span.duration],
28
+ ended: span.ended,
29
+ status: { ...span.status },
30
+ attributes: copyAttributes(attributes),
31
+ resource: span.resource,
32
+ instrumentationScope: { ...span.instrumentationScope },
33
+ events: span.events.map((event) => ({
34
+ ...event,
35
+ time: [...event.time],
36
+ attributes: event.attributes && copyAttributes(event.attributes)
37
+ })),
38
+ links: span.links.map((link) => ({
39
+ ...link,
40
+ context: { ...link.context },
41
+ attributes: link.attributes && copyAttributes(link.attributes)
42
+ })),
43
+ droppedAttributesCount: span.droppedAttributesCount,
44
+ droppedEventsCount: span.droppedEventsCount,
45
+ droppedLinksCount: span.droppedLinksCount
46
+ };
47
+ }
48
+ //# sourceMappingURL=projection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projection.js","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"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"}
package/dist/span.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ import { type Attributes, type SpanKind } from '@opentelemetry/api';
2
+ import { type TraceMetadata } from './context.js';
3
+ /** Disable content limits independently of the export queue and deadlines. */
4
+ export declare const BTX_OTEL_SPAN_LIMITS: Readonly<{
5
+ attributeCountLimit: number;
6
+ attributeValueLengthLimit: number;
7
+ eventCountLimit: number;
8
+ linkCountLimit: number;
9
+ attributePerEventCountLimit: number;
10
+ attributePerLinkCountLimit: number;
11
+ }>;
12
+ export interface BtxSpan {
13
+ /** OTel attributes are retained without exporter filtering or truncation. */
14
+ setAttributes(attributes: Attributes): void;
15
+ }
16
+ export interface BtxSpanOutcome {
17
+ error?: boolean;
18
+ attributes?: Attributes;
19
+ }
20
+ export interface TraceOptions<T = unknown> extends TraceMetadata {
21
+ kind?: SpanKind;
22
+ outcome?: (result: T) => BtxSpanOutcome;
23
+ }
24
+ type TraceWork<T> = (span: BtxSpan) => T | PromiseLike<T>;
25
+ /** Run work in an active span, ending it when the work settles. */
26
+ export declare function trace<T>(name: string, work: TraceWork<T>): Promise<T>;
27
+ export declare function trace<T>(name: string, options: TraceOptions<NoInfer<T>>, work: TraceWork<T>): Promise<T>;
28
+ export {};
package/dist/span.js ADDED
@@ -0,0 +1,90 @@
1
+ import { context, SpanStatusCode, trace as otelTrace } from '@opentelemetry/api';
2
+ import { isTracingSuppressed } from '@opentelemetry/core';
3
+ import { traceScopeContext } from './context.js';
4
+ /** Disable content limits independently of the export queue and deadlines. */
5
+ export const BTX_OTEL_SPAN_LIMITS = Object.freeze({
6
+ attributeCountLimit: Infinity, attributeValueLengthLimit: Infinity,
7
+ eventCountLimit: Infinity, linkCountLimit: Infinity,
8
+ attributePerEventCountLimit: Infinity, attributePerLinkCountLimit: Infinity,
9
+ });
10
+ const noopSpan = Object.freeze({ setAttributes: () => { } });
11
+ function safely(action) {
12
+ try {
13
+ action();
14
+ }
15
+ catch { /* Instrumentation must preserve application work. */ }
16
+ }
17
+ export async function trace(name, optionsOrWork, callback) {
18
+ const options = typeof optionsOrWork === 'function' ? {} : optionsOrWork;
19
+ const work = typeof optionsOrWork === 'function' ? optionsOrWork : callback;
20
+ let span;
21
+ let parent;
22
+ try {
23
+ parent = context.active();
24
+ if (!isTracingSuppressed(parent)) {
25
+ parent = traceScopeContext(parent, options);
26
+ span = otelTrace.getTracer('@btx-second-context/otel', '0.0.1').startSpan(name, {
27
+ kind: options.kind, attributes: options.attributes,
28
+ }, parent);
29
+ if (span.isRecording())
30
+ parent = otelTrace.setSpan(parent, span);
31
+ else
32
+ span = undefined;
33
+ }
34
+ }
35
+ catch {
36
+ safely(() => span?.end());
37
+ span = undefined;
38
+ }
39
+ if (!span || !parent)
40
+ return work(noopSpan);
41
+ const owned = span;
42
+ let ended = false;
43
+ const handle = Object.freeze({
44
+ setAttributes(attributes) {
45
+ if (!ended)
46
+ safely(() => owned.setAttributes(attributes));
47
+ },
48
+ });
49
+ const run = async () => {
50
+ try {
51
+ const result = await work(handle);
52
+ let failed = false;
53
+ safely(() => {
54
+ const response = result instanceof Response ? result : undefined;
55
+ failed = !!response && response.status >= 500;
56
+ if (response)
57
+ owned.setAttribute('http.response.status_code', response.status);
58
+ });
59
+ safely(() => {
60
+ const described = options.outcome?.(result);
61
+ failed ||= !!described?.error;
62
+ if (described?.attributes)
63
+ handle.setAttributes(described.attributes);
64
+ });
65
+ safely(() => owned.setStatus({ code: failed ? SpanStatusCode.ERROR : SpanStatusCode.OK }));
66
+ return result;
67
+ }
68
+ catch (error) {
69
+ safely(() => owned.setStatus({ code: SpanStatusCode.ERROR, ...(error instanceof Error ? { message: error.message } : {}) }));
70
+ safely(() => owned.recordException(error instanceof Error || typeof error === 'string' ? error : String(error)));
71
+ safely(() => owned.setAttribute('error.type', error instanceof Error ? error.name : typeof error));
72
+ throw error;
73
+ }
74
+ finally {
75
+ ended = true;
76
+ safely(() => owned.end());
77
+ }
78
+ };
79
+ // A broken context manager can fail before or after invoking its callback.
80
+ // Reuse the application promise so neither case repeats work or replaces it.
81
+ let execution;
82
+ const once = () => execution ??= run();
83
+ try {
84
+ return await context.with(parent, once);
85
+ }
86
+ catch {
87
+ return execution ?? once();
88
+ }
89
+ }
90
+ //# sourceMappingURL=span.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"span.js","sourceRoot":"","sources":["../src/span.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,54 @@
1
1
  {
2
2
  "name": "@btx-second-context/otel",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.ts",
8
+ "import": "./dist/index.js",
9
+ "default": "./dist/index.js"
10
+ }
11
+ },
12
+ "dependencies": {
13
+ "@opentelemetry/core": "2.12.0",
14
+ "@opentelemetry/exporter-trace-otlp-proto": "0.223.0",
15
+ "@opentelemetry/sdk-trace": "2.12.0"
16
+ },
17
+ "peerDependencies": {
18
+ "@opentelemetry/api": "^1.9.0"
19
+ },
20
+ "devDependencies": {
21
+ "@opentelemetry/api": "1.9.1",
22
+ "@opentelemetry/context-async-hooks": "2.12.0",
23
+ "@opentelemetry/otlp-transformer": "0.223.0",
24
+ "@opentelemetry/resources": "2.12.0",
25
+ "@types/node": "^20",
26
+ "typescript": "^5.9.3",
27
+ "vitest": "^4.1.5"
28
+ },
29
+ "description": "OpenTelemetry trace export and Labels for Node backends",
30
+ "main": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "files": [
33
+ "dist",
34
+ "README.md",
35
+ "CHANGELOG.md"
36
+ ],
37
+ "engines": {
38
+ "node": ">=20.6.0"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public",
42
+ "registry": "https://registry.npmjs.org/"
43
+ },
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/dataporto/btx.git",
47
+ "directory": "code/packages/btx-otel"
48
+ },
49
+ "scripts": {
50
+ "typecheck": "node ../../node_modules/typescript/bin/tsc --noEmit -p tsconfig.json",
51
+ "build": "tsc -p tsconfig.build.json",
52
+ "test": "pnpm build && vitest run --config vitest.config.ts"
53
+ }
6
54
  }