@asenajs/asena-otel 1.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.
Files changed (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +331 -0
  3. package/dist/index.d.ts +9 -0
  4. package/dist/index.js +15 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/lib/OtelConfig.d.ts +15 -0
  7. package/dist/lib/OtelConfig.js +2 -0
  8. package/dist/lib/OtelConfig.js.map +1 -0
  9. package/dist/lib/constants/OtelConstants.d.ts +11 -0
  10. package/dist/lib/constants/OtelConstants.js +12 -0
  11. package/dist/lib/constants/OtelConstants.js.map +1 -0
  12. package/dist/lib/decorators/Otel.d.ts +20 -0
  13. package/dist/lib/decorators/Otel.js +28 -0
  14. package/dist/lib/decorators/Otel.js.map +1 -0
  15. package/dist/lib/decorators/index.d.ts +1 -0
  16. package/dist/lib/decorators/index.js +2 -0
  17. package/dist/lib/decorators/index.js.map +1 -0
  18. package/dist/lib/middleware/OtelTracingMiddleware.d.ts +10 -0
  19. package/dist/lib/middleware/OtelTracingMiddleware.js +94 -0
  20. package/dist/lib/middleware/OtelTracingMiddleware.js.map +1 -0
  21. package/dist/lib/postprocessor/OtelTracingPostProcessor.d.ts +19 -0
  22. package/dist/lib/postprocessor/OtelTracingPostProcessor.js +164 -0
  23. package/dist/lib/postprocessor/OtelTracingPostProcessor.js.map +1 -0
  24. package/dist/lib/samplers/index.d.ts +8 -0
  25. package/dist/lib/samplers/index.js +13 -0
  26. package/dist/lib/samplers/index.js.map +1 -0
  27. package/dist/lib/services/OtelService.d.ts +11 -0
  28. package/dist/lib/services/OtelService.js +62 -0
  29. package/dist/lib/services/OtelService.js.map +1 -0
  30. package/dist/lib/shared/OtelRuntimeConfig.d.ts +8 -0
  31. package/dist/lib/shared/OtelRuntimeConfig.js +21 -0
  32. package/dist/lib/shared/OtelRuntimeConfig.js.map +1 -0
  33. package/dist/lib/types/index.d.ts +1 -0
  34. package/dist/lib/types/index.js +2 -0
  35. package/dist/lib/types/index.js.map +1 -0
  36. package/package.json +65 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 AsenaJs
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 ADDED
@@ -0,0 +1,331 @@
1
+ # @asenajs/asena-otel
2
+
3
+ OpenTelemetry integration for AsenaJS — automatic HTTP tracing, method-level auto-tracing, metrics, and distributed tracing support.
4
+
5
+ A single request automatically produces a full waterfall trace:
6
+
7
+ ```
8
+ GET /api/users (SERVER)
9
+ └─ UserController.list (INTERNAL)
10
+ └─ UserService.getAll (INTERNAL)
11
+ ```
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ bun add @asenajs/asena-otel @opentelemetry/api @opentelemetry/resources @opentelemetry/sdk-trace-base @opentelemetry/sdk-metrics @opentelemetry/semantic-conventions @opentelemetry/context-async-hooks
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ### Step 1: Create an @Otel class
22
+
23
+ Create a class that extends `OtelTracingPostProcessor` and apply the `@Otel` decorator with your configuration. Asena automatically discovers and initializes it during bootstrap.
24
+
25
+ ```typescript
26
+ // src/otel/AppOtel.ts
27
+ import { Otel, OtelTracingPostProcessor } from '@asenajs/asena-otel';
28
+ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
29
+ import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
30
+ import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';
31
+
32
+ @Otel({
33
+ serviceName: 'my-app',
34
+ serviceVersion: '1.0.0',
35
+ traceExporter: new OTLPTraceExporter({
36
+ url: 'http://localhost:4318/v1/traces',
37
+ }),
38
+ metricReader: new PeriodicExportingMetricReader({
39
+ exporter: new OTLPMetricExporter({
40
+ url: 'http://localhost:4318/v1/metrics',
41
+ }),
42
+ }),
43
+ autoTrace: {
44
+ services: true, // auto-trace @Service methods
45
+ controllers: true, // auto-trace @Controller methods
46
+ },
47
+ })
48
+ export class AppOtel extends OtelTracingPostProcessor {}
49
+ ```
50
+
51
+ ### Step 2: Register OtelTracingMiddleware in your Config
52
+
53
+ Add `OtelTracingMiddleware` to your config's `globalMiddlewares()`. This is required so that all HTTP requests are traced automatically.
54
+
55
+ ```typescript
56
+ import { Config } from '@asenajs/asena/decorators';
57
+ import { ConfigService, type Context, HttpException } from '@asenajs/ergenecore';
58
+ import { OtelTracingMiddleware } from '@asenajs/asena-otel';
59
+ import { AppCorsMiddleware } from '../middlewares/AppCorsMiddleware';
60
+
61
+ @Config()
62
+ export class AppConfig extends ConfigService {
63
+
64
+ public globalMiddlewares() {
65
+ return [
66
+ OtelTracingMiddleware, // traces all HTTP requests automatically
67
+ AppCorsMiddleware,
68
+ ];
69
+ }
70
+
71
+ public onError(error: Error, context: Context) {
72
+ if (error instanceof HttpException) {
73
+ return context.send(error.body, error.status);
74
+ }
75
+ return context.send({ error: 'Internal Server Error' }, 500);
76
+ }
77
+
78
+ }
79
+ ```
80
+
81
+ That's it. Asena's IoC container automatically discovers `AppOtel`, `OtelService`, and `OtelTracingMiddleware`. All HTTP requests are traced, service methods are auto-traced, and metrics are collected — without changing any business logic.
82
+
83
+ ## Components
84
+
85
+ ### @Otel Decorator
86
+
87
+ Configures an `OtelTracingPostProcessor` subclass with OpenTelemetry options. Apply it to a class extending `OtelTracingPostProcessor`:
88
+
89
+ ```typescript
90
+ import { Otel, OtelTracingPostProcessor } from '@asenajs/asena-otel';
91
+
92
+ @Otel({
93
+ serviceName: 'my-app',
94
+ traceExporter: exporter,
95
+ autoTrace: { services: true, controllers: true },
96
+ })
97
+ export class AppOtel extends OtelTracingPostProcessor {}
98
+ ```
99
+
100
+ The decorator stores the options as metadata and applies `@PostProcessor()` automatically. During bootstrap, `@PostConstruct()` in `OtelTracingPostProcessor` reads the metadata and initializes the OpenTelemetry SDK — tracer provider, meter provider, context manager, and shutdown hooks.
101
+
102
+ ### OtelService
103
+
104
+ Injectable `@Service` that provides access to OpenTelemetry tracer and meter. Use it for custom spans and distributed tracing. Asena automatically discovers and registers it.
105
+
106
+ ```typescript
107
+ import { Inject } from '@asenajs/asena/decorators/ioc';
108
+ import type { OtelService } from '@asenajs/asena-otel';
109
+
110
+ @Service()
111
+ export class OrderService {
112
+
113
+ @Inject('OtelService')
114
+ private otelService: OtelService;
115
+
116
+ async processOrder(orderId: string) {
117
+ return this.otelService.withSpan('process-order', async (span) => {
118
+ span.setAttribute('order.id', orderId);
119
+ // ... business logic
120
+ return { success: true };
121
+ });
122
+ // span automatically ends, errors are recorded
123
+ }
124
+
125
+ }
126
+ ```
127
+
128
+ **API:**
129
+ - `tracer` — OpenTelemetry `Tracer` instance
130
+ - `meter` — OpenTelemetry `Meter` instance
131
+ - `withSpan(name, fn)` — creates a span, sets OK/ERROR status, records exceptions, ends span automatically
132
+ - `getActiveSpan()` — returns current active span (or undefined)
133
+ - `injectTraceContext(headers?)` — injects W3C `traceparent` header into a headers object for distributed tracing (see [Outgoing Request Context Propagation](#outgoing-request-context-propagation))
134
+
135
+ **Expression injection** (for direct tracer/meter access):
136
+ ```typescript
137
+ @Inject('OtelService', (s) => s.tracer)
138
+ private tracer: Tracer;
139
+
140
+ @Inject('OtelService', (s) => s.meter)
141
+ private meter: Meter;
142
+ ```
143
+
144
+ ### OtelTracingMiddleware
145
+
146
+ `@Middleware` that automatically traces all HTTP requests. Register in your Config's `globalMiddlewares()` to enable request tracing.
147
+
148
+ **Creates for each request:**
149
+ - A `SERVER` span named `"{METHOD} {PATH}"` (e.g., `GET /api/users`)
150
+ - Attributes: `http.request.method`, `url.path`, `http.route` (after route matching), `http.response.status_code`
151
+ - Metrics: `http.server.request.count` (Counter), `http.server.request.duration` (Histogram)
152
+ - Extracts W3C `traceparent` header from incoming requests for distributed tracing
153
+
154
+ ### OtelTracingPostProcessor
155
+
156
+ `@PostProcessor` that wraps Service and Controller methods with tracing spans via JavaScript Proxy.
157
+
158
+ - Creates `INTERNAL` spans named `"{ClassName}.{methodName}"` (e.g., `UserService.getAll`)
159
+ - Spans are automatically children of the HTTP span (proper waterfall hierarchy)
160
+ - Controlled via `autoTrace` config in the `@Otel` decorator options
161
+ - Skips private methods (`_` prefix), constructors, and Symbol properties
162
+
163
+ ## Configuration
164
+
165
+ ### AsenaOtelOptions
166
+
167
+ ```typescript
168
+ interface AsenaOtelOptions {
169
+ serviceName: string; // Required: identifies your service
170
+ serviceVersion?: string; // Optional: defaults to '0.0.0'
171
+ traceExporter: SpanExporter; // Required: where to send spans
172
+ metricReader?: MetricReader; // Optional: enables metrics collection
173
+ autoTrace?: AutoTraceConfig; // Optional: auto-trace settings
174
+ sampler?: Sampler; // Optional: custom sampling strategy
175
+ ignoreRoutes?: string[]; // Optional: routes to exclude from tracing
176
+ }
177
+
178
+ interface AutoTraceConfig {
179
+ services?: boolean; // auto-trace @Service methods (default: false)
180
+ controllers?: boolean; // auto-trace @Controller methods (default: false)
181
+ }
182
+ ```
183
+
184
+ ## Sampling
185
+
186
+ Use `ratioBasedSampler()` for production environments to control trace volume:
187
+
188
+ ```typescript
189
+ import { Otel, OtelTracingPostProcessor, ratioBasedSampler } from '@asenajs/asena-otel';
190
+
191
+ @Otel({
192
+ serviceName: 'my-app',
193
+ traceExporter: exporter,
194
+ sampler: ratioBasedSampler(0.1), // sample 10% of traces
195
+ autoTrace: { services: true, controllers: true },
196
+ })
197
+ export class AppOtel extends OtelTracingPostProcessor {}
198
+ ```
199
+
200
+ `ratioBasedSampler(ratio)` creates a `ParentBasedSampler` wrapping `TraceIdRatioBasedSampler`. Root spans are sampled at the given ratio (0.0–1.0); child spans respect the parent's decision.
201
+
202
+ ## Route Exclusion
203
+
204
+ Use `ignoreRoutes` to skip tracing on specific paths (e.g., health checks, metrics endpoints):
205
+
206
+ ```typescript
207
+ @Otel({
208
+ serviceName: 'my-app',
209
+ traceExporter: exporter,
210
+ ignoreRoutes: ['/health', '/metrics', '/admin/*'],
211
+ })
212
+ export class AppOtel extends OtelTracingPostProcessor {}
213
+ ```
214
+
215
+ - **Exact match:** `/health` matches only `/health`
216
+ - **Wildcard suffix:** `/admin/*` matches `/admin/` and all sub-paths
217
+
218
+ Ignored routes produce no spans and no metrics.
219
+
220
+ ## Outgoing Request Context Propagation
221
+
222
+ `@asenajs/asena-otel` automatically traces **incoming** HTTP requests via `OtelTracingMiddleware` (extracts W3C `traceparent` header). However, **outgoing** HTTP calls (e.g., `fetch` to another service) are **not** automatically instrumented.
223
+
224
+ Use `OtelService.injectTraceContext()` to manually propagate trace context to downstream services:
225
+
226
+ ```typescript
227
+ @Service()
228
+ export class PaymentClient {
229
+
230
+ @Inject('OtelService')
231
+ private otelService: OtelService;
232
+
233
+ async charge(payload: ChargeRequest) {
234
+ // injectTraceContext() adds the W3C traceparent header
235
+ const headers = this.otelService.injectTraceContext({
236
+ 'Content-Type': 'application/json',
237
+ });
238
+
239
+ const res = await fetch('http://payment-service/api/charge', {
240
+ method: 'POST',
241
+ headers,
242
+ body: JSON.stringify(payload),
243
+ });
244
+
245
+ return res.json();
246
+ }
247
+
248
+ }
249
+ ```
250
+
251
+ For full visibility, combine with `withSpan()` to create a dedicated span for the outgoing call:
252
+
253
+ ```typescript
254
+ async charge(payload: ChargeRequest) {
255
+ return this.otelService.withSpan('call-payment-service', async (span) => {
256
+ span.setAttribute('service.target', 'payment-service');
257
+
258
+ const headers = this.otelService.injectTraceContext();
259
+ const res = await fetch('http://payment-service/api/charge', {
260
+ method: 'POST',
261
+ headers: { ...headers, 'Content-Type': 'application/json' },
262
+ body: JSON.stringify(payload),
263
+ });
264
+
265
+ return res.json();
266
+ });
267
+ }
268
+ ```
269
+
270
+ This writes a `traceparent` header in the format `00-{traceId}-{spanId}-{traceFlags}`. The downstream service extracts this header to continue the same trace, enabling end-to-end distributed tracing across microservices.
271
+
272
+ ## Testing
273
+
274
+ Use `InMemorySpanExporter` and `InMemoryMetricExporter` for testing:
275
+
276
+ ```typescript
277
+ import { Otel, OtelTracingPostProcessor } from '@asenajs/asena-otel';
278
+ import { InMemorySpanExporter } from '@opentelemetry/sdk-trace-base';
279
+ import { InMemoryMetricExporter, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
280
+
281
+ const spanExporter = new InMemorySpanExporter();
282
+ const metricExporter = new InMemoryMetricExporter();
283
+ const metricReader = new PeriodicExportingMetricReader({
284
+ exporter: metricExporter,
285
+ exportIntervalMillis: 100,
286
+ });
287
+
288
+ @Otel({
289
+ serviceName: 'test-service',
290
+ traceExporter: spanExporter,
291
+ metricReader,
292
+ autoTrace: { services: true, controllers: true },
293
+ })
294
+ export class TestOtel extends OtelTracingPostProcessor {}
295
+ ```
296
+
297
+ After making requests, flush spans (`BatchSpanProcessor` is lazy):
298
+
299
+ ```typescript
300
+ import { trace } from '@opentelemetry/api';
301
+
302
+ const provider = trace.getTracerProvider() as any;
303
+ await provider.forceFlush?.();
304
+ const spans = spanExporter.getFinishedSpans();
305
+ ```
306
+
307
+ For metrics, wait for periodic export then read from exporter:
308
+
309
+ ```typescript
310
+ await metricReader.forceFlush();
311
+ const metrics = metricExporter.getMetrics();
312
+ ```
313
+
314
+ ### Verifying parent-child hierarchy
315
+
316
+ In OpenTelemetry SDK v2, use `parentSpanContext` (not `parentSpanId`):
317
+
318
+ ```typescript
319
+ const httpSpan = spans.find(s => s.kind === SpanKind.SERVER);
320
+ const serviceSpan = spans.find(s => s.name.includes('UserService'));
321
+
322
+ // Same trace
323
+ expect(serviceSpan.spanContext().traceId).toBe(httpSpan.spanContext().traceId);
324
+
325
+ // Parent-child link (SDK v2)
326
+ expect(serviceSpan.parentSpanContext?.spanId).toBe(httpSpan.spanContext().spanId);
327
+ ```
328
+
329
+ ## License
330
+
331
+ MIT
@@ -0,0 +1,9 @@
1
+ export { Otel } from './lib/decorators';
2
+ export { OtelConstants } from './lib/constants/OtelConstants';
3
+ export { OtelTracingPostProcessor } from './lib/postprocessor/OtelTracingPostProcessor';
4
+ export { OtelService } from './lib/services/OtelService';
5
+ export { OtelTracingMiddleware } from './lib/middleware/OtelTracingMiddleware';
6
+ export { ratioBasedSampler } from './lib/samplers';
7
+ export { isRouteIgnored } from './lib/shared/OtelRuntimeConfig';
8
+ export type { AsenaOtelOptions, AutoTraceConfig } from './lib/types';
9
+ export type { Sampler } from '@opentelemetry/sdk-trace-base';
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ // Decorator
2
+ export { Otel } from './lib/decorators';
3
+ // Constants
4
+ export { OtelConstants } from './lib/constants/OtelConstants';
5
+ // PostProcessor
6
+ export { OtelTracingPostProcessor } from './lib/postprocessor/OtelTracingPostProcessor';
7
+ // Service
8
+ export { OtelService } from './lib/services/OtelService';
9
+ // Middleware
10
+ export { OtelTracingMiddleware } from './lib/middleware/OtelTracingMiddleware';
11
+ // Samplers
12
+ export { ratioBasedSampler } from './lib/samplers';
13
+ // Runtime config utilities
14
+ export { isRouteIgnored } from './lib/shared/OtelRuntimeConfig';
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,YAAY;AACZ,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAExC,YAAY;AACZ,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAE9D,gBAAgB;AAChB,OAAO,EAAE,wBAAwB,EAAE,MAAM,8CAA8C,CAAC;AAExF,UAAU;AACV,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,aAAa;AACb,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAE/E,WAAW;AACX,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD,2BAA2B;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { Sampler, SpanExporter } from '@opentelemetry/sdk-trace-base';
2
+ import type { MetricReader } from '@opentelemetry/sdk-metrics';
3
+ export interface AutoTraceConfig {
4
+ services?: boolean;
5
+ controllers?: boolean;
6
+ }
7
+ export interface AsenaOtelOptions {
8
+ serviceName: string;
9
+ serviceVersion?: string;
10
+ traceExporter: SpanExporter;
11
+ metricReader?: MetricReader;
12
+ autoTrace?: AutoTraceConfig;
13
+ sampler?: Sampler;
14
+ ignoreRoutes?: string[];
15
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=OtelConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OtelConfig.js","sourceRoot":"","sources":["../../lib/OtelConfig.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Metadata constants for OTel decorators.
3
+ * Uses Symbols to prevent naming collisions.
4
+ */
5
+ export declare class OtelConstants {
6
+ /**
7
+ * @Otel() decorator metadata key.
8
+ * Stores AsenaOtelOptions on the PostProcessor subclass.
9
+ */
10
+ static readonly OptionsKey: unique symbol;
11
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Metadata constants for OTel decorators.
3
+ * Uses Symbols to prevent naming collisions.
4
+ */
5
+ export class OtelConstants {
6
+ /**
7
+ * @Otel() decorator metadata key.
8
+ * Stores AsenaOtelOptions on the PostProcessor subclass.
9
+ */
10
+ static OptionsKey = Symbol('otel:options');
11
+ }
12
+ //# sourceMappingURL=OtelConstants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OtelConstants.js","sourceRoot":"","sources":["../../../lib/constants/OtelConstants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,aAAa;IAExB;;;OAGG;IACI,MAAM,CAAU,UAAU,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { AsenaOtelOptions } from '../OtelConfig';
2
+ /**
3
+ * Decorator that configures an OTel Tracing PostProcessor.
4
+ *
5
+ * Apply to a class extending `OtelTracingPostProcessor` to automatically:
6
+ * 1. Initialize the OpenTelemetry SDK (tracer + meter providers)
7
+ * 2. Auto-trace Service and Controller methods via Proxy
8
+ * 3. Register shutdown hooks for graceful cleanup
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * @Otel({
13
+ * serviceName: 'my-app',
14
+ * traceExporter: new OTLPTraceExporter({ url: 'http://jaeger:4318/v1/traces' }),
15
+ * autoTrace: { services: true, controllers: true },
16
+ * })
17
+ * export class AppOtel extends OtelTracingPostProcessor {}
18
+ * ```
19
+ */
20
+ export declare function Otel(options: AsenaOtelOptions): <T extends new (...args: any[]) => any>(target: T) => T;
@@ -0,0 +1,28 @@
1
+ import { PostProcessor } from '@asenajs/asena/decorators';
2
+ import { defineTypedMetadata } from '@asenajs/asena/utils';
3
+ import { OtelConstants } from '../constants/OtelConstants';
4
+ /**
5
+ * Decorator that configures an OTel Tracing PostProcessor.
6
+ *
7
+ * Apply to a class extending `OtelTracingPostProcessor` to automatically:
8
+ * 1. Initialize the OpenTelemetry SDK (tracer + meter providers)
9
+ * 2. Auto-trace Service and Controller methods via Proxy
10
+ * 3. Register shutdown hooks for graceful cleanup
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * @Otel({
15
+ * serviceName: 'my-app',
16
+ * traceExporter: new OTLPTraceExporter({ url: 'http://jaeger:4318/v1/traces' }),
17
+ * autoTrace: { services: true, controllers: true },
18
+ * })
19
+ * export class AppOtel extends OtelTracingPostProcessor {}
20
+ * ```
21
+ */
22
+ export function Otel(options) {
23
+ return function (target) {
24
+ defineTypedMetadata(OtelConstants.OptionsKey, options, target);
25
+ return PostProcessor()(target);
26
+ };
27
+ }
28
+ //# sourceMappingURL=Otel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Otel.js","sourceRoot":"","sources":["../../../lib/decorators/Otel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAG3D;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,IAAI,CAAC,OAAyB;IAC5C,OAAO,UAAiD,MAAS;QAC/D,mBAAmB,CAAC,aAAa,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAE/D,OAAO,aAAa,EAAE,CAAC,MAAM,CAAM,CAAC;IACtC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1 @@
1
+ export { Otel } from './Otel';
@@ -0,0 +1,2 @@
1
+ export { Otel } from './Otel';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/decorators/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { AsenaMiddlewareService } from '@asenajs/asena/middleware';
2
+ import type { AsenaContext } from '@asenajs/asena/adapter';
3
+ export declare class OtelTracingMiddleware extends AsenaMiddlewareService {
4
+ private tracer;
5
+ private meter;
6
+ private requestCounter;
7
+ private requestDuration;
8
+ onInit(): void;
9
+ handle(ctx: AsenaContext<any, any>, next: () => Promise<void>): Promise<void>;
10
+ }
@@ -0,0 +1,94 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { trace, metrics } from '@opentelemetry/api';
11
+ import { context as otelContext, propagation, SpanKind, SpanStatusCode, } from '@opentelemetry/api';
12
+ import { ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_HTTP_ROUTE, ATTR_URL_PATH, } from '@opentelemetry/semantic-conventions';
13
+ import { AsenaMiddlewareService } from '@asenajs/asena/middleware';
14
+ import { Middleware } from '@asenajs/asena/decorators';
15
+ import { PostConstruct } from '@asenajs/asena/decorators/ioc';
16
+ import { isRouteIgnored } from '../shared/OtelRuntimeConfig';
17
+ const LIBRARY_NAME = '@asenajs/asena-otel';
18
+ let OtelTracingMiddleware = class OtelTracingMiddleware extends AsenaMiddlewareService {
19
+ tracer;
20
+ meter;
21
+ requestCounter;
22
+ requestDuration;
23
+ onInit() {
24
+ this.tracer = trace.getTracer(LIBRARY_NAME);
25
+ this.meter = metrics.getMeter(LIBRARY_NAME);
26
+ this.requestCounter = this.meter.createCounter('http.server.request.count', {
27
+ description: 'Total number of HTTP requests',
28
+ });
29
+ this.requestDuration = this.meter.createHistogram('http.server.request.duration', {
30
+ description: 'HTTP request duration in milliseconds',
31
+ unit: 'ms',
32
+ });
33
+ }
34
+ async handle(ctx, next) {
35
+ const startTime = performance.now();
36
+ const method = ctx.req?.method ?? 'UNKNOWN';
37
+ const url = ctx.req?.url ? new URL(ctx.req.url).pathname : '/';
38
+ // Check ignoreRoutes against static URL path (before route matching)
39
+ if (isRouteIgnored(url)) {
40
+ await next();
41
+ return;
42
+ }
43
+ // Extract W3C traceparent from incoming request headers
44
+ const parentContext = propagation.extract(otelContext.active(), ctx.headers);
45
+ await this.tracer.startActiveSpan(`${method} ${url}`, { kind: SpanKind.SERVER }, parentContext, async (span) => {
46
+ span.setAttribute(ATTR_HTTP_REQUEST_METHOD, method);
47
+ span.setAttribute(ATTR_URL_PATH, url);
48
+ try {
49
+ await next();
50
+ span.setStatus({ code: SpanStatusCode.OK });
51
+ }
52
+ catch (error) {
53
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
54
+ span.recordException(error);
55
+ throw error;
56
+ }
57
+ finally {
58
+ const duration = performance.now() - startTime;
59
+ // Read routePattern AFTER next() — in Hono, route matching happens during next()
60
+ const raw = ctx.routePattern;
61
+ const trimmed = raw && raw.length > 1 && raw.endsWith('/') ? raw.slice(0, -1) : raw;
62
+ const routePattern = trimmed && trimmed !== '/*' ? trimmed : undefined;
63
+ if (routePattern) {
64
+ span.updateName(`${method} ${routePattern}`);
65
+ span.setAttribute(ATTR_HTTP_ROUTE, routePattern);
66
+ }
67
+ const metricRoute = routePattern || url;
68
+ const attributes = {
69
+ [ATTR_HTTP_REQUEST_METHOD]: method,
70
+ [ATTR_URL_PATH]: metricRoute,
71
+ };
72
+ const statusCode = ctx.res?.status;
73
+ if (statusCode !== undefined) {
74
+ span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, statusCode);
75
+ attributes[ATTR_HTTP_RESPONSE_STATUS_CODE] = statusCode;
76
+ }
77
+ this.requestCounter.add(1, attributes);
78
+ this.requestDuration.record(duration, attributes);
79
+ span.end();
80
+ }
81
+ });
82
+ }
83
+ };
84
+ __decorate([
85
+ PostConstruct(),
86
+ __metadata("design:type", Function),
87
+ __metadata("design:paramtypes", []),
88
+ __metadata("design:returntype", void 0)
89
+ ], OtelTracingMiddleware.prototype, "onInit", null);
90
+ OtelTracingMiddleware = __decorate([
91
+ Middleware()
92
+ ], OtelTracingMiddleware);
93
+ export { OtelTracingMiddleware };
94
+ //# sourceMappingURL=OtelTracingMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OtelTracingMiddleware.js","sourceRoot":"","sources":["../../../lib/middleware/OtelTracingMiddleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { BasicTracerProvider } from '@opentelemetry/sdk-trace-base';
2
+ import { MeterProvider } from '@opentelemetry/sdk-metrics';
3
+ import type { ComponentPostProcessor } from '@asenajs/asena/ioc/types';
4
+ export declare class OtelTracingPostProcessor implements ComponentPostProcessor {
5
+ protected tracerProvider: BasicTracerProvider | null;
6
+ protected meterProvider: MeterProvider | null;
7
+ private tracer;
8
+ private autoTraceConfig;
9
+ onInit(): void;
10
+ postProcess<T>(instance: T, Class: any): T;
11
+ forceFlush(): Promise<void>;
12
+ shutdown(): Promise<void>;
13
+ private getOptions;
14
+ private setupContextManager;
15
+ private setupTracing;
16
+ private setupMetrics;
17
+ private registerShutdownHook;
18
+ private getComponentType;
19
+ }
@@ -0,0 +1,164 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { trace, metrics, context, propagation, SpanKind, SpanStatusCode } from '@opentelemetry/api';
11
+ import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks';
12
+ import { W3CTraceContextPropagator } from '@opentelemetry/core';
13
+ import { resourceFromAttributes } from '@opentelemetry/resources';
14
+ import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
15
+ import { BasicTracerProvider, BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
16
+ import { MeterProvider } from '@opentelemetry/sdk-metrics';
17
+ import { getMetadata } from 'reflect-metadata/no-conflict';
18
+ import { PostConstruct } from '@asenajs/asena/decorators/ioc';
19
+ import { getOwnTypedMetadata } from '@asenajs/asena/utils';
20
+ import { OtelConstants } from '../constants/OtelConstants';
21
+ import { otelRuntimeConfig } from '../shared/OtelRuntimeConfig';
22
+ const COMPONENT_TYPE_SERVICE = 'SERVICE';
23
+ const COMPONENT_TYPE_CONTROLLER = 'CONTROLLER';
24
+ const LIBRARY_NAME = '@asenajs/asena-otel';
25
+ export class OtelTracingPostProcessor {
26
+ tracerProvider = null;
27
+ meterProvider = null;
28
+ tracer;
29
+ autoTraceConfig = {};
30
+ onInit() {
31
+ const options = this.getOptions();
32
+ if (!options)
33
+ return;
34
+ const resource = resourceFromAttributes({
35
+ [ATTR_SERVICE_NAME]: options.serviceName,
36
+ [ATTR_SERVICE_VERSION]: options.serviceVersion ?? '0.0.0',
37
+ });
38
+ this.setupContextManager();
39
+ this.setupTracing(resource, options.traceExporter, options.sampler);
40
+ if (options.metricReader) {
41
+ this.setupMetrics(resource, options.metricReader);
42
+ }
43
+ this.autoTraceConfig = options.autoTrace ?? {};
44
+ otelRuntimeConfig.ignoreRoutes = options.ignoreRoutes ?? [];
45
+ this.tracer = trace.getTracer(LIBRARY_NAME);
46
+ this.registerShutdownHook();
47
+ }
48
+ postProcess(instance, Class) {
49
+ const config = this.autoTraceConfig;
50
+ const componentType = this.getComponentType(Class);
51
+ if (!componentType)
52
+ return instance;
53
+ if (componentType === COMPONENT_TYPE_SERVICE && !config.services)
54
+ return instance;
55
+ if (componentType === COMPONENT_TYPE_CONTROLLER && !config.controllers)
56
+ return instance;
57
+ const className = Class.name;
58
+ const tracer = this.tracer;
59
+ return new Proxy(instance, {
60
+ get(target, prop, receiver) {
61
+ const value = Reflect.get(target, prop, receiver);
62
+ if (typeof value !== 'function')
63
+ return value;
64
+ if (prop === 'constructor' || typeof prop === 'symbol')
65
+ return value;
66
+ if (String(prop).startsWith('_'))
67
+ return value;
68
+ const spanName = `${className}.${String(prop)}`;
69
+ return function (...args) {
70
+ const span = tracer.startSpan(spanName, { kind: SpanKind.INTERNAL });
71
+ const spanContext = trace.setSpan(context.active(), span);
72
+ let result;
73
+ try {
74
+ result = context.with(spanContext, () => value.apply(target, args));
75
+ }
76
+ catch (error) {
77
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
78
+ span.recordException(error);
79
+ span.end();
80
+ throw error;
81
+ }
82
+ if (result instanceof Promise) {
83
+ return result
84
+ .then((resolved) => {
85
+ span.setStatus({ code: SpanStatusCode.OK });
86
+ span.end();
87
+ return resolved;
88
+ })
89
+ .catch((error) => {
90
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
91
+ span.recordException(error);
92
+ span.end();
93
+ throw error;
94
+ });
95
+ }
96
+ span.setStatus({ code: SpanStatusCode.OK });
97
+ span.end();
98
+ return result;
99
+ };
100
+ },
101
+ });
102
+ }
103
+ async forceFlush() {
104
+ if (this.tracerProvider) {
105
+ await this.tracerProvider.forceFlush();
106
+ }
107
+ if (this.meterProvider) {
108
+ await this.meterProvider.forceFlush();
109
+ }
110
+ }
111
+ async shutdown() {
112
+ if (this.tracerProvider) {
113
+ await this.tracerProvider.shutdown();
114
+ this.tracerProvider = null;
115
+ }
116
+ if (this.meterProvider) {
117
+ await this.meterProvider.shutdown();
118
+ this.meterProvider = null;
119
+ }
120
+ context.disable();
121
+ }
122
+ getOptions() {
123
+ return getOwnTypedMetadata(OtelConstants.OptionsKey, this.constructor);
124
+ }
125
+ setupContextManager() {
126
+ const contextManager = new AsyncLocalStorageContextManager();
127
+ contextManager.enable();
128
+ context.setGlobalContextManager(contextManager);
129
+ propagation.setGlobalPropagator(new W3CTraceContextPropagator());
130
+ }
131
+ setupTracing(resource, exporter, sampler) {
132
+ this.tracerProvider = new BasicTracerProvider({
133
+ resource,
134
+ spanProcessors: [new BatchSpanProcessor(exporter)],
135
+ sampler,
136
+ });
137
+ trace.setGlobalTracerProvider(this.tracerProvider);
138
+ }
139
+ setupMetrics(resource, reader) {
140
+ this.meterProvider = new MeterProvider({ resource, readers: [reader] });
141
+ metrics.setGlobalMeterProvider(this.meterProvider);
142
+ }
143
+ registerShutdownHook() {
144
+ const shutdown = async () => {
145
+ await this.shutdown();
146
+ };
147
+ process.on('SIGTERM', shutdown);
148
+ process.on('SIGINT', shutdown);
149
+ }
150
+ getComponentType(Class) {
151
+ if (getMetadata(COMPONENT_TYPE_SERVICE, Class))
152
+ return COMPONENT_TYPE_SERVICE;
153
+ if (getMetadata(COMPONENT_TYPE_CONTROLLER, Class))
154
+ return COMPONENT_TYPE_CONTROLLER;
155
+ return null;
156
+ }
157
+ }
158
+ __decorate([
159
+ PostConstruct(),
160
+ __metadata("design:type", Function),
161
+ __metadata("design:paramtypes", []),
162
+ __metadata("design:returntype", void 0)
163
+ ], OtelTracingPostProcessor.prototype, "onInit", null);
164
+ //# sourceMappingURL=OtelTracingPostProcessor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OtelTracingPostProcessor.js","sourceRoot":"","sources":["../../../lib/postprocessor/OtelTracingPostProcessor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { type Sampler } from '@opentelemetry/sdk-trace-base';
2
+ /**
3
+ * Creates a production-ready ratio-based sampler.
4
+ * Root spans are sampled at the given ratio; child spans respect the parent's decision.
5
+ *
6
+ * @param ratio - Fraction of traces to sample (0.0 to 1.0). Default 0.1 (10%).
7
+ */
8
+ export declare function ratioBasedSampler(ratio?: number): Sampler;
@@ -0,0 +1,13 @@
1
+ import { ParentBasedSampler, TraceIdRatioBasedSampler } from '@opentelemetry/sdk-trace-base';
2
+ /**
3
+ * Creates a production-ready ratio-based sampler.
4
+ * Root spans are sampled at the given ratio; child spans respect the parent's decision.
5
+ *
6
+ * @param ratio - Fraction of traces to sample (0.0 to 1.0). Default 0.1 (10%).
7
+ */
8
+ export function ratioBasedSampler(ratio = 0.1) {
9
+ return new ParentBasedSampler({
10
+ root: new TraceIdRatioBasedSampler(ratio),
11
+ });
12
+ }
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/samplers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAgB,MAAM,+BAA+B,CAAC;AAE3G;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAK,GAAG,GAAG;IAC3C,OAAO,IAAI,kBAAkB,CAAC;QAC5B,IAAI,EAAE,IAAI,wBAAwB,CAAC,KAAK,CAAC;KAC1C,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { type Tracer, type Meter, type Span } from '@opentelemetry/api';
2
+ export declare class OtelService {
3
+ private _tracer;
4
+ private _meter;
5
+ onInit(): void;
6
+ get tracer(): Tracer;
7
+ get meter(): Meter;
8
+ withSpan<T>(name: string, fn: (span: Span) => Promise<T>): Promise<T>;
9
+ getActiveSpan(): Span | undefined;
10
+ injectTraceContext(headers?: Record<string, string>): Record<string, string>;
11
+ }
@@ -0,0 +1,62 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { trace, metrics, context, propagation } from '@opentelemetry/api';
11
+ import { Service } from '@asenajs/asena/decorators';
12
+ import { PostConstruct } from '@asenajs/asena/decorators/ioc';
13
+ const LIBRARY_NAME = '@asenajs/asena-otel';
14
+ let OtelService = class OtelService {
15
+ _tracer;
16
+ _meter;
17
+ onInit() {
18
+ this._tracer = trace.getTracer(LIBRARY_NAME);
19
+ this._meter = metrics.getMeter(LIBRARY_NAME);
20
+ }
21
+ get tracer() {
22
+ return this._tracer;
23
+ }
24
+ get meter() {
25
+ return this._meter;
26
+ }
27
+ async withSpan(name, fn) {
28
+ return this._tracer.startActiveSpan(name, async (span) => {
29
+ try {
30
+ const result = await fn(span);
31
+ span.setStatus({ code: 1 }); // SpanStatusCode.OK
32
+ return result;
33
+ }
34
+ catch (error) {
35
+ span.setStatus({ code: 2 }); // SpanStatusCode.ERROR
36
+ span.recordException(error);
37
+ throw error;
38
+ }
39
+ finally {
40
+ span.end();
41
+ }
42
+ });
43
+ }
44
+ getActiveSpan() {
45
+ return trace.getSpan(context.active());
46
+ }
47
+ injectTraceContext(headers = {}) {
48
+ propagation.inject(context.active(), headers);
49
+ return headers;
50
+ }
51
+ };
52
+ __decorate([
53
+ PostConstruct(),
54
+ __metadata("design:type", Function),
55
+ __metadata("design:paramtypes", []),
56
+ __metadata("design:returntype", void 0)
57
+ ], OtelService.prototype, "onInit", null);
58
+ OtelService = __decorate([
59
+ Service('OtelService')
60
+ ], OtelService);
61
+ export { OtelService };
62
+ //# sourceMappingURL=OtelService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OtelService.js","sourceRoot":"","sources":["../../../lib/services/OtelService.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAsC,MAAM,oBAAoB,CAAC;AAC9G,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAE9D,MAAM,YAAY,GAAG,qBAAqB,CAAC;AAGpC,IAAM,WAAW,GAAjB,MAAM,WAAW;IAEd,OAAO,CAAU;IAEjB,MAAM,CAAS;IAGhB,MAAM;QACX,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IAC/C,CAAC;IAED,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,IAAW,KAAK;QACd,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAEM,KAAK,CAAC,QAAQ,CAAI,IAAY,EAAE,EAA8B;QACnE,OAAO,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACvD,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;gBAE9B,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,oBAAoB;gBAEjD,OAAO,MAAM,CAAC;YAChB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,uBAAuB;gBACpD,IAAI,CAAC,eAAe,CAAC,KAAc,CAAC,CAAC;gBAErC,MAAM,KAAK,CAAC;YACd,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,GAAG,EAAE,CAAC;YACb,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAEM,aAAa;QAClB,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,CAAC;IAEM,kBAAkB,CAAC,UAAkC,EAAE;QAC5D,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,OAAO,CAAC,CAAC;QAE9C,OAAO,OAAO,CAAC;IACjB,CAAC;CAEF,CAAA;AA1CQ;IADN,aAAa,EAAE;;;;yCAIf;AAVU,WAAW;IADvB,OAAO,CAAC,aAAa,CAAC;GACV,WAAW,CAiDvB"}
@@ -0,0 +1,8 @@
1
+ export declare const otelRuntimeConfig: {
2
+ ignoreRoutes: string[];
3
+ };
4
+ /**
5
+ * Check if a route path should be excluded from tracing.
6
+ * Supports exact match and wildcard suffix (e.g., `/admin/*`).
7
+ */
8
+ export declare function isRouteIgnored(path: string): boolean;
@@ -0,0 +1,21 @@
1
+ export const otelRuntimeConfig = {
2
+ ignoreRoutes: [],
3
+ };
4
+ /**
5
+ * Check if a route path should be excluded from tracing.
6
+ * Supports exact match and wildcard suffix (e.g., `/admin/*`).
7
+ */
8
+ export function isRouteIgnored(path) {
9
+ for (const route of otelRuntimeConfig.ignoreRoutes) {
10
+ if (route.endsWith('*')) {
11
+ if (path.startsWith(route.slice(0, -1)))
12
+ return true;
13
+ }
14
+ else {
15
+ if (path === route)
16
+ return true;
17
+ }
18
+ }
19
+ return false;
20
+ }
21
+ //# sourceMappingURL=OtelRuntimeConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OtelRuntimeConfig.js","sourceRoot":"","sources":["../../../lib/shared/OtelRuntimeConfig.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,YAAY,EAAE,EAAc;CAC7B,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,KAAK,MAAM,KAAK,IAAI,iBAAiB,CAAC,YAAY,EAAE,CAAC;QACnD,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAC;QACvD,CAAC;aAAM,CAAC;YACN,IAAI,IAAI,KAAK,KAAK;gBAAE,OAAO,IAAI,CAAC;QAClC,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1 @@
1
+ export type { AsenaOtelOptions, AutoTraceConfig } from '../OtelConfig';
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/types/index.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@asenajs/asena-otel",
3
+ "version": "1.0.0",
4
+ "author": "LibirSoft",
5
+ "description": "OpenTelemetry integration for AsenaJS - auto-tracing, HTTP metrics, distributed tracing",
6
+ "main": "dist/index.js",
7
+ "module": "index.ts",
8
+ "types": "dist/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "README.md",
12
+ "LICENSE"
13
+ ],
14
+ "license": "MIT",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/AsenaJs/asena-otel.git"
18
+ },
19
+ "scripts": {
20
+ "test": "bun test",
21
+ "test:watch": "bun test --watch",
22
+ "test:coverage": "bun test --coverage",
23
+ "build": "bun run clean && tsc",
24
+ "clean": "rm -rf dist",
25
+ "prepublishOnly": "bun run build",
26
+ "changeset": "changeset",
27
+ "version": "changeset version",
28
+ "release": "bun run build && changeset publish"
29
+ },
30
+ "devDependencies": {
31
+ "@asenajs/asena": "^0.7.1",
32
+ "@changesets/cli": "^2.30.0",
33
+ "@opentelemetry/api": "^1.9.1",
34
+ "@opentelemetry/resources": "^2.6.1",
35
+ "@opentelemetry/sdk-metrics": "^2.6.1",
36
+ "@opentelemetry/sdk-trace-base": "^2.6.1",
37
+ "@opentelemetry/semantic-conventions": "^1.40.0",
38
+ "@types/bun": "latest",
39
+ "@typescript-eslint/eslint-plugin": "^6.21.0",
40
+ "eslint": "^8.57.1",
41
+ "eslint-config-alloy": "^5.1.2",
42
+ "eslint-config-prettier": "^9.1.2",
43
+ "eslint-plugin-alloy": "^1.2.1",
44
+ "eslint-plugin-import": "^2.32.0",
45
+ "eslint-plugin-n": "^16.6.2",
46
+ "eslint-plugin-prettier": "^5.5.5",
47
+ "eslint-plugin-promise": "^6.6.0",
48
+ "prettier": "^3.8.2",
49
+ "reflect-metadata": "^0.2.2"
50
+ },
51
+ "peerDependencies": {
52
+ "@asenajs/asena": "^0.7.0",
53
+ "@opentelemetry/api": "^1.9.0",
54
+ "@opentelemetry/resources": "^2.6.0",
55
+ "@opentelemetry/sdk-metrics": "^2.6.0",
56
+ "@opentelemetry/sdk-trace-base": "^2.6.0",
57
+ "@opentelemetry/semantic-conventions": "^1.40.0",
58
+ "@opentelemetry/context-async-hooks": "^2.6.0",
59
+ "reflect-metadata": "^0.2.2",
60
+ "typescript": "^5.9.3"
61
+ },
62
+ "dependencies": {
63
+ "@opentelemetry/context-async-hooks": "^2.6.1"
64
+ }
65
+ }