@dex-monit/observability-sdk-node 1.0.3 → 1.0.5

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.
@@ -0,0 +1,18 @@
1
+ import { NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
2
+ import { Observable } from 'rxjs';
3
+ import { MonitoringClient } from './monitoring-client.js';
4
+ /**
5
+ * Error Capture Interceptor
6
+ *
7
+ * Intercepts all errors and sends them to the monitoring service.
8
+ * Unlike ExceptionFilters, Interceptors run BEFORE other filters,
9
+ * so this will capture errors even if another filter handles them.
10
+ */
11
+ export declare class ErrorCaptureInterceptor implements NestInterceptor {
12
+ private readonly monitoringClient?;
13
+ constructor(monitoringClient?: MonitoringClient | undefined);
14
+ intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
15
+ private captureError;
16
+ private sanitizeHeaders;
17
+ }
18
+ //# sourceMappingURL=error-capture.interceptor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-capture.interceptor.d.ts","sourceRoot":"","sources":["../../src/lib/error-capture.interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,eAAe,EACf,gBAAgB,EAChB,WAAW,EACZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,UAAU,EAAc,MAAM,MAAM,CAAC;AAG9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D;;;;;;GAMG;AACH,qBACa,uBAAwB,YAAW,eAAe;IAE3D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;gBAAjB,gBAAgB,CAAC,EAAE,gBAAgB,YAAA;IAGtD,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC;IAY5E,OAAO,CAAC,YAAY;IAiCpB,OAAO,CAAC,eAAe;CAcxB"}
@@ -0,0 +1,83 @@
1
+ import { __esDecorate, __runInitializers } from "tslib";
2
+ import { Injectable, } from '@nestjs/common';
3
+ import { throwError } from 'rxjs';
4
+ import { catchError } from 'rxjs/operators';
5
+ /**
6
+ * Error Capture Interceptor
7
+ *
8
+ * Intercepts all errors and sends them to the monitoring service.
9
+ * Unlike ExceptionFilters, Interceptors run BEFORE other filters,
10
+ * so this will capture errors even if another filter handles them.
11
+ */
12
+ let ErrorCaptureInterceptor = (() => {
13
+ let _classDecorators = [Injectable()];
14
+ let _classDescriptor;
15
+ let _classExtraInitializers = [];
16
+ let _classThis;
17
+ var ErrorCaptureInterceptor = class {
18
+ static { _classThis = this; }
19
+ static {
20
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
21
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
22
+ ErrorCaptureInterceptor = _classThis = _classDescriptor.value;
23
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
24
+ __runInitializers(_classThis, _classExtraInitializers);
25
+ }
26
+ monitoringClient;
27
+ constructor(monitoringClient) {
28
+ this.monitoringClient = monitoringClient;
29
+ }
30
+ intercept(context, next) {
31
+ return next.handle().pipe(catchError((error) => {
32
+ // Capture the error
33
+ this.captureError(error, context);
34
+ // Re-throw so other handlers can process it
35
+ return throwError(() => error);
36
+ }));
37
+ }
38
+ captureError(error, context) {
39
+ const ctx = context.switchToHttp();
40
+ const request = ctx.getRequest();
41
+ // Log the error
42
+ const errorObj = error instanceof Error ? error : new Error(String(error));
43
+ console.log('[DEX SDK] Error captured by interceptor:', errorObj.message);
44
+ // Send to monitoring service (async, don't wait)
45
+ if (this.monitoringClient) {
46
+ console.log('[DEX SDK] Sending error to monitoring...');
47
+ this.monitoringClient
48
+ .captureException(errorObj, {
49
+ request: {
50
+ url: request?.url,
51
+ method: request?.method,
52
+ headers: this.sanitizeHeaders(request?.headers || {}),
53
+ query: request?.query,
54
+ },
55
+ })
56
+ .then(() => {
57
+ console.log('[DEX SDK] Error sent successfully!');
58
+ })
59
+ .catch((err) => {
60
+ console.error('[DEX SDK] Failed to send error:', err);
61
+ });
62
+ }
63
+ else {
64
+ console.log('[DEX SDK] No monitoring client configured');
65
+ }
66
+ }
67
+ sanitizeHeaders(headers) {
68
+ const sensitiveHeaders = ['authorization', 'cookie', 'x-api-key', 'x-dex-key'];
69
+ const sanitized = {};
70
+ for (const [key, value] of Object.entries(headers)) {
71
+ if (sensitiveHeaders.includes(key.toLowerCase())) {
72
+ sanitized[key] = '[REDACTED]';
73
+ }
74
+ else if (typeof value === 'string') {
75
+ sanitized[key] = value;
76
+ }
77
+ }
78
+ return sanitized;
79
+ }
80
+ };
81
+ return ErrorCaptureInterceptor = _classThis;
82
+ })();
83
+ export { ErrorCaptureInterceptor };
@@ -61,10 +61,32 @@ export declare class MonitoringClient {
61
61
  * Capture a message as an event
62
62
  */
63
63
  captureMessage(message: string, level?: Severity, options?: Omit<CaptureOptions, 'level'>): Promise<string | null>;
64
+ /**
65
+ * Capture a single log entry and send to monitoring service
66
+ */
67
+ captureLog(level: Severity, message: string, data?: Record<string, unknown>, tags?: Record<string, string>): Promise<void>;
68
+ /**
69
+ * Capture multiple log entries in batch
70
+ */
71
+ captureLogs(logs: Array<{
72
+ level: Severity;
73
+ message: string;
74
+ data?: Record<string, unknown>;
75
+ tags?: Record<string, string>;
76
+ timestamp?: string;
77
+ }>): Promise<void>;
64
78
  /**
65
79
  * Send event to monitoring API
66
80
  */
67
81
  private sendEvent;
82
+ /**
83
+ * Send single log to monitoring API
84
+ */
85
+ private sendLog;
86
+ /**
87
+ * Send logs batch to monitoring API
88
+ */
89
+ private sendLogsBatch;
68
90
  /**
69
91
  * Check if the client is enabled
70
92
  */
@@ -1 +1 @@
1
- {"version":3,"file":"monitoring-client.d.ts","sourceRoot":"","sources":["../../src/lib/monitoring-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAc,MAAM,oCAAoC,CAAC;AAEpG,OAAO,EAAsB,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAExF;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0CAA0C;IAC1C,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,uDAAuD;IACvD,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,UAAU,GAAG,IAAI,CAAC;CACvD;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,yBAAyB;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAChC,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,WAAW;IACX,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,iBAAiB;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,sBAAsB;IACtB,OAAO,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAClC,mBAAmB;IACnB,IAAI,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;CAC7B;AAyCD;;;;;GAKG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAmC;gBAErC,MAAM,EAAE,sBAAsB;IAe1C;;OAEG;IACG,gBAAgB,CACpB,KAAK,EAAE,KAAK,EACZ,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAgEzB;;OAEG;IACG,cAAc,CAClB,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,QAAiB,EACxB,OAAO,GAAE,IAAI,CAAC,cAAc,EAAE,OAAO,CAAM,GAC1C,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAwDzB;;OAEG;YACW,SAAS;IA6BvB;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,UAAU,IAAI,MAAM;CAGrB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,sBAAsB,GAC7B,gBAAgB,CAElB"}
1
+ {"version":3,"file":"monitoring-client.d.ts","sourceRoot":"","sources":["../../src/lib/monitoring-client.ts"],"names":[],"mappings":"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"}
@@ -173,6 +173,63 @@ export class MonitoringClient {
173
173
  return null;
174
174
  }
175
175
  }
176
+ /**
177
+ * Capture a single log entry and send to monitoring service
178
+ */
179
+ async captureLog(level, message, data, tags) {
180
+ if (!this.config.enabled) {
181
+ return;
182
+ }
183
+ const requestContext = RequestContextService.get();
184
+ const logEvent = {
185
+ id: uuidv4(),
186
+ timestamp: new Date().toISOString(),
187
+ level,
188
+ message,
189
+ project: this.config.project,
190
+ environment: this.config.environment,
191
+ serverName: this.config.serverName,
192
+ requestId: requestContext?.requestId,
193
+ transactionId: requestContext?.transactionId,
194
+ data: data ? scrubSensitiveData(data, this.config.scrubberOptions) : undefined,
195
+ tags,
196
+ };
197
+ try {
198
+ await this.sendLog(logEvent);
199
+ }
200
+ catch (err) {
201
+ // Silently fail - don't break the app for logging issues
202
+ console.error('[MonitoringClient] Failed to send log:', err);
203
+ }
204
+ }
205
+ /**
206
+ * Capture multiple log entries in batch
207
+ */
208
+ async captureLogs(logs) {
209
+ if (!this.config.enabled || logs.length === 0) {
210
+ return;
211
+ }
212
+ const requestContext = RequestContextService.get();
213
+ const logEvents = logs.map(log => ({
214
+ id: uuidv4(),
215
+ timestamp: log.timestamp || new Date().toISOString(),
216
+ level: log.level,
217
+ message: log.message,
218
+ project: this.config.project,
219
+ environment: this.config.environment,
220
+ serverName: this.config.serverName,
221
+ requestId: requestContext?.requestId,
222
+ transactionId: requestContext?.transactionId,
223
+ data: log.data ? scrubSensitiveData(log.data, this.config.scrubberOptions) : undefined,
224
+ tags: log.tags,
225
+ }));
226
+ try {
227
+ await this.sendLogsBatch(logEvents);
228
+ }
229
+ catch (err) {
230
+ console.error('[MonitoringClient] Failed to send logs batch:', err);
231
+ }
232
+ }
176
233
  /**
177
234
  * Send event to monitoring API
178
235
  */
@@ -201,6 +258,60 @@ export class MonitoringClient {
201
258
  clearTimeout(timeoutId);
202
259
  }
203
260
  }
261
+ /**
262
+ * Send single log to monitoring API
263
+ */
264
+ async sendLog(log) {
265
+ const controller = new AbortController();
266
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
267
+ const headers = {
268
+ 'Content-Type': 'application/json',
269
+ };
270
+ if (this.config.apiKey) {
271
+ headers['X-Dex-Key'] = this.config.apiKey;
272
+ }
273
+ try {
274
+ const response = await fetch(`${this.config.apiUrl}/ingest/logs`, {
275
+ method: 'POST',
276
+ headers,
277
+ body: JSON.stringify(log),
278
+ signal: controller.signal,
279
+ });
280
+ if (!response.ok) {
281
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
282
+ }
283
+ }
284
+ finally {
285
+ clearTimeout(timeoutId);
286
+ }
287
+ }
288
+ /**
289
+ * Send logs batch to monitoring API
290
+ */
291
+ async sendLogsBatch(logs) {
292
+ const controller = new AbortController();
293
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
294
+ const headers = {
295
+ 'Content-Type': 'application/json',
296
+ };
297
+ if (this.config.apiKey) {
298
+ headers['X-Dex-Key'] = this.config.apiKey;
299
+ }
300
+ try {
301
+ const response = await fetch(`${this.config.apiUrl}/ingest/logs/batch`, {
302
+ method: 'POST',
303
+ headers,
304
+ body: JSON.stringify({ logs }),
305
+ signal: controller.signal,
306
+ });
307
+ if (!response.ok) {
308
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
309
+ }
310
+ }
311
+ finally {
312
+ clearTimeout(timeoutId);
313
+ }
314
+ }
204
315
  /**
205
316
  * Check if the client is enabled
206
317
  */
@@ -0,0 +1,59 @@
1
+ import { Logger, LogContext, LoggerConfig } from '@dex-monit/observability-logger';
2
+ import { MonitoringClient } from './monitoring-client.js';
3
+ import { Severity } from '@dex-monit/observability-contracts';
4
+ /**
5
+ * Configuration for remote logger
6
+ */
7
+ export interface RemoteLoggerConfig extends LoggerConfig {
8
+ /** Monitoring client for remote log capture */
9
+ monitoringClient?: MonitoringClient;
10
+ /** Minimum level to send remotely (default: 'info') */
11
+ remoteLevel?: Severity;
12
+ /** Whether to buffer logs and send in batches (default: true) */
13
+ bufferLogs?: boolean;
14
+ /** Buffer flush interval in ms (default: 5000) */
15
+ flushInterval?: number;
16
+ /** Maximum buffer size before auto-flush (default: 100) */
17
+ maxBufferSize?: number;
18
+ }
19
+ /**
20
+ * Remote Logger
21
+ *
22
+ * Extends the base Logger with automatic remote log capture.
23
+ * Logs are buffered and sent in batches for efficiency.
24
+ */
25
+ export declare class RemoteLogger extends Logger {
26
+ private monitoringClient?;
27
+ private remoteLevel;
28
+ private bufferLogs;
29
+ private buffer;
30
+ private flushInterval;
31
+ private maxBufferSize;
32
+ private flushTimer?;
33
+ constructor(config: RemoteLoggerConfig);
34
+ private startFlushTimer;
35
+ private shouldSendRemotely;
36
+ private addToBuffer;
37
+ private sendLog;
38
+ /**
39
+ * Flush buffered logs to monitoring service
40
+ */
41
+ flush(): Promise<void>;
42
+ /**
43
+ * Stop the flush timer and flush remaining logs
44
+ */
45
+ close(): Promise<void>;
46
+ debug(message: string, context?: LogContext): void;
47
+ info(message: string, context?: LogContext): void;
48
+ warn(message: string, context?: LogContext): void;
49
+ error(message: string, context?: LogContext): void;
50
+ error(error: Error, context?: LogContext): void;
51
+ error(message: string, error: Error, context?: LogContext): void;
52
+ fatal(message: string, context?: LogContext): void;
53
+ fatal(error: Error, context?: LogContext): void;
54
+ }
55
+ /**
56
+ * Create a remote logger instance
57
+ */
58
+ export declare function createRemoteLogger(config: RemoteLoggerConfig): RemoteLogger;
59
+ //# sourceMappingURL=remote-logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-logger.d.ts","sourceRoot":"","sources":["../../src/lib/remote-logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACnF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,+CAA+C;IAC/C,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,uDAAuD;IACvD,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,iEAAiE;IACjE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kDAAkD;IAClD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAkBD;;;;;GAKG;AACH,qBAAa,YAAa,SAAQ,MAAM;IACtC,OAAO,CAAC,gBAAgB,CAAC,CAAmB;IAC5C,OAAO,CAAC,WAAW,CAAW;IAC9B,OAAO,CAAC,UAAU,CAAU;IAC5B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,UAAU,CAAC,CAAiC;gBAExC,MAAM,EAAE,kBAAkB;IActC,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,OAAO;IAmBf;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB5B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAUnB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI;IAKlD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI;IAKjD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI;IAKjD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI;IAClD,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI;IAC/C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI;IAiChE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI;IAClD,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI;CAiBzD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAE3E"}
@@ -0,0 +1,160 @@
1
+ import { Logger } from '@dex-monit/observability-logger';
2
+ const LEVEL_PRIORITY = {
3
+ debug: 0,
4
+ info: 1,
5
+ warning: 2,
6
+ error: 3,
7
+ fatal: 4,
8
+ };
9
+ /**
10
+ * Remote Logger
11
+ *
12
+ * Extends the base Logger with automatic remote log capture.
13
+ * Logs are buffered and sent in batches for efficiency.
14
+ */
15
+ export class RemoteLogger extends Logger {
16
+ monitoringClient;
17
+ remoteLevel;
18
+ bufferLogs;
19
+ buffer = [];
20
+ flushInterval;
21
+ maxBufferSize;
22
+ flushTimer;
23
+ constructor(config) {
24
+ super(config);
25
+ this.monitoringClient = config.monitoringClient;
26
+ this.remoteLevel = config.remoteLevel || 'info';
27
+ this.bufferLogs = config.bufferLogs ?? true;
28
+ this.flushInterval = config.flushInterval || 5000;
29
+ this.maxBufferSize = config.maxBufferSize || 100;
30
+ // Start buffer flush timer
31
+ if (this.bufferLogs && this.monitoringClient) {
32
+ this.startFlushTimer();
33
+ }
34
+ }
35
+ startFlushTimer() {
36
+ this.flushTimer = setInterval(() => {
37
+ this.flush().catch(console.error);
38
+ }, this.flushInterval);
39
+ }
40
+ shouldSendRemotely(level) {
41
+ return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[this.remoteLevel];
42
+ }
43
+ addToBuffer(log) {
44
+ this.buffer.push(log);
45
+ // Auto-flush if buffer is full
46
+ if (this.buffer.length >= this.maxBufferSize) {
47
+ this.flush().catch(console.error);
48
+ }
49
+ }
50
+ sendLog(level, message, context) {
51
+ if (!this.monitoringClient || !this.shouldSendRemotely(level)) {
52
+ return;
53
+ }
54
+ const log = {
55
+ level,
56
+ message,
57
+ data: context,
58
+ timestamp: new Date().toISOString(),
59
+ };
60
+ if (this.bufferLogs) {
61
+ this.addToBuffer(log);
62
+ }
63
+ else {
64
+ this.monitoringClient.captureLog(level, message, context).catch(console.error);
65
+ }
66
+ }
67
+ /**
68
+ * Flush buffered logs to monitoring service
69
+ */
70
+ async flush() {
71
+ if (!this.monitoringClient || this.buffer.length === 0) {
72
+ return;
73
+ }
74
+ const logsToSend = [...this.buffer];
75
+ this.buffer = [];
76
+ try {
77
+ await this.monitoringClient.captureLogs(logsToSend);
78
+ }
79
+ catch (err) {
80
+ // Put logs back in buffer on failure (up to max size)
81
+ this.buffer = [...logsToSend.slice(0, this.maxBufferSize - this.buffer.length), ...this.buffer];
82
+ console.error('[RemoteLogger] Failed to flush logs:', err);
83
+ }
84
+ }
85
+ /**
86
+ * Stop the flush timer and flush remaining logs
87
+ */
88
+ async close() {
89
+ if (this.flushTimer) {
90
+ clearInterval(this.flushTimer);
91
+ this.flushTimer = undefined;
92
+ }
93
+ await this.flush();
94
+ }
95
+ // Override log methods to also send remotely
96
+ debug(message, context) {
97
+ super.debug(message, context);
98
+ this.sendLog('debug', message, context);
99
+ }
100
+ info(message, context) {
101
+ super.info(message, context);
102
+ this.sendLog('info', message, context);
103
+ }
104
+ warn(message, context) {
105
+ super.warn(message, context);
106
+ this.sendLog('warning', message, context);
107
+ }
108
+ error(messageOrError, errorOrContext, context) {
109
+ // Call parent error method
110
+ if (messageOrError instanceof Error) {
111
+ super.error(messageOrError, errorOrContext);
112
+ this.sendLog('error', messageOrError.message, {
113
+ error: {
114
+ name: messageOrError.name,
115
+ message: messageOrError.message,
116
+ stack: messageOrError.stack,
117
+ },
118
+ ...errorOrContext,
119
+ });
120
+ }
121
+ else if (errorOrContext instanceof Error) {
122
+ super.error(messageOrError, errorOrContext, context);
123
+ this.sendLog('error', messageOrError, {
124
+ error: {
125
+ name: errorOrContext.name,
126
+ message: errorOrContext.message,
127
+ stack: errorOrContext.stack,
128
+ },
129
+ ...context,
130
+ });
131
+ }
132
+ else {
133
+ super.error(messageOrError, errorOrContext);
134
+ this.sendLog('error', messageOrError, errorOrContext);
135
+ }
136
+ }
137
+ fatal(messageOrError, context) {
138
+ if (messageOrError instanceof Error) {
139
+ super.fatal(messageOrError, context);
140
+ this.sendLog('fatal', messageOrError.message, {
141
+ error: {
142
+ name: messageOrError.name,
143
+ message: messageOrError.message,
144
+ stack: messageOrError.stack,
145
+ },
146
+ ...context,
147
+ });
148
+ }
149
+ else {
150
+ super.fatal(messageOrError, context);
151
+ this.sendLog('fatal', messageOrError, context);
152
+ }
153
+ }
154
+ }
155
+ /**
156
+ * Create a remote logger instance
157
+ */
158
+ export function createRemoteLogger(config) {
159
+ return new RemoteLogger(config);
160
+ }
@@ -1,5 +1,6 @@
1
1
  export * from './sdk-node.module.js';
2
2
  export * from './request-id.middleware.js';
3
3
  export * from './global-exception.filter.js';
4
+ export * from './error-capture.interceptor.js';
4
5
  export * from './monitoring-client.js';
5
6
  //# sourceMappingURL=sdk-node.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sdk-node.d.ts","sourceRoot":"","sources":["../../src/lib/sdk-node.ts"],"names":[],"mappings":"AACA,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,wBAAwB,CAAC"}
1
+ {"version":3,"file":"sdk-node.d.ts","sourceRoot":"","sources":["../../src/lib/sdk-node.ts"],"names":[],"mappings":"AACA,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,wBAAwB,CAAC"}
@@ -2,4 +2,5 @@
2
2
  export * from './sdk-node.module.js';
3
3
  export * from './request-id.middleware.js';
4
4
  export * from './global-exception.filter.js';
5
+ export * from './error-capture.interceptor.js';
5
6
  export * from './monitoring-client.js';
@@ -1,6 +1,7 @@
1
1
  import { DynamicModule, MiddlewareConsumer, NestModule } from '@nestjs/common';
2
2
  import { MonitoringClientConfig } from './monitoring-client.js';
3
3
  import { LoggerConfig, LOGGER_TOKEN } from '@dex-monit/observability-logger';
4
+ import { Severity } from '@dex-monit/observability-contracts';
4
5
  /**
5
6
  * SDK Node module configuration
6
7
  */
@@ -11,6 +12,8 @@ export interface SdkNodeModuleConfig {
11
12
  monitoring?: MonitoringClientConfig;
12
13
  /** Whether to apply middleware globally */
13
14
  global?: boolean;
15
+ /** Minimum log level to send remotely (default: 'info') */
16
+ remoteLogLevel?: Severity;
14
17
  }
15
18
  export { LOGGER_TOKEN };
16
19
  /**
@@ -22,8 +25,9 @@ export declare const MONITORING_CLIENT_TOKEN = "OBSERVABILITY_MONITORING_CLIENT"
22
25
  *
23
26
  * Provides:
24
27
  * - Request ID middleware for tracing
28
+ * - Error capture interceptor (captures ALL errors, even if other filters exist)
25
29
  * - Global exception filter for error capture
26
- * - Logger instance
30
+ * - Logger instance (with automatic remote log capture if monitoring is configured)
27
31
  * - Monitoring client instance
28
32
  */
29
33
  export declare class SdkNodeModule implements NestModule {
@@ -1 +1 @@
1
- {"version":3,"file":"sdk-node.module.d.ts","sourceRoot":"","sources":["../../src/lib/sdk-node.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,aAAa,EAEb,kBAAkB,EAClB,UAAU,EACX,MAAM,gBAAgB,CAAC;AAIxB,OAAO,EAEL,sBAAsB,EAEvB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAGL,YAAY,EACZ,YAAY,EACb,MAAM,iCAAiC,CAAC;AAEzC;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,2BAA2B;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,yFAAyF;IACzF,UAAU,CAAC,EAAE,sBAAsB,CAAC;IACpC,2CAA2C;IAC3C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAGD,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB;;GAEG;AACH,eAAO,MAAM,uBAAuB,oCAAoC,CAAC;AAEzE;;;;;;;;GAQG;AACH,qBAEa,aAAc,YAAW,UAAU;IAC9C;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,mBAAmB,GAAG,aAAa;IAwC1D;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI;CAI9C"}
1
+ {"version":3,"file":"sdk-node.module.d.ts","sourceRoot":"","sources":["../../src/lib/sdk-node.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,aAAa,EAEb,kBAAkB,EAClB,UAAU,EACX,MAAM,gBAAgB,CAAC;AAKxB,OAAO,EAEL,sBAAsB,EAEvB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAEL,YAAY,EACZ,YAAY,EACb,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,2BAA2B;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,yFAAyF;IACzF,UAAU,CAAC,EAAE,sBAAsB,CAAC;IACpC,2CAA2C;IAC3C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2DAA2D;IAC3D,cAAc,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAGD,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB;;GAEG;AACH,eAAO,MAAM,uBAAuB,oCAAoC,CAAC;AAEzE;;;;;;;;;GASG;AACH,qBAEa,aAAc,YAAW,UAAU;IAC9C;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,mBAAmB,GAAG,aAAa;IA4D1D;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI;CAI9C"}
@@ -1,10 +1,12 @@
1
1
  import { __esDecorate, __runInitializers } from "tslib";
2
2
  import { Module, Global, } from '@nestjs/common';
3
- import { APP_FILTER } from '@nestjs/core';
3
+ import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
4
4
  import { RequestIdMiddleware } from './request-id.middleware.js';
5
5
  import { GlobalExceptionFilter } from './global-exception.filter.js';
6
+ import { ErrorCaptureInterceptor } from './error-capture.interceptor.js';
6
7
  import { MonitoringClient, createMonitoringClient, } from './monitoring-client.js';
7
- import { Logger, createLogger, LOGGER_TOKEN, } from '@dex-monit/observability-logger';
8
+ import { RemoteLogger, createRemoteLogger } from './remote-logger.js';
9
+ import { Logger, LOGGER_TOKEN, } from '@dex-monit/observability-logger';
8
10
  // Re-export LOGGER_TOKEN for convenience
9
11
  export { LOGGER_TOKEN };
10
12
  /**
@@ -16,8 +18,9 @@ export const MONITORING_CLIENT_TOKEN = 'OBSERVABILITY_MONITORING_CLIENT';
16
18
  *
17
19
  * Provides:
18
20
  * - Request ID middleware for tracing
21
+ * - Error capture interceptor (captures ALL errors, even if other filters exist)
19
22
  * - Global exception filter for error capture
20
- * - Logger instance
23
+ * - Logger instance (with automatic remote log capture if monitoring is configured)
21
24
  * - Monitoring client instance
22
25
  */
23
26
  let SdkNodeModule = (() => {
@@ -38,10 +41,17 @@ let SdkNodeModule = (() => {
38
41
  * Register the module with configuration
39
42
  */
40
43
  static forRoot(config) {
41
- const logger = createLogger(config.logger);
42
44
  const monitoringClient = config.monitoring
43
45
  ? createMonitoringClient(config.monitoring)
44
46
  : undefined;
47
+ // Use RemoteLogger if monitoring is configured, otherwise use base Logger
48
+ const logger = monitoringClient
49
+ ? createRemoteLogger({
50
+ ...config.logger,
51
+ monitoringClient,
52
+ remoteLevel: config.remoteLogLevel || 'info',
53
+ })
54
+ : createRemoteLogger(config.logger);
45
55
  return {
46
56
  module: SdkNodeModule,
47
57
  providers: [
@@ -53,6 +63,10 @@ let SdkNodeModule = (() => {
53
63
  provide: Logger,
54
64
  useValue: logger,
55
65
  },
66
+ {
67
+ provide: RemoteLogger,
68
+ useValue: logger,
69
+ },
56
70
  {
57
71
  provide: MONITORING_CLIENT_TOKEN,
58
72
  useValue: monitoringClient,
@@ -61,6 +75,13 @@ let SdkNodeModule = (() => {
61
75
  provide: MonitoringClient,
62
76
  useValue: monitoringClient,
63
77
  },
78
+ // Interceptor captures errors BEFORE any exception filter
79
+ // This ensures we capture errors even if another filter handles them
80
+ {
81
+ provide: APP_INTERCEPTOR,
82
+ useFactory: () => new ErrorCaptureInterceptor(monitoringClient),
83
+ },
84
+ // Keep the filter as fallback for non-HTTP contexts
64
85
  {
65
86
  provide: APP_FILTER,
66
87
  useFactory: () => new GlobalExceptionFilter(logger, monitoringClient),
@@ -70,6 +91,7 @@ let SdkNodeModule = (() => {
70
91
  exports: [
71
92
  LOGGER_TOKEN,
72
93
  Logger,
94
+ RemoteLogger,
73
95
  MONITORING_CLIENT_TOKEN,
74
96
  MonitoringClient,
75
97
  ],