abxbus 2.4.15 → 2.4.16

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.
@@ -6,6 +6,8 @@ export { EventBus } from './event_bus.js';
6
6
  export type { EventBusJSON, EventBusOptions } from './event_bus.js';
7
7
  export { monotonicDatetime } from './helpers.js';
8
8
  export type { EventBusMiddleware, EventBusMiddlewareCtor, EventBusMiddlewareInput } from './middlewares.js';
9
+ export { OtelTracingMiddleware } from './middleware_otel_tracing.js';
10
+ export type { OtelTracingMiddlewareOptions } from './middleware_otel_tracing.js';
9
11
  export { EventHandlerTimeoutError, EventHandlerCancelledError, EventHandlerAbortedError, EventHandlerResultSchemaError, } from './event_handler.js';
10
12
  export type { EventConcurrencyMode, EventHandlerConcurrencyMode, EventHandlerCompletionMode, EventBusInterfaceForLockManager, } from './lock_manager.js';
11
13
  export type { EventClass, EventHandlerCallable as EventHandler, EventPattern, EventStatus, FindOptions, FindWindow } from './types.js';
package/dist/cjs/index.js CHANGED
@@ -30,6 +30,7 @@ __export(index_exports, {
30
30
  HTTPEventBridge: () => import_bridges.HTTPEventBridge,
31
31
  JSONLEventBridge: () => import_bridges.JSONLEventBridge,
32
32
  NATSEventBridge: () => import_bridges.NATSEventBridge,
33
+ OtelTracingMiddleware: () => import_middleware_otel_tracing.OtelTracingMiddleware,
33
34
  PostgresEventBridge: () => import_bridges.PostgresEventBridge,
34
35
  RedisEventBridge: () => import_bridges.RedisEventBridge,
35
36
  RetryTimeoutError: () => import_retry.RetryTimeoutError,
@@ -47,6 +48,7 @@ var import_event_history = require("./event_history.js");
47
48
  var import_event_result = require("./event_result.js");
48
49
  var import_event_bus = require("./event_bus.js");
49
50
  var import_helpers = require("./helpers.js");
51
+ var import_middleware_otel_tracing = require("./middleware_otel_tracing.js");
50
52
  var import_event_handler = require("./event_handler.js");
51
53
  var import_retry = require("./retry.js");
52
54
  var import_bridges = require("./bridges.js");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
- "sourcesContent": ["export { BaseEvent, BaseEventSchema } from './base_event.js'\nexport { EventHistory } from './event_history.js'\nexport type { EventHistoryFindOptions, EventHistoryTrimOptions } from './event_history.js'\nexport { EventResult } from './event_result.js'\nexport { EventBus } from './event_bus.js'\nexport type { EventBusJSON, EventBusOptions } from './event_bus.js'\nexport { monotonicDatetime } from './helpers.js'\nexport type { EventBusMiddleware, EventBusMiddlewareCtor, EventBusMiddlewareInput } from './middlewares.js'\nexport {\n EventHandlerTimeoutError,\n EventHandlerCancelledError,\n EventHandlerAbortedError,\n EventHandlerResultSchemaError,\n} from './event_handler.js'\nexport type {\n EventConcurrencyMode,\n EventHandlerConcurrencyMode,\n EventHandlerCompletionMode,\n EventBusInterfaceForLockManager,\n} from './lock_manager.js'\nexport type { EventClass, EventHandlerCallable as EventHandler, EventPattern, EventStatus, FindOptions, FindWindow } from './types.js'\nexport { retry, clearSemaphoreRegistry, RetryTimeoutError, SemaphoreTimeoutError } from './retry.js'\nexport type { RetryOptions } from './retry.js'\nexport {\n HTTPEventBridge,\n SocketEventBridge,\n NATSEventBridge,\n RedisEventBridge,\n PostgresEventBridge,\n JSONLEventBridge,\n SQLiteEventBridge,\n} from './bridges.js'\nexport type { HTTPEventBridgeOptions } from './bridges.js'\nexport { events_suck } from './events_suck.js'\nexport type { EventsSuckClient, EventsSuckClientClass, GeneratedEvents } from './events_suck.js'\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAA2C;AAC3C,2BAA6B;AAE7B,0BAA4B;AAC5B,uBAAyB;AAEzB,qBAAkC;AAElC,2BAKO;AAQP,mBAAwF;AAExF,qBAQO;AAEP,yBAA4B;",
4
+ "sourcesContent": ["export { BaseEvent, BaseEventSchema } from './base_event.js'\nexport { EventHistory } from './event_history.js'\nexport type { EventHistoryFindOptions, EventHistoryTrimOptions } from './event_history.js'\nexport { EventResult } from './event_result.js'\nexport { EventBus } from './event_bus.js'\nexport type { EventBusJSON, EventBusOptions } from './event_bus.js'\nexport { monotonicDatetime } from './helpers.js'\nexport type { EventBusMiddleware, EventBusMiddlewareCtor, EventBusMiddlewareInput } from './middlewares.js'\nexport { OtelTracingMiddleware } from './middleware_otel_tracing.js'\nexport type { OtelTracingMiddlewareOptions } from './middleware_otel_tracing.js'\nexport {\n EventHandlerTimeoutError,\n EventHandlerCancelledError,\n EventHandlerAbortedError,\n EventHandlerResultSchemaError,\n} from './event_handler.js'\nexport type {\n EventConcurrencyMode,\n EventHandlerConcurrencyMode,\n EventHandlerCompletionMode,\n EventBusInterfaceForLockManager,\n} from './lock_manager.js'\nexport type { EventClass, EventHandlerCallable as EventHandler, EventPattern, EventStatus, FindOptions, FindWindow } from './types.js'\nexport { retry, clearSemaphoreRegistry, RetryTimeoutError, SemaphoreTimeoutError } from './retry.js'\nexport type { RetryOptions } from './retry.js'\nexport {\n HTTPEventBridge,\n SocketEventBridge,\n NATSEventBridge,\n RedisEventBridge,\n PostgresEventBridge,\n JSONLEventBridge,\n SQLiteEventBridge,\n} from './bridges.js'\nexport type { HTTPEventBridgeOptions } from './bridges.js'\nexport { events_suck } from './events_suck.js'\nexport type { EventsSuckClient, EventsSuckClientClass, GeneratedEvents } from './events_suck.js'\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAA2C;AAC3C,2BAA6B;AAE7B,0BAA4B;AAC5B,uBAAyB;AAEzB,qBAAkC;AAElC,qCAAsC;AAEtC,2BAKO;AAQP,mBAAwF;AAExF,qBAQO;AAEP,yBAA4B;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,28 @@
1
+ import { trace, type Tracer } from '@opentelemetry/api';
2
+ import type { BaseEvent } from './base_event.js';
3
+ import type { EventBus } from './event_bus.js';
4
+ import type { EventResult } from './event_result.js';
5
+ import type { EventBusMiddleware } from './middlewares.js';
6
+ import type { EventStatus } from './types.js';
7
+ type OpenTelemetryTraceApi = Pick<typeof trace, 'getTracer' | 'setSpan'>;
8
+ export type OtelTracingMiddlewareOptions = {
9
+ tracer?: Tracer;
10
+ trace_api?: OpenTelemetryTraceApi;
11
+ };
12
+ export declare class OtelTracingMiddleware implements EventBusMiddleware {
13
+ private readonly tracer;
14
+ private readonly trace_api;
15
+ private readonly event_spans;
16
+ private readonly event_contexts;
17
+ private readonly handler_spans;
18
+ private readonly handler_contexts;
19
+ constructor(options?: OtelTracingMiddlewareOptions);
20
+ onEventChange(eventbus: EventBus, event: BaseEvent, status: EventStatus): void;
21
+ onEventResultChange(eventbus: EventBus, event: BaseEvent, event_result: EventResult, status: EventStatus): void;
22
+ private startEventSpan;
23
+ private completeEventSpan;
24
+ private startHandlerSpan;
25
+ private completeHandlerSpan;
26
+ private parentContextForEvent;
27
+ }
28
+ export {};
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var middleware_otel_tracing_exports = {};
20
+ __export(middleware_otel_tracing_exports, {
21
+ OtelTracingMiddleware: () => OtelTracingMiddleware
22
+ });
23
+ module.exports = __toCommonJS(middleware_otel_tracing_exports);
24
+ var import_api = require("@opentelemetry/api");
25
+ class OtelTracingMiddleware {
26
+ tracer;
27
+ trace_api;
28
+ event_spans = /* @__PURE__ */ new Map();
29
+ event_contexts = /* @__PURE__ */ new Map();
30
+ handler_spans = /* @__PURE__ */ new Map();
31
+ handler_contexts = /* @__PURE__ */ new Map();
32
+ constructor(options = {}) {
33
+ this.trace_api = options.trace_api ?? import_api.trace;
34
+ this.tracer = options.tracer ?? this.trace_api.getTracer("abxbus");
35
+ }
36
+ onEventChange(eventbus, event, status) {
37
+ if (status === "started") {
38
+ this.startEventSpan(eventbus, event);
39
+ return;
40
+ }
41
+ if (status === "completed") {
42
+ this.completeEventSpan(eventbus, event);
43
+ }
44
+ }
45
+ onEventResultChange(eventbus, event, event_result, status) {
46
+ if (status === "started") {
47
+ this.startHandlerSpan(eventbus, event, event_result);
48
+ return;
49
+ }
50
+ if (status === "completed") {
51
+ this.completeHandlerSpan(event_result);
52
+ }
53
+ }
54
+ startEventSpan(eventbus, event) {
55
+ const existing = this.event_spans.get(event.event_id);
56
+ if (existing) {
57
+ return existing;
58
+ }
59
+ const parent_context = this.parentContextForEvent(event);
60
+ const span = this.tracer.startSpan(
61
+ `abxbus.event ${event.event_type}`,
62
+ {
63
+ attributes: compactAttributes({
64
+ "abxbus.bus.id": eventbus.id,
65
+ "abxbus.bus.name": eventbus.name,
66
+ "abxbus.event.id": event.event_id,
67
+ "abxbus.event.type": event.event_type,
68
+ "abxbus.event.version": event.event_version,
69
+ "abxbus.event.parent_id": event.event_parent_id,
70
+ "abxbus.event.emitted_by_handler_id": event.event_emitted_by_handler_id,
71
+ "abxbus.event.path": event.event_path.join(" ")
72
+ }),
73
+ startTime: dateFromIso(event.event_started_at)
74
+ },
75
+ parent_context
76
+ );
77
+ const span_context = this.trace_api.setSpan(parent_context ?? import_api.context.active(), span);
78
+ this.event_spans.set(event.event_id, span);
79
+ this.event_contexts.set(event.event_id, span_context);
80
+ return span;
81
+ }
82
+ completeEventSpan(eventbus, event) {
83
+ const span = this.event_spans.get(event.event_id) ?? this.startEventSpan(eventbus, event);
84
+ if (event.event_errors.length > 0) {
85
+ recordSpanError(span, event.event_errors[0]);
86
+ } else {
87
+ span.setStatus({ code: import_api.SpanStatusCode.OK });
88
+ }
89
+ span.setAttributes(
90
+ compactAttributes({
91
+ "abxbus.event.status": event.event_status,
92
+ "abxbus.event.result_count": event.event_results.size,
93
+ "abxbus.event.error_count": event.event_errors.length,
94
+ "abxbus.event.child_count": event.event_children.length
95
+ })
96
+ );
97
+ span.end(dateFromIso(event.event_completed_at));
98
+ this.event_spans.delete(event.event_id);
99
+ this.event_contexts.delete(event.event_id);
100
+ }
101
+ startHandlerSpan(eventbus, event, event_result) {
102
+ const existing = this.handler_spans.get(event_result.id);
103
+ if (existing) {
104
+ return existing;
105
+ }
106
+ const parent_context = this.event_contexts.get(event.event_id) ?? this.trace_api.setSpan(import_api.context.active(), this.startEventSpan(eventbus, event));
107
+ const span = this.tracer.startSpan(
108
+ `abxbus.handler ${event.event_type} ${event_result.handler_name}`,
109
+ {
110
+ attributes: compactAttributes({
111
+ "abxbus.bus.id": eventbus.id,
112
+ "abxbus.bus.name": eventbus.name,
113
+ "abxbus.event.id": event.event_id,
114
+ "abxbus.event.type": event.event_type,
115
+ "abxbus.handler.id": event_result.handler_id,
116
+ "abxbus.handler.name": event_result.handler_name,
117
+ "abxbus.handler.file_path": event_result.handler_file_path,
118
+ "abxbus.handler.event_pattern": event_result.handler.event_pattern,
119
+ "abxbus.event_result.id": event_result.id
120
+ }),
121
+ startTime: dateFromIso(event_result.started_at)
122
+ },
123
+ parent_context
124
+ );
125
+ const span_context = this.trace_api.setSpan(parent_context, span);
126
+ this.handler_spans.set(event_result.id, span);
127
+ this.handler_contexts.set(handlerSpanKey(event_result.event_id, event_result.handler_id), span_context);
128
+ return span;
129
+ }
130
+ completeHandlerSpan(event_result) {
131
+ const span = this.handler_spans.get(event_result.id);
132
+ if (!span) {
133
+ return;
134
+ }
135
+ if (event_result.error !== void 0) {
136
+ recordSpanError(span, event_result.error);
137
+ } else {
138
+ span.setStatus({ code: import_api.SpanStatusCode.OK });
139
+ }
140
+ span.setAttributes(
141
+ compactAttributes({
142
+ "abxbus.event_result.status": event_result.status,
143
+ "abxbus.handler.child_count": event_result.event_children.length
144
+ })
145
+ );
146
+ span.end(dateFromIso(event_result.completed_at));
147
+ this.handler_spans.delete(event_result.id);
148
+ this.handler_contexts.delete(handlerSpanKey(event_result.event_id, event_result.handler_id));
149
+ }
150
+ parentContextForEvent(event) {
151
+ if (event.event_parent_id && event.event_emitted_by_handler_id) {
152
+ const handler_context = this.handler_contexts.get(handlerSpanKey(event.event_parent_id, event.event_emitted_by_handler_id));
153
+ if (handler_context) {
154
+ return handler_context;
155
+ }
156
+ }
157
+ return event.event_parent_id ? this.event_contexts.get(event.event_parent_id) : void 0;
158
+ }
159
+ }
160
+ function handlerSpanKey(event_id, handler_id) {
161
+ return `${event_id}:${handler_id}`;
162
+ }
163
+ function dateFromIso(value) {
164
+ if (value == null) {
165
+ return void 0;
166
+ }
167
+ const date = new Date(value);
168
+ return Number.isNaN(date.getTime()) ? void 0 : date;
169
+ }
170
+ function compactAttributes(attributes) {
171
+ const compacted = {};
172
+ for (const [key, value] of Object.entries(attributes)) {
173
+ if (value !== null && value !== void 0) {
174
+ compacted[key] = value;
175
+ }
176
+ }
177
+ return compacted;
178
+ }
179
+ function recordSpanError(span, error) {
180
+ if (error instanceof Error) {
181
+ span.recordException(error);
182
+ span.setStatus({ code: import_api.SpanStatusCode.ERROR, message: error.message });
183
+ return;
184
+ }
185
+ const message = typeof error === "string" ? error : "Unknown abxbus handler error";
186
+ span.recordException(message);
187
+ span.setStatus({ code: import_api.SpanStatusCode.ERROR, message });
188
+ }
189
+ //# sourceMappingURL=middleware_otel_tracing.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/middleware_otel_tracing.ts"],
4
+ "sourcesContent": ["import { context, SpanStatusCode, trace, type Context, type Span, type Tracer } from '@opentelemetry/api'\n\nimport type { BaseEvent } from './base_event.js'\nimport type { EventBus } from './event_bus.js'\nimport type { EventResult } from './event_result.js'\nimport type { EventBusMiddleware } from './middlewares.js'\nimport type { EventStatus } from './types.js'\n\ntype OpenTelemetryTraceApi = Pick<typeof trace, 'getTracer' | 'setSpan'>\n\nexport type OtelTracingMiddlewareOptions = {\n tracer?: Tracer\n trace_api?: OpenTelemetryTraceApi\n}\n\nexport class OtelTracingMiddleware implements EventBusMiddleware {\n private readonly tracer: Tracer\n private readonly trace_api: OpenTelemetryTraceApi\n private readonly event_spans = new Map<string, Span>()\n private readonly event_contexts = new Map<string, Context>()\n private readonly handler_spans = new Map<string, Span>()\n private readonly handler_contexts = new Map<string, Context>()\n\n constructor(options: OtelTracingMiddlewareOptions = {}) {\n this.trace_api = options.trace_api ?? trace\n this.tracer = options.tracer ?? this.trace_api.getTracer('abxbus')\n }\n\n onEventChange(eventbus: EventBus, event: BaseEvent, status: EventStatus): void {\n if (status === 'started') {\n this.startEventSpan(eventbus, event)\n return\n }\n\n if (status === 'completed') {\n this.completeEventSpan(eventbus, event)\n }\n }\n\n onEventResultChange(eventbus: EventBus, event: BaseEvent, event_result: EventResult, status: EventStatus): void {\n if (status === 'started') {\n this.startHandlerSpan(eventbus, event, event_result)\n return\n }\n\n if (status === 'completed') {\n this.completeHandlerSpan(event_result)\n }\n }\n\n private startEventSpan(eventbus: EventBus, event: BaseEvent): Span {\n const existing = this.event_spans.get(event.event_id)\n if (existing) {\n return existing\n }\n\n const parent_context = this.parentContextForEvent(event)\n const span = this.tracer.startSpan(\n `abxbus.event ${event.event_type}`,\n {\n attributes: compactAttributes({\n 'abxbus.bus.id': eventbus.id,\n 'abxbus.bus.name': eventbus.name,\n 'abxbus.event.id': event.event_id,\n 'abxbus.event.type': event.event_type,\n 'abxbus.event.version': event.event_version,\n 'abxbus.event.parent_id': event.event_parent_id,\n 'abxbus.event.emitted_by_handler_id': event.event_emitted_by_handler_id,\n 'abxbus.event.path': event.event_path.join(' '),\n }),\n startTime: dateFromIso(event.event_started_at),\n },\n parent_context\n )\n const span_context = this.trace_api.setSpan(parent_context ?? context.active(), span)\n this.event_spans.set(event.event_id, span)\n this.event_contexts.set(event.event_id, span_context)\n return span\n }\n\n private completeEventSpan(eventbus: EventBus, event: BaseEvent): void {\n const span = this.event_spans.get(event.event_id) ?? this.startEventSpan(eventbus, event)\n if (event.event_errors.length > 0) {\n recordSpanError(span, event.event_errors[0])\n } else {\n span.setStatus({ code: SpanStatusCode.OK })\n }\n span.setAttributes(\n compactAttributes({\n 'abxbus.event.status': event.event_status,\n 'abxbus.event.result_count': event.event_results.size,\n 'abxbus.event.error_count': event.event_errors.length,\n 'abxbus.event.child_count': event.event_children.length,\n })\n )\n span.end(dateFromIso(event.event_completed_at))\n this.event_spans.delete(event.event_id)\n this.event_contexts.delete(event.event_id)\n }\n\n private startHandlerSpan(eventbus: EventBus, event: BaseEvent, event_result: EventResult): Span {\n const existing = this.handler_spans.get(event_result.id)\n if (existing) {\n return existing\n }\n\n const parent_context =\n this.event_contexts.get(event.event_id) ?? this.trace_api.setSpan(context.active(), this.startEventSpan(eventbus, event))\n const span = this.tracer.startSpan(\n `abxbus.handler ${event.event_type} ${event_result.handler_name}`,\n {\n attributes: compactAttributes({\n 'abxbus.bus.id': eventbus.id,\n 'abxbus.bus.name': eventbus.name,\n 'abxbus.event.id': event.event_id,\n 'abxbus.event.type': event.event_type,\n 'abxbus.handler.id': event_result.handler_id,\n 'abxbus.handler.name': event_result.handler_name,\n 'abxbus.handler.file_path': event_result.handler_file_path,\n 'abxbus.handler.event_pattern': event_result.handler.event_pattern,\n 'abxbus.event_result.id': event_result.id,\n }),\n startTime: dateFromIso(event_result.started_at),\n },\n parent_context\n )\n const span_context = this.trace_api.setSpan(parent_context, span)\n this.handler_spans.set(event_result.id, span)\n this.handler_contexts.set(handlerSpanKey(event_result.event_id, event_result.handler_id), span_context)\n return span\n }\n\n private completeHandlerSpan(event_result: EventResult): void {\n const span = this.handler_spans.get(event_result.id)\n if (!span) {\n return\n }\n\n if (event_result.error !== undefined) {\n recordSpanError(span, event_result.error)\n } else {\n span.setStatus({ code: SpanStatusCode.OK })\n }\n span.setAttributes(\n compactAttributes({\n 'abxbus.event_result.status': event_result.status,\n 'abxbus.handler.child_count': event_result.event_children.length,\n })\n )\n span.end(dateFromIso(event_result.completed_at))\n this.handler_spans.delete(event_result.id)\n this.handler_contexts.delete(handlerSpanKey(event_result.event_id, event_result.handler_id))\n }\n\n private parentContextForEvent(event: BaseEvent): Context | undefined {\n if (event.event_parent_id && event.event_emitted_by_handler_id) {\n const handler_context = this.handler_contexts.get(handlerSpanKey(event.event_parent_id, event.event_emitted_by_handler_id))\n if (handler_context) {\n return handler_context\n }\n }\n\n return event.event_parent_id ? this.event_contexts.get(event.event_parent_id) : undefined\n }\n}\n\nfunction handlerSpanKey(event_id: string, handler_id: string): string {\n return `${event_id}:${handler_id}`\n}\n\nfunction dateFromIso(value: string | null | undefined): Date | undefined {\n if (value == null) {\n return undefined\n }\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? undefined : date\n}\n\nfunction compactAttributes(\n attributes: Record<string, string | number | boolean | null | undefined>\n): Record<string, string | number | boolean> {\n const compacted: Record<string, string | number | boolean> = {}\n for (const [key, value] of Object.entries(attributes)) {\n if (value !== null && value !== undefined) {\n compacted[key] = value\n }\n }\n return compacted\n}\n\nfunction recordSpanError(span: Span, error: unknown): void {\n if (error instanceof Error) {\n span.recordException(error)\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message })\n return\n }\n\n const message = typeof error === 'string' ? error : 'Unknown abxbus handler error'\n span.recordException(message)\n span.setStatus({ code: SpanStatusCode.ERROR, message })\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
package/dist/esm/index.js CHANGED
@@ -3,6 +3,7 @@ import { EventHistory } from "./event_history.js";
3
3
  import { EventResult } from "./event_result.js";
4
4
  import { EventBus } from "./event_bus.js";
5
5
  import { monotonicDatetime } from "./helpers.js";
6
+ import { OtelTracingMiddleware } from "./middleware_otel_tracing.js";
6
7
  import {
7
8
  EventHandlerTimeoutError,
8
9
  EventHandlerCancelledError,
@@ -33,6 +34,7 @@ export {
33
34
  HTTPEventBridge,
34
35
  JSONLEventBridge,
35
36
  NATSEventBridge,
37
+ OtelTracingMiddleware,
36
38
  PostgresEventBridge,
37
39
  RedisEventBridge,
38
40
  RetryTimeoutError,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
- "sourcesContent": ["export { BaseEvent, BaseEventSchema } from './base_event.js'\nexport { EventHistory } from './event_history.js'\nexport type { EventHistoryFindOptions, EventHistoryTrimOptions } from './event_history.js'\nexport { EventResult } from './event_result.js'\nexport { EventBus } from './event_bus.js'\nexport type { EventBusJSON, EventBusOptions } from './event_bus.js'\nexport { monotonicDatetime } from './helpers.js'\nexport type { EventBusMiddleware, EventBusMiddlewareCtor, EventBusMiddlewareInput } from './middlewares.js'\nexport {\n EventHandlerTimeoutError,\n EventHandlerCancelledError,\n EventHandlerAbortedError,\n EventHandlerResultSchemaError,\n} from './event_handler.js'\nexport type {\n EventConcurrencyMode,\n EventHandlerConcurrencyMode,\n EventHandlerCompletionMode,\n EventBusInterfaceForLockManager,\n} from './lock_manager.js'\nexport type { EventClass, EventHandlerCallable as EventHandler, EventPattern, EventStatus, FindOptions, FindWindow } from './types.js'\nexport { retry, clearSemaphoreRegistry, RetryTimeoutError, SemaphoreTimeoutError } from './retry.js'\nexport type { RetryOptions } from './retry.js'\nexport {\n HTTPEventBridge,\n SocketEventBridge,\n NATSEventBridge,\n RedisEventBridge,\n PostgresEventBridge,\n JSONLEventBridge,\n SQLiteEventBridge,\n} from './bridges.js'\nexport type { HTTPEventBridgeOptions } from './bridges.js'\nexport { events_suck } from './events_suck.js'\nexport type { EventsSuckClient, EventsSuckClientClass, GeneratedEvents } from './events_suck.js'\n"],
5
- "mappings": "AAAA,SAAS,WAAW,uBAAuB;AAC3C,SAAS,oBAAoB;AAE7B,SAAS,mBAAmB;AAC5B,SAAS,gBAAgB;AAEzB,SAAS,yBAAyB;AAElC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAQP,SAAS,OAAO,wBAAwB,mBAAmB,6BAA6B;AAExF;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,mBAAmB;",
4
+ "sourcesContent": ["export { BaseEvent, BaseEventSchema } from './base_event.js'\nexport { EventHistory } from './event_history.js'\nexport type { EventHistoryFindOptions, EventHistoryTrimOptions } from './event_history.js'\nexport { EventResult } from './event_result.js'\nexport { EventBus } from './event_bus.js'\nexport type { EventBusJSON, EventBusOptions } from './event_bus.js'\nexport { monotonicDatetime } from './helpers.js'\nexport type { EventBusMiddleware, EventBusMiddlewareCtor, EventBusMiddlewareInput } from './middlewares.js'\nexport { OtelTracingMiddleware } from './middleware_otel_tracing.js'\nexport type { OtelTracingMiddlewareOptions } from './middleware_otel_tracing.js'\nexport {\n EventHandlerTimeoutError,\n EventHandlerCancelledError,\n EventHandlerAbortedError,\n EventHandlerResultSchemaError,\n} from './event_handler.js'\nexport type {\n EventConcurrencyMode,\n EventHandlerConcurrencyMode,\n EventHandlerCompletionMode,\n EventBusInterfaceForLockManager,\n} from './lock_manager.js'\nexport type { EventClass, EventHandlerCallable as EventHandler, EventPattern, EventStatus, FindOptions, FindWindow } from './types.js'\nexport { retry, clearSemaphoreRegistry, RetryTimeoutError, SemaphoreTimeoutError } from './retry.js'\nexport type { RetryOptions } from './retry.js'\nexport {\n HTTPEventBridge,\n SocketEventBridge,\n NATSEventBridge,\n RedisEventBridge,\n PostgresEventBridge,\n JSONLEventBridge,\n SQLiteEventBridge,\n} from './bridges.js'\nexport type { HTTPEventBridgeOptions } from './bridges.js'\nexport { events_suck } from './events_suck.js'\nexport type { EventsSuckClient, EventsSuckClientClass, GeneratedEvents } from './events_suck.js'\n"],
5
+ "mappings": "AAAA,SAAS,WAAW,uBAAuB;AAC3C,SAAS,oBAAoB;AAE7B,SAAS,mBAAmB;AAC5B,SAAS,gBAAgB;AAEzB,SAAS,yBAAyB;AAElC,SAAS,6BAA6B;AAEtC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAQP,SAAS,OAAO,wBAAwB,mBAAmB,6BAA6B;AAExF;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,mBAAmB;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,169 @@
1
+ import { context, SpanStatusCode, trace } from "@opentelemetry/api";
2
+ class OtelTracingMiddleware {
3
+ tracer;
4
+ trace_api;
5
+ event_spans = /* @__PURE__ */ new Map();
6
+ event_contexts = /* @__PURE__ */ new Map();
7
+ handler_spans = /* @__PURE__ */ new Map();
8
+ handler_contexts = /* @__PURE__ */ new Map();
9
+ constructor(options = {}) {
10
+ this.trace_api = options.trace_api ?? trace;
11
+ this.tracer = options.tracer ?? this.trace_api.getTracer("abxbus");
12
+ }
13
+ onEventChange(eventbus, event, status) {
14
+ if (status === "started") {
15
+ this.startEventSpan(eventbus, event);
16
+ return;
17
+ }
18
+ if (status === "completed") {
19
+ this.completeEventSpan(eventbus, event);
20
+ }
21
+ }
22
+ onEventResultChange(eventbus, event, event_result, status) {
23
+ if (status === "started") {
24
+ this.startHandlerSpan(eventbus, event, event_result);
25
+ return;
26
+ }
27
+ if (status === "completed") {
28
+ this.completeHandlerSpan(event_result);
29
+ }
30
+ }
31
+ startEventSpan(eventbus, event) {
32
+ const existing = this.event_spans.get(event.event_id);
33
+ if (existing) {
34
+ return existing;
35
+ }
36
+ const parent_context = this.parentContextForEvent(event);
37
+ const span = this.tracer.startSpan(
38
+ `abxbus.event ${event.event_type}`,
39
+ {
40
+ attributes: compactAttributes({
41
+ "abxbus.bus.id": eventbus.id,
42
+ "abxbus.bus.name": eventbus.name,
43
+ "abxbus.event.id": event.event_id,
44
+ "abxbus.event.type": event.event_type,
45
+ "abxbus.event.version": event.event_version,
46
+ "abxbus.event.parent_id": event.event_parent_id,
47
+ "abxbus.event.emitted_by_handler_id": event.event_emitted_by_handler_id,
48
+ "abxbus.event.path": event.event_path.join(" ")
49
+ }),
50
+ startTime: dateFromIso(event.event_started_at)
51
+ },
52
+ parent_context
53
+ );
54
+ const span_context = this.trace_api.setSpan(parent_context ?? context.active(), span);
55
+ this.event_spans.set(event.event_id, span);
56
+ this.event_contexts.set(event.event_id, span_context);
57
+ return span;
58
+ }
59
+ completeEventSpan(eventbus, event) {
60
+ const span = this.event_spans.get(event.event_id) ?? this.startEventSpan(eventbus, event);
61
+ if (event.event_errors.length > 0) {
62
+ recordSpanError(span, event.event_errors[0]);
63
+ } else {
64
+ span.setStatus({ code: SpanStatusCode.OK });
65
+ }
66
+ span.setAttributes(
67
+ compactAttributes({
68
+ "abxbus.event.status": event.event_status,
69
+ "abxbus.event.result_count": event.event_results.size,
70
+ "abxbus.event.error_count": event.event_errors.length,
71
+ "abxbus.event.child_count": event.event_children.length
72
+ })
73
+ );
74
+ span.end(dateFromIso(event.event_completed_at));
75
+ this.event_spans.delete(event.event_id);
76
+ this.event_contexts.delete(event.event_id);
77
+ }
78
+ startHandlerSpan(eventbus, event, event_result) {
79
+ const existing = this.handler_spans.get(event_result.id);
80
+ if (existing) {
81
+ return existing;
82
+ }
83
+ const parent_context = this.event_contexts.get(event.event_id) ?? this.trace_api.setSpan(context.active(), this.startEventSpan(eventbus, event));
84
+ const span = this.tracer.startSpan(
85
+ `abxbus.handler ${event.event_type} ${event_result.handler_name}`,
86
+ {
87
+ attributes: compactAttributes({
88
+ "abxbus.bus.id": eventbus.id,
89
+ "abxbus.bus.name": eventbus.name,
90
+ "abxbus.event.id": event.event_id,
91
+ "abxbus.event.type": event.event_type,
92
+ "abxbus.handler.id": event_result.handler_id,
93
+ "abxbus.handler.name": event_result.handler_name,
94
+ "abxbus.handler.file_path": event_result.handler_file_path,
95
+ "abxbus.handler.event_pattern": event_result.handler.event_pattern,
96
+ "abxbus.event_result.id": event_result.id
97
+ }),
98
+ startTime: dateFromIso(event_result.started_at)
99
+ },
100
+ parent_context
101
+ );
102
+ const span_context = this.trace_api.setSpan(parent_context, span);
103
+ this.handler_spans.set(event_result.id, span);
104
+ this.handler_contexts.set(handlerSpanKey(event_result.event_id, event_result.handler_id), span_context);
105
+ return span;
106
+ }
107
+ completeHandlerSpan(event_result) {
108
+ const span = this.handler_spans.get(event_result.id);
109
+ if (!span) {
110
+ return;
111
+ }
112
+ if (event_result.error !== void 0) {
113
+ recordSpanError(span, event_result.error);
114
+ } else {
115
+ span.setStatus({ code: SpanStatusCode.OK });
116
+ }
117
+ span.setAttributes(
118
+ compactAttributes({
119
+ "abxbus.event_result.status": event_result.status,
120
+ "abxbus.handler.child_count": event_result.event_children.length
121
+ })
122
+ );
123
+ span.end(dateFromIso(event_result.completed_at));
124
+ this.handler_spans.delete(event_result.id);
125
+ this.handler_contexts.delete(handlerSpanKey(event_result.event_id, event_result.handler_id));
126
+ }
127
+ parentContextForEvent(event) {
128
+ if (event.event_parent_id && event.event_emitted_by_handler_id) {
129
+ const handler_context = this.handler_contexts.get(handlerSpanKey(event.event_parent_id, event.event_emitted_by_handler_id));
130
+ if (handler_context) {
131
+ return handler_context;
132
+ }
133
+ }
134
+ return event.event_parent_id ? this.event_contexts.get(event.event_parent_id) : void 0;
135
+ }
136
+ }
137
+ function handlerSpanKey(event_id, handler_id) {
138
+ return `${event_id}:${handler_id}`;
139
+ }
140
+ function dateFromIso(value) {
141
+ if (value == null) {
142
+ return void 0;
143
+ }
144
+ const date = new Date(value);
145
+ return Number.isNaN(date.getTime()) ? void 0 : date;
146
+ }
147
+ function compactAttributes(attributes) {
148
+ const compacted = {};
149
+ for (const [key, value] of Object.entries(attributes)) {
150
+ if (value !== null && value !== void 0) {
151
+ compacted[key] = value;
152
+ }
153
+ }
154
+ return compacted;
155
+ }
156
+ function recordSpanError(span, error) {
157
+ if (error instanceof Error) {
158
+ span.recordException(error);
159
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
160
+ return;
161
+ }
162
+ const message = typeof error === "string" ? error : "Unknown abxbus handler error";
163
+ span.recordException(message);
164
+ span.setStatus({ code: SpanStatusCode.ERROR, message });
165
+ }
166
+ export {
167
+ OtelTracingMiddleware
168
+ };
169
+ //# sourceMappingURL=middleware_otel_tracing.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/middleware_otel_tracing.ts"],
4
+ "sourcesContent": ["import { context, SpanStatusCode, trace, type Context, type Span, type Tracer } from '@opentelemetry/api'\n\nimport type { BaseEvent } from './base_event.js'\nimport type { EventBus } from './event_bus.js'\nimport type { EventResult } from './event_result.js'\nimport type { EventBusMiddleware } from './middlewares.js'\nimport type { EventStatus } from './types.js'\n\ntype OpenTelemetryTraceApi = Pick<typeof trace, 'getTracer' | 'setSpan'>\n\nexport type OtelTracingMiddlewareOptions = {\n tracer?: Tracer\n trace_api?: OpenTelemetryTraceApi\n}\n\nexport class OtelTracingMiddleware implements EventBusMiddleware {\n private readonly tracer: Tracer\n private readonly trace_api: OpenTelemetryTraceApi\n private readonly event_spans = new Map<string, Span>()\n private readonly event_contexts = new Map<string, Context>()\n private readonly handler_spans = new Map<string, Span>()\n private readonly handler_contexts = new Map<string, Context>()\n\n constructor(options: OtelTracingMiddlewareOptions = {}) {\n this.trace_api = options.trace_api ?? trace\n this.tracer = options.tracer ?? this.trace_api.getTracer('abxbus')\n }\n\n onEventChange(eventbus: EventBus, event: BaseEvent, status: EventStatus): void {\n if (status === 'started') {\n this.startEventSpan(eventbus, event)\n return\n }\n\n if (status === 'completed') {\n this.completeEventSpan(eventbus, event)\n }\n }\n\n onEventResultChange(eventbus: EventBus, event: BaseEvent, event_result: EventResult, status: EventStatus): void {\n if (status === 'started') {\n this.startHandlerSpan(eventbus, event, event_result)\n return\n }\n\n if (status === 'completed') {\n this.completeHandlerSpan(event_result)\n }\n }\n\n private startEventSpan(eventbus: EventBus, event: BaseEvent): Span {\n const existing = this.event_spans.get(event.event_id)\n if (existing) {\n return existing\n }\n\n const parent_context = this.parentContextForEvent(event)\n const span = this.tracer.startSpan(\n `abxbus.event ${event.event_type}`,\n {\n attributes: compactAttributes({\n 'abxbus.bus.id': eventbus.id,\n 'abxbus.bus.name': eventbus.name,\n 'abxbus.event.id': event.event_id,\n 'abxbus.event.type': event.event_type,\n 'abxbus.event.version': event.event_version,\n 'abxbus.event.parent_id': event.event_parent_id,\n 'abxbus.event.emitted_by_handler_id': event.event_emitted_by_handler_id,\n 'abxbus.event.path': event.event_path.join(' '),\n }),\n startTime: dateFromIso(event.event_started_at),\n },\n parent_context\n )\n const span_context = this.trace_api.setSpan(parent_context ?? context.active(), span)\n this.event_spans.set(event.event_id, span)\n this.event_contexts.set(event.event_id, span_context)\n return span\n }\n\n private completeEventSpan(eventbus: EventBus, event: BaseEvent): void {\n const span = this.event_spans.get(event.event_id) ?? this.startEventSpan(eventbus, event)\n if (event.event_errors.length > 0) {\n recordSpanError(span, event.event_errors[0])\n } else {\n span.setStatus({ code: SpanStatusCode.OK })\n }\n span.setAttributes(\n compactAttributes({\n 'abxbus.event.status': event.event_status,\n 'abxbus.event.result_count': event.event_results.size,\n 'abxbus.event.error_count': event.event_errors.length,\n 'abxbus.event.child_count': event.event_children.length,\n })\n )\n span.end(dateFromIso(event.event_completed_at))\n this.event_spans.delete(event.event_id)\n this.event_contexts.delete(event.event_id)\n }\n\n private startHandlerSpan(eventbus: EventBus, event: BaseEvent, event_result: EventResult): Span {\n const existing = this.handler_spans.get(event_result.id)\n if (existing) {\n return existing\n }\n\n const parent_context =\n this.event_contexts.get(event.event_id) ?? this.trace_api.setSpan(context.active(), this.startEventSpan(eventbus, event))\n const span = this.tracer.startSpan(\n `abxbus.handler ${event.event_type} ${event_result.handler_name}`,\n {\n attributes: compactAttributes({\n 'abxbus.bus.id': eventbus.id,\n 'abxbus.bus.name': eventbus.name,\n 'abxbus.event.id': event.event_id,\n 'abxbus.event.type': event.event_type,\n 'abxbus.handler.id': event_result.handler_id,\n 'abxbus.handler.name': event_result.handler_name,\n 'abxbus.handler.file_path': event_result.handler_file_path,\n 'abxbus.handler.event_pattern': event_result.handler.event_pattern,\n 'abxbus.event_result.id': event_result.id,\n }),\n startTime: dateFromIso(event_result.started_at),\n },\n parent_context\n )\n const span_context = this.trace_api.setSpan(parent_context, span)\n this.handler_spans.set(event_result.id, span)\n this.handler_contexts.set(handlerSpanKey(event_result.event_id, event_result.handler_id), span_context)\n return span\n }\n\n private completeHandlerSpan(event_result: EventResult): void {\n const span = this.handler_spans.get(event_result.id)\n if (!span) {\n return\n }\n\n if (event_result.error !== undefined) {\n recordSpanError(span, event_result.error)\n } else {\n span.setStatus({ code: SpanStatusCode.OK })\n }\n span.setAttributes(\n compactAttributes({\n 'abxbus.event_result.status': event_result.status,\n 'abxbus.handler.child_count': event_result.event_children.length,\n })\n )\n span.end(dateFromIso(event_result.completed_at))\n this.handler_spans.delete(event_result.id)\n this.handler_contexts.delete(handlerSpanKey(event_result.event_id, event_result.handler_id))\n }\n\n private parentContextForEvent(event: BaseEvent): Context | undefined {\n if (event.event_parent_id && event.event_emitted_by_handler_id) {\n const handler_context = this.handler_contexts.get(handlerSpanKey(event.event_parent_id, event.event_emitted_by_handler_id))\n if (handler_context) {\n return handler_context\n }\n }\n\n return event.event_parent_id ? this.event_contexts.get(event.event_parent_id) : undefined\n }\n}\n\nfunction handlerSpanKey(event_id: string, handler_id: string): string {\n return `${event_id}:${handler_id}`\n}\n\nfunction dateFromIso(value: string | null | undefined): Date | undefined {\n if (value == null) {\n return undefined\n }\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? undefined : date\n}\n\nfunction compactAttributes(\n attributes: Record<string, string | number | boolean | null | undefined>\n): Record<string, string | number | boolean> {\n const compacted: Record<string, string | number | boolean> = {}\n for (const [key, value] of Object.entries(attributes)) {\n if (value !== null && value !== undefined) {\n compacted[key] = value\n }\n }\n return compacted\n}\n\nfunction recordSpanError(span: Span, error: unknown): void {\n if (error instanceof Error) {\n span.recordException(error)\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message })\n return\n }\n\n const message = typeof error === 'string' ? error : 'Unknown abxbus handler error'\n span.recordException(message)\n span.setStatus({ code: SpanStatusCode.ERROR, message })\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -6,6 +6,8 @@ export { EventBus } from './event_bus.js';
6
6
  export type { EventBusJSON, EventBusOptions } from './event_bus.js';
7
7
  export { monotonicDatetime } from './helpers.js';
8
8
  export type { EventBusMiddleware, EventBusMiddlewareCtor, EventBusMiddlewareInput } from './middlewares.js';
9
+ export { OtelTracingMiddleware } from './middleware_otel_tracing.js';
10
+ export type { OtelTracingMiddlewareOptions } from './middleware_otel_tracing.js';
9
11
  export { EventHandlerTimeoutError, EventHandlerCancelledError, EventHandlerAbortedError, EventHandlerResultSchemaError, } from './event_handler.js';
10
12
  export type { EventConcurrencyMode, EventHandlerConcurrencyMode, EventHandlerCompletionMode, EventBusInterfaceForLockManager, } from './lock_manager.js';
11
13
  export type { EventClass, EventHandlerCallable as EventHandler, EventPattern, EventStatus, FindOptions, FindWindow } from './types.js';
@@ -0,0 +1,28 @@
1
+ import { trace, type Tracer } from '@opentelemetry/api';
2
+ import type { BaseEvent } from './base_event.js';
3
+ import type { EventBus } from './event_bus.js';
4
+ import type { EventResult } from './event_result.js';
5
+ import type { EventBusMiddleware } from './middlewares.js';
6
+ import type { EventStatus } from './types.js';
7
+ type OpenTelemetryTraceApi = Pick<typeof trace, 'getTracer' | 'setSpan'>;
8
+ export type OtelTracingMiddlewareOptions = {
9
+ tracer?: Tracer;
10
+ trace_api?: OpenTelemetryTraceApi;
11
+ };
12
+ export declare class OtelTracingMiddleware implements EventBusMiddleware {
13
+ private readonly tracer;
14
+ private readonly trace_api;
15
+ private readonly event_spans;
16
+ private readonly event_contexts;
17
+ private readonly handler_spans;
18
+ private readonly handler_contexts;
19
+ constructor(options?: OtelTracingMiddlewareOptions);
20
+ onEventChange(eventbus: EventBus, event: BaseEvent, status: EventStatus): void;
21
+ onEventResultChange(eventbus: EventBus, event: BaseEvent, event_result: EventResult, status: EventStatus): void;
22
+ private startEventSpan;
23
+ private completeEventSpan;
24
+ private startHandlerSpan;
25
+ private completeHandlerSpan;
26
+ private parentContextForEvent;
27
+ }
28
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abxbus",
3
- "version": "2.4.15",
3
+ "version": "2.4.16",
4
4
  "description": "Event bus library for browsers and ESM Node.js",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -30,6 +30,7 @@
30
30
  "author": "",
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
+ "@opentelemetry/api": "^1.9.1",
33
34
  "uuid": "^13.0.0",
34
35
  "zod": "^4.3.6"
35
36
  },