@breadstone/archipel-platform-telemetry 0.0.19 → 0.0.21

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/README.md CHANGED
@@ -20,6 +20,9 @@ import { TelemetryModule } from '@breadstone/archipel-platform-telemetry';
20
20
  - **Graceful degradation**: Falls back to `NoopTelemetryFacade` when disabled — zero overhead
21
21
  - **Health checks**: Integration with health module for observability readiness
22
22
  - **Structured logging**: `TelemetryLoggerService` with request correlation IDs
23
+ - **Eager metric init**: `MetricsService` creates instruments at startup via `OnModuleInit`
24
+ - **Write-once SDK guard**: `OtelSdkHolder` prevents accidental double-initialization of the OpenTelemetry SDK
25
+ - **Shutdown timeout**: SDK shutdown is capped at 5 seconds to prevent hanging processes
23
26
 
24
27
  ## Development
25
28
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@breadstone/archipel-platform-telemetry",
3
- "version": "0.0.19",
3
+ "version": "0.0.21",
4
4
  "description": "OpenTelemetry integration with tracing, metrics, and graceful no-op fallback for NestJS.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -28,8 +28,8 @@
28
28
  "README.md"
29
29
  ],
30
30
  "dependencies": {
31
- "@breadstone/archipel-platform-caching": "0.0.19",
32
- "@breadstone/archipel-platform-core": "0.0.19",
31
+ "@breadstone/archipel-platform-caching": "0.0.21",
32
+ "@breadstone/archipel-platform-core": "0.0.21",
33
33
  "@opentelemetry/api": "1.9.1",
34
34
  "@opentelemetry/exporter-metrics-otlp-http": "0.215.0",
35
35
  "@opentelemetry/exporter-trace-otlp-http": "0.215.0",
@@ -12,6 +12,7 @@ export declare class OtelSdkHolder implements OnApplicationShutdown {
12
12
  private _sdk?;
13
13
  /**
14
14
  * Stores the SDK reference so it can be shut down later.
15
+ * Can only be called once — subsequent calls are ignored.
15
16
  */
16
17
  setSdk(sdk: unknown): void;
17
18
  /** @inheritdoc */
@@ -24,8 +24,13 @@ let OtelSdkHolder = OtelSdkHolder_1 = class OtelSdkHolder {
24
24
  // #region Methods
25
25
  /**
26
26
  * Stores the SDK reference so it can be shut down later.
27
+ * Can only be called once — subsequent calls are ignored.
27
28
  */
28
29
  setSdk(sdk) {
30
+ if (this._sdk) {
31
+ this._logger.warn('OtelSdkHolder.setSdk() called more than once — ignoring.');
32
+ return;
33
+ }
29
34
  this._sdk = sdk;
30
35
  }
31
36
  /** @inheritdoc */
@@ -34,7 +39,7 @@ let OtelSdkHolder = OtelSdkHolder_1 = class OtelSdkHolder {
34
39
  return;
35
40
  }
36
41
  try {
37
- await this._sdk.shutdown();
42
+ await Promise.race([this._sdk.shutdown(), new Promise((resolve) => setTimeout(resolve, 5_000))]);
38
43
  this._logger.log('OpenTelemetry SDK shut down successfully.');
39
44
  }
40
45
  catch (error) {
@@ -7,18 +7,40 @@ const TelemetryRuleEngine_1 = require("../rules/TelemetryRuleEngine");
7
7
  let TelemetryLoggerService = class TelemetryLoggerService extends common_1.ConsoleLogger {
8
8
  // #endregion
9
9
  // #region Ctor
10
- constructor(ruleEngine) { super(); this._ruleEngine = ruleEngine; }
10
+ constructor(ruleEngine) {
11
+ super();
12
+ this._ruleEngine = ruleEngine;
13
+ }
11
14
  // #endregion
12
15
  // #region Methods
13
- log(message, context) { this.forward(message); super.log(message, context); }
14
- error(message, stack, context) { this.forward(message); super.error(message, stack, context); }
15
- warn(message, context) { this.forward(message); super.warn(message, context); }
16
- debug(message, context) { this.forward(message); super.debug?.(message, context); }
17
- verbose(message, context) { this.forward(message); super.verbose?.(message, context); }
18
- forward(message) { try {
19
- this._ruleEngine?.process(message);
16
+ log(message, context) {
17
+ this.forward(message);
18
+ super.log(message, context);
19
+ }
20
+ error(message, stack, context) {
21
+ this.forward(message);
22
+ super.error(message, stack, context);
23
+ }
24
+ warn(message, context) {
25
+ this.forward(message);
26
+ super.warn(message, context);
27
+ }
28
+ debug(message, context) {
29
+ this.forward(message);
30
+ super.debug?.(message, context);
31
+ }
32
+ verbose(message, context) {
33
+ this.forward(message);
34
+ super.verbose?.(message, context);
35
+ }
36
+ forward(message) {
37
+ try {
38
+ this._ruleEngine?.process(message);
39
+ }
40
+ catch (error) {
41
+ super.warn?.(`TelemetryRuleEngine.process failed: ${error instanceof Error ? error.message : String(error)}`);
42
+ }
20
43
  }
21
- catch { /* noop */ } }
22
44
  };
23
45
  exports.TelemetryLoggerService = TelemetryLoggerService;
24
46
  exports.TelemetryLoggerService = TelemetryLoggerService = tslib_1.__decorate([
@@ -1,20 +1,27 @@
1
+ import { type OnModuleInit } from '@nestjs/common';
1
2
  export interface IMetricInstruments {
2
3
  cacheCounter?: unknown;
3
4
  opCounter?: unknown;
4
5
  opDuration?: unknown;
5
6
  }
6
7
  /**
7
- * Provides lazily-initialized OpenTelemetry metric instruments.
8
+ * Provides eagerly-initialized OpenTelemetry metric instruments.
8
9
  *
9
10
  * Replaces the former module-level `let instruments` closure (TEL-4).
10
11
  *
11
12
  * @public
12
13
  */
13
- export declare class MetricsService {
14
+ export declare class MetricsService implements OnModuleInit {
14
15
  private readonly _logger;
15
16
  private _instruments?;
16
17
  /**
17
- * Returns the cached metric instruments, creating them on first access.
18
+ * Eagerly initializes metric instruments at module startup.
19
+ *
20
+ * @public
21
+ */
22
+ onModuleInit(): void;
23
+ /**
24
+ * Returns the cached metric instruments.
18
25
  */
19
26
  getInstruments(): IMetricInstruments | undefined;
20
27
  }
@@ -7,7 +7,7 @@ const tslib_1 = require("tslib");
7
7
  const common_1 = require("@nestjs/common");
8
8
  // #endregion
9
9
  /**
10
- * Provides lazily-initialized OpenTelemetry metric instruments.
10
+ * Provides eagerly-initialized OpenTelemetry metric instruments.
11
11
  *
12
12
  * Replaces the former module-level `let instruments` closure (TEL-4).
13
13
  *
@@ -22,11 +22,13 @@ let MetricsService = MetricsService_1 = class MetricsService {
22
22
  // #endregion
23
23
  // #region Methods
24
24
  /**
25
- * Returns the cached metric instruments, creating them on first access.
25
+ * Eagerly initializes metric instruments at module startup.
26
+ *
27
+ * @public
26
28
  */
27
- getInstruments() {
29
+ onModuleInit() {
28
30
  if (this._instruments) {
29
- return this._instruments;
31
+ return;
30
32
  }
31
33
  try {
32
34
  const api = require('@opentelemetry/api');
@@ -36,13 +38,17 @@ let MetricsService = MetricsService_1 = class MetricsService {
36
38
  opCounter: meter.createCounter('operation_event_total', { description: 'Operation success/error events' }),
37
39
  opDuration: meter.createHistogram('operation_duration_ms', { description: 'Operation durations ms' }),
38
40
  };
39
- return this._instruments;
40
41
  }
41
42
  catch (error) {
42
43
  this._logger.warn('Failed to initialize metric instruments.', error.message);
43
- return undefined;
44
44
  }
45
45
  }
46
+ /**
47
+ * Returns the cached metric instruments.
48
+ */
49
+ getInstruments() {
50
+ return this._instruments;
51
+ }
46
52
  };
47
53
  exports.MetricsService = MetricsService;
48
54
  exports.MetricsService = MetricsService = MetricsService_1 = tslib_1.__decorate([
@@ -3,6 +3,7 @@ import { ITelemetryOptions } from '../options/TelemetryOptions';
3
3
  export declare class TelemetryRuleEngine {
4
4
  private readonly _options;
5
5
  private readonly _metricsService;
6
+ private readonly _logger;
6
7
  constructor(options: ITelemetryOptions, metricsService: MetricsService);
7
8
  process(message: string): void;
8
9
  private handleCache;
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TelemetryRuleEngine = void 0;
4
+ const common_1 = require("@nestjs/common");
4
5
  class TelemetryRuleEngine {
5
6
  // #endregion
6
7
  // #region Ctor
7
8
  constructor(options, metricsService) {
9
+ this._logger = new common_1.Logger(TelemetryRuleEngine.name);
8
10
  this._options = options;
9
11
  this._metricsService = metricsService;
10
12
  }
@@ -37,8 +39,8 @@ class TelemetryRuleEngine {
37
39
  phase: tokens['phase'] ?? 'none',
38
40
  });
39
41
  }
40
- catch {
41
- /* noop */
42
+ catch (error) {
43
+ this._logger.warn('Cache metric recording failed', { error });
42
44
  }
43
45
  }
44
46
  handleOperation(line) {
@@ -58,16 +60,16 @@ class TelemetryRuleEngine {
58
60
  try {
59
61
  counter?.add(1, labels);
60
62
  }
61
- catch {
62
- /* noop */
63
+ catch (error) {
64
+ this._logger.warn('Operation counter recording failed', { error });
63
65
  }
64
66
  const dur = tokens['duration_ms'] ? Number(tokens['duration_ms']) : undefined;
65
67
  if (dur !== undefined && !Number.isNaN(dur)) {
66
68
  try {
67
69
  histogram?.record(dur, labels);
68
70
  }
69
- catch {
70
- /* noop */
71
+ catch (error) {
72
+ this._logger.warn('Operation histogram recording failed', { error });
71
73
  }
72
74
  }
73
75
  }
@@ -93,8 +95,8 @@ class TelemetryRuleEngine {
93
95
  histogram?.record(valueRaw, baseLabels);
94
96
  }
95
97
  }
96
- catch {
97
- /* noop */
98
+ catch (error) {
99
+ this._logger.warn('Client metric recording failed', { error });
98
100
  }
99
101
  }
100
102
  parseTokens(line) {