@relaybus/otel 0.0.0-stage → 0.0.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 +21 -0
- package/README.md +75 -2
- package/dist/index.cjs +477 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +79 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.js +472 -0
- package/dist/index.js.map +1 -0
- package/package.json +74 -3
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
|
-
#
|
|
1
|
+
# @relaybus/otel
|
|
2
2
|
|
|
3
|
-
|
|
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"]}
|