@backendkit-labs/observability 0.1.1 → 0.2.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.
- package/README.md +369 -367
- package/dist/index.cjs +82 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -4
- package/dist/index.d.ts +12 -4
- package/dist/index.js +81 -31
- package/dist/index.js.map +1 -1
- package/package.json +96 -96
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
|
|
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 =
|
|
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
|
|
93
|
+
/** Current correlation ID, or 'no-context' when called outside a context. */
|
|
99
94
|
get() {
|
|
100
|
-
return storage.getStore() ??
|
|
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: () =>
|
|
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
|
-
|
|
190
|
+
this.emit("logged", info);
|
|
178
191
|
if (this.buffer.length < this.maxBufferSize) {
|
|
179
|
-
|
|
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(...
|
|
223
|
+
if (room > 0) this.buffer.unshift(...retryable.slice(0, room));
|
|
199
224
|
if (!(err instanceof circuitBreaker.CircuitBreakerOpenError)) {
|
|
200
|
-
|
|
225
|
+
this.fallbackLogger.warn(`flush failed \u2014 re-queued ${retryable.length}/${batch.length} entries`, err.message);
|
|
201
226
|
}
|
|
202
227
|
}
|
|
203
228
|
}
|
|
@@ -253,9 +278,10 @@ exports.LoggerService = class LoggerService {
|
|
|
253
278
|
this.winston.verbose(message, this.buildMeta(context));
|
|
254
279
|
}
|
|
255
280
|
/** Log with additional arbitrary metadata. */
|
|
256
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
257
281
|
logWithMeta(level, message, meta) {
|
|
258
|
-
|
|
282
|
+
const validLevels = ["error", "warn", "info", "debug", "verbose"];
|
|
283
|
+
const safeLevel = validLevels.includes(level) ? level : "info";
|
|
284
|
+
this.winston.log(safeLevel, message, { ...this.buildMeta(), ...meta });
|
|
259
285
|
}
|
|
260
286
|
buildMeta(context) {
|
|
261
287
|
const base = {
|
|
@@ -305,7 +331,10 @@ exports.MetricsService = class MetricsService {
|
|
|
305
331
|
...TRANSPORT_CB_DEFAULTS2,
|
|
306
332
|
...m.circuitBreaker,
|
|
307
333
|
name: "MetricsService",
|
|
308
|
-
isFailure: () =>
|
|
334
|
+
isFailure: (error) => {
|
|
335
|
+
const status = error.response?.status;
|
|
336
|
+
return status !== void 0 ? status >= 400 : true;
|
|
337
|
+
},
|
|
309
338
|
onStateChange: (from, to, metrics) => {
|
|
310
339
|
if (to === circuitBreaker.CircuitBreakerState.OPEN) {
|
|
311
340
|
this.logger.warn(
|
|
@@ -333,6 +362,10 @@ exports.MetricsService = class MetricsService {
|
|
|
333
362
|
logger = new common.Logger(exports.MetricsService.name);
|
|
334
363
|
buffer = [];
|
|
335
364
|
maxBufferSize = 5e3;
|
|
365
|
+
maxEntryAgeMs = 3e5;
|
|
366
|
+
// 5 min
|
|
367
|
+
retryCounts = /* @__PURE__ */ new WeakMap();
|
|
368
|
+
maxRetries = 5;
|
|
336
369
|
flushTimer = null;
|
|
337
370
|
/**
|
|
338
371
|
* Enqueue a metric event. Fire-and-forget; batched and sent on the next
|
|
@@ -358,20 +391,33 @@ exports.MetricsService = class MetricsService {
|
|
|
358
391
|
/** Flush on graceful shutdown. */
|
|
359
392
|
async onModuleDestroy() {
|
|
360
393
|
if (this.flushTimer) clearInterval(this.flushTimer);
|
|
361
|
-
|
|
394
|
+
try {
|
|
395
|
+
await this.flush();
|
|
396
|
+
} catch {
|
|
397
|
+
}
|
|
362
398
|
}
|
|
363
399
|
async flush() {
|
|
364
400
|
if (!this.client || this.buffer.length === 0) return;
|
|
401
|
+
const now = Date.now();
|
|
402
|
+
this.buffer = this.buffer.filter(
|
|
403
|
+
(e) => now - new Date(e.timestamp).getTime() < this.maxEntryAgeMs
|
|
404
|
+
);
|
|
365
405
|
const batch = this.buffer.splice(0, 500);
|
|
366
406
|
try {
|
|
367
407
|
await this.cb.execute(() => this.client.post("", batch));
|
|
368
408
|
} catch (err) {
|
|
409
|
+
const retryable = batch.filter((entry) => {
|
|
410
|
+
const retries = this.retryCounts.get(entry) ?? 0;
|
|
411
|
+
if (retries >= this.maxRetries) return false;
|
|
412
|
+
this.retryCounts.set(entry, retries + 1);
|
|
413
|
+
return true;
|
|
414
|
+
});
|
|
369
415
|
const room = this.maxBufferSize - this.buffer.length;
|
|
370
|
-
if (room > 0) this.buffer.unshift(...
|
|
416
|
+
if (room > 0) this.buffer.unshift(...retryable.slice(0, room));
|
|
371
417
|
if (!(err instanceof circuitBreaker.CircuitBreakerOpenError)) {
|
|
372
418
|
this.logger.warn(
|
|
373
|
-
`[MetricsService] flush failed \u2014 re-queueing ${batch.length} events`,
|
|
374
|
-
err
|
|
419
|
+
`[MetricsService] flush failed \u2014 re-queueing ${retryable.length}/${batch.length} events`,
|
|
420
|
+
err.message
|
|
375
421
|
);
|
|
376
422
|
}
|
|
377
423
|
}
|
|
@@ -383,6 +429,13 @@ exports.MetricsService = __decorateClass([
|
|
|
383
429
|
__decorateParam(1, common.Optional())
|
|
384
430
|
], exports.MetricsService);
|
|
385
431
|
var CORRELATION_HEADER = "x-correlation-id";
|
|
432
|
+
var CORRELATION_ID_REGEX = /^[a-zA-Z0-9\-_:]{1,64}$/;
|
|
433
|
+
function sanitizeCorrelationId(raw) {
|
|
434
|
+
if (typeof raw !== "string") return null;
|
|
435
|
+
if (raw.length > 64) return null;
|
|
436
|
+
if (!CORRELATION_ID_REGEX.test(raw)) return null;
|
|
437
|
+
return raw;
|
|
438
|
+
}
|
|
386
439
|
exports.CorrelationInterceptor = class CorrelationInterceptor {
|
|
387
440
|
constructor(correlationSvc) {
|
|
388
441
|
this.correlationSvc = correlationSvc;
|
|
@@ -392,14 +445,11 @@ exports.CorrelationInterceptor = class CorrelationInterceptor {
|
|
|
392
445
|
const req = ctx.switchToHttp().getRequest();
|
|
393
446
|
const res = ctx.switchToHttp().getResponse();
|
|
394
447
|
const incomingId = req.headers?.[CORRELATION_HEADER];
|
|
395
|
-
const correlationId =
|
|
448
|
+
const correlationId = sanitizeCorrelationId(incomingId) ?? crypto.randomUUID();
|
|
396
449
|
res.setHeader(CORRELATION_HEADER, correlationId);
|
|
397
450
|
return new rxjs.Observable((subscriber) => {
|
|
398
451
|
this.correlationSvc.run(correlationId, () => {
|
|
399
|
-
next.handle().
|
|
400
|
-
operators.tap({ error: () => {
|
|
401
|
-
} })
|
|
402
|
-
).subscribe(subscriber);
|
|
452
|
+
next.handle().subscribe(subscriber);
|
|
403
453
|
});
|
|
404
454
|
});
|
|
405
455
|
}
|
|
@@ -507,7 +557,8 @@ exports.AllExceptionsFilter = class AllExceptionsFilter {
|
|
|
507
557
|
}
|
|
508
558
|
if (exception instanceof common.HttpException) {
|
|
509
559
|
const body = exception.getResponse();
|
|
510
|
-
const
|
|
560
|
+
const rawMessage = typeof body === "string" ? body : body.message ?? exception.message;
|
|
561
|
+
const message = Array.isArray(rawMessage) ? rawMessage.join("; ") : String(rawMessage ?? exception.message);
|
|
511
562
|
return { statusCode: exception.getStatus(), message };
|
|
512
563
|
}
|
|
513
564
|
return {
|
package/dist/index.cjs.map
CHANGED
|
@@ -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 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 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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
/**
|