@relaybus/otel 0.0.0-stage → 0.3.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Berkcan Şavur
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,76 @@
1
- # Temporary Holding Version
1
+ # @relaybus/otel
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
+ @relaybus/otel is the OpenTelemetry integration of relaybus, a reliable, storage-agnostic event bus for NestJS and Node.js: messaging metrics after the OpenTelemetry semantic conventions, outbox backlog gauges, and one trace from `publish` in one service to the handler in another.
4
+
5
+ ```sh
6
+ pnpm add @relaybus/otel @opentelemetry/api
7
+ ```
8
+
9
+ It depends only on `@opentelemetry/api` (peer, `^1.3.0`); the SDK, exporters and collector are yours.
10
+
11
+ ```ts
12
+ import { relayBusOpenTelemetry } from '@relaybus/otel';
13
+
14
+ const otel = relayBusOpenTelemetry(); // global meter and tracer providers, W3C propagation
15
+
16
+ const bus = new RelayBus({
17
+ store,
18
+ observers: [otel.observer], // metrics
19
+ tracer: otel.tracer, // spans + traceparent/tracestate in event headers
20
+ backlogInterval: '30s', // feeds the backlog gauges
21
+ });
22
+ ```
23
+
24
+ In NestJS pass the same three options to `RelayBusModule.forRoot`.
25
+
26
+ | Option | Default | What it does |
27
+ | ---------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------- |
28
+ | `meterProvider` | `metrics.getMeterProvider()` | Where the metrics are created. |
29
+ | `tracerProvider` | `trace.getTracerProvider()` | Where the spans are created. |
30
+ | `propagator` | W3C Trace Context (built in) | Writes and reads the trace context of events. Pass `propagation` from `@opentelemetry/api` for the SDK's. |
31
+
32
+ ## Traces
33
+
34
+ | Span | Kind | Parent | In which process |
35
+ | ---------------------- | -------- | ---------------------------------------------------- | ------------------------------------ |
36
+ | `publish <event type>` | producer | the caller's active span | the publisher, inside `publish` |
37
+ | `send <destination>` | producer | the publish span (from the stored event), linked | the publisher, when a route sends it |
38
+ | `process <event type>` | consumer | the context the event arrived with (send or publish) | the consumer, around the handler |
39
+
40
+ The publish span's context is stored with the event (`traceparent`, `tracestate` headers), so the
41
+ trace survives the outbox, retries and restarts. A route sends the event with the send span's
42
+ context; the consumer's `process` span continues from it, and whatever the handler does or
43
+ publishes joins that span. Without a tracer the headers are still readable with `traceContextOf`
44
+ from `@relaybus/core`.
45
+
46
+ ## Metrics
47
+
48
+ Durations are in seconds. `error.type` is the relaybus error code (`RELAYBUS_*`) when there is one,
49
+ else the error's class name; it is present only on failures.
50
+
51
+ | Metric | Type | Unit | Attributes |
52
+ | ------------------------------------- | --------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
53
+ | `messaging.client.operation.duration` | histogram | `s` | `messaging.system`, `messaging.operation.name` (`publish`, `claim`, `send`, `receive`), `messaging.operation.type`, `messaging.destination.name`, `error.type` |
54
+ | `messaging.client.sent.messages` | counter | `{message}` | `messaging.system`, `messaging.destination.name`, `messaging.operation.name`, `messaging.operation.type`, `error.type` |
55
+ | `messaging.client.consumed.messages` | counter | `{message}` | as above, plus `relaybus.receive.outcome` (`accepted`, `duplicate`, `ignored`, `invalid`, `error`) |
56
+ | `messaging.process.duration` | histogram | `s` | `messaging.system`, `messaging.destination.name` (event type), `messaging.consumer.group.name` (handler id), `messaging.operation.*`, `error.type` |
57
+ | `relaybus.messages.invalid` | counter | `{message}` | `messaging.system`, `messaging.destination.name`, `error.type` (inbound messages a transport could not decode; they never reach `receive`) |
58
+ | `relaybus.events.published` | counter | `{event}` | `messaging.system`, `messaging.destination.name` (event type), `error.type` |
59
+ | `relaybus.claim.deliveries` | histogram | `{delivery}` | `messaging.system`, `messaging.operation.*` |
60
+ | `relaybus.claim.lanes` | histogram | `{lane}` | `messaging.system`, `messaging.operation.*` |
61
+ | `relaybus.end_to_end.latency` | histogram | `s` | `messaging.system`, `messaging.destination.name`, `messaging.consumer.group.name`, `relaybus.delivery.kind` (`handler`, `route`) |
62
+ | `relaybus.deliveries.retried` | counter | `{delivery}` | `messaging.system`, `messaging.destination.name`, `messaging.consumer.group.name`, `error.type` |
63
+ | `relaybus.deliveries.dead_lettered` | counter | `{delivery}` | as above |
64
+ | `relaybus.lease.lost` | counter | `{delivery}` | `messaging.system`, `messaging.destination.name`, `messaging.consumer.group.name` |
65
+ | `relaybus.deliveries.pending` | gauge | `{delivery}` | none (needs `backlogInterval`) |
66
+ | `relaybus.deliveries.oldest_age` | gauge | `s` | none (needs `backlogInterval`) |
67
+ | `relaybus.failures` | counter | `{failure}` | `relaybus.failure.stage` (`claim`, `complete`, `purge`, `backlog`, ...), `error.type` |
68
+
69
+ `messaging.system` is `relaybus` for work on the outbox (publish, claim, process) and the
70
+ transport's system (`kafka`, `rabbitmq`) for sends and receives. The full guide, with a collector,
71
+ Prometheus and alert rules, is the [Observability](https://berkcansavur.github.io/relaybus/concepts/observability)
72
+ page.
73
+
74
+ ## License
75
+
76
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,477 @@
1
+ 'use strict';
2
+
3
+ var api = require('@opentelemetry/api');
4
+ var core = require('@relaybus/core');
5
+
6
+ // src/relay-bus-open-telemetry.ts
7
+
8
+ // src/semantic-conventions.ts
9
+ var INSTRUMENTATION_SCOPE = "@relaybus/otel";
10
+ var RELAYBUS_SYSTEM = "relaybus";
11
+ var ATTRIBUTES = {
12
+ system: "messaging.system",
13
+ destination: "messaging.destination.name",
14
+ operationName: "messaging.operation.name",
15
+ operationType: "messaging.operation.type",
16
+ consumerGroup: "messaging.consumer.group.name",
17
+ messageId: "messaging.message.id",
18
+ conversationId: "messaging.message.conversation_id",
19
+ batchCount: "messaging.batch.message_count",
20
+ errorType: "error.type",
21
+ eventType: "relaybus.event.type",
22
+ deliveryKind: "relaybus.delivery.kind",
23
+ deliveryAttempt: "relaybus.delivery.attempt",
24
+ receiveOutcome: "relaybus.receive.outcome",
25
+ failureStage: "relaybus.failure.stage"
26
+ };
27
+ var METRICS = {
28
+ operationDuration: "messaging.client.operation.duration",
29
+ sentMessages: "messaging.client.sent.messages",
30
+ consumedMessages: "messaging.client.consumed.messages",
31
+ processDuration: "messaging.process.duration",
32
+ invalidMessages: "relaybus.messages.invalid",
33
+ published: "relaybus.events.published",
34
+ claimedDeliveries: "relaybus.claim.deliveries",
35
+ claimedLanes: "relaybus.claim.lanes",
36
+ pending: "relaybus.deliveries.pending",
37
+ oldestAge: "relaybus.deliveries.oldest_age",
38
+ deadLettered: "relaybus.deliveries.dead_lettered",
39
+ retried: "relaybus.deliveries.retried",
40
+ leaseLost: "relaybus.lease.lost",
41
+ endToEndLatency: "relaybus.end_to_end.latency",
42
+ failures: "relaybus.failures"
43
+ };
44
+ function errorType(error) {
45
+ return error.code ?? error.name;
46
+ }
47
+
48
+ // src/relay-bus-metrics-observer.ts
49
+ var MILLISECONDS_PER_SECOND = 1e3;
50
+ var DURATION_BUCKETS = [
51
+ 5e-3,
52
+ 0.01,
53
+ 0.025,
54
+ 0.05,
55
+ 0.075,
56
+ 0.1,
57
+ 0.25,
58
+ 0.5,
59
+ 0.75,
60
+ 1,
61
+ 2.5,
62
+ 5,
63
+ 7.5,
64
+ 10
65
+ ];
66
+ var LATENCY_BUCKETS = [
67
+ 5e-3,
68
+ 0.01,
69
+ 0.025,
70
+ 0.05,
71
+ 0.1,
72
+ 0.25,
73
+ 0.5,
74
+ 1,
75
+ 2.5,
76
+ 5,
77
+ 10,
78
+ 30,
79
+ 60,
80
+ 300,
81
+ 900,
82
+ 3600
83
+ ];
84
+ var COUNT_BUCKETS = [1, 2, 5, 10, 20, 50, 100, 200, 500, 1e3];
85
+ function seconds(milliseconds) {
86
+ return milliseconds / MILLISECONDS_PER_SECOND;
87
+ }
88
+ function withError(attributes, error) {
89
+ return error === void 0 ? attributes : { ...attributes, [ATTRIBUTES.errorType]: errorType(error) };
90
+ }
91
+ function deliveryAttributes(delivery) {
92
+ return {
93
+ [ATTRIBUTES.system]: RELAYBUS_SYSTEM,
94
+ [ATTRIBUTES.destination]: delivery.event.type,
95
+ [ATTRIBUTES.consumerGroup]: delivery.handler
96
+ };
97
+ }
98
+ function createHistograms(meter) {
99
+ const histogram = (name, options) => meter.createHistogram(name, {
100
+ unit: options.unit,
101
+ description: options.description,
102
+ advice: { explicitBucketBoundaries: options.buckets }
103
+ });
104
+ return {
105
+ operationDuration: histogram(METRICS.operationDuration, {
106
+ unit: "s",
107
+ description: "Duration of publish, claim, send and receive operations.",
108
+ buckets: DURATION_BUCKETS
109
+ }),
110
+ processDuration: histogram(METRICS.processDuration, {
111
+ unit: "s",
112
+ description: "Duration of local handler runs.",
113
+ buckets: DURATION_BUCKETS
114
+ }),
115
+ claimedDeliveries: histogram(METRICS.claimedDeliveries, {
116
+ unit: "{delivery}",
117
+ description: "Deliveries one claim query returned.",
118
+ buckets: COUNT_BUCKETS
119
+ }),
120
+ claimedLanes: histogram(METRICS.claimedLanes, {
121
+ unit: "{lane}",
122
+ description: "Distinct leases (lanes) among the deliveries of one claim.",
123
+ buckets: COUNT_BUCKETS
124
+ }),
125
+ endToEndLatency: histogram(METRICS.endToEndLatency, {
126
+ unit: "s",
127
+ description: "Time from an event occurring to a handler or route starting on it.",
128
+ buckets: LATENCY_BUCKETS
129
+ })
130
+ };
131
+ }
132
+ function createInstruments(meter) {
133
+ const counter = (name, [unit, description]) => meter.createCounter(name, { unit, description });
134
+ return {
135
+ ...createHistograms(meter),
136
+ sentMessages: counter(METRICS.sentMessages, [
137
+ "{message}",
138
+ "Events a route attempted to send to a broker."
139
+ ]),
140
+ consumedMessages: counter(METRICS.consumedMessages, [
141
+ "{message}",
142
+ "Events received from a broker, by what receive did with them."
143
+ ]),
144
+ invalidMessages: counter(METRICS.invalidMessages, [
145
+ "{message}",
146
+ "Inbound messages a transport could not decode, so they never reached the bus."
147
+ ]),
148
+ published: counter(METRICS.published, ["{event}", "Events passed to publish."]),
149
+ deadLettered: counter(METRICS.deadLettered, ["{delivery}", "Deliveries moved to dead."]),
150
+ retried: counter(METRICS.retried, ["{delivery}", "Failed attempts rescheduled for a retry."]),
151
+ leaseLost: counter(METRICS.leaseLost, [
152
+ "{delivery}",
153
+ "Deliveries whose lease was lost while they ran."
154
+ ]),
155
+ failures: counter(METRICS.failures, [
156
+ "{failure}",
157
+ "Errors relaybus caught and reported, by stage."
158
+ ])
159
+ };
160
+ }
161
+ var RelayBusMetricsObserver = class {
162
+ #instruments;
163
+ #backlog;
164
+ constructor(meter) {
165
+ this.#instruments = createInstruments(meter);
166
+ meter.createObservableGauge(METRICS.pending, {
167
+ unit: "{delivery}",
168
+ description: "Deliveries waiting to run, at the last backlog reading."
169
+ }).addCallback((result) => {
170
+ if (this.#backlog !== void 0) {
171
+ result.observe(this.#backlog.pending);
172
+ }
173
+ });
174
+ meter.createObservableGauge(METRICS.oldestAge, {
175
+ unit: "s",
176
+ description: "How long the oldest due delivery had waited, at the last backlog reading."
177
+ }).addCallback((result) => {
178
+ if (this.#backlog !== void 0) {
179
+ result.observe(seconds(this.#backlog.oldestPendingAgeMs));
180
+ }
181
+ });
182
+ }
183
+ onPublishFinished(event) {
184
+ const types = new Set(event.eventTypes);
185
+ const base = {
186
+ [ATTRIBUTES.system]: RELAYBUS_SYSTEM,
187
+ [ATTRIBUTES.operationName]: "publish",
188
+ [ATTRIBUTES.operationType]: "create"
189
+ };
190
+ const [onlyType] = types;
191
+ this.#instruments.operationDuration.record(
192
+ seconds(event.durationMs),
193
+ withError(
194
+ types.size === 1 ? { ...base, [ATTRIBUTES.destination]: onlyType } : base,
195
+ event.error
196
+ )
197
+ );
198
+ for (const type of event.eventTypes) {
199
+ this.#instruments.published.add(
200
+ 1,
201
+ withError(
202
+ { [ATTRIBUTES.system]: RELAYBUS_SYSTEM, [ATTRIBUTES.destination]: type },
203
+ event.error
204
+ )
205
+ );
206
+ }
207
+ }
208
+ onClaimFinished(event) {
209
+ const attributes = {
210
+ [ATTRIBUTES.system]: RELAYBUS_SYSTEM,
211
+ [ATTRIBUTES.operationName]: "claim",
212
+ [ATTRIBUTES.operationType]: "receive"
213
+ };
214
+ this.#instruments.operationDuration.record(
215
+ seconds(event.durationMs),
216
+ withError(attributes, event.error)
217
+ );
218
+ if (event.outcome === "success") {
219
+ this.#instruments.claimedDeliveries.record(event.claimed, attributes);
220
+ this.#instruments.claimedLanes.record(event.lanes, attributes);
221
+ }
222
+ }
223
+ onHandlerStarted(event) {
224
+ this.#instruments.endToEndLatency.record(seconds(event.endToEndLatencyMs), {
225
+ ...deliveryAttributes(event.delivery),
226
+ [ATTRIBUTES.deliveryKind]: event.kind
227
+ });
228
+ }
229
+ onHandlerFinished(event) {
230
+ if (event.kind === "handler") {
231
+ this.#instruments.processDuration.record(
232
+ seconds(event.durationMs),
233
+ withError(
234
+ {
235
+ ...deliveryAttributes(event.delivery),
236
+ [ATTRIBUTES.operationName]: "process",
237
+ [ATTRIBUTES.operationType]: "process"
238
+ },
239
+ event.error
240
+ )
241
+ );
242
+ }
243
+ }
244
+ onTransportSent(event) {
245
+ const attributes = withError(
246
+ {
247
+ [ATTRIBUTES.system]: event.system,
248
+ [ATTRIBUTES.destination]: event.destination,
249
+ [ATTRIBUTES.operationName]: "send",
250
+ [ATTRIBUTES.operationType]: "send"
251
+ },
252
+ event.error
253
+ );
254
+ this.#instruments.sentMessages.add(1, attributes);
255
+ this.#instruments.operationDuration.record(seconds(event.durationMs), attributes);
256
+ }
257
+ onReceiveFinished(event) {
258
+ const attributes = withError(
259
+ {
260
+ [ATTRIBUTES.system]: event.system ?? RELAYBUS_SYSTEM,
261
+ [ATTRIBUTES.operationName]: "receive",
262
+ [ATTRIBUTES.operationType]: "receive",
263
+ ...event.destination === void 0 ? {} : { [ATTRIBUTES.destination]: event.destination }
264
+ },
265
+ event.error
266
+ );
267
+ this.#instruments.consumedMessages.add(1, {
268
+ ...attributes,
269
+ [ATTRIBUTES.receiveOutcome]: event.outcome
270
+ });
271
+ this.#instruments.operationDuration.record(seconds(event.durationMs), attributes);
272
+ }
273
+ onInvalidMessage(event) {
274
+ this.#instruments.invalidMessages.add(
275
+ 1,
276
+ withError(
277
+ { [ATTRIBUTES.system]: event.system, [ATTRIBUTES.destination]: event.destination },
278
+ event.error
279
+ )
280
+ );
281
+ }
282
+ onDeliveryRetryScheduled(event) {
283
+ this.#instruments.retried.add(1, withError(deliveryAttributes(event.delivery), event.error));
284
+ }
285
+ onDeliveryDead(event) {
286
+ this.#instruments.deadLettered.add(
287
+ 1,
288
+ withError(deliveryAttributes(event.delivery), event.error)
289
+ );
290
+ }
291
+ onLeaseLost(event) {
292
+ this.#instruments.leaseLost.add(1, deliveryAttributes(event.delivery));
293
+ }
294
+ onBacklog(event) {
295
+ this.#backlog = event;
296
+ }
297
+ onFailure(event) {
298
+ this.#instruments.failures.add(
299
+ 1,
300
+ withError({ [ATTRIBUTES.failureStage]: event.stage }, event.error)
301
+ );
302
+ }
303
+ };
304
+ async function endWhenSettled(span, work) {
305
+ try {
306
+ return await work;
307
+ } catch (error) {
308
+ const info = core.toErrorInfo(error);
309
+ span.recordException(error instanceof Error ? error : info.message);
310
+ span.setAttribute(ATTRIBUTES.errorType, errorType(info));
311
+ span.setStatus({ code: api.SpanStatusCode.ERROR, message: info.message });
312
+ throw error;
313
+ } finally {
314
+ span.end();
315
+ }
316
+ }
317
+ var RelayBusOpenTelemetryTracer = class {
318
+ #tracer;
319
+ #propagator;
320
+ constructor({ tracer, propagator }) {
321
+ this.#tracer = tracer;
322
+ this.#propagator = propagator;
323
+ }
324
+ /** A producer span, child of the caller's active span; its context goes into every event. */
325
+ publish(input, run) {
326
+ const types = [...new Set(input.eventTypes)];
327
+ const attributes = {
328
+ [ATTRIBUTES.system]: RELAYBUS_SYSTEM,
329
+ [ATTRIBUTES.operationName]: "publish",
330
+ [ATTRIBUTES.operationType]: "create",
331
+ ...types.length === 1 ? { [ATTRIBUTES.destination]: types[0] } : {},
332
+ ...input.eventTypes.length > 1 ? { [ATTRIBUTES.batchCount]: input.eventTypes.length } : {}
333
+ };
334
+ const name = types.length === 1 ? `publish ${types[0]}` : "publish";
335
+ return this.#runInSpan(
336
+ { name, kind: api.SpanKind.PRODUCER, attributes, parent: api.context.active() },
337
+ run
338
+ );
339
+ }
340
+ /**
341
+ * A producer span, child of the publish span whose context the stored event carries (and linked
342
+ * to it); the event goes to the broker with this span's context.
343
+ */
344
+ send(input, run) {
345
+ const { envelope } = input;
346
+ const parent = this.#extract(envelope.headers);
347
+ const published = api.trace.getSpanContext(parent);
348
+ const attributes = {
349
+ [ATTRIBUTES.system]: input.system,
350
+ [ATTRIBUTES.destination]: input.destination,
351
+ [ATTRIBUTES.operationName]: "send",
352
+ [ATTRIBUTES.operationType]: "send",
353
+ [ATTRIBUTES.messageId]: envelope.id,
354
+ [ATTRIBUTES.eventType]: envelope.type,
355
+ ...envelope.correlationId === null ? {} : { [ATTRIBUTES.conversationId]: envelope.correlationId }
356
+ };
357
+ return this.#runInSpan(
358
+ {
359
+ name: `send ${input.destination}`,
360
+ kind: api.SpanKind.PRODUCER,
361
+ attributes,
362
+ parent,
363
+ ...published === void 0 ? {} : { links: [{ context: published }] }
364
+ },
365
+ run
366
+ );
367
+ }
368
+ /** A consumer span, child of the trace context the event arrived with, active while the handler runs. */
369
+ process(input, run) {
370
+ const { delivery } = input;
371
+ const parent = this.#extract(delivery.event.headers);
372
+ const span = this.#tracer.startSpan(
373
+ `process ${delivery.event.type}`,
374
+ {
375
+ kind: api.SpanKind.CONSUMER,
376
+ attributes: {
377
+ [ATTRIBUTES.system]: RELAYBUS_SYSTEM,
378
+ [ATTRIBUTES.destination]: delivery.event.type,
379
+ [ATTRIBUTES.operationName]: "process",
380
+ [ATTRIBUTES.operationType]: "process",
381
+ [ATTRIBUTES.consumerGroup]: delivery.handler,
382
+ [ATTRIBUTES.messageId]: delivery.event.id,
383
+ [ATTRIBUTES.deliveryAttempt]: delivery.attempt
384
+ }
385
+ },
386
+ parent
387
+ );
388
+ return endWhenSettled(span, api.context.with(api.trace.setSpan(parent, span), run));
389
+ }
390
+ #extract(headers) {
391
+ return this.#propagator.extract(api.context.active(), headers, api.defaultTextMapGetter);
392
+ }
393
+ #runInSpan(span, run) {
394
+ const started = this.#tracer.startSpan(
395
+ span.name,
396
+ {
397
+ kind: span.kind,
398
+ attributes: span.attributes,
399
+ ...span.links === void 0 ? {} : { links: span.links }
400
+ },
401
+ span.parent
402
+ );
403
+ const active = api.trace.setSpan(span.parent, started);
404
+ const headers = {};
405
+ this.#propagator.inject(active, headers, api.defaultTextMapSetter);
406
+ return endWhenSettled(
407
+ started,
408
+ api.context.with(active, () => run(headers))
409
+ );
410
+ }
411
+ };
412
+ var HEX = 16;
413
+ var FLAGS_DIGITS = 2;
414
+ var DEFINED_FLAGS = 255;
415
+ function firstValue(value) {
416
+ return Array.isArray(value) ? value[0] : value;
417
+ }
418
+ var W3CTraceContextPropagator = class {
419
+ // eslint-disable-next-line relaybus/max-two-params -- TextMapPropagator.inject signature
420
+ inject(context2, carrier, setter) {
421
+ const spanContext = api.trace.getSpanContext(context2);
422
+ if (spanContext === void 0 || !api.isSpanContextValid(spanContext)) {
423
+ return;
424
+ }
425
+ const flags = (spanContext.traceFlags & DEFINED_FLAGS).toString(HEX).padStart(FLAGS_DIGITS, "0");
426
+ setter.set(
427
+ carrier,
428
+ core.TRACEPARENT_HEADER,
429
+ `00-${spanContext.traceId}-${spanContext.spanId}-${flags}`
430
+ );
431
+ const traceState = spanContext.traceState?.serialize();
432
+ if (traceState !== void 0 && traceState.length > 0) {
433
+ setter.set(carrier, core.TRACESTATE_HEADER, traceState);
434
+ }
435
+ }
436
+ // eslint-disable-next-line relaybus/max-two-params -- TextMapPropagator.extract signature
437
+ extract(context2, carrier, getter) {
438
+ const traceparent = firstValue(getter.get(carrier, core.TRACEPARENT_HEADER));
439
+ const tracestate = firstValue(getter.get(carrier, core.TRACESTATE_HEADER));
440
+ const traceContext = traceparent === void 0 ? void 0 : core.traceContextOf(tracestate === void 0 ? { traceparent } : { traceparent, tracestate });
441
+ if (traceContext === void 0) {
442
+ return context2;
443
+ }
444
+ const [, traceId, spanId, flags] = traceContext.traceparent.split("-");
445
+ const spanContext = {
446
+ traceId,
447
+ spanId,
448
+ traceFlags: Number.parseInt(flags, HEX),
449
+ isRemote: true,
450
+ ...traceContext.tracestate === void 0 ? {} : { traceState: api.createTraceState(traceContext.tracestate) }
451
+ };
452
+ return api.trace.setSpanContext(context2, spanContext);
453
+ }
454
+ fields() {
455
+ return [core.TRACEPARENT_HEADER, core.TRACESTATE_HEADER];
456
+ }
457
+ };
458
+
459
+ // src/relay-bus-open-telemetry.ts
460
+ function relayBusOpenTelemetry(options = {}) {
461
+ const meterProvider = options.meterProvider ?? api.metrics.getMeterProvider();
462
+ const tracerProvider = options.tracerProvider ?? api.trace.getTracerProvider();
463
+ return {
464
+ observer: new RelayBusMetricsObserver(meterProvider.getMeter(INSTRUMENTATION_SCOPE)),
465
+ tracer: new RelayBusOpenTelemetryTracer({
466
+ tracer: tracerProvider.getTracer(INSTRUMENTATION_SCOPE),
467
+ propagator: options.propagator ?? new W3CTraceContextPropagator()
468
+ })
469
+ };
470
+ }
471
+
472
+ exports.ATTRIBUTES = ATTRIBUTES;
473
+ exports.METRICS = METRICS;
474
+ exports.W3CTraceContextPropagator = W3CTraceContextPropagator;
475
+ exports.relayBusOpenTelemetry = relayBusOpenTelemetry;
476
+ //# sourceMappingURL=index.cjs.map
477
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/semantic-conventions.ts","../src/relay-bus-metrics-observer.ts","../src/relay-bus-open-telemetry-tracer.ts","../src/w3c-trace-context-propagator.ts","../src/relay-bus-open-telemetry.ts"],"names":["toErrorInfo","SpanStatusCode","SpanKind","context","trace","defaultTextMapGetter","defaultTextMapSetter","isSpanContextValid","TRACEPARENT_HEADER","TRACESTATE_HEADER","traceContextOf","createTraceState","metrics"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ErrorInfo } from '@relaybus/core';\n\n/** Instrumentation scope of every meter and tracer of this package. */\nexport const INSTRUMENTATION_SCOPE = '@relaybus/otel';\n\n/** `messaging.system` of operations on the outbox itself (publish, claim, process). */\nexport const RELAYBUS_SYSTEM = 'relaybus';\n\n/** OpenTelemetry semantic convention attributes for messaging, and relaybus's own. */\nexport const ATTRIBUTES = {\n system: 'messaging.system',\n destination: 'messaging.destination.name',\n operationName: 'messaging.operation.name',\n operationType: 'messaging.operation.type',\n consumerGroup: 'messaging.consumer.group.name',\n messageId: 'messaging.message.id',\n conversationId: 'messaging.message.conversation_id',\n batchCount: 'messaging.batch.message_count',\n errorType: 'error.type',\n eventType: 'relaybus.event.type',\n deliveryKind: 'relaybus.delivery.kind',\n deliveryAttempt: 'relaybus.delivery.attempt',\n receiveOutcome: 'relaybus.receive.outcome',\n failureStage: 'relaybus.failure.stage',\n} as const;\n\n/** Metric names: the messaging semantic conventions where they exist, else `relaybus.*`. */\nexport const METRICS = {\n operationDuration: 'messaging.client.operation.duration',\n sentMessages: 'messaging.client.sent.messages',\n consumedMessages: 'messaging.client.consumed.messages',\n processDuration: 'messaging.process.duration',\n invalidMessages: 'relaybus.messages.invalid',\n published: 'relaybus.events.published',\n claimedDeliveries: 'relaybus.claim.deliveries',\n claimedLanes: 'relaybus.claim.lanes',\n pending: 'relaybus.deliveries.pending',\n oldestAge: 'relaybus.deliveries.oldest_age',\n deadLettered: 'relaybus.deliveries.dead_lettered',\n retried: 'relaybus.deliveries.retried',\n leaseLost: 'relaybus.lease.lost',\n endToEndLatency: 'relaybus.end_to_end.latency',\n failures: 'relaybus.failures',\n} as const;\n\n/** `error.type`: the relaybus error code when there is one, else the error's class name. */\nexport function errorType(error: ErrorInfo): string {\n return error.code ?? error.name;\n}\n","import type { Attributes, Counter, Histogram, Meter } from '@opentelemetry/api';\nimport type {\n BacklogEvent,\n ClaimFinishedEvent,\n ClaimedDelivery,\n ErrorInfo,\n FailureStage,\n HandlerFinishedEvent,\n HandlerStartedEvent,\n InvalidMessageEvent,\n PublishFinishedEvent,\n ReceiveFinishedEvent,\n RelayBusObserver,\n TransportSentEvent,\n} from '@relaybus/core';\nimport { ATTRIBUTES, METRICS, RELAYBUS_SYSTEM, errorType } from './semantic-conventions.js';\n\nconst MILLISECONDS_PER_SECOND = 1_000;\n\n/* eslint-disable no-magic-numbers -- histogram bucket boundaries */\n/** Bucket boundaries in seconds the messaging semantic conventions advise for durations. */\nconst 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\n/** End-to-end latency spans retries and delays: buckets up to an hour. */\nconst LATENCY_BUCKETS = [\n 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60, 300, 900, 3_600,\n];\n\nconst COUNT_BUCKETS = [1, 2, 5, 10, 20, 50, 100, 200, 500, 1_000];\n/* eslint-enable no-magic-numbers */\n\n/** The metric instruments, created once per meter. */\ntype Instruments = {\n readonly operationDuration: Histogram;\n readonly sentMessages: Counter;\n readonly consumedMessages: Counter;\n readonly invalidMessages: Counter;\n readonly processDuration: Histogram;\n readonly published: Counter;\n readonly claimedDeliveries: Histogram;\n readonly claimedLanes: Histogram;\n readonly deadLettered: Counter;\n readonly retried: Counter;\n readonly leaseLost: Counter;\n readonly endToEndLatency: Histogram;\n readonly failures: Counter;\n};\n\nfunction seconds(milliseconds: number): number {\n return milliseconds / MILLISECONDS_PER_SECOND;\n}\n\nfunction withError(attributes: Attributes, error: ErrorInfo | undefined): Attributes {\n return error === undefined\n ? attributes\n : { ...attributes, [ATTRIBUTES.errorType]: errorType(error) };\n}\n\n/** Attributes of a delivery: the handler is the consumer group, the event type the destination. */\nfunction deliveryAttributes(delivery: ClaimedDelivery): Attributes {\n return {\n [ATTRIBUTES.system]: RELAYBUS_SYSTEM,\n [ATTRIBUTES.destination]: delivery.event.type,\n [ATTRIBUTES.consumerGroup]: delivery.handler,\n };\n}\n\ntype HistogramOptions = { unit: string; description: string; buckets: number[] };\n\nfunction createHistograms(\n meter: Meter,\n): Pick<\n Instruments,\n 'operationDuration' | 'processDuration' | 'claimedDeliveries' | 'claimedLanes' | 'endToEndLatency'\n> {\n const histogram = (name: string, options: HistogramOptions): Histogram =>\n meter.createHistogram(name, {\n unit: options.unit,\n description: options.description,\n advice: { explicitBucketBoundaries: options.buckets },\n });\n return {\n operationDuration: histogram(METRICS.operationDuration, {\n unit: 's',\n description: 'Duration of publish, claim, send and receive operations.',\n buckets: DURATION_BUCKETS,\n }),\n processDuration: histogram(METRICS.processDuration, {\n unit: 's',\n description: 'Duration of local handler runs.',\n buckets: DURATION_BUCKETS,\n }),\n claimedDeliveries: histogram(METRICS.claimedDeliveries, {\n unit: '{delivery}',\n description: 'Deliveries one claim query returned.',\n buckets: COUNT_BUCKETS,\n }),\n claimedLanes: histogram(METRICS.claimedLanes, {\n unit: '{lane}',\n description: 'Distinct leases (lanes) among the deliveries of one claim.',\n buckets: COUNT_BUCKETS,\n }),\n endToEndLatency: histogram(METRICS.endToEndLatency, {\n unit: 's',\n description: 'Time from an event occurring to a handler or route starting on it.',\n buckets: LATENCY_BUCKETS,\n }),\n };\n}\n\nfunction createInstruments(meter: Meter): Instruments {\n const counter = (name: string, [unit, description]: [string, string]): Counter =>\n meter.createCounter(name, { unit, description });\n return {\n ...createHistograms(meter),\n sentMessages: counter(METRICS.sentMessages, [\n '{message}',\n 'Events a route attempted to send to a broker.',\n ]),\n consumedMessages: counter(METRICS.consumedMessages, [\n '{message}',\n 'Events received from a broker, by what receive did with them.',\n ]),\n invalidMessages: counter(METRICS.invalidMessages, [\n '{message}',\n 'Inbound messages a transport could not decode, so they never reached the bus.',\n ]),\n published: counter(METRICS.published, ['{event}', 'Events passed to publish.']),\n deadLettered: counter(METRICS.deadLettered, ['{delivery}', 'Deliveries moved to dead.']),\n retried: counter(METRICS.retried, ['{delivery}', 'Failed attempts rescheduled for a retry.']),\n leaseLost: counter(METRICS.leaseLost, [\n '{delivery}',\n 'Deliveries whose lease was lost while they ran.',\n ]),\n failures: counter(METRICS.failures, [\n '{failure}',\n 'Errors relaybus caught and reported, by stage.',\n ]),\n };\n}\n\n/**\n * Turns relaybus observer hooks into OpenTelemetry metrics. The backlog gauges report the last\n * reading of `onBacklog`, so they need `backlogInterval` on the bus.\n */\nexport class RelayBusMetricsObserver implements RelayBusObserver {\n readonly #instruments: Instruments;\n #backlog: BacklogEvent | undefined;\n\n constructor(meter: Meter) {\n this.#instruments = createInstruments(meter);\n meter\n .createObservableGauge(METRICS.pending, {\n unit: '{delivery}',\n description: 'Deliveries waiting to run, at the last backlog reading.',\n })\n .addCallback((result) => {\n if (this.#backlog !== undefined) {\n result.observe(this.#backlog.pending);\n }\n });\n meter\n .createObservableGauge(METRICS.oldestAge, {\n unit: 's',\n description: 'How long the oldest due delivery had waited, at the last backlog reading.',\n })\n .addCallback((result) => {\n if (this.#backlog !== undefined) {\n result.observe(seconds(this.#backlog.oldestPendingAgeMs));\n }\n });\n }\n\n onPublishFinished(event: PublishFinishedEvent): void {\n const types = new Set(event.eventTypes);\n const base: Attributes = {\n [ATTRIBUTES.system]: RELAYBUS_SYSTEM,\n [ATTRIBUTES.operationName]: 'publish',\n [ATTRIBUTES.operationType]: 'create',\n };\n const [onlyType] = types;\n this.#instruments.operationDuration.record(\n seconds(event.durationMs),\n withError(\n types.size === 1 ? { ...base, [ATTRIBUTES.destination]: onlyType } : base,\n event.error,\n ),\n );\n for (const type of event.eventTypes) {\n this.#instruments.published.add(\n 1,\n withError(\n { [ATTRIBUTES.system]: RELAYBUS_SYSTEM, [ATTRIBUTES.destination]: type },\n event.error,\n ),\n );\n }\n }\n\n onClaimFinished(event: ClaimFinishedEvent): void {\n const attributes: Attributes = {\n [ATTRIBUTES.system]: RELAYBUS_SYSTEM,\n [ATTRIBUTES.operationName]: 'claim',\n [ATTRIBUTES.operationType]: 'receive',\n };\n this.#instruments.operationDuration.record(\n seconds(event.durationMs),\n withError(attributes, event.error),\n );\n if (event.outcome === 'success') {\n this.#instruments.claimedDeliveries.record(event.claimed, attributes);\n this.#instruments.claimedLanes.record(event.lanes, attributes);\n }\n }\n\n onHandlerStarted(event: HandlerStartedEvent): void {\n this.#instruments.endToEndLatency.record(seconds(event.endToEndLatencyMs), {\n ...deliveryAttributes(event.delivery),\n [ATTRIBUTES.deliveryKind]: event.kind,\n });\n }\n\n onHandlerFinished(event: HandlerFinishedEvent): void {\n // A route's run is its send, measured by onTransportSent.\n if (event.kind === 'handler') {\n this.#instruments.processDuration.record(\n seconds(event.durationMs),\n withError(\n {\n ...deliveryAttributes(event.delivery),\n [ATTRIBUTES.operationName]: 'process',\n [ATTRIBUTES.operationType]: 'process',\n },\n event.error,\n ),\n );\n }\n }\n\n onTransportSent(event: TransportSentEvent): void {\n const attributes = withError(\n {\n [ATTRIBUTES.system]: event.system,\n [ATTRIBUTES.destination]: event.destination,\n [ATTRIBUTES.operationName]: 'send',\n [ATTRIBUTES.operationType]: 'send',\n },\n event.error,\n );\n this.#instruments.sentMessages.add(1, attributes);\n this.#instruments.operationDuration.record(seconds(event.durationMs), attributes);\n }\n\n onReceiveFinished(event: ReceiveFinishedEvent): void {\n const attributes: Attributes = withError(\n {\n [ATTRIBUTES.system]: event.system ?? RELAYBUS_SYSTEM,\n [ATTRIBUTES.operationName]: 'receive',\n [ATTRIBUTES.operationType]: 'receive',\n ...(event.destination === undefined ? {} : { [ATTRIBUTES.destination]: event.destination }),\n },\n event.error,\n );\n this.#instruments.consumedMessages.add(1, {\n ...attributes,\n [ATTRIBUTES.receiveOutcome]: event.outcome,\n });\n this.#instruments.operationDuration.record(seconds(event.durationMs), attributes);\n }\n\n onInvalidMessage(event: InvalidMessageEvent): void {\n this.#instruments.invalidMessages.add(\n 1,\n withError(\n { [ATTRIBUTES.system]: event.system, [ATTRIBUTES.destination]: event.destination },\n event.error,\n ),\n );\n }\n\n onDeliveryRetryScheduled(event: {\n readonly delivery: ClaimedDelivery;\n readonly error: ErrorInfo;\n }): void {\n this.#instruments.retried.add(1, withError(deliveryAttributes(event.delivery), event.error));\n }\n\n onDeliveryDead(event: { readonly delivery: ClaimedDelivery; readonly error: ErrorInfo }): void {\n this.#instruments.deadLettered.add(\n 1,\n withError(deliveryAttributes(event.delivery), event.error),\n );\n }\n\n onLeaseLost(event: { readonly delivery: ClaimedDelivery }): void {\n this.#instruments.leaseLost.add(1, deliveryAttributes(event.delivery));\n }\n\n onBacklog(event: BacklogEvent): void {\n this.#backlog = event;\n }\n\n onFailure(event: { readonly stage: FailureStage; readonly error: ErrorInfo }): void {\n this.#instruments.failures.add(\n 1,\n withError({ [ATTRIBUTES.failureStage]: event.stage }, event.error),\n );\n }\n}\n","import {\n SpanKind,\n SpanStatusCode,\n context,\n defaultTextMapGetter,\n defaultTextMapSetter,\n trace,\n type Attributes,\n type Context,\n type Link,\n type Span,\n type TextMapPropagator,\n type Tracer,\n} from '@opentelemetry/api';\nimport {\n toErrorInfo,\n type EventHeaders,\n type ProcessTraceInput,\n type PublishTraceInput,\n type RelayBusTracer,\n type SendTraceInput,\n} from '@relaybus/core';\nimport { ATTRIBUTES, RELAYBUS_SYSTEM, errorType } from './semantic-conventions.js';\n\ntype TracerInput = {\n readonly tracer: Tracer;\n readonly propagator: TextMapPropagator;\n};\n\n/** Ends `span` when `work` settles, marking it failed with the error's type when it rejects. */\nasync function endWhenSettled<T>(span: Span, work: Promise<T>): Promise<T> {\n try {\n return await work;\n } catch (error) {\n const info = toErrorInfo(error);\n span.recordException(error instanceof Error ? error : info.message);\n span.setAttribute(ATTRIBUTES.errorType, errorType(info));\n span.setStatus({ code: SpanStatusCode.ERROR, message: info.message });\n throw error;\n } finally {\n span.end();\n }\n}\n\n/**\n * relaybus tracing hooks on an OpenTelemetry tracer: `publish <type>` and `send <destination>`\n * producer spans, `process <type>` consumer spans, and the trace context in event headers.\n */\nexport class RelayBusOpenTelemetryTracer implements RelayBusTracer {\n readonly #tracer: Tracer;\n readonly #propagator: TextMapPropagator;\n\n constructor({ tracer, propagator }: TracerInput) {\n this.#tracer = tracer;\n this.#propagator = propagator;\n }\n\n /** A producer span, child of the caller's active span; its context goes into every event. */\n publish<T>(input: PublishTraceInput, run: (headers: EventHeaders) => Promise<T>): Promise<T> {\n const types = [...new Set(input.eventTypes)];\n const attributes: Attributes = {\n [ATTRIBUTES.system]: RELAYBUS_SYSTEM,\n [ATTRIBUTES.operationName]: 'publish',\n [ATTRIBUTES.operationType]: 'create',\n ...(types.length === 1 ? { [ATTRIBUTES.destination]: types[0] } : {}),\n ...(input.eventTypes.length > 1 ? { [ATTRIBUTES.batchCount]: input.eventTypes.length } : {}),\n };\n const name = types.length === 1 ? `publish ${types[0]}` : 'publish';\n return this.#runInSpan(\n { name, kind: SpanKind.PRODUCER, attributes, parent: context.active() },\n run,\n );\n }\n\n /**\n * A producer span, child of the publish span whose context the stored event carries (and linked\n * to it); the event goes to the broker with this span's context.\n */\n send<T>(input: SendTraceInput, run: (headers: EventHeaders) => Promise<T>): Promise<T> {\n const { envelope } = input;\n const parent = this.#extract(envelope.headers);\n const published = trace.getSpanContext(parent);\n const attributes: Attributes = {\n [ATTRIBUTES.system]: input.system,\n [ATTRIBUTES.destination]: input.destination,\n [ATTRIBUTES.operationName]: 'send',\n [ATTRIBUTES.operationType]: 'send',\n [ATTRIBUTES.messageId]: envelope.id,\n [ATTRIBUTES.eventType]: envelope.type,\n ...(envelope.correlationId === null\n ? {}\n : { [ATTRIBUTES.conversationId]: envelope.correlationId }),\n };\n return this.#runInSpan(\n {\n name: `send ${input.destination}`,\n kind: SpanKind.PRODUCER,\n attributes,\n parent,\n ...(published === undefined ? {} : { links: [{ context: published }] }),\n },\n run,\n );\n }\n\n /** A consumer span, child of the trace context the event arrived with, active while the handler runs. */\n process<T>(input: ProcessTraceInput, run: () => Promise<T>): Promise<T> {\n const { delivery } = input;\n const parent = this.#extract(delivery.event.headers);\n const span = this.#tracer.startSpan(\n `process ${delivery.event.type}`,\n {\n kind: SpanKind.CONSUMER,\n attributes: {\n [ATTRIBUTES.system]: RELAYBUS_SYSTEM,\n [ATTRIBUTES.destination]: delivery.event.type,\n [ATTRIBUTES.operationName]: 'process',\n [ATTRIBUTES.operationType]: 'process',\n [ATTRIBUTES.consumerGroup]: delivery.handler,\n [ATTRIBUTES.messageId]: delivery.event.id,\n [ATTRIBUTES.deliveryAttempt]: delivery.attempt,\n },\n },\n parent,\n );\n return endWhenSettled(span, context.with(trace.setSpan(parent, span), run));\n }\n\n #extract(headers: EventHeaders): Context {\n return this.#propagator.extract(context.active(), headers, defaultTextMapGetter);\n }\n\n #runInSpan<T>(\n span: {\n readonly name: string;\n readonly kind: SpanKind;\n readonly attributes: Attributes;\n readonly parent: Context;\n readonly links?: Link[];\n },\n run: (headers: EventHeaders) => Promise<T>,\n ): Promise<T> {\n const started = this.#tracer.startSpan(\n span.name,\n {\n kind: span.kind,\n attributes: span.attributes,\n ...(span.links === undefined ? {} : { links: span.links }),\n },\n span.parent,\n );\n const active = trace.setSpan(span.parent, started);\n const headers: Record<string, string> = {};\n this.#propagator.inject(active, headers, defaultTextMapSetter);\n return endWhenSettled(\n started,\n context.with(active, () => run(headers)),\n );\n }\n}\n","import {\n createTraceState,\n isSpanContextValid,\n trace,\n type Context,\n type SpanContext,\n type TextMapGetter,\n type TextMapPropagator,\n type TextMapSetter,\n} from '@opentelemetry/api';\nimport { TRACEPARENT_HEADER, TRACESTATE_HEADER, traceContextOf } from '@relaybus/core';\n\nconst HEX = 16;\nconst FLAGS_DIGITS = 2;\n/** Trace flags are one byte, written as two hex digits. */\nconst DEFINED_FLAGS = 0xff;\n\n/** A header value as a getter returns it: some carriers repeat headers. */\nfunction firstValue(value: string | string[] | undefined): string | undefined {\n return Array.isArray(value) ? value[0] : value;\n}\n\n/**\n * The W3C Trace Context propagator (`traceparent`, `tracestate`), so relaybus propagates traces\n * without `@opentelemetry/core`. Pass another one, such as the global `propagation`, as the\n * `propagator` option of `relayBusOpenTelemetry` to use what the SDK configured.\n */\nexport class W3CTraceContextPropagator implements TextMapPropagator {\n // eslint-disable-next-line relaybus/max-two-params -- TextMapPropagator.inject signature\n inject(context: Context, carrier: unknown, setter: TextMapSetter): void {\n const spanContext = trace.getSpanContext(context);\n if (spanContext === undefined || !isSpanContextValid(spanContext)) {\n return;\n }\n const flags = (spanContext.traceFlags & DEFINED_FLAGS)\n .toString(HEX)\n .padStart(FLAGS_DIGITS, '0');\n setter.set(\n carrier,\n TRACEPARENT_HEADER,\n `00-${spanContext.traceId}-${spanContext.spanId}-${flags}`,\n );\n const traceState = spanContext.traceState?.serialize();\n if (traceState !== undefined && traceState.length > 0) {\n setter.set(carrier, TRACESTATE_HEADER, traceState);\n }\n }\n\n // eslint-disable-next-line relaybus/max-two-params -- TextMapPropagator.extract signature\n extract(context: Context, carrier: unknown, getter: TextMapGetter): Context {\n const traceparent = firstValue(getter.get(carrier, TRACEPARENT_HEADER));\n const tracestate = firstValue(getter.get(carrier, TRACESTATE_HEADER));\n const traceContext =\n traceparent === undefined\n ? undefined\n : traceContextOf(tracestate === undefined ? { traceparent } : { traceparent, tracestate });\n if (traceContext === undefined) {\n return context;\n }\n const [, traceId, spanId, flags] = traceContext.traceparent.split('-') as [\n string,\n string,\n string,\n string,\n ];\n const spanContext: SpanContext = {\n traceId,\n spanId,\n traceFlags: Number.parseInt(flags, HEX),\n isRemote: true,\n ...(traceContext.tracestate === undefined\n ? {}\n : { traceState: createTraceState(traceContext.tracestate) }),\n };\n return trace.setSpanContext(context, spanContext);\n }\n\n fields(): string[] {\n return [TRACEPARENT_HEADER, TRACESTATE_HEADER];\n }\n}\n","import {\n metrics,\n trace,\n type MeterProvider,\n type TextMapPropagator,\n type TracerProvider,\n} from '@opentelemetry/api';\nimport type { RelayBusObserver, RelayBusTracer } from '@relaybus/core';\nimport { RelayBusMetricsObserver } from './relay-bus-metrics-observer.js';\nimport { RelayBusOpenTelemetryTracer } from './relay-bus-open-telemetry-tracer.js';\nimport { INSTRUMENTATION_SCOPE } from './semantic-conventions.js';\nimport { W3CTraceContextPropagator } from './w3c-trace-context-propagator.js';\n\nexport interface RelayBusOpenTelemetryOptions {\n /** Default: the global meter provider (`metrics.getMeterProvider()`). */\n readonly meterProvider?: MeterProvider;\n /** Default: the global tracer provider (`trace.getTracerProvider()`). */\n readonly tracerProvider?: TracerProvider;\n /**\n * Writes and reads the trace context of events. Default: W3C Trace Context (`traceparent`,\n * `tracestate`). Pass `propagation` from `@opentelemetry/api` to use the SDK's global one.\n */\n readonly propagator?: TextMapPropagator;\n}\n\n/** What `relayBusOpenTelemetry` returns: pass both to the bus. */\nexport interface RelayBusOpenTelemetry {\n /** Metrics; add it to `observers`. */\n readonly observer: RelayBusObserver;\n /** Spans and trace propagation; pass it as `tracer`. */\n readonly tracer: RelayBusTracer;\n}\n\n/**\n * OpenTelemetry metrics and traces for a relaybus.\n *\n * ```ts\n * const otel = relayBusOpenTelemetry();\n * const bus = new RelayBus({ store, observers: [otel.observer], tracer: otel.tracer, backlogInterval: '30s' });\n * ```\n */\nexport function relayBusOpenTelemetry(\n options: RelayBusOpenTelemetryOptions = {},\n): RelayBusOpenTelemetry {\n const meterProvider = options.meterProvider ?? metrics.getMeterProvider();\n const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();\n return {\n observer: new RelayBusMetricsObserver(meterProvider.getMeter(INSTRUMENTATION_SCOPE)),\n tracer: new RelayBusOpenTelemetryTracer({\n tracer: tracerProvider.getTracer(INSTRUMENTATION_SCOPE),\n propagator: options.propagator ?? new W3CTraceContextPropagator(),\n }),\n };\n}\n"]}