@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/dist/index.d.cts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { MeterProvider, TracerProvider, TextMapPropagator, Context, TextMapSetter, TextMapGetter } from '@opentelemetry/api';
|
|
2
|
+
import { RelayBusObserver, RelayBusTracer } from '@relaybus/core';
|
|
3
|
+
|
|
4
|
+
interface RelayBusOpenTelemetryOptions {
|
|
5
|
+
/** Default: the global meter provider (`metrics.getMeterProvider()`). */
|
|
6
|
+
readonly meterProvider?: MeterProvider;
|
|
7
|
+
/** Default: the global tracer provider (`trace.getTracerProvider()`). */
|
|
8
|
+
readonly tracerProvider?: TracerProvider;
|
|
9
|
+
/**
|
|
10
|
+
* Writes and reads the trace context of events. Default: W3C Trace Context (`traceparent`,
|
|
11
|
+
* `tracestate`). Pass `propagation` from `@opentelemetry/api` to use the SDK's global one.
|
|
12
|
+
*/
|
|
13
|
+
readonly propagator?: TextMapPropagator;
|
|
14
|
+
}
|
|
15
|
+
/** What `relayBusOpenTelemetry` returns: pass both to the bus. */
|
|
16
|
+
interface RelayBusOpenTelemetry {
|
|
17
|
+
/** Metrics; add it to `observers`. */
|
|
18
|
+
readonly observer: RelayBusObserver;
|
|
19
|
+
/** Spans and trace propagation; pass it as `tracer`. */
|
|
20
|
+
readonly tracer: RelayBusTracer;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* OpenTelemetry metrics and traces for a relaybus.
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* const otel = relayBusOpenTelemetry();
|
|
27
|
+
* const bus = new RelayBus({ store, observers: [otel.observer], tracer: otel.tracer, backlogInterval: '30s' });
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
declare function relayBusOpenTelemetry(options?: RelayBusOpenTelemetryOptions): RelayBusOpenTelemetry;
|
|
31
|
+
|
|
32
|
+
/** OpenTelemetry semantic convention attributes for messaging, and relaybus's own. */
|
|
33
|
+
declare const ATTRIBUTES: {
|
|
34
|
+
readonly system: "messaging.system";
|
|
35
|
+
readonly destination: "messaging.destination.name";
|
|
36
|
+
readonly operationName: "messaging.operation.name";
|
|
37
|
+
readonly operationType: "messaging.operation.type";
|
|
38
|
+
readonly consumerGroup: "messaging.consumer.group.name";
|
|
39
|
+
readonly messageId: "messaging.message.id";
|
|
40
|
+
readonly conversationId: "messaging.message.conversation_id";
|
|
41
|
+
readonly batchCount: "messaging.batch.message_count";
|
|
42
|
+
readonly errorType: "error.type";
|
|
43
|
+
readonly eventType: "relaybus.event.type";
|
|
44
|
+
readonly deliveryKind: "relaybus.delivery.kind";
|
|
45
|
+
readonly deliveryAttempt: "relaybus.delivery.attempt";
|
|
46
|
+
readonly receiveOutcome: "relaybus.receive.outcome";
|
|
47
|
+
readonly failureStage: "relaybus.failure.stage";
|
|
48
|
+
};
|
|
49
|
+
/** Metric names: the messaging semantic conventions where they exist, else `relaybus.*`. */
|
|
50
|
+
declare const METRICS: {
|
|
51
|
+
readonly operationDuration: "messaging.client.operation.duration";
|
|
52
|
+
readonly sentMessages: "messaging.client.sent.messages";
|
|
53
|
+
readonly consumedMessages: "messaging.client.consumed.messages";
|
|
54
|
+
readonly processDuration: "messaging.process.duration";
|
|
55
|
+
readonly invalidMessages: "relaybus.messages.invalid";
|
|
56
|
+
readonly published: "relaybus.events.published";
|
|
57
|
+
readonly claimedDeliveries: "relaybus.claim.deliveries";
|
|
58
|
+
readonly claimedLanes: "relaybus.claim.lanes";
|
|
59
|
+
readonly pending: "relaybus.deliveries.pending";
|
|
60
|
+
readonly oldestAge: "relaybus.deliveries.oldest_age";
|
|
61
|
+
readonly deadLettered: "relaybus.deliveries.dead_lettered";
|
|
62
|
+
readonly retried: "relaybus.deliveries.retried";
|
|
63
|
+
readonly leaseLost: "relaybus.lease.lost";
|
|
64
|
+
readonly endToEndLatency: "relaybus.end_to_end.latency";
|
|
65
|
+
readonly failures: "relaybus.failures";
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The W3C Trace Context propagator (`traceparent`, `tracestate`), so relaybus propagates traces
|
|
70
|
+
* without `@opentelemetry/core`. Pass another one, such as the global `propagation`, as the
|
|
71
|
+
* `propagator` option of `relayBusOpenTelemetry` to use what the SDK configured.
|
|
72
|
+
*/
|
|
73
|
+
declare class W3CTraceContextPropagator implements TextMapPropagator {
|
|
74
|
+
inject(context: Context, carrier: unknown, setter: TextMapSetter): void;
|
|
75
|
+
extract(context: Context, carrier: unknown, getter: TextMapGetter): Context;
|
|
76
|
+
fields(): string[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export { ATTRIBUTES, METRICS, type RelayBusOpenTelemetry, type RelayBusOpenTelemetryOptions, W3CTraceContextPropagator, relayBusOpenTelemetry };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { MeterProvider, TracerProvider, TextMapPropagator, Context, TextMapSetter, TextMapGetter } from '@opentelemetry/api';
|
|
2
|
+
import { RelayBusObserver, RelayBusTracer } from '@relaybus/core';
|
|
3
|
+
|
|
4
|
+
interface RelayBusOpenTelemetryOptions {
|
|
5
|
+
/** Default: the global meter provider (`metrics.getMeterProvider()`). */
|
|
6
|
+
readonly meterProvider?: MeterProvider;
|
|
7
|
+
/** Default: the global tracer provider (`trace.getTracerProvider()`). */
|
|
8
|
+
readonly tracerProvider?: TracerProvider;
|
|
9
|
+
/**
|
|
10
|
+
* Writes and reads the trace context of events. Default: W3C Trace Context (`traceparent`,
|
|
11
|
+
* `tracestate`). Pass `propagation` from `@opentelemetry/api` to use the SDK's global one.
|
|
12
|
+
*/
|
|
13
|
+
readonly propagator?: TextMapPropagator;
|
|
14
|
+
}
|
|
15
|
+
/** What `relayBusOpenTelemetry` returns: pass both to the bus. */
|
|
16
|
+
interface RelayBusOpenTelemetry {
|
|
17
|
+
/** Metrics; add it to `observers`. */
|
|
18
|
+
readonly observer: RelayBusObserver;
|
|
19
|
+
/** Spans and trace propagation; pass it as `tracer`. */
|
|
20
|
+
readonly tracer: RelayBusTracer;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* OpenTelemetry metrics and traces for a relaybus.
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* const otel = relayBusOpenTelemetry();
|
|
27
|
+
* const bus = new RelayBus({ store, observers: [otel.observer], tracer: otel.tracer, backlogInterval: '30s' });
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
declare function relayBusOpenTelemetry(options?: RelayBusOpenTelemetryOptions): RelayBusOpenTelemetry;
|
|
31
|
+
|
|
32
|
+
/** OpenTelemetry semantic convention attributes for messaging, and relaybus's own. */
|
|
33
|
+
declare const ATTRIBUTES: {
|
|
34
|
+
readonly system: "messaging.system";
|
|
35
|
+
readonly destination: "messaging.destination.name";
|
|
36
|
+
readonly operationName: "messaging.operation.name";
|
|
37
|
+
readonly operationType: "messaging.operation.type";
|
|
38
|
+
readonly consumerGroup: "messaging.consumer.group.name";
|
|
39
|
+
readonly messageId: "messaging.message.id";
|
|
40
|
+
readonly conversationId: "messaging.message.conversation_id";
|
|
41
|
+
readonly batchCount: "messaging.batch.message_count";
|
|
42
|
+
readonly errorType: "error.type";
|
|
43
|
+
readonly eventType: "relaybus.event.type";
|
|
44
|
+
readonly deliveryKind: "relaybus.delivery.kind";
|
|
45
|
+
readonly deliveryAttempt: "relaybus.delivery.attempt";
|
|
46
|
+
readonly receiveOutcome: "relaybus.receive.outcome";
|
|
47
|
+
readonly failureStage: "relaybus.failure.stage";
|
|
48
|
+
};
|
|
49
|
+
/** Metric names: the messaging semantic conventions where they exist, else `relaybus.*`. */
|
|
50
|
+
declare const METRICS: {
|
|
51
|
+
readonly operationDuration: "messaging.client.operation.duration";
|
|
52
|
+
readonly sentMessages: "messaging.client.sent.messages";
|
|
53
|
+
readonly consumedMessages: "messaging.client.consumed.messages";
|
|
54
|
+
readonly processDuration: "messaging.process.duration";
|
|
55
|
+
readonly invalidMessages: "relaybus.messages.invalid";
|
|
56
|
+
readonly published: "relaybus.events.published";
|
|
57
|
+
readonly claimedDeliveries: "relaybus.claim.deliveries";
|
|
58
|
+
readonly claimedLanes: "relaybus.claim.lanes";
|
|
59
|
+
readonly pending: "relaybus.deliveries.pending";
|
|
60
|
+
readonly oldestAge: "relaybus.deliveries.oldest_age";
|
|
61
|
+
readonly deadLettered: "relaybus.deliveries.dead_lettered";
|
|
62
|
+
readonly retried: "relaybus.deliveries.retried";
|
|
63
|
+
readonly leaseLost: "relaybus.lease.lost";
|
|
64
|
+
readonly endToEndLatency: "relaybus.end_to_end.latency";
|
|
65
|
+
readonly failures: "relaybus.failures";
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The W3C Trace Context propagator (`traceparent`, `tracestate`), so relaybus propagates traces
|
|
70
|
+
* without `@opentelemetry/core`. Pass another one, such as the global `propagation`, as the
|
|
71
|
+
* `propagator` option of `relayBusOpenTelemetry` to use what the SDK configured.
|
|
72
|
+
*/
|
|
73
|
+
declare class W3CTraceContextPropagator implements TextMapPropagator {
|
|
74
|
+
inject(context: Context, carrier: unknown, setter: TextMapSetter): void;
|
|
75
|
+
extract(context: Context, carrier: unknown, getter: TextMapGetter): Context;
|
|
76
|
+
fields(): string[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export { ATTRIBUTES, METRICS, type RelayBusOpenTelemetry, type RelayBusOpenTelemetryOptions, W3CTraceContextPropagator, relayBusOpenTelemetry };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
import { trace, isSpanContextValid, createTraceState, metrics, SpanKind, context, defaultTextMapGetter, defaultTextMapSetter, SpanStatusCode } from '@opentelemetry/api';
|
|
2
|
+
import { TRACEPARENT_HEADER, TRACESTATE_HEADER, traceContextOf, toErrorInfo } from '@relaybus/core';
|
|
3
|
+
|
|
4
|
+
// src/relay-bus-open-telemetry.ts
|
|
5
|
+
|
|
6
|
+
// src/semantic-conventions.ts
|
|
7
|
+
var INSTRUMENTATION_SCOPE = "@relaybus/otel";
|
|
8
|
+
var RELAYBUS_SYSTEM = "relaybus";
|
|
9
|
+
var ATTRIBUTES = {
|
|
10
|
+
system: "messaging.system",
|
|
11
|
+
destination: "messaging.destination.name",
|
|
12
|
+
operationName: "messaging.operation.name",
|
|
13
|
+
operationType: "messaging.operation.type",
|
|
14
|
+
consumerGroup: "messaging.consumer.group.name",
|
|
15
|
+
messageId: "messaging.message.id",
|
|
16
|
+
conversationId: "messaging.message.conversation_id",
|
|
17
|
+
batchCount: "messaging.batch.message_count",
|
|
18
|
+
errorType: "error.type",
|
|
19
|
+
eventType: "relaybus.event.type",
|
|
20
|
+
deliveryKind: "relaybus.delivery.kind",
|
|
21
|
+
deliveryAttempt: "relaybus.delivery.attempt",
|
|
22
|
+
receiveOutcome: "relaybus.receive.outcome",
|
|
23
|
+
failureStage: "relaybus.failure.stage"
|
|
24
|
+
};
|
|
25
|
+
var METRICS = {
|
|
26
|
+
operationDuration: "messaging.client.operation.duration",
|
|
27
|
+
sentMessages: "messaging.client.sent.messages",
|
|
28
|
+
consumedMessages: "messaging.client.consumed.messages",
|
|
29
|
+
processDuration: "messaging.process.duration",
|
|
30
|
+
invalidMessages: "relaybus.messages.invalid",
|
|
31
|
+
published: "relaybus.events.published",
|
|
32
|
+
claimedDeliveries: "relaybus.claim.deliveries",
|
|
33
|
+
claimedLanes: "relaybus.claim.lanes",
|
|
34
|
+
pending: "relaybus.deliveries.pending",
|
|
35
|
+
oldestAge: "relaybus.deliveries.oldest_age",
|
|
36
|
+
deadLettered: "relaybus.deliveries.dead_lettered",
|
|
37
|
+
retried: "relaybus.deliveries.retried",
|
|
38
|
+
leaseLost: "relaybus.lease.lost",
|
|
39
|
+
endToEndLatency: "relaybus.end_to_end.latency",
|
|
40
|
+
failures: "relaybus.failures"
|
|
41
|
+
};
|
|
42
|
+
function errorType(error) {
|
|
43
|
+
return error.code ?? error.name;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/relay-bus-metrics-observer.ts
|
|
47
|
+
var MILLISECONDS_PER_SECOND = 1e3;
|
|
48
|
+
var DURATION_BUCKETS = [
|
|
49
|
+
5e-3,
|
|
50
|
+
0.01,
|
|
51
|
+
0.025,
|
|
52
|
+
0.05,
|
|
53
|
+
0.075,
|
|
54
|
+
0.1,
|
|
55
|
+
0.25,
|
|
56
|
+
0.5,
|
|
57
|
+
0.75,
|
|
58
|
+
1,
|
|
59
|
+
2.5,
|
|
60
|
+
5,
|
|
61
|
+
7.5,
|
|
62
|
+
10
|
|
63
|
+
];
|
|
64
|
+
var LATENCY_BUCKETS = [
|
|
65
|
+
5e-3,
|
|
66
|
+
0.01,
|
|
67
|
+
0.025,
|
|
68
|
+
0.05,
|
|
69
|
+
0.1,
|
|
70
|
+
0.25,
|
|
71
|
+
0.5,
|
|
72
|
+
1,
|
|
73
|
+
2.5,
|
|
74
|
+
5,
|
|
75
|
+
10,
|
|
76
|
+
30,
|
|
77
|
+
60,
|
|
78
|
+
300,
|
|
79
|
+
900,
|
|
80
|
+
3600
|
|
81
|
+
];
|
|
82
|
+
var COUNT_BUCKETS = [1, 2, 5, 10, 20, 50, 100, 200, 500, 1e3];
|
|
83
|
+
function seconds(milliseconds) {
|
|
84
|
+
return milliseconds / MILLISECONDS_PER_SECOND;
|
|
85
|
+
}
|
|
86
|
+
function withError(attributes, error) {
|
|
87
|
+
return error === void 0 ? attributes : { ...attributes, [ATTRIBUTES.errorType]: errorType(error) };
|
|
88
|
+
}
|
|
89
|
+
function deliveryAttributes(delivery) {
|
|
90
|
+
return {
|
|
91
|
+
[ATTRIBUTES.system]: RELAYBUS_SYSTEM,
|
|
92
|
+
[ATTRIBUTES.destination]: delivery.event.type,
|
|
93
|
+
[ATTRIBUTES.consumerGroup]: delivery.handler
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function createHistograms(meter) {
|
|
97
|
+
const histogram = (name, options) => meter.createHistogram(name, {
|
|
98
|
+
unit: options.unit,
|
|
99
|
+
description: options.description,
|
|
100
|
+
advice: { explicitBucketBoundaries: options.buckets }
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
operationDuration: histogram(METRICS.operationDuration, {
|
|
104
|
+
unit: "s",
|
|
105
|
+
description: "Duration of publish, claim, send and receive operations.",
|
|
106
|
+
buckets: DURATION_BUCKETS
|
|
107
|
+
}),
|
|
108
|
+
processDuration: histogram(METRICS.processDuration, {
|
|
109
|
+
unit: "s",
|
|
110
|
+
description: "Duration of local handler runs.",
|
|
111
|
+
buckets: DURATION_BUCKETS
|
|
112
|
+
}),
|
|
113
|
+
claimedDeliveries: histogram(METRICS.claimedDeliveries, {
|
|
114
|
+
unit: "{delivery}",
|
|
115
|
+
description: "Deliveries one claim query returned.",
|
|
116
|
+
buckets: COUNT_BUCKETS
|
|
117
|
+
}),
|
|
118
|
+
claimedLanes: histogram(METRICS.claimedLanes, {
|
|
119
|
+
unit: "{lane}",
|
|
120
|
+
description: "Distinct leases (lanes) among the deliveries of one claim.",
|
|
121
|
+
buckets: COUNT_BUCKETS
|
|
122
|
+
}),
|
|
123
|
+
endToEndLatency: histogram(METRICS.endToEndLatency, {
|
|
124
|
+
unit: "s",
|
|
125
|
+
description: "Time from an event occurring to a handler or route starting on it.",
|
|
126
|
+
buckets: LATENCY_BUCKETS
|
|
127
|
+
})
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function createInstruments(meter) {
|
|
131
|
+
const counter = (name, [unit, description]) => meter.createCounter(name, { unit, description });
|
|
132
|
+
return {
|
|
133
|
+
...createHistograms(meter),
|
|
134
|
+
sentMessages: counter(METRICS.sentMessages, [
|
|
135
|
+
"{message}",
|
|
136
|
+
"Events a route attempted to send to a broker."
|
|
137
|
+
]),
|
|
138
|
+
consumedMessages: counter(METRICS.consumedMessages, [
|
|
139
|
+
"{message}",
|
|
140
|
+
"Events received from a broker, by what receive did with them."
|
|
141
|
+
]),
|
|
142
|
+
invalidMessages: counter(METRICS.invalidMessages, [
|
|
143
|
+
"{message}",
|
|
144
|
+
"Inbound messages a transport could not decode, so they never reached the bus."
|
|
145
|
+
]),
|
|
146
|
+
published: counter(METRICS.published, ["{event}", "Events passed to publish."]),
|
|
147
|
+
deadLettered: counter(METRICS.deadLettered, ["{delivery}", "Deliveries moved to dead."]),
|
|
148
|
+
retried: counter(METRICS.retried, ["{delivery}", "Failed attempts rescheduled for a retry."]),
|
|
149
|
+
leaseLost: counter(METRICS.leaseLost, [
|
|
150
|
+
"{delivery}",
|
|
151
|
+
"Deliveries whose lease was lost while they ran."
|
|
152
|
+
]),
|
|
153
|
+
failures: counter(METRICS.failures, [
|
|
154
|
+
"{failure}",
|
|
155
|
+
"Errors relaybus caught and reported, by stage."
|
|
156
|
+
])
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
var RelayBusMetricsObserver = class {
|
|
160
|
+
#instruments;
|
|
161
|
+
#backlog;
|
|
162
|
+
constructor(meter) {
|
|
163
|
+
this.#instruments = createInstruments(meter);
|
|
164
|
+
meter.createObservableGauge(METRICS.pending, {
|
|
165
|
+
unit: "{delivery}",
|
|
166
|
+
description: "Deliveries waiting to run, at the last backlog reading."
|
|
167
|
+
}).addCallback((result) => {
|
|
168
|
+
if (this.#backlog !== void 0) {
|
|
169
|
+
result.observe(this.#backlog.pending);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
meter.createObservableGauge(METRICS.oldestAge, {
|
|
173
|
+
unit: "s",
|
|
174
|
+
description: "How long the oldest due delivery had waited, at the last backlog reading."
|
|
175
|
+
}).addCallback((result) => {
|
|
176
|
+
if (this.#backlog !== void 0) {
|
|
177
|
+
result.observe(seconds(this.#backlog.oldestPendingAgeMs));
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
onPublishFinished(event) {
|
|
182
|
+
const types = new Set(event.eventTypes);
|
|
183
|
+
const base = {
|
|
184
|
+
[ATTRIBUTES.system]: RELAYBUS_SYSTEM,
|
|
185
|
+
[ATTRIBUTES.operationName]: "publish",
|
|
186
|
+
[ATTRIBUTES.operationType]: "create"
|
|
187
|
+
};
|
|
188
|
+
const [onlyType] = types;
|
|
189
|
+
this.#instruments.operationDuration.record(
|
|
190
|
+
seconds(event.durationMs),
|
|
191
|
+
withError(
|
|
192
|
+
types.size === 1 ? { ...base, [ATTRIBUTES.destination]: onlyType } : base,
|
|
193
|
+
event.error
|
|
194
|
+
)
|
|
195
|
+
);
|
|
196
|
+
for (const type of event.eventTypes) {
|
|
197
|
+
this.#instruments.published.add(
|
|
198
|
+
1,
|
|
199
|
+
withError(
|
|
200
|
+
{ [ATTRIBUTES.system]: RELAYBUS_SYSTEM, [ATTRIBUTES.destination]: type },
|
|
201
|
+
event.error
|
|
202
|
+
)
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
onClaimFinished(event) {
|
|
207
|
+
const attributes = {
|
|
208
|
+
[ATTRIBUTES.system]: RELAYBUS_SYSTEM,
|
|
209
|
+
[ATTRIBUTES.operationName]: "claim",
|
|
210
|
+
[ATTRIBUTES.operationType]: "receive"
|
|
211
|
+
};
|
|
212
|
+
this.#instruments.operationDuration.record(
|
|
213
|
+
seconds(event.durationMs),
|
|
214
|
+
withError(attributes, event.error)
|
|
215
|
+
);
|
|
216
|
+
if (event.outcome === "success") {
|
|
217
|
+
this.#instruments.claimedDeliveries.record(event.claimed, attributes);
|
|
218
|
+
this.#instruments.claimedLanes.record(event.lanes, attributes);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
onHandlerStarted(event) {
|
|
222
|
+
this.#instruments.endToEndLatency.record(seconds(event.endToEndLatencyMs), {
|
|
223
|
+
...deliveryAttributes(event.delivery),
|
|
224
|
+
[ATTRIBUTES.deliveryKind]: event.kind
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
onHandlerFinished(event) {
|
|
228
|
+
if (event.kind === "handler") {
|
|
229
|
+
this.#instruments.processDuration.record(
|
|
230
|
+
seconds(event.durationMs),
|
|
231
|
+
withError(
|
|
232
|
+
{
|
|
233
|
+
...deliveryAttributes(event.delivery),
|
|
234
|
+
[ATTRIBUTES.operationName]: "process",
|
|
235
|
+
[ATTRIBUTES.operationType]: "process"
|
|
236
|
+
},
|
|
237
|
+
event.error
|
|
238
|
+
)
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
onTransportSent(event) {
|
|
243
|
+
const attributes = withError(
|
|
244
|
+
{
|
|
245
|
+
[ATTRIBUTES.system]: event.system,
|
|
246
|
+
[ATTRIBUTES.destination]: event.destination,
|
|
247
|
+
[ATTRIBUTES.operationName]: "send",
|
|
248
|
+
[ATTRIBUTES.operationType]: "send"
|
|
249
|
+
},
|
|
250
|
+
event.error
|
|
251
|
+
);
|
|
252
|
+
this.#instruments.sentMessages.add(1, attributes);
|
|
253
|
+
this.#instruments.operationDuration.record(seconds(event.durationMs), attributes);
|
|
254
|
+
}
|
|
255
|
+
onReceiveFinished(event) {
|
|
256
|
+
const attributes = withError(
|
|
257
|
+
{
|
|
258
|
+
[ATTRIBUTES.system]: event.system ?? RELAYBUS_SYSTEM,
|
|
259
|
+
[ATTRIBUTES.operationName]: "receive",
|
|
260
|
+
[ATTRIBUTES.operationType]: "receive",
|
|
261
|
+
...event.destination === void 0 ? {} : { [ATTRIBUTES.destination]: event.destination }
|
|
262
|
+
},
|
|
263
|
+
event.error
|
|
264
|
+
);
|
|
265
|
+
this.#instruments.consumedMessages.add(1, {
|
|
266
|
+
...attributes,
|
|
267
|
+
[ATTRIBUTES.receiveOutcome]: event.outcome
|
|
268
|
+
});
|
|
269
|
+
this.#instruments.operationDuration.record(seconds(event.durationMs), attributes);
|
|
270
|
+
}
|
|
271
|
+
onInvalidMessage(event) {
|
|
272
|
+
this.#instruments.invalidMessages.add(
|
|
273
|
+
1,
|
|
274
|
+
withError(
|
|
275
|
+
{ [ATTRIBUTES.system]: event.system, [ATTRIBUTES.destination]: event.destination },
|
|
276
|
+
event.error
|
|
277
|
+
)
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
onDeliveryRetryScheduled(event) {
|
|
281
|
+
this.#instruments.retried.add(1, withError(deliveryAttributes(event.delivery), event.error));
|
|
282
|
+
}
|
|
283
|
+
onDeliveryDead(event) {
|
|
284
|
+
this.#instruments.deadLettered.add(
|
|
285
|
+
1,
|
|
286
|
+
withError(deliveryAttributes(event.delivery), event.error)
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
onLeaseLost(event) {
|
|
290
|
+
this.#instruments.leaseLost.add(1, deliveryAttributes(event.delivery));
|
|
291
|
+
}
|
|
292
|
+
onBacklog(event) {
|
|
293
|
+
this.#backlog = event;
|
|
294
|
+
}
|
|
295
|
+
onFailure(event) {
|
|
296
|
+
this.#instruments.failures.add(
|
|
297
|
+
1,
|
|
298
|
+
withError({ [ATTRIBUTES.failureStage]: event.stage }, event.error)
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
async function endWhenSettled(span, work) {
|
|
303
|
+
try {
|
|
304
|
+
return await work;
|
|
305
|
+
} catch (error) {
|
|
306
|
+
const info = toErrorInfo(error);
|
|
307
|
+
span.recordException(error instanceof Error ? error : info.message);
|
|
308
|
+
span.setAttribute(ATTRIBUTES.errorType, errorType(info));
|
|
309
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: info.message });
|
|
310
|
+
throw error;
|
|
311
|
+
} finally {
|
|
312
|
+
span.end();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
var RelayBusOpenTelemetryTracer = class {
|
|
316
|
+
#tracer;
|
|
317
|
+
#propagator;
|
|
318
|
+
constructor({ tracer, propagator }) {
|
|
319
|
+
this.#tracer = tracer;
|
|
320
|
+
this.#propagator = propagator;
|
|
321
|
+
}
|
|
322
|
+
/** A producer span, child of the caller's active span; its context goes into every event. */
|
|
323
|
+
publish(input, run) {
|
|
324
|
+
const types = [...new Set(input.eventTypes)];
|
|
325
|
+
const attributes = {
|
|
326
|
+
[ATTRIBUTES.system]: RELAYBUS_SYSTEM,
|
|
327
|
+
[ATTRIBUTES.operationName]: "publish",
|
|
328
|
+
[ATTRIBUTES.operationType]: "create",
|
|
329
|
+
...types.length === 1 ? { [ATTRIBUTES.destination]: types[0] } : {},
|
|
330
|
+
...input.eventTypes.length > 1 ? { [ATTRIBUTES.batchCount]: input.eventTypes.length } : {}
|
|
331
|
+
};
|
|
332
|
+
const name = types.length === 1 ? `publish ${types[0]}` : "publish";
|
|
333
|
+
return this.#runInSpan(
|
|
334
|
+
{ name, kind: SpanKind.PRODUCER, attributes, parent: context.active() },
|
|
335
|
+
run
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* A producer span, child of the publish span whose context the stored event carries (and linked
|
|
340
|
+
* to it); the event goes to the broker with this span's context.
|
|
341
|
+
*/
|
|
342
|
+
send(input, run) {
|
|
343
|
+
const { envelope } = input;
|
|
344
|
+
const parent = this.#extract(envelope.headers);
|
|
345
|
+
const published = trace.getSpanContext(parent);
|
|
346
|
+
const attributes = {
|
|
347
|
+
[ATTRIBUTES.system]: input.system,
|
|
348
|
+
[ATTRIBUTES.destination]: input.destination,
|
|
349
|
+
[ATTRIBUTES.operationName]: "send",
|
|
350
|
+
[ATTRIBUTES.operationType]: "send",
|
|
351
|
+
[ATTRIBUTES.messageId]: envelope.id,
|
|
352
|
+
[ATTRIBUTES.eventType]: envelope.type,
|
|
353
|
+
...envelope.correlationId === null ? {} : { [ATTRIBUTES.conversationId]: envelope.correlationId }
|
|
354
|
+
};
|
|
355
|
+
return this.#runInSpan(
|
|
356
|
+
{
|
|
357
|
+
name: `send ${input.destination}`,
|
|
358
|
+
kind: SpanKind.PRODUCER,
|
|
359
|
+
attributes,
|
|
360
|
+
parent,
|
|
361
|
+
...published === void 0 ? {} : { links: [{ context: published }] }
|
|
362
|
+
},
|
|
363
|
+
run
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
/** A consumer span, child of the trace context the event arrived with, active while the handler runs. */
|
|
367
|
+
process(input, run) {
|
|
368
|
+
const { delivery } = input;
|
|
369
|
+
const parent = this.#extract(delivery.event.headers);
|
|
370
|
+
const span = this.#tracer.startSpan(
|
|
371
|
+
`process ${delivery.event.type}`,
|
|
372
|
+
{
|
|
373
|
+
kind: SpanKind.CONSUMER,
|
|
374
|
+
attributes: {
|
|
375
|
+
[ATTRIBUTES.system]: RELAYBUS_SYSTEM,
|
|
376
|
+
[ATTRIBUTES.destination]: delivery.event.type,
|
|
377
|
+
[ATTRIBUTES.operationName]: "process",
|
|
378
|
+
[ATTRIBUTES.operationType]: "process",
|
|
379
|
+
[ATTRIBUTES.consumerGroup]: delivery.handler,
|
|
380
|
+
[ATTRIBUTES.messageId]: delivery.event.id,
|
|
381
|
+
[ATTRIBUTES.deliveryAttempt]: delivery.attempt
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
parent
|
|
385
|
+
);
|
|
386
|
+
return endWhenSettled(span, context.with(trace.setSpan(parent, span), run));
|
|
387
|
+
}
|
|
388
|
+
#extract(headers) {
|
|
389
|
+
return this.#propagator.extract(context.active(), headers, defaultTextMapGetter);
|
|
390
|
+
}
|
|
391
|
+
#runInSpan(span, run) {
|
|
392
|
+
const started = this.#tracer.startSpan(
|
|
393
|
+
span.name,
|
|
394
|
+
{
|
|
395
|
+
kind: span.kind,
|
|
396
|
+
attributes: span.attributes,
|
|
397
|
+
...span.links === void 0 ? {} : { links: span.links }
|
|
398
|
+
},
|
|
399
|
+
span.parent
|
|
400
|
+
);
|
|
401
|
+
const active = trace.setSpan(span.parent, started);
|
|
402
|
+
const headers = {};
|
|
403
|
+
this.#propagator.inject(active, headers, defaultTextMapSetter);
|
|
404
|
+
return endWhenSettled(
|
|
405
|
+
started,
|
|
406
|
+
context.with(active, () => run(headers))
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
var HEX = 16;
|
|
411
|
+
var FLAGS_DIGITS = 2;
|
|
412
|
+
var DEFINED_FLAGS = 255;
|
|
413
|
+
function firstValue(value) {
|
|
414
|
+
return Array.isArray(value) ? value[0] : value;
|
|
415
|
+
}
|
|
416
|
+
var W3CTraceContextPropagator = class {
|
|
417
|
+
// eslint-disable-next-line relaybus/max-two-params -- TextMapPropagator.inject signature
|
|
418
|
+
inject(context2, carrier, setter) {
|
|
419
|
+
const spanContext = trace.getSpanContext(context2);
|
|
420
|
+
if (spanContext === void 0 || !isSpanContextValid(spanContext)) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const flags = (spanContext.traceFlags & DEFINED_FLAGS).toString(HEX).padStart(FLAGS_DIGITS, "0");
|
|
424
|
+
setter.set(
|
|
425
|
+
carrier,
|
|
426
|
+
TRACEPARENT_HEADER,
|
|
427
|
+
`00-${spanContext.traceId}-${spanContext.spanId}-${flags}`
|
|
428
|
+
);
|
|
429
|
+
const traceState = spanContext.traceState?.serialize();
|
|
430
|
+
if (traceState !== void 0 && traceState.length > 0) {
|
|
431
|
+
setter.set(carrier, TRACESTATE_HEADER, traceState);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
// eslint-disable-next-line relaybus/max-two-params -- TextMapPropagator.extract signature
|
|
435
|
+
extract(context2, carrier, getter) {
|
|
436
|
+
const traceparent = firstValue(getter.get(carrier, TRACEPARENT_HEADER));
|
|
437
|
+
const tracestate = firstValue(getter.get(carrier, TRACESTATE_HEADER));
|
|
438
|
+
const traceContext = traceparent === void 0 ? void 0 : traceContextOf(tracestate === void 0 ? { traceparent } : { traceparent, tracestate });
|
|
439
|
+
if (traceContext === void 0) {
|
|
440
|
+
return context2;
|
|
441
|
+
}
|
|
442
|
+
const [, traceId, spanId, flags] = traceContext.traceparent.split("-");
|
|
443
|
+
const spanContext = {
|
|
444
|
+
traceId,
|
|
445
|
+
spanId,
|
|
446
|
+
traceFlags: Number.parseInt(flags, HEX),
|
|
447
|
+
isRemote: true,
|
|
448
|
+
...traceContext.tracestate === void 0 ? {} : { traceState: createTraceState(traceContext.tracestate) }
|
|
449
|
+
};
|
|
450
|
+
return trace.setSpanContext(context2, spanContext);
|
|
451
|
+
}
|
|
452
|
+
fields() {
|
|
453
|
+
return [TRACEPARENT_HEADER, TRACESTATE_HEADER];
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
// src/relay-bus-open-telemetry.ts
|
|
458
|
+
function relayBusOpenTelemetry(options = {}) {
|
|
459
|
+
const meterProvider = options.meterProvider ?? metrics.getMeterProvider();
|
|
460
|
+
const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
|
|
461
|
+
return {
|
|
462
|
+
observer: new RelayBusMetricsObserver(meterProvider.getMeter(INSTRUMENTATION_SCOPE)),
|
|
463
|
+
tracer: new RelayBusOpenTelemetryTracer({
|
|
464
|
+
tracer: tracerProvider.getTracer(INSTRUMENTATION_SCOPE),
|
|
465
|
+
propagator: options.propagator ?? new W3CTraceContextPropagator()
|
|
466
|
+
})
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export { ATTRIBUTES, METRICS, W3CTraceContextPropagator, relayBusOpenTelemetry };
|
|
471
|
+
//# sourceMappingURL=index.js.map
|
|
472
|
+
//# sourceMappingURL=index.js.map
|