@onebun/core 0.2.15 → 0.2.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onebun/core",
3
- "version": "0.2.15",
3
+ "version": "0.2.17",
4
4
  "description": "Core package for OneBun framework - decorators, DI, modules, controllers",
5
5
  "license": "LGPL-3.0",
6
6
  "author": "RemRyahirev",
@@ -43,13 +43,14 @@
43
43
  "test": "bun test"
44
44
  },
45
45
  "dependencies": {
46
- "effect": "^3.13.10",
47
- "arktype": "^2.0.0",
48
- "@onebun/logger": "^0.2.1",
49
46
  "@onebun/envs": "^0.2.2",
47
+ "@onebun/logger": "^0.2.2",
50
48
  "@onebun/metrics": "^0.2.2",
51
49
  "@onebun/requests": "^0.2.1",
52
- "@onebun/trace": "^0.2.1"
50
+ "@onebun/trace": "^0.2.3",
51
+ "@opentelemetry/api": "^1.9.1",
52
+ "arktype": "^2.0.0",
53
+ "effect": "^3.13.10"
53
54
  },
54
55
  "devDependencies": {
55
56
  "bun-types": "^1.3.8",
@@ -20,6 +20,7 @@ import {
20
20
  LoggerService,
21
21
  makeLogger,
22
22
  makeLoggerFromOptions,
23
+ shutdownLogger,
23
24
  type SyncLogger,
24
25
  } from '@onebun/logger';
25
26
  import {
@@ -295,9 +296,26 @@ export class OneBunApplication<QA extends import('../queue/types').QueueAdapterC
295
296
 
296
297
  // Use provided logger layer, or create from options, or use default
297
298
  // Priority: loggerLayer > loggerOptions > env variables > NODE_ENV defaults
299
+ // Auto-populate OTLP resource attributes from tracing config if available
300
+ const loggerOptions = this.options.loggerOptions
301
+ ? {
302
+ ...this.options.loggerOptions,
303
+ otlpResourceAttributes: this.options.loggerOptions.otlpResourceAttributes ?? (
304
+ this.options.loggerOptions.otlpEndpoint && this.options.tracing
305
+ ? {
306
+ // eslint-disable-next-line @typescript-eslint/naming-convention
307
+ 'service.name': this.options.tracing.serviceName ?? 'onebun-service',
308
+ // eslint-disable-next-line @typescript-eslint/naming-convention
309
+ 'service.version': this.options.tracing.serviceVersion ?? '1.0.0',
310
+ }
311
+ : undefined
312
+ ),
313
+ }
314
+ : undefined;
315
+
298
316
  this.loggerLayer = this.options.loggerLayer
299
- ?? (this.options.loggerOptions
300
- ? makeLoggerFromOptions(this.options.loggerOptions)
317
+ ?? (loggerOptions
318
+ ? makeLoggerFromOptions(loggerOptions)
301
319
  : makeLogger());
302
320
 
303
321
  // Initialize logger with application class name as context
@@ -503,7 +521,12 @@ export class OneBunApplication<QA extends import('../queue/types').QueueAdapterC
503
521
  // Create the root module AFTER config is initialized and QueueService proxy is registered,
504
522
  // so services can safely use this.config.get() in their constructors
505
523
  // and inject QueueService in any module depth.
506
- this.rootModule = OneBunModule.create(this.moduleClass, this.loggerLayer, this.config);
524
+ this.rootModule = OneBunModule.create(
525
+ this.moduleClass, this.loggerLayer, this.config,
526
+ this.options.tracing?.traceAll
527
+ ? { traceAll: true, traceFilter: this.options.tracing.traceFilter }
528
+ : undefined,
529
+ );
507
530
 
508
531
  // Register test provider overrides (must happen before setup() so controllers receive mocks)
509
532
  if (this.options._testProviders) {
@@ -1744,6 +1767,12 @@ export class OneBunApplication<QA extends import('../queue/types').QueueAdapterC
1744
1767
  this.logger.debug('HTTP server stopped');
1745
1768
  }
1746
1769
 
1770
+ // Shutdown trace service — flush pending spans before module destroy
1771
+ if (this.traceService?.shutdown) {
1772
+ this.logger.debug('Shutting down trace service');
1773
+ await this.traceService.shutdown();
1774
+ }
1775
+
1747
1776
  // Call onModuleDestroy lifecycle hook
1748
1777
  if (this.rootModule?.callOnModuleDestroy) {
1749
1778
  this.logger.debug('Calling onModuleDestroy hooks');
@@ -1763,6 +1792,9 @@ export class OneBunApplication<QA extends import('../queue/types').QueueAdapterC
1763
1792
  }
1764
1793
 
1765
1794
  this.logger.info('OneBun application stopped');
1795
+
1796
+ // Shutdown logger transport LAST — flush OTLP log batches after final log message
1797
+ await shutdownLogger();
1766
1798
  }
1767
1799
 
1768
1800
  /**
@@ -1,9 +1,12 @@
1
+ import { trace } from '@opentelemetry/api';
2
+
1
3
  import type { IConfig, OneBunAppConfig } from './config.interface';
2
4
  import type {
3
5
  OneBunRequest,
4
6
  SseEvent,
5
7
  SseOptions,
6
8
  } from '../types';
9
+ import type { Span } from '@opentelemetry/api';
7
10
  import type { Context } from 'effect';
8
11
 
9
12
  import type { SyncLogger } from '@onebun/logger';
@@ -134,6 +137,14 @@ export class Controller {
134
137
  this.logger.debug(`Controller ${className} initialized`);
135
138
  }
136
139
 
140
+ /**
141
+ * Get the currently active OpenTelemetry span.
142
+ * Returns undefined when no span is active.
143
+ */
144
+ protected get span(): Span | undefined {
145
+ return trace.getActiveSpan();
146
+ }
147
+
137
148
  /**
138
149
  * Get a service instance by tag
139
150
  * @param tag - The service tag
@@ -14,6 +14,11 @@ import {
14
14
  makeLogger,
15
15
  type SyncLogger,
16
16
  } from '@onebun/logger';
17
+ import {
18
+ applyAutoTrace,
19
+ shouldAutoTrace,
20
+ type TraceFilterOptions,
21
+ } from '@onebun/trace';
17
22
 
18
23
  import {
19
24
  autoDetectDependencies,
@@ -48,6 +53,7 @@ import {
48
53
  getServiceTag,
49
54
  } from './service';
50
55
 
56
+
51
57
  /**
52
58
  * Global services registry
53
59
  * Stores services from modules marked with @Global() decorator
@@ -127,11 +133,17 @@ export class OneBunModule implements ModuleInstance {
127
133
  */
128
134
  private resolvedAncestorMiddleware: Function[] = [];
129
135
 
136
+ /**
137
+ * Tracing options for auto-trace (traceAll + filters)
138
+ */
139
+ private readonly tracingOptions?: { traceAll?: boolean; traceFilter?: TraceFilterOptions };
140
+
130
141
  constructor(
131
142
  private moduleClass: Function,
132
143
  private loggerLayer?: Layer.Layer<never, never, unknown>,
133
144
  config?: IConfig<OneBunAppConfig>,
134
145
  ancestorMiddleware?: Function[],
146
+ tracingOptions?: { traceAll?: boolean; traceFilter?: TraceFilterOptions },
135
147
  ) {
136
148
  // Initialize logger with module class name as context
137
149
  const effectLogger = Effect.runSync(
@@ -144,6 +156,7 @@ export class OneBunModule implements ModuleInstance {
144
156
  ) as Logger;
145
157
  this.logger = createSyncLogger(effectLogger);
146
158
  this.config = config ?? new NotInitializedConfig();
159
+ this.tracingOptions = tracingOptions;
147
160
  this.ancestorMiddlewareClasses = ancestorMiddleware ?? [];
148
161
 
149
162
  // Read module-level middleware from OnModuleConfigure interface
@@ -230,7 +243,10 @@ export class OneBunModule implements ModuleInstance {
230
243
 
231
244
  // Pass the logger layer, config, and accumulated middleware class refs to child modules
232
245
  const accumulatedMiddleware = [...this.ancestorMiddlewareClasses, ...this.ownMiddlewareClasses];
233
- const childModule = new OneBunModule(importModule, this.loggerLayer, this.config, accumulatedMiddleware);
246
+ const childModule = new OneBunModule(
247
+ importModule, this.loggerLayer, this.config,
248
+ accumulatedMiddleware, this.tracingOptions,
249
+ );
234
250
  this.childModules.push(childModule);
235
251
 
236
252
  // Merge layers
@@ -426,6 +442,14 @@ export class OneBunModule implements ModuleInstance {
426
442
  });
427
443
  }
428
444
 
445
+ // Apply auto-tracing if enabled
446
+ if (
447
+ this.tracingOptions &&
448
+ shouldAutoTrace(provider, provider.name, !!this.tracingOptions.traceAll, this.tracingOptions.traceFilter)
449
+ ) {
450
+ applyAutoTrace(serviceInstance, provider.name, this.tracingOptions.traceFilter);
451
+ }
452
+
429
453
  this.serviceInstances.set(serviceMetadata.tag, serviceInstance);
430
454
  createdServices.add(provider.name);
431
455
  this.logger.debug(
@@ -633,6 +657,17 @@ export class OneBunModule implements ModuleInstance {
633
657
  controller.initializeController(this.logger, this.config);
634
658
  }
635
659
 
660
+ // Apply auto-tracing if enabled
661
+ if (
662
+ this.tracingOptions &&
663
+ shouldAutoTrace(
664
+ controllerClass, controllerClass.name,
665
+ !!this.tracingOptions.traceAll, this.tracingOptions.traceFilter,
666
+ )
667
+ ) {
668
+ applyAutoTrace(controller, controllerClass.name, this.tracingOptions.traceFilter);
669
+ }
670
+
636
671
  this.controllerInstances.set(controllerClass, controller);
637
672
 
638
673
  // Inject all services into controller (for legacy compatibility)
@@ -1145,9 +1180,10 @@ export class OneBunModule implements ModuleInstance {
1145
1180
  moduleClass: Function,
1146
1181
  loggerLayer?: Layer.Layer<never, never, unknown>,
1147
1182
  config?: IConfig<OneBunAppConfig>,
1183
+ tracingOptions?: { traceAll?: boolean; traceFilter?: TraceFilterOptions },
1148
1184
  ): ModuleInstance {
1149
1185
  // Using console.log here because we don't have access to the logger instance yet
1150
1186
  // The instance will create its own logger in the constructor
1151
- return new OneBunModule(moduleClass, loggerLayer, config);
1187
+ return new OneBunModule(moduleClass, loggerLayer, config, undefined, tracingOptions);
1152
1188
  }
1153
1189
  }
@@ -1,3 +1,4 @@
1
+ import { trace } from '@opentelemetry/api';
1
2
  import {
2
3
  Context,
3
4
  Effect,
@@ -5,6 +6,7 @@ import {
5
6
  } from 'effect';
6
7
 
7
8
  import type { IConfig, OneBunAppConfig } from './config.interface';
9
+ import type { Span } from '@opentelemetry/api';
8
10
 
9
11
  import type { SyncLogger } from '@onebun/logger';
10
12
 
@@ -22,7 +24,7 @@ const META_SERVICES = new Map<
22
24
  * Services extending BaseService will have logger and config available
23
25
  * immediately after super() in the constructor (via ambient init context),
24
26
  * as well as through the initializeService fallback method.
25
- *
27
+ *
26
28
  * @param tag - Optional Effect Context tag for the service
27
29
  */
28
30
  export function Service<T>(tag?: Context.Tag<T, T>) {
@@ -170,6 +172,21 @@ export class BaseService {
170
172
  return this._initialized;
171
173
  }
172
174
 
175
+ /**
176
+ * Get the currently active OpenTelemetry span.
177
+ * Returns undefined when no span is active (e.g. outside of @Traced context).
178
+ *
179
+ * The returned Span has a fully synchronous API:
180
+ * - `span.setAttribute(key, value)`
181
+ * - `span.setAttributes({ key: value })`
182
+ * - `span.addEvent(name, attributes?)`
183
+ * - `span.recordException(error)`
184
+ * - `span.setStatus({ code, message })`
185
+ */
186
+ protected get span(): Span | undefined {
187
+ return trace.getActiveSpan();
188
+ }
189
+
173
190
  /**
174
191
  * Run an effect with error handling
175
192
  * @param effect - The effect to run
package/src/types.ts CHANGED
@@ -385,6 +385,47 @@ export interface ApplicationOptions<QA extends QueueAdapterConstructor<any> = Qu
385
385
  */
386
386
  traceDatabaseQueries?: boolean;
387
387
 
388
+ /**
389
+ * Auto-trace all async methods on services and controllers.
390
+ * When enabled, all async methods are wrapped in spans without
391
+ * requiring @Traced() on each method.
392
+ *
393
+ * Priority: method-level decorators > class-level > this setting.
394
+ * Use @NoTrace() to opt out, @TraceAll() on a class to opt in
395
+ * when traceAll is false.
396
+ *
397
+ * @defaultValue false
398
+ */
399
+ traceAll?: boolean;
400
+
401
+ /**
402
+ * Filter options for traceAll. Only effective when traceAll is true.
403
+ */
404
+ traceFilter?: {
405
+ /**
406
+ * Only trace async methods. Sync methods are skipped.
407
+ * @defaultValue true
408
+ */
409
+ asyncOnly?: boolean;
410
+
411
+ /**
412
+ * Additional method names to exclude from auto-tracing
413
+ * (on top of built-in exclusions like lifecycle hooks and base class methods).
414
+ */
415
+ excludeMethods?: string[];
416
+
417
+ /**
418
+ * Class name glob patterns to include (e.g. ['*Service', '*Repository']).
419
+ * When set, only matching classes are auto-traced.
420
+ */
421
+ includeClasses?: string[];
422
+
423
+ /**
424
+ * Class name glob patterns to exclude (e.g. ['HealthController']).
425
+ */
426
+ excludeClasses?: string[];
427
+ };
428
+
388
429
  /**
389
430
  * Custom attributes to add to all spans
390
431
  */