mercury-composable 4.12.1 → 4.12.15

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.
@@ -0,0 +1,65 @@
1
+ import { getLogger } from '../log.js';
2
+ import { DISTRIBUTED_TRACE_FORWARDER } from '../trace.js';
3
+ import { FORWARDING_SWITCH, HEADERS, settingsFromConfig } from './config.js';
4
+ import { Exporter } from './export.js';
5
+ import { hex, spanFromDataset } from './span.js';
6
+ export { DISTRIBUTED_TRACE_FORWARDER } from '../trace.js';
7
+ export { FORWARDING_SWITCH, fixedSettings, parseHeaders, settingsFromConfig } from './config.js';
8
+ export { describeHttpFailure, Exporter, ExportFailure, INSTRUMENTATION_SCOPE } from './export.js';
9
+ export { attribute, parseIso8601Nanos, spanFromDataset } from './span.js';
10
+ export { encodeExportRequest, partialSuccess, ProtoReader, ProtoWriter, SPAN_FLAGS_SAMPLED_LOCAL } from './otlp.js';
11
+ const log = getLogger('mercury.otel');
12
+ /** The engines run the forwarder with two workers. */
13
+ export const FORWARDER_INSTANCES = 2;
14
+ /**
15
+ * The `distributed.trace.forwarder` function bound to one exporter: map the
16
+ * dataset to a span and export it, logging - never throwing - a failure.
17
+ */
18
+ export function forwarder(exporter) {
19
+ return async (_headers, body) => {
20
+ // a dataset without W3C-valid ids cannot become a span without forging ids - skipped
21
+ const span = spanFromDataset(body);
22
+ if (!span)
23
+ return;
24
+ try {
25
+ await exporter.export(span);
26
+ }
27
+ catch (e) {
28
+ const failure = e;
29
+ log.warn(`OTLP export failed for span ${hex(span.spanId)} of trace ${hex(span.traceId)} - ` +
30
+ failure.message);
31
+ }
32
+ };
33
+ }
34
+ /** Whether `otel.forwarding` is switched on (the text `true`, case-insensitive). */
35
+ export function enabled(config) {
36
+ const value = config.getProperty(FORWARDING_SWITCH);
37
+ return value !== undefined && value.trim().toLowerCase() === 'true';
38
+ }
39
+ /**
40
+ * Register the built-in forwarder on `distributed.trace.forwarder` when
41
+ * `otel.forwarding=true` - the host's start-up hook. Returns the exporter, or
42
+ * undefined when forwarding is off or a function already occupies the route (an
43
+ * application's own forwarder wins, as in the engines). A misconfigured endpoint
44
+ * fails the start.
45
+ */
46
+ export function activate(config, registry) {
47
+ if (!enabled(config))
48
+ return undefined;
49
+ if (registry.exists(DISTRIBUTED_TRACE_FORWARDER)) {
50
+ log.info(`${DISTRIBUTED_TRACE_FORWARDER} is provided by the application - the built-in ` +
51
+ 'OpenTelemetry forwarder stands down');
52
+ return undefined;
53
+ }
54
+ const exporter = new Exporter(settingsFromConfig(config));
55
+ const names = exporter.headerNames();
56
+ log.info(`OpenTelemetry trace forwarder ready - service=${exporter.serviceName}, OTLP ` +
57
+ `endpoint=${exporter.endpoint}, compression=${exporter.compression}, credential ` +
58
+ `headers=${JSON.stringify(names)}`);
59
+ if (!names.length) {
60
+ log.info(`No OTLP credential header yet (${HEADERS} unset) - it is re-read on every export, ` +
61
+ 'so a credential published later takes effect without a restart');
62
+ }
63
+ registry.register(DISTRIBUTED_TRACE_FORWARDER, forwarder(exporter), { instances: FORWARDER_INSTANCES, isPrivate: true });
64
+ return exporter;
65
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * The OTLP wire format - a hand-written protobuf encoder for the one message the
3
+ * forwarder sends, `ExportTraceServiceRequest`, and a reader for the one it
4
+ * receives, `ExportTraceServiceResponse` (the Rust port's `otlp.rs`, line for line).
5
+ *
6
+ * No protobuf library, no generated code, no OpenTelemetry SDK: the OTLP v1 trace
7
+ * schema is frozen and the forwarder needs eight message types with scalar fields.
8
+ * Field numbers are the OTLP trace.proto / common.proto / resource.proto /
9
+ * trace_service.proto definitions; the encoding rules are
10
+ * https://protobuf.dev/programming-guides/encoding/
11
+ *
12
+ * ExportTraceServiceRequest { repeated ResourceSpans resource_spans = 1; }
13
+ * ResourceSpans { Resource resource = 1; repeated ScopeSpans scope_spans = 2; }
14
+ * Resource { repeated KeyValue attributes = 1; }
15
+ * ScopeSpans { InstrumentationScope scope = 1; repeated Span spans = 2; }
16
+ * InstrumentationScope { string name = 1; string version = 2; }
17
+ * KeyValue { string key = 1; AnyValue value = 2; }
18
+ * AnyValue { oneof value { string string_value = 1; bool bool_value = 2;
19
+ * int64 int_value = 3; double double_value = 4; } }
20
+ * Span { bytes trace_id = 1; bytes span_id = 2; bytes parent_span_id = 4;
21
+ * string name = 5; SpanKind kind = 6; fixed64 start_time_unix_nano = 7;
22
+ * fixed64 end_time_unix_nano = 8; repeated KeyValue attributes = 9;
23
+ * Status status = 15; fixed32 flags = 16; }
24
+ * Status { string message = 2; StatusCode code = 3; }
25
+ * ExportTraceServiceResponse { ExportTracePartialSuccess partial_success = 1; }
26
+ * ExportTracePartialSuccess { int64 rejected_spans = 1; string error_message = 2; }
27
+ */
28
+ import { Span } from './span.js';
29
+ export declare const WIRE_VARINT = 0;
30
+ export declare const WIRE_FIXED64 = 1;
31
+ export declare const WIRE_LEN = 2;
32
+ export declare const WIRE_FIXED32 = 5;
33
+ export declare const SPAN_FLAGS_SAMPLED_LOCAL = 257;
34
+ /**
35
+ * A minimal protobuf writer: field tags, varints, fixed-width scalars and
36
+ * length-delimited payloads. Proto3 default values are omitted, except for
37
+ * `oneof` members (which have explicit presence) - the `*Always` methods.
38
+ */
39
+ export declare class ProtoWriter {
40
+ private chunks;
41
+ intoBytes(): Uint8Array;
42
+ /** Base-128 varint: little-endian groups of 7 bits, high bit = more follows. */
43
+ varint(value: number | bigint): void;
44
+ private tag;
45
+ /** `string` - omitted when empty (proto3 default). */
46
+ string(field: number, value: string): void;
47
+ /** A `oneof` string member - always written, even when empty. */
48
+ stringAlways(field: number, value: string): void;
49
+ /** `bytes` - omitted when empty. */
50
+ bytes(field: number, value: Uint8Array): void;
51
+ /** An embedded message - always written (an empty message is meaningful). */
52
+ message(field: number, body: Uint8Array): void;
53
+ /** `uint32` / `uint64` / enum - omitted when zero. */
54
+ uint(field: number, value: number): void;
55
+ /** A `oneof` `int64` member - always written (two's complement varint). */
56
+ int64Always(field: number, value: number | bigint): void;
57
+ /** `fixed64` - omitted when zero. */
58
+ fixed64(field: number, value: bigint): void;
59
+ /** A `oneof` `double` member - always written (IEEE-754, little-endian). */
60
+ doubleAlways(field: number, value: number): void;
61
+ /** `fixed32` - omitted when zero. */
62
+ fixed32(field: number, value: number): void;
63
+ }
64
+ /** The OTLP `Span` message for one mapped span. */
65
+ export declare function spanMessage(span: Span): Uint8Array;
66
+ /**
67
+ * One `ExportTraceServiceRequest` carrying one span under one resource and one
68
+ * instrumentation scope - the request body of an OTLP/HTTP export.
69
+ */
70
+ export declare function encodeExportRequest(serviceName: string, scopeName: string, scopeVersion: string, span: Span): Uint8Array;
71
+ /**
72
+ * A minimal, bounds-checked protobuf reader - enough to walk the OTLP message
73
+ * tree (the response's `partial_success`, and the test collector's decoding of
74
+ * what this host sent). Every read returns `undefined` past the end instead of
75
+ * throwing, so a malformed body is reported, never fatal.
76
+ */
77
+ export declare class ProtoReader {
78
+ private readonly buf;
79
+ private pos;
80
+ private readonly view;
81
+ constructor(buf: Uint8Array);
82
+ hasMore(): boolean;
83
+ /** The next field tag as `[fieldNumber, wireType]`. */
84
+ readTag(): [number, number] | undefined;
85
+ readVarint(): bigint | undefined;
86
+ readFixed64(): bigint | undefined;
87
+ readDouble(): number | undefined;
88
+ readFixed32(): number | undefined;
89
+ /** A length-delimited chunk: bytes, a string, or an embedded message. */
90
+ readBytes(): Uint8Array | undefined;
91
+ readString(): string | undefined;
92
+ /** Advance past a field whose value is not needed, honouring its wire type. */
93
+ skip(wire: number): boolean;
94
+ }
95
+ /**
96
+ * The `partial_success` block of an `ExportTraceServiceResponse`: a backend
97
+ * that accepted the request but rejected spans says so here.
98
+ */
99
+ export interface PartialSuccess {
100
+ rejectedSpans: number;
101
+ errorMessage: string;
102
+ }
103
+ /** The response's partial-success block when it carries content, else undefined. */
104
+ export declare function partialSuccess(body: Uint8Array): PartialSuccess | undefined;
@@ -0,0 +1,358 @@
1
+ /**
2
+ * The OTLP wire format - a hand-written protobuf encoder for the one message the
3
+ * forwarder sends, `ExportTraceServiceRequest`, and a reader for the one it
4
+ * receives, `ExportTraceServiceResponse` (the Rust port's `otlp.rs`, line for line).
5
+ *
6
+ * No protobuf library, no generated code, no OpenTelemetry SDK: the OTLP v1 trace
7
+ * schema is frozen and the forwarder needs eight message types with scalar fields.
8
+ * Field numbers are the OTLP trace.proto / common.proto / resource.proto /
9
+ * trace_service.proto definitions; the encoding rules are
10
+ * https://protobuf.dev/programming-guides/encoding/
11
+ *
12
+ * ExportTraceServiceRequest { repeated ResourceSpans resource_spans = 1; }
13
+ * ResourceSpans { Resource resource = 1; repeated ScopeSpans scope_spans = 2; }
14
+ * Resource { repeated KeyValue attributes = 1; }
15
+ * ScopeSpans { InstrumentationScope scope = 1; repeated Span spans = 2; }
16
+ * InstrumentationScope { string name = 1; string version = 2; }
17
+ * KeyValue { string key = 1; AnyValue value = 2; }
18
+ * AnyValue { oneof value { string string_value = 1; bool bool_value = 2;
19
+ * int64 int_value = 3; double double_value = 4; } }
20
+ * Span { bytes trace_id = 1; bytes span_id = 2; bytes parent_span_id = 4;
21
+ * string name = 5; SpanKind kind = 6; fixed64 start_time_unix_nano = 7;
22
+ * fixed64 end_time_unix_nano = 8; repeated KeyValue attributes = 9;
23
+ * Status status = 15; fixed32 flags = 16; }
24
+ * Status { string message = 2; StatusCode code = 3; }
25
+ * ExportTraceServiceResponse { ExportTracePartialSuccess partial_success = 1; }
26
+ * ExportTracePartialSuccess { int64 rejected_spans = 1; string error_message = 2; }
27
+ */
28
+ import { STATUS_UNSET } from './span.js';
29
+ // protobuf wire types (the low 3 bits of a field tag)
30
+ export const WIRE_VARINT = 0;
31
+ export const WIRE_FIXED64 = 1;
32
+ export const WIRE_LEN = 2;
33
+ export const WIRE_FIXED32 = 5;
34
+ // OTLP span flags: bits 0-7 are the W3C trace flags (0x01 = sampled), bit 8 says
35
+ // the is-remote bit is known, bit 9 is the is-remote bit itself. A span this host
36
+ // produced is sampled and local - the value the Java SDK writes.
37
+ export const SPAN_FLAGS_SAMPLED_LOCAL = 0x0101;
38
+ const encoder = new TextEncoder();
39
+ const decoder = new TextDecoder('utf-8');
40
+ const MASK_64 = (1n << 64n) - 1n;
41
+ /**
42
+ * A minimal protobuf writer: field tags, varints, fixed-width scalars and
43
+ * length-delimited payloads. Proto3 default values are omitted, except for
44
+ * `oneof` members (which have explicit presence) - the `*Always` methods.
45
+ */
46
+ export class ProtoWriter {
47
+ chunks = [];
48
+ intoBytes() {
49
+ return Uint8Array.from(this.chunks);
50
+ }
51
+ /** Base-128 varint: little-endian groups of 7 bits, high bit = more follows. */
52
+ varint(value) {
53
+ let v = typeof value === 'bigint' ? value : BigInt(Math.trunc(value));
54
+ if (v < 0n) {
55
+ v &= MASK_64; // two's complement, 64-bit
56
+ }
57
+ for (;;) {
58
+ const byte = Number(v & 0x7fn);
59
+ v >>= 7n;
60
+ if (v === 0n) {
61
+ this.chunks.push(byte);
62
+ return;
63
+ }
64
+ this.chunks.push(byte | 0x80);
65
+ }
66
+ }
67
+ tag(field, wire) {
68
+ this.varint((field << 3) | wire);
69
+ }
70
+ /** `string` - omitted when empty (proto3 default). */
71
+ string(field, value) {
72
+ if (value) {
73
+ this.bytes(field, encoder.encode(value));
74
+ }
75
+ }
76
+ /** A `oneof` string member - always written, even when empty. */
77
+ stringAlways(field, value) {
78
+ const data = encoder.encode(value);
79
+ this.tag(field, WIRE_LEN);
80
+ this.varint(data.length);
81
+ this.chunks.push(...data);
82
+ }
83
+ /** `bytes` - omitted when empty. */
84
+ bytes(field, value) {
85
+ if (value.length) {
86
+ this.tag(field, WIRE_LEN);
87
+ this.varint(value.length);
88
+ this.chunks.push(...value);
89
+ }
90
+ }
91
+ /** An embedded message - always written (an empty message is meaningful). */
92
+ message(field, body) {
93
+ this.tag(field, WIRE_LEN);
94
+ this.varint(body.length);
95
+ this.chunks.push(...body);
96
+ }
97
+ /** `uint32` / `uint64` / enum - omitted when zero. */
98
+ uint(field, value) {
99
+ if (value !== 0) {
100
+ this.tag(field, WIRE_VARINT);
101
+ this.varint(value);
102
+ }
103
+ }
104
+ /** A `oneof` `int64` member - always written (two's complement varint). */
105
+ int64Always(field, value) {
106
+ this.tag(field, WIRE_VARINT);
107
+ this.varint(value);
108
+ }
109
+ /** `fixed64` - omitted when zero. */
110
+ fixed64(field, value) {
111
+ if (value !== 0n) {
112
+ this.tag(field, WIRE_FIXED64);
113
+ const buf = new Uint8Array(8);
114
+ new DataView(buf.buffer).setBigUint64(0, value & MASK_64, true);
115
+ this.chunks.push(...buf);
116
+ }
117
+ }
118
+ /** A `oneof` `double` member - always written (IEEE-754, little-endian). */
119
+ doubleAlways(field, value) {
120
+ this.tag(field, WIRE_FIXED64);
121
+ const buf = new Uint8Array(8);
122
+ new DataView(buf.buffer).setFloat64(0, value, true);
123
+ this.chunks.push(...buf);
124
+ }
125
+ /** `fixed32` - omitted when zero. */
126
+ fixed32(field, value) {
127
+ if (value !== 0) {
128
+ this.tag(field, WIRE_FIXED32);
129
+ const buf = new Uint8Array(4);
130
+ new DataView(buf.buffer).setUint32(0, value, true);
131
+ this.chunks.push(...buf);
132
+ }
133
+ }
134
+ }
135
+ function anyValue(value) {
136
+ const w = new ProtoWriter();
137
+ switch (value.type) {
138
+ case 'string':
139
+ w.stringAlways(1, value.value);
140
+ break;
141
+ case 'int':
142
+ w.int64Always(3, value.value);
143
+ break;
144
+ case 'double':
145
+ w.doubleAlways(4, value.value);
146
+ break;
147
+ }
148
+ return w.intoBytes();
149
+ }
150
+ function keyValue(key, value) {
151
+ const w = new ProtoWriter();
152
+ w.string(1, key);
153
+ w.message(2, anyValue(value));
154
+ return w.intoBytes();
155
+ }
156
+ function resource(serviceName) {
157
+ const w = new ProtoWriter();
158
+ w.message(1, keyValue('service.name', { type: 'string', value: serviceName }));
159
+ return w.intoBytes();
160
+ }
161
+ function instrumentationScope(name, version) {
162
+ const w = new ProtoWriter();
163
+ w.string(1, name);
164
+ w.string(2, version);
165
+ return w.intoBytes();
166
+ }
167
+ function status(span) {
168
+ if (span.statusCode === STATUS_UNSET && !span.statusMessage) {
169
+ return undefined;
170
+ }
171
+ const w = new ProtoWriter();
172
+ w.string(2, span.statusMessage);
173
+ w.uint(3, span.statusCode);
174
+ return w.intoBytes();
175
+ }
176
+ /** The OTLP `Span` message for one mapped span. */
177
+ export function spanMessage(span) {
178
+ const w = new ProtoWriter();
179
+ w.bytes(1, span.traceId);
180
+ w.bytes(2, span.spanId);
181
+ if (span.parentSpanId) {
182
+ w.bytes(4, span.parentSpanId);
183
+ }
184
+ w.string(5, span.name);
185
+ w.uint(6, span.kind);
186
+ w.fixed64(7, span.startUnixNano);
187
+ w.fixed64(8, span.endUnixNano);
188
+ for (const [key, value] of span.attributes) {
189
+ w.message(9, keyValue(key, value));
190
+ }
191
+ const st = status(span);
192
+ if (st) {
193
+ w.message(15, st);
194
+ }
195
+ w.fixed32(16, SPAN_FLAGS_SAMPLED_LOCAL);
196
+ return w.intoBytes();
197
+ }
198
+ /**
199
+ * One `ExportTraceServiceRequest` carrying one span under one resource and one
200
+ * instrumentation scope - the request body of an OTLP/HTTP export.
201
+ */
202
+ export function encodeExportRequest(serviceName, scopeName, scopeVersion, span) {
203
+ const scopeSpans = new ProtoWriter();
204
+ scopeSpans.message(1, instrumentationScope(scopeName, scopeVersion));
205
+ scopeSpans.message(2, spanMessage(span));
206
+ const resourceSpans = new ProtoWriter();
207
+ resourceSpans.message(1, resource(serviceName));
208
+ resourceSpans.message(2, scopeSpans.intoBytes());
209
+ const request = new ProtoWriter();
210
+ request.message(1, resourceSpans.intoBytes());
211
+ return request.intoBytes();
212
+ }
213
+ // ---------------------------------------------------------------------------
214
+ // reading
215
+ // ---------------------------------------------------------------------------
216
+ /**
217
+ * A minimal, bounds-checked protobuf reader - enough to walk the OTLP message
218
+ * tree (the response's `partial_success`, and the test collector's decoding of
219
+ * what this host sent). Every read returns `undefined` past the end instead of
220
+ * throwing, so a malformed body is reported, never fatal.
221
+ */
222
+ export class ProtoReader {
223
+ buf;
224
+ pos = 0;
225
+ view;
226
+ constructor(buf) {
227
+ this.buf = buf;
228
+ this.view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
229
+ }
230
+ hasMore() {
231
+ return this.pos < this.buf.length;
232
+ }
233
+ /** The next field tag as `[fieldNumber, wireType]`. */
234
+ readTag() {
235
+ const tag = this.readVarint();
236
+ if (tag === undefined) {
237
+ return undefined;
238
+ }
239
+ return [Number(tag >> 3n), Number(tag & 0x7n)];
240
+ }
241
+ readVarint() {
242
+ let result = 0n;
243
+ let shift = 0n;
244
+ for (;;) {
245
+ if (this.pos >= this.buf.length) {
246
+ return undefined;
247
+ }
248
+ const byte = this.buf[this.pos++];
249
+ if (shift > 63n) {
250
+ return undefined;
251
+ }
252
+ result |= BigInt(byte & 0x7f) << shift;
253
+ if ((byte & 0x80) === 0) {
254
+ return result;
255
+ }
256
+ shift += 7n;
257
+ }
258
+ }
259
+ readFixed64() {
260
+ if (this.pos + 8 > this.buf.length) {
261
+ return undefined;
262
+ }
263
+ const value = this.view.getBigUint64(this.pos, true);
264
+ this.pos += 8;
265
+ return value;
266
+ }
267
+ readDouble() {
268
+ if (this.pos + 8 > this.buf.length) {
269
+ return undefined;
270
+ }
271
+ const value = this.view.getFloat64(this.pos, true);
272
+ this.pos += 8;
273
+ return value;
274
+ }
275
+ readFixed32() {
276
+ if (this.pos + 4 > this.buf.length) {
277
+ return undefined;
278
+ }
279
+ const value = this.view.getUint32(this.pos, true);
280
+ this.pos += 4;
281
+ return value;
282
+ }
283
+ /** A length-delimited chunk: bytes, a string, or an embedded message. */
284
+ readBytes() {
285
+ const length = this.readVarint();
286
+ if (length === undefined) {
287
+ return undefined;
288
+ }
289
+ const n = Number(length);
290
+ if (this.pos + n > this.buf.length) {
291
+ return undefined;
292
+ }
293
+ const out = this.buf.subarray(this.pos, this.pos + n);
294
+ this.pos += n;
295
+ return out;
296
+ }
297
+ readString() {
298
+ const data = this.readBytes();
299
+ return data === undefined ? undefined : decoder.decode(data);
300
+ }
301
+ /** Advance past a field whose value is not needed, honouring its wire type. */
302
+ skip(wire) {
303
+ switch (wire) {
304
+ case WIRE_VARINT: return this.readVarint() !== undefined;
305
+ case WIRE_FIXED64: return this.readFixed64() !== undefined;
306
+ case WIRE_LEN: return this.readBytes() !== undefined;
307
+ case WIRE_FIXED32: return this.readFixed32() !== undefined;
308
+ default: return false;
309
+ }
310
+ }
311
+ }
312
+ /** The response's partial-success block when it carries content, else undefined. */
313
+ export function partialSuccess(body) {
314
+ const reader = new ProtoReader(body);
315
+ let result;
316
+ while (reader.hasMore()) {
317
+ const tag = reader.readTag();
318
+ if (!tag)
319
+ return undefined;
320
+ const [field, wire] = tag;
321
+ if (field === 1 && wire === WIRE_LEN) {
322
+ const block = reader.readBytes();
323
+ if (!block)
324
+ return undefined;
325
+ const inner = new ProtoReader(block);
326
+ const partial = { rejectedSpans: 0, errorMessage: '' };
327
+ while (inner.hasMore()) {
328
+ const innerTag = inner.readTag();
329
+ if (!innerTag)
330
+ return undefined;
331
+ const [f, w] = innerTag;
332
+ if (f === 1 && w === WIRE_VARINT) {
333
+ const value = inner.readVarint();
334
+ if (value === undefined)
335
+ return undefined;
336
+ partial.rejectedSpans = Number(BigInt.asIntN(64, value));
337
+ }
338
+ else if (f === 2 && w === WIRE_LEN) {
339
+ const text = inner.readString();
340
+ if (text === undefined)
341
+ return undefined;
342
+ partial.errorMessage = text;
343
+ }
344
+ else if (!inner.skip(w)) {
345
+ return undefined;
346
+ }
347
+ }
348
+ result = partial;
349
+ }
350
+ else if (!reader.skip(wire)) {
351
+ return undefined;
352
+ }
353
+ }
354
+ if (result && (result.rejectedSpans !== 0 || result.errorMessage)) {
355
+ return result;
356
+ }
357
+ return undefined;
358
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The telemetry dataset -> OpenTelemetry span mapping (the engines'
3
+ * `TraceMetricsSpanData` / the Rust port's `span.rs`).
4
+ *
5
+ * The host has already produced the W3C-compatible trace id, span id and parent
6
+ * span id during execution, so the span built here carries those EXACT ids - an
7
+ * OpenTelemetry tracer would mint new ones and break the lineage. A dataset whose
8
+ * ids are not W3C-valid (32 / 16 lowercase hex, not all zeros) is skipped rather
9
+ * than exported with forged ids.
10
+ *
11
+ * | Dataset metric | Span |
12
+ * |----------------|------|
13
+ * | `id` | trace id |
14
+ * | `span_id` | span id |
15
+ * | `parent_span_id` | parent span id (a root span when absent) |
16
+ * | `service` (route) | span name (`path`, then `task`, when absent) |
17
+ * | `start` + `exec_time` | start / end timestamps |
18
+ * | `success` / `status` / `exception` | status OK, or ERROR with a description |
19
+ * | `service` = `http.request` (the edge's round-trip record) | kind SERVER (every function execution is INTERNAL) |
20
+ * | `path`, `from`, `origin`, `status`, `exec_time_ms`, `round_trip_ms`, `exception` | attributes |
21
+ * | `service` | the `route` attribute |
22
+ * | `annotations` entries | `annotation.<key>` attributes |
23
+ */
24
+ export declare const KIND_INTERNAL = 1;
25
+ export declare const KIND_SERVER = 2;
26
+ export declare const STATUS_UNSET = 0;
27
+ export declare const STATUS_OK = 1;
28
+ export declare const STATUS_ERROR = 2;
29
+ /** An OTLP AnyValue - the scalar shapes the mapping emits. */
30
+ export type AttributeValue = {
31
+ type: 'string';
32
+ value: string;
33
+ } | {
34
+ type: 'int';
35
+ value: number;
36
+ } | {
37
+ type: 'double';
38
+ value: number;
39
+ };
40
+ /** One completed span, ready for the OTLP encoder. */
41
+ export interface Span {
42
+ traceId: Uint8Array;
43
+ spanId: Uint8Array;
44
+ parentSpanId?: Uint8Array;
45
+ name: string;
46
+ kind: number;
47
+ startUnixNano: bigint;
48
+ endUnixNano: bigint;
49
+ attributes: Array<[string, AttributeValue]>;
50
+ statusCode: number;
51
+ statusMessage: string;
52
+ }
53
+ export declare function hex(bytes: Uint8Array): string;
54
+ /** The value of a span attribute, as text (numbers rendered the way the collector tests read them). */
55
+ export declare function attribute(span: Span, key: string): string | undefined;
56
+ /**
57
+ * Map one telemetry dataset (`{trace: {...}, annotations: {...}}`) to a span -
58
+ * undefined when the dataset has no `trace` block or its trace / span id is
59
+ * not W3C-valid.
60
+ */
61
+ export declare function spanFromDataset(dataset: unknown): Span | undefined;
62
+ /**
63
+ * Parse an ISO-8601 UTC instant (`YYYY-MM-DDTHH:MM:SS[.fraction]Z`, the shape the
64
+ * engines and this host write) to nanoseconds since the Unix epoch - the inverse of
65
+ * the envelope formatter, with no date library (days from civil per Howard Hinnant).
66
+ */
67
+ export declare function parseIso8601Nanos(text: string): bigint | undefined;