@backendkit-labs/observability 0.1.1 → 0.2.1

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/dist/index.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  var common = require('@nestjs/common');
4
4
  var async_hooks = require('async_hooks');
5
- var crypto = require('crypto');
5
+ var module$1 = require('module');
6
6
  var winston = require('winston');
7
7
  var TransportStream = require('winston-transport');
8
8
  var axios = require('axios');
@@ -10,8 +10,10 @@ var http = require('http');
10
10
  var https = require('https');
11
11
  var circuitBreaker = require('@backendkit-labs/circuit-breaker');
12
12
  var rxjs = require('rxjs');
13
+ var crypto = require('crypto');
13
14
  var operators = require('rxjs/operators');
14
15
 
16
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
15
17
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
16
18
 
17
19
  function _interopNamespace(e) {
@@ -39,12 +41,6 @@ var http__namespace = /*#__PURE__*/_interopNamespace(http);
39
41
  var https__namespace = /*#__PURE__*/_interopNamespace(https);
40
42
 
41
43
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
42
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
43
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
44
- }) : x)(function(x) {
45
- if (typeof require !== "undefined") return require.apply(this, arguments);
46
- throw Error('Dynamic require of "' + x + '" is not supported');
47
- });
48
44
  var __decorateClass = (decorators, target, key, kind) => {
49
45
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
50
46
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
@@ -53,11 +49,10 @@ var __decorateClass = (decorators, target, key, kind) => {
53
49
  return result;
54
50
  };
55
51
  var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
56
-
57
- // src/internal/otel.ts
52
+ var _require = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
58
53
  var otel = null;
59
54
  try {
60
- otel = __require("@opentelemetry/api");
55
+ otel = _require("@opentelemetry/api");
61
56
  } catch {
62
57
  }
63
58
  var getTracer = (name) => otel?.trace?.getTracer(name) ?? noopTracer;
@@ -95,9 +90,9 @@ exports.CorrelationIdService = class CorrelationIdService {
95
90
  run(correlationId, fn) {
96
91
  return storage.run(correlationId, fn);
97
92
  }
98
- /** Current correlation ID, or a fresh UUID when called outside a context. */
93
+ /** Current correlation ID, or 'no-context' when called outside a context. */
99
94
  get() {
100
- return storage.getStore() ?? crypto.randomUUID();
95
+ return storage.getStore() ?? "no-context";
101
96
  }
102
97
  /**
103
98
  * Current correlation ID, or `undefined` when called outside a context.
@@ -134,12 +129,18 @@ var TRANSPORT_CB_DEFAULTS = {
134
129
  halfOpenMaxCalls: 1,
135
130
  openTimeoutMs: 3e4
136
131
  };
137
- var WinstonHttpTransport = class extends TransportStream__default.default {
132
+ var WinstonHttpTransport = class _WinstonHttpTransport extends TransportStream__default.default {
138
133
  client;
139
134
  cb;
140
135
  buffer = [];
141
136
  batchSize;
142
137
  maxBufferSize;
138
+ maxEntryAgeMs = 3e5;
139
+ // 5 min
140
+ fallbackLogger = new common.Logger(_WinstonHttpTransport.name);
141
+ retryCounts = /* @__PURE__ */ new WeakMap();
142
+ entryTimes = /* @__PURE__ */ new WeakMap();
143
+ maxRetries = 5;
143
144
  flushTimer;
144
145
  constructor(opts) {
145
146
  super(opts);
@@ -158,11 +159,23 @@ var WinstonHttpTransport = class extends TransportStream__default.default {
158
159
  ...opts.headers
159
160
  }
160
161
  });
162
+ this.client.interceptors.response.use(
163
+ (response) => response,
164
+ (error) => {
165
+ if (error.config?.headers?.Authorization) {
166
+ error.config.headers.Authorization = "Bearer ***REDACTED***";
167
+ }
168
+ return Promise.reject(error);
169
+ }
170
+ );
161
171
  this.cb = new circuitBreaker.CircuitBreaker({
162
172
  ...TRANSPORT_CB_DEFAULTS,
163
173
  ...opts.circuitBreaker,
164
174
  name: "WinstonHttpTransport",
165
- isFailure: () => true
175
+ isFailure: (error) => {
176
+ const status = error.response?.status;
177
+ return status !== void 0 ? status >= 400 : true;
178
+ }
166
179
  });
167
180
  this.flushTimer = setInterval(
168
181
  () => {
@@ -174,9 +187,11 @@ var WinstonHttpTransport = class extends TransportStream__default.default {
174
187
  }
175
188
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
176
189
  log(info, callback) {
177
- setImmediate(() => this.emit("logged", info));
190
+ this.emit("logged", info);
178
191
  if (this.buffer.length < this.maxBufferSize) {
179
- this.buffer.push(info);
192
+ const entry = { ...info };
193
+ this.entryTimes.set(entry, Date.now());
194
+ this.buffer.push(entry);
180
195
  }
181
196
  if (this.buffer.length >= this.batchSize) {
182
197
  void this.flush();
@@ -190,14 +205,24 @@ var WinstonHttpTransport = class extends TransportStream__default.default {
190
205
  }
191
206
  async flush() {
192
207
  if (this.buffer.length === 0) return;
208
+ const now = Date.now();
209
+ this.buffer = this.buffer.filter(
210
+ (e) => now - (this.entryTimes.get(e) ?? now) < this.maxEntryAgeMs
211
+ );
193
212
  const batch = this.buffer.splice(0, this.batchSize);
194
213
  try {
195
214
  await this.cb.execute(() => this.client.post("", batch));
196
215
  } catch (err) {
216
+ const retryable = batch.filter((entry) => {
217
+ const retries = this.retryCounts.get(entry) ?? 0;
218
+ if (retries >= this.maxRetries) return false;
219
+ this.retryCounts.set(entry, retries + 1);
220
+ return true;
221
+ });
197
222
  const room = this.maxBufferSize - this.buffer.length;
198
- if (room > 0) this.buffer.unshift(...batch.slice(0, room));
223
+ if (room > 0) this.buffer.unshift(...retryable.slice(0, room));
199
224
  if (!(err instanceof circuitBreaker.CircuitBreakerOpenError)) {
200
- console.error(`[WinstonHttpTransport] flush failed \u2014 re-queued ${batch.length} entries`, err);
225
+ this.fallbackLogger.warn(`flush failed \u2014 re-queued ${retryable.length}/${batch.length} entries`, err.message);
201
226
  }
202
227
  }
203
228
  }
@@ -214,8 +239,10 @@ exports.LoggerService = class LoggerService {
214
239
  winston__namespace.format.timestamp(),
215
240
  winston__namespace.format.colorize(),
216
241
  winston__namespace.format.printf(({ level, message, timestamp, ...meta }) => {
217
- const extra = Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : "";
218
- return `${timestamp} [${level}] ${message}${extra}`;
242
+ const m = meta;
243
+ const ctxPart = m["context"] ? ` [${String(m["context"])}]` : "";
244
+ const corrPart = typeof m["correlationId"] === "string" && m["correlationId"] !== "no-context" ? ` [${m["correlationId"]}]` : "";
245
+ return `${timestamp} [${level}]${ctxPart}${corrPart} ${message}`;
219
246
  })
220
247
  )
221
248
  })
@@ -253,9 +280,10 @@ exports.LoggerService = class LoggerService {
253
280
  this.winston.verbose(message, this.buildMeta(context));
254
281
  }
255
282
  /** Log with additional arbitrary metadata. */
256
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
257
283
  logWithMeta(level, message, meta) {
258
- this.winston.log(level, message, { ...this.buildMeta(), ...meta });
284
+ const validLevels = ["error", "warn", "info", "debug", "verbose"];
285
+ const safeLevel = validLevels.includes(level) ? level : "info";
286
+ this.winston.log(safeLevel, message, { ...this.buildMeta(), ...meta });
259
287
  }
260
288
  buildMeta(context) {
261
289
  const base = {
@@ -305,7 +333,10 @@ exports.MetricsService = class MetricsService {
305
333
  ...TRANSPORT_CB_DEFAULTS2,
306
334
  ...m.circuitBreaker,
307
335
  name: "MetricsService",
308
- isFailure: () => true,
336
+ isFailure: (error) => {
337
+ const status = error.response?.status;
338
+ return status !== void 0 ? status >= 400 : true;
339
+ },
309
340
  onStateChange: (from, to, metrics) => {
310
341
  if (to === circuitBreaker.CircuitBreakerState.OPEN) {
311
342
  this.logger.warn(
@@ -333,6 +364,10 @@ exports.MetricsService = class MetricsService {
333
364
  logger = new common.Logger(exports.MetricsService.name);
334
365
  buffer = [];
335
366
  maxBufferSize = 5e3;
367
+ maxEntryAgeMs = 3e5;
368
+ // 5 min
369
+ retryCounts = /* @__PURE__ */ new WeakMap();
370
+ maxRetries = 5;
336
371
  flushTimer = null;
337
372
  /**
338
373
  * Enqueue a metric event. Fire-and-forget; batched and sent on the next
@@ -358,20 +393,33 @@ exports.MetricsService = class MetricsService {
358
393
  /** Flush on graceful shutdown. */
359
394
  async onModuleDestroy() {
360
395
  if (this.flushTimer) clearInterval(this.flushTimer);
361
- await this.flush();
396
+ try {
397
+ await this.flush();
398
+ } catch {
399
+ }
362
400
  }
363
401
  async flush() {
364
402
  if (!this.client || this.buffer.length === 0) return;
403
+ const now = Date.now();
404
+ this.buffer = this.buffer.filter(
405
+ (e) => now - new Date(e.timestamp).getTime() < this.maxEntryAgeMs
406
+ );
365
407
  const batch = this.buffer.splice(0, 500);
366
408
  try {
367
409
  await this.cb.execute(() => this.client.post("", batch));
368
410
  } catch (err) {
411
+ const retryable = batch.filter((entry) => {
412
+ const retries = this.retryCounts.get(entry) ?? 0;
413
+ if (retries >= this.maxRetries) return false;
414
+ this.retryCounts.set(entry, retries + 1);
415
+ return true;
416
+ });
369
417
  const room = this.maxBufferSize - this.buffer.length;
370
- if (room > 0) this.buffer.unshift(...batch.slice(0, room));
418
+ if (room > 0) this.buffer.unshift(...retryable.slice(0, room));
371
419
  if (!(err instanceof circuitBreaker.CircuitBreakerOpenError)) {
372
420
  this.logger.warn(
373
- `[MetricsService] flush failed \u2014 re-queueing ${batch.length} events`,
374
- err
421
+ `[MetricsService] flush failed \u2014 re-queueing ${retryable.length}/${batch.length} events`,
422
+ err.message
375
423
  );
376
424
  }
377
425
  }
@@ -383,6 +431,13 @@ exports.MetricsService = __decorateClass([
383
431
  __decorateParam(1, common.Optional())
384
432
  ], exports.MetricsService);
385
433
  var CORRELATION_HEADER = "x-correlation-id";
434
+ var CORRELATION_ID_REGEX = /^[a-zA-Z0-9\-_:]{1,64}$/;
435
+ function sanitizeCorrelationId(raw) {
436
+ if (typeof raw !== "string") return null;
437
+ if (raw.length > 64) return null;
438
+ if (!CORRELATION_ID_REGEX.test(raw)) return null;
439
+ return raw;
440
+ }
386
441
  exports.CorrelationInterceptor = class CorrelationInterceptor {
387
442
  constructor(correlationSvc) {
388
443
  this.correlationSvc = correlationSvc;
@@ -392,14 +447,11 @@ exports.CorrelationInterceptor = class CorrelationInterceptor {
392
447
  const req = ctx.switchToHttp().getRequest();
393
448
  const res = ctx.switchToHttp().getResponse();
394
449
  const incomingId = req.headers?.[CORRELATION_HEADER];
395
- const correlationId = typeof incomingId === "string" && incomingId ? incomingId : crypto.randomUUID();
450
+ const correlationId = sanitizeCorrelationId(incomingId) ?? crypto.randomUUID();
396
451
  res.setHeader(CORRELATION_HEADER, correlationId);
397
452
  return new rxjs.Observable((subscriber) => {
398
453
  this.correlationSvc.run(correlationId, () => {
399
- next.handle().pipe(
400
- operators.tap({ error: () => {
401
- } })
402
- ).subscribe(subscriber);
454
+ next.handle().subscribe(subscriber);
403
455
  });
404
456
  });
405
457
  }
@@ -507,7 +559,8 @@ exports.AllExceptionsFilter = class AllExceptionsFilter {
507
559
  }
508
560
  if (exception instanceof common.HttpException) {
509
561
  const body = exception.getResponse();
510
- const message = typeof body === "string" ? body : body.message ?? exception.message;
562
+ const rawMessage = typeof body === "string" ? body : body.message ?? exception.message;
563
+ const message = Array.isArray(rawMessage) ? rawMessage.join("; ") : String(rawMessage ?? exception.message);
511
564
  return { statusCode: exception.getStatus(), message };
512
565
  }
513
566
  return {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/otel.ts","../src/correlation/correlation.service.ts","../src/observability.constants.ts","../src/logger/winston-http.transport.ts","../src/logger/logger.service.ts","../src/metrics/metrics.service.ts","../src/interceptors/correlation.interceptor.ts","../src/interceptors/performance.interceptor.ts","../src/filters/all-exceptions.filter.ts","../src/observability.module.ts","../src/decorators/track-performance.decorator.ts"],"names":["AsyncLocalStorage","CorrelationIdService","randomUUID","Injectable","TransportStream","http","https","axios","CircuitBreaker","CircuitBreakerOpenError","LoggerService","transports","winston","Optional","TRANSPORT_CB_DEFAULTS","MetricsService","http2","https2","CircuitBreakerState","Logger","Inject","CorrelationInterceptor","Observable","tap","PerformanceInterceptor","AllExceptionsFilter","HttpException","HttpStatus","Catch","ObservabilityModule","Module"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Optional OpenTelemetry shim.\n * If @opentelemetry/api is not installed all operations become no-ops,\n * so the package works without any tracing backend.\n */\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet otel: any = null;\n\ntry {\n // Dynamic require keeps OTel out of the bundle when not installed\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n otel = require('@opentelemetry/api');\n} catch {\n // OTel not installed — spans will be no-ops\n}\n\nexport const isOtelAvailable = (): boolean => otel !== null;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const getTracer = (name: string): any =>\n otel?.trace?.getTracer(name) ?? noopTracer;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const getActiveSpan = (): any =>\n otel ? otel.trace.getSpan(otel.context.active()) : undefined;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const runInOtelContext = async <T>(span: any, fn: () => Promise<T>): Promise<T> => {\n if (!otel || !span) return fn();\n return otel.context.with(otel.trace.setSpan(otel.context.active(), span), fn);\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst noopSpan: any = {\n end: () => {},\n setAttribute: () => {},\n setAttributes: () => {},\n recordException: () => {},\n setStatus: () => {},\n spanContext: () => ({ traceId: '', spanId: '' }),\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst noopTracer: any = {\n startSpan: () => noopSpan,\n startActiveSpan: (_name: string, fn: (span: unknown) => unknown) => fn(noopSpan),\n};\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport { Injectable } from '@nestjs/common';\nimport { randomUUID } from 'node:crypto';\nimport { getActiveSpan } from '../internal/otel.js';\n\nconst storage = new AsyncLocalStorage<string>();\n\n@Injectable()\nexport class CorrelationIdService {\n /**\n * Run `fn` inside a context that carries `correlationId`.\n * All code executed within `fn` (including async continuations) can call\n * `get()` and receive the same ID without passing it explicitly.\n */\n run<T>(correlationId: string, fn: () => T): T {\n return storage.run(correlationId, fn);\n }\n\n /** Current correlation ID, or a fresh UUID when called outside a context. */\n get(): string {\n return storage.getStore() ?? randomUUID();\n }\n\n /**\n * Current correlation ID, or `undefined` when called outside a context.\n * Prefer `get()` for logging; use this only when you need to distinguish\n * \"no context\" from \"context with a random ID\".\n */\n getOrUndefined(): string | undefined {\n return storage.getStore();\n }\n\n /**\n * Active OTel trace + span IDs when @opentelemetry/api is installed and a\n * span is active; `undefined` otherwise.\n */\n getTraceContext(): { traceId: string; spanId: string } | undefined {\n const span = getActiveSpan();\n if (!span) return undefined;\n const ctx = span.spanContext?.();\n if (!ctx?.traceId) return undefined;\n return { traceId: ctx.traceId, spanId: ctx.spanId };\n }\n}\n","export const OBSERVABILITY_OPTIONS = 'OBSERVABILITY_OPTIONS' as const;\n","import TransportStream, { TransportStreamOptions } from 'winston-transport';\nimport axios, { AxiosInstance } from 'axios';\nimport * as http from 'node:http';\nimport * as https from 'node:https';\nimport {\n CircuitBreaker,\n CircuitBreakerConfig,\n CircuitBreakerOpenError,\n} from '@backendkit-labs/circuit-breaker';\n\nexport interface WinstonHttpTransportOptions extends TransportStreamOptions {\n /** Full URL of the log-ingest endpoint. */\n url: string;\n\n /** Bearer token sent in `Authorization` header. */\n authToken?: string;\n\n /** Additional static headers merged into every request. */\n headers?: Record<string, string>;\n\n /** Flush batch when it reaches this many entries (default 100). */\n batchSize?: number;\n\n /** Maximum entries held in the in-memory buffer (default 2000). */\n maxBufferSize?: number;\n\n /** Flush interval in ms — also flushes on `close()` (default 5000). */\n flushIntervalMs?: number;\n\n /** Request timeout in ms (default 5000). */\n timeoutMs?: number;\n\n /**\n * Override any circuit breaker config fields.\n * `name` and `isFailure` are set internally and cannot be overridden.\n *\n * Transport defaults: failureThreshold 60%, slidingWindowSize 5,\n * minimumCalls 3, openTimeoutMs 30 000, halfOpenMaxCalls 1.\n */\n circuitBreaker?: Partial<Omit<CircuitBreakerConfig, 'name' | 'isFailure'>>;\n}\n\ninterface LogEntry {\n level: string;\n message: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [key: string]: any;\n}\n\nconst TRANSPORT_CB_DEFAULTS: Omit<CircuitBreakerConfig, 'name' | 'isFailure'> = {\n failureThreshold: 60,\n slowCallThreshold: 100,\n slowCallDurationMs: 60_000,\n minimumCalls: 3,\n slidingWindowSize: 5,\n halfOpenMaxCalls: 1,\n openTimeoutMs: 30_000,\n};\n\nexport class WinstonHttpTransport extends TransportStream {\n private readonly client: AxiosInstance;\n private readonly cb: CircuitBreaker;\n private readonly buffer: LogEntry[] = [];\n private readonly batchSize: number;\n private readonly maxBufferSize: number;\n private readonly flushTimer: ReturnType<typeof setInterval>;\n\n constructor(opts: WinstonHttpTransportOptions) {\n super(opts);\n\n this.batchSize = opts.batchSize ?? 100;\n this.maxBufferSize = opts.maxBufferSize ?? 2_000;\n\n const keepAlive = new http.Agent({ keepAlive: true });\n const keepAliveHttps = new https.Agent({ keepAlive: true });\n\n this.client = axios.create({\n baseURL: opts.url,\n timeout: opts.timeoutMs ?? 5_000,\n httpAgent: keepAlive,\n httpsAgent: keepAliveHttps,\n headers: {\n 'Content-Type': 'application/json',\n ...(opts.authToken ? { Authorization: `Bearer ${opts.authToken}` } : {}),\n ...opts.headers,\n },\n });\n\n this.cb = new CircuitBreaker({\n ...TRANSPORT_CB_DEFAULTS,\n ...opts.circuitBreaker,\n name: 'WinstonHttpTransport',\n isFailure: () => true,\n });\n\n this.flushTimer = setInterval(\n () => { void this.flush(); },\n opts.flushIntervalMs ?? 5_000,\n );\n this.flushTimer.unref?.();\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n override log(info: any, callback: () => void): void {\n setImmediate(() => this.emit('logged', info));\n\n if (this.buffer.length < this.maxBufferSize) {\n this.buffer.push(info as LogEntry);\n }\n // silently drop when full — buffer-full warn would cause infinite recursion\n\n if (this.buffer.length >= this.batchSize) {\n void this.flush();\n }\n\n callback();\n }\n\n /** Flush remaining buffer on graceful shutdown. */\n override async close(): Promise<void> {\n clearInterval(this.flushTimer);\n await this.flush();\n }\n\n private async flush(): Promise<void> {\n if (this.buffer.length === 0) return;\n\n const batch = this.buffer.splice(0, this.batchSize);\n\n try {\n await this.cb.execute(() => this.client.post('', batch));\n } catch (err) {\n // Re-queue the batch (whether CB was open or the request failed)\n const room = this.maxBufferSize - this.buffer.length;\n if (room > 0) this.buffer.unshift(...batch.slice(0, room));\n\n if (!(err instanceof CircuitBreakerOpenError)) {\n // Network errors are worth logging; CB-open state was already surfaced\n // via onStateChange in the CircuitBreaker itself\n console.error(`[WinstonHttpTransport] flush failed — re-queued ${batch.length} entries`, err);\n }\n }\n }\n}\n","import { Injectable, Inject, Optional, LoggerService as NestLoggerService } from '@nestjs/common';\nimport * as winston from 'winston';\nimport { CorrelationIdService } from '../correlation/correlation.service.js';\nimport { OBSERVABILITY_OPTIONS } from '../observability.constants.js';\nimport { ObservabilityOptions } from '../observability.types.js';\nimport { WinstonHttpTransport } from './winston-http.transport.js';\n\n@Injectable()\nexport class LoggerService implements NestLoggerService {\n private readonly winston: winston.Logger;\n\n constructor(\n @Inject(OBSERVABILITY_OPTIONS)\n private readonly opts: ObservabilityOptions,\n @Optional()\n private readonly correlationSvc?: CorrelationIdService,\n ) {\n const transports: winston.transport[] = [\n new winston.transports.Console({\n format: winston.format.combine(\n winston.format.timestamp(),\n winston.format.colorize(),\n winston.format.printf(({ level, message, timestamp, ...meta }) => {\n const extra = Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : '';\n return `${timestamp} [${level}] ${message}${extra}`;\n }),\n ),\n }),\n ];\n\n if (opts.http) {\n transports.push(\n new WinstonHttpTransport({\n ...opts.http,\n format: winston.format.json(),\n }),\n );\n }\n\n this.winston = winston.createLogger({\n level: opts.logLevel ?? 'info',\n transports,\n format: winston.format.json(),\n });\n }\n\n log(message: string, context?: string): void {\n this.winston.info(message, this.buildMeta(context));\n }\n\n error(message: string, trace?: string, context?: string): void {\n this.winston.error(message, { ...this.buildMeta(context), trace });\n }\n\n warn(message: string, context?: string): void {\n this.winston.warn(message, this.buildMeta(context));\n }\n\n debug(message: string, context?: string): void {\n this.winston.debug(message, this.buildMeta(context));\n }\n\n verbose(message: string, context?: string): void {\n this.winston.verbose(message, this.buildMeta(context));\n }\n\n /** Log with additional arbitrary metadata. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n logWithMeta(level: string, message: string, meta: Record<string, any>): void {\n this.winston.log(level, message, { ...this.buildMeta(), ...meta });\n }\n\n private buildMeta(context?: string): Record<string, unknown> {\n const base: Record<string, unknown> = {\n service: this.opts.serviceName,\n environment: this.opts.environment ?? 'production',\n correlationId: this.correlationSvc?.get(),\n };\n if (context) base.context = context;\n return base;\n }\n}\n","import { Injectable, Inject, Optional, OnModuleDestroy, Logger } from '@nestjs/common';\nimport axios, { AxiosInstance } from 'axios';\nimport * as http from 'node:http';\nimport * as https from 'node:https';\nimport {\n CircuitBreaker,\n CircuitBreakerConfig,\n CircuitBreakerOpenError,\n CircuitBreakerState,\n} from '@backendkit-labs/circuit-breaker';\nimport { CorrelationIdService } from '../correlation/correlation.service.js';\nimport { OBSERVABILITY_OPTIONS } from '../observability.constants.js';\nimport { ObservabilityOptions, MetricEvent } from '../observability.types.js';\n\nconst TRANSPORT_CB_DEFAULTS: Omit<CircuitBreakerConfig, 'name' | 'isFailure'> = {\n failureThreshold: 60,\n slowCallThreshold: 100,\n slowCallDurationMs: 60_000,\n minimumCalls: 3,\n slidingWindowSize: 5,\n halfOpenMaxCalls: 1,\n openTimeoutMs: 30_000,\n};\n\n@Injectable()\nexport class MetricsService implements OnModuleDestroy {\n private readonly client: AxiosInstance | null = null;\n private readonly cb: CircuitBreaker | null = null;\n private readonly logger = new Logger(MetricsService.name);\n private readonly buffer: MetricEvent[] = [];\n private readonly maxBufferSize: number = 5_000;\n private readonly flushTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(\n @Inject(OBSERVABILITY_OPTIONS)\n private readonly opts: ObservabilityOptions,\n @Optional()\n private readonly correlationSvc?: CorrelationIdService,\n ) {\n if (!opts.metrics) return;\n\n const m = opts.metrics;\n this.maxBufferSize = m.maxBufferSize ?? 5_000;\n\n const keepAlive = new http.Agent({ keepAlive: true });\n const keepAliveHttps = new https.Agent({ keepAlive: true });\n\n this.client = axios.create({\n baseURL: m.url,\n timeout: m.timeoutMs ?? 5_000,\n httpAgent: keepAlive,\n httpsAgent: keepAliveHttps,\n headers: {\n 'Content-Type': 'application/json',\n ...(m.authToken ? { Authorization: `Bearer ${m.authToken}` } : {}),\n ...m.headers,\n },\n });\n\n this.cb = new CircuitBreaker({\n ...TRANSPORT_CB_DEFAULTS,\n ...m.circuitBreaker,\n name: 'MetricsService',\n isFailure: () => true,\n onStateChange: (from, to, metrics) => {\n if (to === CircuitBreakerState.OPEN) {\n this.logger.warn(\n `[MetricsService] circuit breaker OPEN — pausing metric sends for ${(m.circuitBreaker?.openTimeoutMs ?? TRANSPORT_CB_DEFAULTS.openTimeoutMs) / 1_000}s`,\n metrics,\n );\n } else if (to === CircuitBreakerState.CLOSED && from !== CircuitBreakerState.HALF_OPEN) {\n this.logger.log(`[MetricsService] circuit breaker CLOSED — recovered`);\n }\n m.circuitBreaker?.onStateChange?.(from, to, metrics);\n },\n });\n\n this.flushTimer = setInterval(\n () => { void this.flush(); },\n m.flushIntervalMs ?? 10_000,\n );\n this.flushTimer.unref?.();\n }\n\n /**\n * Enqueue a metric event. Fire-and-forget; batched and sent on the next\n * flush interval or when the buffer reaches `maxBufferSize`.\n */\n record(\n name: string,\n value: number,\n options?: { unit?: string; tags?: Record<string, string> },\n ): void {\n if (!this.client) return;\n\n if (this.buffer.length >= this.maxBufferSize) {\n this.logger.warn('[MetricsService] buffer full — dropping metric');\n return;\n }\n\n this.buffer.push({\n name,\n value,\n unit: options?.unit,\n tags: options?.tags,\n timestamp: new Date().toISOString(),\n serviceName: this.opts.serviceName,\n environment: this.opts.environment ?? 'production',\n correlationId: this.correlationSvc?.getOrUndefined(),\n });\n }\n\n /** Flush on graceful shutdown. */\n async onModuleDestroy(): Promise<void> {\n if (this.flushTimer) clearInterval(this.flushTimer);\n await this.flush();\n }\n\n private async flush(): Promise<void> {\n if (!this.client || this.buffer.length === 0) return;\n\n const batch = this.buffer.splice(0, 500);\n\n try {\n await this.cb!.execute(() => this.client!.post('', batch));\n } catch (err) {\n // Re-queue in both cases (CB open or network error)\n const room = this.maxBufferSize - this.buffer.length;\n if (room > 0) this.buffer.unshift(...batch.slice(0, room));\n\n if (!(err instanceof CircuitBreakerOpenError)) {\n this.logger.warn(\n `[MetricsService] flush failed — re-queueing ${batch.length} events`,\n err,\n );\n }\n }\n }\n}\n","import {\n Injectable,\n NestInterceptor,\n ExecutionContext,\n CallHandler,\n Inject,\n} from '@nestjs/common';\nimport { Observable } from 'rxjs';\nimport { tap } from 'rxjs/operators';\nimport { randomUUID } from 'node:crypto';\nimport { CorrelationIdService } from '../correlation/correlation.service.js';\n\nconst CORRELATION_HEADER = 'x-correlation-id';\n\n@Injectable()\nexport class CorrelationInterceptor implements NestInterceptor {\n constructor(\n @Inject(CorrelationIdService)\n private readonly correlationSvc: CorrelationIdService,\n ) {}\n\n intercept(ctx: ExecutionContext, next: CallHandler): Observable<unknown> {\n const req = ctx.switchToHttp().getRequest<Record<string, unknown>>();\n const res = ctx.switchToHttp().getResponse<{\n setHeader(name: string, value: string): void;\n }>();\n\n const incomingId =\n (req.headers as Record<string, string | undefined>)?.[CORRELATION_HEADER];\n const correlationId = (typeof incomingId === 'string' && incomingId)\n ? incomingId\n : randomUUID();\n\n res.setHeader(CORRELATION_HEADER, correlationId);\n\n return new Observable(subscriber => {\n this.correlationSvc.run(correlationId, () => {\n next.handle().pipe(\n tap({ error: () => {} }),\n ).subscribe(subscriber);\n });\n });\n }\n}\n","import {\n Injectable,\n NestInterceptor,\n ExecutionContext,\n CallHandler,\n Inject,\n Optional,\n} from '@nestjs/common';\nimport { Observable } from 'rxjs';\nimport { tap } from 'rxjs/operators';\nimport { LoggerService } from '../logger/logger.service.js';\nimport { MetricsService } from '../metrics/metrics.service.js';\nimport { CorrelationIdService } from '../correlation/correlation.service.js';\nimport { getActiveSpan } from '../internal/otel.js';\n\n@Injectable()\nexport class PerformanceInterceptor implements NestInterceptor {\n constructor(\n @Inject(LoggerService)\n private readonly logger: LoggerService,\n @Optional()\n @Inject(MetricsService)\n private readonly metrics: MetricsService | undefined,\n @Optional()\n @Inject(CorrelationIdService)\n private readonly correlationSvc: CorrelationIdService | undefined,\n ) {}\n\n intercept(ctx: ExecutionContext, next: CallHandler): Observable<unknown> {\n const start = Date.now();\n const req = ctx.switchToHttp().getRequest<{\n method: string;\n url: string;\n }>();\n const method = req.method ?? 'UNKNOWN';\n const path = req.url ?? 'UNKNOWN';\n\n return next.handle().pipe(\n tap({\n next: () => {\n const durationMs = Date.now() - start;\n this.record(method, path, 'success', durationMs);\n },\n error: () => {\n const durationMs = Date.now() - start;\n this.record(method, path, 'error', durationMs);\n },\n }),\n );\n }\n\n private record(\n method: string,\n path: string,\n outcome: 'success' | 'error',\n durationMs: number,\n ): void {\n const correlationId = this.correlationSvc?.getOrUndefined();\n const span = getActiveSpan();\n\n span?.setAttribute('http.method', method);\n span?.setAttribute('http.target', path);\n span?.setAttribute('http.duration_ms', durationMs);\n\n this.logger.logWithMeta('info', `${method} ${path} [${outcome}] ${durationMs}ms`, {\n method,\n path,\n outcome,\n durationMs,\n correlationId,\n });\n\n this.metrics?.record('http.request.duration', durationMs, {\n unit: 'ms',\n tags: { method, path, outcome },\n });\n }\n}\n","import {\n ExceptionFilter,\n Catch,\n ArgumentsHost,\n HttpException,\n HttpStatus,\n Inject,\n Optional,\n} from '@nestjs/common';\nimport { LoggerService } from '../logger/logger.service.js';\nimport { CorrelationIdService } from '../correlation/correlation.service.js';\nimport { getActiveSpan } from '../internal/otel.js';\n\nexport type ErrorMapper = (error: unknown) => {\n statusCode: number;\n message: string;\n code?: string;\n} | null;\n\n@Catch()\nexport class AllExceptionsFilter implements ExceptionFilter {\n private readonly mappers: ErrorMapper[] = [];\n\n constructor(\n @Inject(LoggerService)\n private readonly logger: LoggerService,\n @Optional()\n @Inject(CorrelationIdService)\n private readonly correlationSvc: CorrelationIdService | undefined,\n ) {}\n\n /**\n * Register a custom error mapper.\n * Mappers are tried in order; the first non-null result wins.\n * Return `null` to fall through to the next mapper.\n */\n addMapper(mapper: ErrorMapper): this {\n this.mappers.push(mapper);\n return this;\n }\n\n catch(exception: unknown, host: ArgumentsHost): void {\n const ctx = host.switchToHttp();\n const res = ctx.getResponse<{\n status(code: number): { json(body: unknown): void };\n }>();\n\n const mapped = this.resolveError(exception);\n\n const correlationId = this.correlationSvc?.getOrUndefined();\n const span = getActiveSpan();\n span?.recordException(exception instanceof Error ? exception : new Error(String(exception)));\n\n this.logger.error(\n mapped.message,\n exception instanceof Error ? exception.stack : undefined,\n AllExceptionsFilter.name,\n );\n\n res.status(mapped.statusCode).json({\n ok: false,\n statusCode: mapped.statusCode,\n message: mapped.message,\n code: mapped.code,\n correlationId,\n timestamp: new Date().toISOString(),\n });\n }\n\n private resolveError(exception: unknown): {\n statusCode: number;\n message: string;\n code?: string;\n } {\n for (const mapper of this.mappers) {\n const result = mapper(exception);\n if (result !== null) return result;\n }\n\n if (exception instanceof HttpException) {\n const body = exception.getResponse();\n const message =\n typeof body === 'string'\n ? body\n : (body as { message?: string }).message ?? exception.message;\n return { statusCode: exception.getStatus(), message };\n }\n\n return {\n statusCode: HttpStatus.INTERNAL_SERVER_ERROR,\n message: 'Internal server error',\n };\n }\n}\n","import { DynamicModule, Module, Provider } from '@nestjs/common';\nimport { CorrelationIdService } from './correlation/correlation.service.js';\nimport { LoggerService } from './logger/logger.service.js';\nimport { MetricsService } from './metrics/metrics.service.js';\nimport { CorrelationInterceptor } from './interceptors/correlation.interceptor.js';\nimport { PerformanceInterceptor } from './interceptors/performance.interceptor.js';\nimport { AllExceptionsFilter } from './filters/all-exceptions.filter.js';\nimport { OBSERVABILITY_OPTIONS } from './observability.constants.js';\nimport { ObservabilityOptions } from './observability.types.js';\n\n@Module({})\nexport class ObservabilityModule {\n static forRoot(options: ObservabilityOptions): DynamicModule {\n const optionsProvider: Provider = {\n provide: OBSERVABILITY_OPTIONS,\n useValue: options,\n };\n\n const providers: Provider[] = [\n optionsProvider,\n CorrelationIdService,\n LoggerService,\n MetricsService,\n CorrelationInterceptor,\n PerformanceInterceptor,\n AllExceptionsFilter,\n ];\n\n return {\n module: ObservabilityModule,\n global: true,\n providers,\n exports: [\n CorrelationIdService,\n LoggerService,\n MetricsService,\n CorrelationInterceptor,\n PerformanceInterceptor,\n AllExceptionsFilter,\n ],\n };\n }\n}\n","import { getTracer, runInOtelContext } from '../internal/otel.js';\n\nexport interface TrackPerformanceOptions {\n /** OTel span / log operation name. Defaults to `ClassName.methodName`. */\n operation?: string;\n\n /** Attributes added to the OTel span. */\n attributes?: Record<string, string | number | boolean>;\n}\n\n/**\n * Method decorator that wraps the decorated async method in an OTel span\n * (when @opentelemetry/api is available) and records its duration.\n * Works with regular methods and NestJS service methods.\n *\n * @example\n * \\@TrackPerformance()\n * async processPayment(id: string) { ... }\n */\nexport function TrackPerformance(options: TrackPerformanceOptions = {}): MethodDecorator {\n return function (\n target: object,\n propertyKey: string | symbol,\n descriptor: PropertyDescriptor,\n ): PropertyDescriptor {\n const original = descriptor.value as (...args: unknown[]) => Promise<unknown>;\n const operationName =\n options.operation ?? `${target.constructor.name}.${String(propertyKey)}`;\n\n descriptor.value = async function (...args: unknown[]): Promise<unknown> {\n const tracer = getTracer('@backendkit-labs/observability');\n const span = tracer.startSpan(operationName);\n\n if (options.attributes) {\n span.setAttributes(options.attributes);\n }\n\n try {\n const result = await runInOtelContext(span, () => original.apply(this, args));\n span.setStatus({ code: 1 }); // SpanStatusCode.OK\n return result;\n } catch (err) {\n span.recordException(err instanceof Error ? err : new Error(String(err)));\n span.setStatus({ code: 2, message: String(err) }); // SpanStatusCode.ERROR\n throw err;\n } finally {\n span.end();\n }\n };\n\n return descriptor;\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/otel.ts","../src/correlation/correlation.service.ts","../src/observability.constants.ts","../src/logger/winston-http.transport.ts","../src/logger/logger.service.ts","../src/metrics/metrics.service.ts","../src/interceptors/correlation.interceptor.ts","../src/interceptors/performance.interceptor.ts","../src/filters/all-exceptions.filter.ts","../src/observability.module.ts","../src/decorators/track-performance.decorator.ts"],"names":["createRequire","AsyncLocalStorage","CorrelationIdService","Injectable","TransportStream","Logger","http","https","axios","CircuitBreaker","CircuitBreakerOpenError","LoggerService","transports","winston","Optional","TRANSPORT_CB_DEFAULTS","MetricsService","http2","https2","CircuitBreakerState","Inject","CorrelationInterceptor","randomUUID","Observable","PerformanceInterceptor","tap","AllExceptionsFilter","HttpException","HttpStatus","Catch","ObservabilityModule","Module"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Optional OpenTelemetry shim.\n * If @opentelemetry/api is not installed all operations become no-ops,\n * so the package works without any tracing backend.\n */\n\nimport { createRequire } from 'node:module';\n\nconst _require = createRequire(import.meta.url);\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet otel: any = null;\n\ntry {\n // Dynamic require keeps OTel out of the bundle when not installed\n otel = _require('@opentelemetry/api');\n} catch {\n // OTel not installed — spans will be no-ops\n}\n\nexport const isOtelAvailable = (): boolean => otel !== null;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const getTracer = (name: string): any =>\n otel?.trace?.getTracer(name) ?? noopTracer;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const getActiveSpan = (): any =>\n otel ? otel.trace.getSpan(otel.context.active()) : undefined;\n\nexport const runInOtelContext = async <T>(span: unknown, fn: () => Promise<T>): Promise<T> => {\n if (!otel || !span) return fn();\n return otel.context.with(otel.trace.setSpan(otel.context.active(), span), fn);\n};\n\nexport interface SpanShim {\n setAttribute(key: string, value: unknown): void;\n setAttributes(attrs: Record<string, unknown>): void;\n recordException(err: Error): void;\n setStatus(status: { code: number; message?: string }): void;\n end(): void;\n spanContext(): { traceId: string; spanId: string };\n}\n\nconst noopSpan: SpanShim = {\n end: () => {},\n setAttribute: () => {},\n setAttributes: () => {},\n recordException: () => {},\n setStatus: () => {},\n spanContext: () => ({ traceId: '', spanId: '' }),\n};\n\nconst noopTracer = {\n startSpan: () => noopSpan,\n startActiveSpan: (_name: string, fn: (span: unknown) => unknown) => fn(noopSpan),\n};\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport { Injectable } from '@nestjs/common';\nimport { getActiveSpan } from '../internal/otel.js';\n\nconst storage = new AsyncLocalStorage<string>();\n\n@Injectable()\nexport class CorrelationIdService {\n /**\n * Run `fn` inside a context that carries `correlationId`.\n * All code executed within `fn` (including async continuations) can call\n * `get()` and receive the same ID without passing it explicitly.\n */\n run<T>(correlationId: string, fn: () => T): T {\n return storage.run(correlationId, fn);\n }\n\n /** Current correlation ID, or 'no-context' when called outside a context. */\n get(): string {\n return storage.getStore() ?? 'no-context';\n }\n\n /**\n * Current correlation ID, or `undefined` when called outside a context.\n * Prefer `get()` for logging; use this only when you need to distinguish\n * \"no context\" from \"context with a random ID\".\n */\n getOrUndefined(): string | undefined {\n return storage.getStore();\n }\n\n /**\n * Active OTel trace + span IDs when @opentelemetry/api is installed and a\n * span is active; `undefined` otherwise.\n */\n getTraceContext(): { traceId: string; spanId: string } | undefined {\n const span = getActiveSpan();\n if (!span) return undefined;\n const ctx = span.spanContext?.();\n if (!ctx?.traceId) return undefined;\n return { traceId: ctx.traceId, spanId: ctx.spanId };\n }\n}\n","export const OBSERVABILITY_OPTIONS = 'OBSERVABILITY_OPTIONS' as const;\n","import { Logger } from '@nestjs/common';\nimport TransportStream, { TransportStreamOptions } from 'winston-transport';\nimport axios, { AxiosInstance } from 'axios';\nimport * as http from 'node:http';\nimport * as https from 'node:https';\nimport {\n CircuitBreaker,\n CircuitBreakerConfig,\n CircuitBreakerOpenError,\n} from '@backendkit-labs/circuit-breaker';\n\nexport interface WinstonHttpTransportOptions extends TransportStreamOptions {\n /** Full URL of the log-ingest endpoint. */\n url: string;\n\n /** Bearer token sent in `Authorization` header. */\n authToken?: string;\n\n /** Additional static headers merged into every request. */\n headers?: Record<string, string>;\n\n /** Flush batch when it reaches this many entries (default 100). */\n batchSize?: number;\n\n /** Maximum entries held in the in-memory buffer (default 2000). */\n maxBufferSize?: number;\n\n /** Flush interval in ms — also flushes on `close()` (default 5000). */\n flushIntervalMs?: number;\n\n /** Request timeout in ms (default 5000). */\n timeoutMs?: number;\n\n /**\n * Override any circuit breaker config fields.\n * `name` and `isFailure` are set internally and cannot be overridden.\n *\n * Transport defaults: failureThreshold 60%, slidingWindowSize 5,\n * minimumCalls 3, openTimeoutMs 30 000, halfOpenMaxCalls 1.\n */\n circuitBreaker?: Partial<Omit<CircuitBreakerConfig, 'name' | 'isFailure'>>;\n}\n\ninterface LogEntry {\n level: string;\n message: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [key: string]: any;\n}\n\nconst TRANSPORT_CB_DEFAULTS: Omit<CircuitBreakerConfig, 'name' | 'isFailure'> = {\n failureThreshold: 60,\n slowCallThreshold: 100,\n slowCallDurationMs: 60_000,\n minimumCalls: 3,\n slidingWindowSize: 5,\n halfOpenMaxCalls: 1,\n openTimeoutMs: 30_000,\n};\n\nexport class WinstonHttpTransport extends TransportStream {\n private readonly client: AxiosInstance;\n private readonly cb: CircuitBreaker;\n private buffer: LogEntry[] = [];\n private readonly batchSize: number;\n private readonly maxBufferSize: number;\n private readonly maxEntryAgeMs = 300_000; // 5 min\n private readonly fallbackLogger = new Logger(WinstonHttpTransport.name);\n private readonly retryCounts = new WeakMap<LogEntry, number>();\n private readonly entryTimes = new WeakMap<LogEntry, number>();\n private readonly maxRetries = 5;\n private readonly flushTimer: ReturnType<typeof setInterval>;\n\n constructor(opts: WinstonHttpTransportOptions) {\n super(opts);\n\n this.batchSize = opts.batchSize ?? 100;\n this.maxBufferSize = opts.maxBufferSize ?? 2_000;\n\n const keepAlive = new http.Agent({ keepAlive: true });\n const keepAliveHttps = new https.Agent({ keepAlive: true });\n\n this.client = axios.create({\n baseURL: opts.url,\n timeout: opts.timeoutMs ?? 5_000,\n httpAgent: keepAlive,\n httpsAgent: keepAliveHttps,\n headers: {\n 'Content-Type': 'application/json',\n ...(opts.authToken ? { Authorization: `Bearer ${opts.authToken}` } : {}),\n ...opts.headers,\n },\n });\n\n // Redact sensitive headers from error output\n this.client.interceptors.response.use(\n (response) => response,\n (error) => {\n if (error.config?.headers?.Authorization) {\n error.config.headers.Authorization = 'Bearer ***REDACTED***';\n }\n return Promise.reject(error);\n },\n );\n\n this.cb = new CircuitBreaker({\n ...TRANSPORT_CB_DEFAULTS,\n ...opts.circuitBreaker,\n name: 'WinstonHttpTransport',\n isFailure: (error: unknown) => {\n const status = (error as { response?: { status?: number } }).response?.status;\n return status !== undefined ? status >= 400 : true;\n },\n });\n\n this.flushTimer = setInterval(\n () => { void this.flush(); },\n opts.flushIntervalMs ?? 5_000,\n );\n this.flushTimer.unref?.();\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n override log(info: any, callback: () => void): void {\n this.emit('logged', info);\n\n if (this.buffer.length < this.maxBufferSize) {\n const entry = { ...info } as LogEntry;\n this.entryTimes.set(entry, Date.now());\n this.buffer.push(entry);\n }\n // silently drop when full — buffer-full warn would cause infinite recursion\n\n if (this.buffer.length >= this.batchSize) {\n void this.flush();\n }\n\n callback();\n }\n\n /** Flush remaining buffer on graceful shutdown. */\n override async close(): Promise<void> {\n clearInterval(this.flushTimer);\n await this.flush();\n }\n\n private async flush(): Promise<void> {\n if (this.buffer.length === 0) return;\n\n // Discard entries older than maxEntryAgeMs\n const now = Date.now();\n this.buffer = this.buffer.filter(\n (e) => now - (this.entryTimes.get(e) ?? now) < this.maxEntryAgeMs,\n );\n\n const batch = this.buffer.splice(0, this.batchSize);\n\n try {\n await this.cb.execute(() => this.client.post('', batch));\n } catch (err) {\n // Re-queue with retry limit to prevent infinite re-enqueue\n const retryable = batch.filter(entry => {\n const retries = this.retryCounts.get(entry) ?? 0;\n if (retries >= this.maxRetries) return false;\n this.retryCounts.set(entry, retries + 1);\n return true;\n });\n const room = this.maxBufferSize - this.buffer.length;\n if (room > 0) this.buffer.unshift(...retryable.slice(0, room));\n\n if (!(err instanceof CircuitBreakerOpenError)) {\n this.fallbackLogger.warn(`flush failed — re-queued ${retryable.length}/${batch.length} entries`, (err as Error).message);\n }\n }\n }\n}\n","import { Injectable, Inject, Optional, LoggerService as NestLoggerService } from '@nestjs/common';\nimport * as winston from 'winston';\nimport { CorrelationIdService } from '../correlation/correlation.service.js';\nimport { OBSERVABILITY_OPTIONS } from '../observability.constants.js';\nimport { ObservabilityOptions } from '../observability.types.js';\nimport { WinstonHttpTransport } from './winston-http.transport.js';\n\n@Injectable()\nexport class LoggerService implements NestLoggerService {\n private readonly winston: winston.Logger;\n\n constructor(\n @Inject(OBSERVABILITY_OPTIONS)\n private readonly opts: ObservabilityOptions,\n @Optional()\n private readonly correlationSvc?: CorrelationIdService,\n ) {\n const transports: winston.transport[] = [\n new winston.transports.Console({\n format: winston.format.combine(\n winston.format.timestamp(),\n winston.format.colorize(),\n winston.format.printf(({ level, message, timestamp, ...meta }) => {\n const m = meta as Record<string, unknown>;\n const ctxPart = m['context'] ? ` [${String(m['context'])}]` : '';\n const corrPart = typeof m['correlationId'] === 'string' && m['correlationId'] !== 'no-context'\n ? ` [${m['correlationId']}]`\n : '';\n\n return `${timestamp} [${level}]${ctxPart}${corrPart} ${message}`;\n }),\n ),\n }),\n ];\n\n if (opts.http) {\n transports.push(\n new WinstonHttpTransport({\n ...opts.http,\n format: winston.format.json(),\n }),\n );\n }\n\n this.winston = winston.createLogger({\n level: opts.logLevel ?? 'info',\n transports,\n format: winston.format.json(),\n });\n }\n\n log(message: string, context?: string): void {\n this.winston.info(message, this.buildMeta(context));\n }\n\n error(message: string, trace?: string, context?: string): void {\n this.winston.error(message, { ...this.buildMeta(context), trace });\n }\n\n warn(message: string, context?: string): void {\n this.winston.warn(message, this.buildMeta(context));\n }\n\n debug(message: string, context?: string): void {\n this.winston.debug(message, this.buildMeta(context));\n }\n\n verbose(message: string, context?: string): void {\n this.winston.verbose(message, this.buildMeta(context));\n }\n\n /** Log with additional arbitrary metadata. */\n logWithMeta(level: string, message: string, meta: Record<string, unknown>): void {\n const validLevels = ['error', 'warn', 'info', 'debug', 'verbose'];\n const safeLevel = validLevels.includes(level) ? level : 'info';\n this.winston.log(safeLevel, message, { ...this.buildMeta(), ...meta });\n }\n\n private buildMeta(context?: string): Record<string, unknown> {\n const base: Record<string, unknown> = {\n service: this.opts.serviceName,\n environment: this.opts.environment ?? 'production',\n correlationId: this.correlationSvc?.get(),\n };\n if (context) base.context = context;\n return base;\n }\n}\n","import { Injectable, Inject, Optional, OnModuleDestroy, Logger } from '@nestjs/common';\nimport axios, { AxiosInstance } from 'axios';\nimport * as http from 'node:http';\nimport * as https from 'node:https';\nimport {\n CircuitBreaker,\n CircuitBreakerConfig,\n CircuitBreakerOpenError,\n CircuitBreakerState,\n} from '@backendkit-labs/circuit-breaker';\nimport { CorrelationIdService } from '../correlation/correlation.service.js';\nimport { OBSERVABILITY_OPTIONS } from '../observability.constants.js';\nimport { ObservabilityOptions, MetricEvent } from '../observability.types.js';\n\nconst TRANSPORT_CB_DEFAULTS: Omit<CircuitBreakerConfig, 'name' | 'isFailure'> = {\n failureThreshold: 60,\n slowCallThreshold: 100,\n slowCallDurationMs: 60_000,\n minimumCalls: 3,\n slidingWindowSize: 5,\n halfOpenMaxCalls: 1,\n openTimeoutMs: 30_000,\n};\n\n@Injectable()\nexport class MetricsService implements OnModuleDestroy {\n private readonly client: AxiosInstance | null = null;\n private readonly cb: CircuitBreaker | null = null;\n private readonly logger = new Logger(MetricsService.name);\n private buffer: MetricEvent[] = [];\n private readonly maxBufferSize: number = 5_000;\n private readonly maxEntryAgeMs = 300_000; // 5 min\n private readonly retryCounts = new WeakMap<MetricEvent, number>();\n private readonly maxRetries = 5;\n private readonly flushTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(\n @Inject(OBSERVABILITY_OPTIONS)\n private readonly opts: ObservabilityOptions,\n @Optional()\n private readonly correlationSvc?: CorrelationIdService,\n ) {\n if (!opts.metrics) return;\n\n const m = opts.metrics;\n this.maxBufferSize = m.maxBufferSize ?? 5_000;\n\n const keepAlive = new http.Agent({ keepAlive: true });\n const keepAliveHttps = new https.Agent({ keepAlive: true });\n\n this.client = axios.create({\n baseURL: m.url,\n timeout: m.timeoutMs ?? 5_000,\n httpAgent: keepAlive,\n httpsAgent: keepAliveHttps,\n headers: {\n 'Content-Type': 'application/json',\n ...(m.authToken ? { Authorization: `Bearer ${m.authToken}` } : {}),\n ...m.headers,\n },\n });\n\n this.cb = new CircuitBreaker({\n ...TRANSPORT_CB_DEFAULTS,\n ...m.circuitBreaker,\n name: 'MetricsService',\n isFailure: (error: unknown) => {\n const status = (error as { response?: { status?: number } }).response?.status;\n return status !== undefined ? status >= 400 : true;\n },\n onStateChange: (from, to, metrics) => {\n if (to === CircuitBreakerState.OPEN) {\n this.logger.warn(\n `[MetricsService] circuit breaker OPEN — pausing metric sends for ${(m.circuitBreaker?.openTimeoutMs ?? TRANSPORT_CB_DEFAULTS.openTimeoutMs) / 1_000}s`,\n metrics,\n );\n } else if (to === CircuitBreakerState.CLOSED && from !== CircuitBreakerState.HALF_OPEN) {\n this.logger.log(`[MetricsService] circuit breaker CLOSED — recovered`);\n }\n m.circuitBreaker?.onStateChange?.(from, to, metrics);\n },\n });\n\n this.flushTimer = setInterval(\n () => { void this.flush(); },\n m.flushIntervalMs ?? 10_000,\n );\n this.flushTimer.unref?.();\n }\n\n /**\n * Enqueue a metric event. Fire-and-forget; batched and sent on the next\n * flush interval or when the buffer reaches `maxBufferSize`.\n */\n record(\n name: string,\n value: number,\n options?: { unit?: string; tags?: Record<string, string> },\n ): void {\n if (!this.client) return;\n\n if (this.buffer.length >= this.maxBufferSize) {\n this.logger.warn('[MetricsService] buffer full — dropping metric');\n return;\n }\n\n this.buffer.push({\n name,\n value,\n unit: options?.unit,\n tags: options?.tags,\n timestamp: new Date().toISOString(),\n serviceName: this.opts.serviceName,\n environment: this.opts.environment ?? 'production',\n correlationId: this.correlationSvc?.getOrUndefined(),\n });\n }\n\n /** Flush on graceful shutdown. */\n async onModuleDestroy(): Promise<void> {\n if (this.flushTimer) clearInterval(this.flushTimer);\n try {\n await this.flush();\n } catch {\n // Flush already handles errors internally; nothing to do here\n }\n }\n\n private async flush(): Promise<void> {\n if (!this.client || this.buffer.length === 0) return;\n\n // Discard entries older than maxEntryAgeMs\n const now = Date.now();\n this.buffer = this.buffer.filter(\n (e) => now - new Date(e.timestamp).getTime() < this.maxEntryAgeMs,\n );\n\n const batch = this.buffer.splice(0, 500);\n\n try {\n await this.cb!.execute(() => this.client!.post('', batch));\n } catch (err) {\n // Re-queue with retry limit to prevent infinite re-enqueue\n const retryable = batch.filter(entry => {\n const retries = this.retryCounts.get(entry) ?? 0;\n if (retries >= this.maxRetries) return false;\n this.retryCounts.set(entry, retries + 1);\n return true;\n });\n const room = this.maxBufferSize - this.buffer.length;\n if (room > 0) this.buffer.unshift(...retryable.slice(0, room));\n\n if (!(err instanceof CircuitBreakerOpenError)) {\n this.logger.warn(\n `[MetricsService] flush failed — re-queueing ${retryable.length}/${batch.length} events`,\n (err as Error).message,\n );\n }\n }\n }\n}\n","import {\n Injectable,\n NestInterceptor,\n ExecutionContext,\n CallHandler,\n Inject,\n} from '@nestjs/common';\nimport { Observable } from 'rxjs';\nimport { randomUUID } from 'node:crypto';\nimport { CorrelationIdService } from '../correlation/correlation.service.js';\n\nconst CORRELATION_HEADER = 'x-correlation-id';\n\n/**\n * Only allow characters safe for logs, HTTP headers and JSON.\n * Rejects newlines, control chars, and excessively long values.\n */\nconst CORRELATION_ID_REGEX = /^[a-zA-Z0-9\\-_:]{1,64}$/;\n\nfunction sanitizeCorrelationId(raw: unknown): string | null {\n if (typeof raw !== 'string') return null;\n if (raw.length > 64) return null;\n if (!CORRELATION_ID_REGEX.test(raw)) return null;\n return raw;\n}\n\n@Injectable()\nexport class CorrelationInterceptor implements NestInterceptor {\n constructor(\n @Inject(CorrelationIdService)\n private readonly correlationSvc: CorrelationIdService,\n ) {}\n\n intercept(ctx: ExecutionContext, next: CallHandler): Observable<unknown> {\n const req = ctx.switchToHttp().getRequest<Record<string, unknown>>();\n const res = ctx.switchToHttp().getResponse<{\n setHeader(name: string, value: string): void;\n }>();\n\n const incomingId =\n (req.headers as Record<string, string | undefined>)?.[CORRELATION_HEADER];\n const correlationId = sanitizeCorrelationId(incomingId) ?? randomUUID();\n\n res.setHeader(CORRELATION_HEADER, correlationId);\n\n return new Observable(subscriber => {\n this.correlationSvc.run(correlationId, () => {\n next.handle().subscribe(subscriber);\n });\n });\n }\n}\n","import {\n Injectable,\n NestInterceptor,\n ExecutionContext,\n CallHandler,\n Inject,\n Optional,\n} from '@nestjs/common';\nimport { Observable } from 'rxjs';\nimport { tap } from 'rxjs/operators';\nimport { LoggerService } from '../logger/logger.service.js';\nimport { MetricsService } from '../metrics/metrics.service.js';\nimport { CorrelationIdService } from '../correlation/correlation.service.js';\nimport { getActiveSpan } from '../internal/otel.js';\n\n@Injectable()\nexport class PerformanceInterceptor implements NestInterceptor {\n constructor(\n @Inject(LoggerService)\n private readonly logger: LoggerService,\n @Optional()\n @Inject(MetricsService)\n private readonly metrics: MetricsService | undefined,\n @Optional()\n @Inject(CorrelationIdService)\n private readonly correlationSvc: CorrelationIdService | undefined,\n ) {}\n\n intercept(ctx: ExecutionContext, next: CallHandler): Observable<unknown> {\n const start = Date.now();\n const req = ctx.switchToHttp().getRequest<{\n method: string;\n url: string;\n }>();\n const method = req.method ?? 'UNKNOWN';\n const path = req.url ?? 'UNKNOWN';\n\n return next.handle().pipe(\n tap({\n next: () => {\n const durationMs = Date.now() - start;\n this.record(method, path, 'success', durationMs);\n },\n error: () => {\n const durationMs = Date.now() - start;\n this.record(method, path, 'error', durationMs);\n },\n }),\n );\n }\n\n private record(\n method: string,\n path: string,\n outcome: 'success' | 'error',\n durationMs: number,\n ): void {\n const correlationId = this.correlationSvc?.getOrUndefined();\n const span = getActiveSpan();\n\n span?.setAttribute('http.method', method);\n span?.setAttribute('http.target', path);\n span?.setAttribute('http.duration_ms', durationMs);\n\n this.logger.logWithMeta('info', `${method} ${path} [${outcome}] ${durationMs}ms`, {\n method,\n path,\n outcome,\n durationMs,\n correlationId,\n });\n\n this.metrics?.record('http.request.duration', durationMs, {\n unit: 'ms',\n tags: { method, path, outcome },\n });\n }\n}\n","import {\n ExceptionFilter,\n Catch,\n ArgumentsHost,\n HttpException,\n HttpStatus,\n Inject,\n Optional,\n} from '@nestjs/common';\nimport { LoggerService } from '../logger/logger.service.js';\nimport { CorrelationIdService } from '../correlation/correlation.service.js';\nimport { getActiveSpan } from '../internal/otel.js';\n\nexport type ErrorMapper = (error: unknown) => {\n statusCode: number;\n message: string;\n code?: string;\n} | null;\n\n@Catch()\nexport class AllExceptionsFilter implements ExceptionFilter {\n private readonly mappers: ErrorMapper[] = [];\n\n constructor(\n @Inject(LoggerService)\n private readonly logger: LoggerService,\n @Optional()\n @Inject(CorrelationIdService)\n private readonly correlationSvc: CorrelationIdService | undefined,\n ) {}\n\n /**\n * Register a custom error mapper.\n * Mappers are tried in order; the first non-null result wins.\n * Return `null` to fall through to the next mapper.\n */\n addMapper(mapper: ErrorMapper): this {\n this.mappers.push(mapper);\n return this;\n }\n\n catch(exception: unknown, host: ArgumentsHost): void {\n const ctx = host.switchToHttp();\n const res = ctx.getResponse<{\n status(code: number): { json(body: unknown): void };\n }>();\n\n const mapped = this.resolveError(exception);\n\n const correlationId = this.correlationSvc?.getOrUndefined();\n const span = getActiveSpan();\n span?.recordException(exception instanceof Error ? exception : new Error(String(exception)));\n\n this.logger.error(\n mapped.message,\n exception instanceof Error ? exception.stack : undefined,\n AllExceptionsFilter.name,\n );\n\n res.status(mapped.statusCode).json({\n ok: false,\n statusCode: mapped.statusCode,\n message: mapped.message,\n code: mapped.code,\n correlationId,\n timestamp: new Date().toISOString(),\n });\n }\n\n private resolveError(exception: unknown): {\n statusCode: number;\n message: string;\n code?: string;\n } {\n for (const mapper of this.mappers) {\n const result = mapper(exception);\n if (result !== null) return result;\n }\n\n if (exception instanceof HttpException) {\n const body = exception.getResponse();\n const rawMessage =\n typeof body === 'string'\n ? body\n : (body as { message?: unknown }).message ?? exception.message;\n const message = Array.isArray(rawMessage)\n ? rawMessage.join('; ')\n : String(rawMessage ?? exception.message);\n return { statusCode: exception.getStatus(), message };\n }\n\n return {\n statusCode: HttpStatus.INTERNAL_SERVER_ERROR,\n message: 'Internal server error',\n };\n }\n}\n","import { DynamicModule, Module, Provider } from '@nestjs/common';\nimport { CorrelationIdService } from './correlation/correlation.service.js';\nimport { LoggerService } from './logger/logger.service.js';\nimport { MetricsService } from './metrics/metrics.service.js';\nimport { CorrelationInterceptor } from './interceptors/correlation.interceptor.js';\nimport { PerformanceInterceptor } from './interceptors/performance.interceptor.js';\nimport { AllExceptionsFilter } from './filters/all-exceptions.filter.js';\nimport { OBSERVABILITY_OPTIONS } from './observability.constants.js';\nimport { ObservabilityOptions } from './observability.types.js';\n\n@Module({})\nexport class ObservabilityModule {\n static forRoot(options: ObservabilityOptions): DynamicModule {\n const optionsProvider: Provider = {\n provide: OBSERVABILITY_OPTIONS,\n useValue: options,\n };\n\n const providers: Provider[] = [\n optionsProvider,\n CorrelationIdService,\n LoggerService,\n MetricsService,\n CorrelationInterceptor,\n PerformanceInterceptor,\n AllExceptionsFilter,\n ];\n\n return {\n module: ObservabilityModule,\n global: true,\n providers,\n exports: [\n CorrelationIdService,\n LoggerService,\n MetricsService,\n CorrelationInterceptor,\n PerformanceInterceptor,\n AllExceptionsFilter,\n ],\n };\n }\n}\n","import { getTracer, runInOtelContext } from '../internal/otel.js';\n\nexport interface TrackPerformanceOptions {\n /** OTel span / log operation name. Defaults to `ClassName.methodName`. */\n operation?: string;\n\n /** Attributes added to the OTel span. */\n attributes?: Record<string, string | number | boolean>;\n}\n\n/**\n * Method decorator that wraps the decorated async method in an OTel span\n * (when @opentelemetry/api is available) and records its duration.\n * Works with regular methods and NestJS service methods.\n *\n * @example\n * \\@TrackPerformance()\n * async processPayment(id: string) { ... }\n */\nexport function TrackPerformance(options: TrackPerformanceOptions = {}): MethodDecorator {\n return function (\n target: object,\n propertyKey: string | symbol,\n descriptor: PropertyDescriptor,\n ): PropertyDescriptor {\n const original = descriptor.value as (...args: unknown[]) => Promise<unknown>;\n const operationName =\n options.operation ?? `${target.constructor.name}.${String(propertyKey)}`;\n\n descriptor.value = async function (...args: unknown[]): Promise<unknown> {\n const tracer = getTracer('@backendkit-labs/observability');\n const span = tracer.startSpan(operationName);\n\n if (options.attributes) {\n span.setAttributes(options.attributes);\n }\n\n try {\n const result = await runInOtelContext(span, () => original.apply(this, args));\n span.setStatus({ code: 1 }); // SpanStatusCode.OK\n return result;\n } catch (err) {\n span.recordException(err instanceof Error ? err : new Error(String(err)));\n span.setStatus({ code: 2, message: String(err) }); // SpanStatusCode.ERROR\n throw err;\n } finally {\n span.end();\n }\n };\n\n return descriptor;\n };\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -30,9 +30,14 @@ interface WinstonHttpTransportOptions extends TransportStreamOptions {
30
30
  declare class WinstonHttpTransport extends TransportStream {
31
31
  private readonly client;
32
32
  private readonly cb;
33
- private readonly buffer;
33
+ private buffer;
34
34
  private readonly batchSize;
35
35
  private readonly maxBufferSize;
36
+ private readonly maxEntryAgeMs;
37
+ private readonly fallbackLogger;
38
+ private readonly retryCounts;
39
+ private readonly entryTimes;
40
+ private readonly maxRetries;
36
41
  private readonly flushTimer;
37
42
  constructor(opts: WinstonHttpTransportOptions);
38
43
  log(info: any, callback: () => void): void;
@@ -103,7 +108,7 @@ declare class CorrelationIdService {
103
108
  * `get()` and receive the same ID without passing it explicitly.
104
109
  */
105
110
  run<T>(correlationId: string, fn: () => T): T;
106
- /** Current correlation ID, or a fresh UUID when called outside a context. */
111
+ /** Current correlation ID, or 'no-context' when called outside a context. */
107
112
  get(): string;
108
113
  /**
109
114
  * Current correlation ID, or `undefined` when called outside a context.
@@ -132,7 +137,7 @@ declare class LoggerService implements LoggerService$1 {
132
137
  debug(message: string, context?: string): void;
133
138
  verbose(message: string, context?: string): void;
134
139
  /** Log with additional arbitrary metadata. */
135
- logWithMeta(level: string, message: string, meta: Record<string, any>): void;
140
+ logWithMeta(level: string, message: string, meta: Record<string, unknown>): void;
136
141
  private buildMeta;
137
142
  }
138
143
 
@@ -142,8 +147,11 @@ declare class MetricsService implements OnModuleDestroy {
142
147
  private readonly client;
143
148
  private readonly cb;
144
149
  private readonly logger;
145
- private readonly buffer;
150
+ private buffer;
146
151
  private readonly maxBufferSize;
152
+ private readonly maxEntryAgeMs;
153
+ private readonly retryCounts;
154
+ private readonly maxRetries;
147
155
  private readonly flushTimer;
148
156
  constructor(opts: ObservabilityOptions, correlationSvc?: CorrelationIdService | undefined);
149
157
  /**
package/dist/index.d.ts CHANGED
@@ -30,9 +30,14 @@ interface WinstonHttpTransportOptions extends TransportStreamOptions {
30
30
  declare class WinstonHttpTransport extends TransportStream {
31
31
  private readonly client;
32
32
  private readonly cb;
33
- private readonly buffer;
33
+ private buffer;
34
34
  private readonly batchSize;
35
35
  private readonly maxBufferSize;
36
+ private readonly maxEntryAgeMs;
37
+ private readonly fallbackLogger;
38
+ private readonly retryCounts;
39
+ private readonly entryTimes;
40
+ private readonly maxRetries;
36
41
  private readonly flushTimer;
37
42
  constructor(opts: WinstonHttpTransportOptions);
38
43
  log(info: any, callback: () => void): void;
@@ -103,7 +108,7 @@ declare class CorrelationIdService {
103
108
  * `get()` and receive the same ID without passing it explicitly.
104
109
  */
105
110
  run<T>(correlationId: string, fn: () => T): T;
106
- /** Current correlation ID, or a fresh UUID when called outside a context. */
111
+ /** Current correlation ID, or 'no-context' when called outside a context. */
107
112
  get(): string;
108
113
  /**
109
114
  * Current correlation ID, or `undefined` when called outside a context.
@@ -132,7 +137,7 @@ declare class LoggerService implements LoggerService$1 {
132
137
  debug(message: string, context?: string): void;
133
138
  verbose(message: string, context?: string): void;
134
139
  /** Log with additional arbitrary metadata. */
135
- logWithMeta(level: string, message: string, meta: Record<string, any>): void;
140
+ logWithMeta(level: string, message: string, meta: Record<string, unknown>): void;
136
141
  private buildMeta;
137
142
  }
138
143
 
@@ -142,8 +147,11 @@ declare class MetricsService implements OnModuleDestroy {
142
147
  private readonly client;
143
148
  private readonly cb;
144
149
  private readonly logger;
145
- private readonly buffer;
150
+ private buffer;
146
151
  private readonly maxBufferSize;
152
+ private readonly maxEntryAgeMs;
153
+ private readonly retryCounts;
154
+ private readonly maxRetries;
147
155
  private readonly flushTimer;
148
156
  constructor(opts: ObservabilityOptions, correlationSvc?: CorrelationIdService | undefined);
149
157
  /**