@dex-monit/observability-sdk-node 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/console-capture.d.ts +14 -0
- package/dist/lib/console-capture.d.ts.map +1 -0
- package/dist/lib/console-capture.js +87 -0
- package/dist/lib/monitoring-client.d.ts +22 -0
- package/dist/lib/monitoring-client.d.ts.map +1 -1
- package/dist/lib/monitoring-client.js +111 -0
- package/dist/lib/remote-logger.d.ts +59 -0
- package/dist/lib/remote-logger.d.ts.map +1 -0
- package/dist/lib/remote-logger.js +160 -0
- package/dist/lib/sdk-node.d.ts +1 -0
- package/dist/lib/sdk-node.d.ts.map +1 -1
- package/dist/lib/sdk-node.js +1 -0
- package/dist/lib/sdk-node.module.d.ts +14 -3
- package/dist/lib/sdk-node.module.d.ts.map +1 -1
- package/dist/lib/sdk-node.module.js +37 -4
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MonitoringClient } from './monitoring-client.js';
|
|
2
|
+
/**
|
|
3
|
+
* Start capturing console output and sending to monitoring
|
|
4
|
+
*/
|
|
5
|
+
export declare function startConsoleCapture(monitoringClient: MonitoringClient): void;
|
|
6
|
+
/**
|
|
7
|
+
* Stop capturing console output
|
|
8
|
+
*/
|
|
9
|
+
export declare function stopConsoleCapture(): void;
|
|
10
|
+
/**
|
|
11
|
+
* Check if console capture is active
|
|
12
|
+
*/
|
|
13
|
+
export declare function isConsoleCaptureActive(): boolean;
|
|
14
|
+
//# sourceMappingURL=console-capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console-capture.d.ts","sourceRoot":"","sources":["../../src/lib/console-capture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAwB1D;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAyC5E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAazC;AAqBD;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const CONSOLE_TO_SEVERITY = {
|
|
2
|
+
debug: 'debug',
|
|
3
|
+
log: 'info',
|
|
4
|
+
info: 'info',
|
|
5
|
+
warn: 'warning',
|
|
6
|
+
error: 'error',
|
|
7
|
+
};
|
|
8
|
+
let originalMethods = null;
|
|
9
|
+
let isCapturing = false;
|
|
10
|
+
/**
|
|
11
|
+
* Start capturing console output and sending to monitoring
|
|
12
|
+
*/
|
|
13
|
+
export function startConsoleCapture(monitoringClient) {
|
|
14
|
+
if (isCapturing) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
// Store original methods
|
|
18
|
+
originalMethods = {
|
|
19
|
+
log: console.log.bind(console),
|
|
20
|
+
info: console.info.bind(console),
|
|
21
|
+
warn: console.warn.bind(console),
|
|
22
|
+
error: console.error.bind(console),
|
|
23
|
+
debug: console.debug.bind(console),
|
|
24
|
+
};
|
|
25
|
+
const methods = ['log', 'info', 'warn', 'error', 'debug'];
|
|
26
|
+
for (const method of methods) {
|
|
27
|
+
const original = originalMethods[method];
|
|
28
|
+
const severity = CONSOLE_TO_SEVERITY[method];
|
|
29
|
+
console[method] = (...args) => {
|
|
30
|
+
// Call original console method first
|
|
31
|
+
original(...args);
|
|
32
|
+
// Skip if it's our own SDK log to avoid infinite loops
|
|
33
|
+
const message = formatConsoleArgs(args);
|
|
34
|
+
if (message.startsWith('[DEX SDK]') || message.startsWith('[MonitoringClient]') || message.startsWith('[RemoteLogger]')) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
// Send to monitoring (fire and forget)
|
|
38
|
+
monitoringClient.captureLog(severity, message, {
|
|
39
|
+
source: 'console',
|
|
40
|
+
method,
|
|
41
|
+
}).catch(() => {
|
|
42
|
+
// Silently ignore - don't use console.error here to avoid loops
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
isCapturing = true;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Stop capturing console output
|
|
50
|
+
*/
|
|
51
|
+
export function stopConsoleCapture() {
|
|
52
|
+
if (!isCapturing || !originalMethods) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
console.log = originalMethods.log;
|
|
56
|
+
console.info = originalMethods.info;
|
|
57
|
+
console.warn = originalMethods.warn;
|
|
58
|
+
console.error = originalMethods.error;
|
|
59
|
+
console.debug = originalMethods.debug;
|
|
60
|
+
originalMethods = null;
|
|
61
|
+
isCapturing = false;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Format console arguments into a single message string
|
|
65
|
+
*/
|
|
66
|
+
function formatConsoleArgs(args) {
|
|
67
|
+
return args.map(arg => {
|
|
68
|
+
if (typeof arg === 'string') {
|
|
69
|
+
return arg;
|
|
70
|
+
}
|
|
71
|
+
if (arg instanceof Error) {
|
|
72
|
+
return `${arg.name}: ${arg.message}\n${arg.stack || ''}`;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
return JSON.stringify(arg, null, 2);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return String(arg);
|
|
79
|
+
}
|
|
80
|
+
}).join(' ');
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if console capture is active
|
|
84
|
+
*/
|
|
85
|
+
export function isConsoleCaptureActive() {
|
|
86
|
+
return isCapturing;
|
|
87
|
+
}
|
|
@@ -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,
|
|
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
|
+
}
|
package/dist/lib/sdk-node.d.ts
CHANGED
|
@@ -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,gCAAgC,CAAC;AAC/C,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;AACvC,cAAc,sBAAsB,CAAC"}
|
package/dist/lib/sdk-node.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { DynamicModule, MiddlewareConsumer, NestModule } from '@nestjs/common';
|
|
1
|
+
import { DynamicModule, MiddlewareConsumer, NestModule, OnModuleDestroy } 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,10 @@ 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: 'debug' = capture all) */
|
|
16
|
+
remoteLogLevel?: Severity;
|
|
17
|
+
/** Whether to capture console.log/warn/error (default: true) */
|
|
18
|
+
captureConsole?: boolean;
|
|
14
19
|
}
|
|
15
20
|
export { LOGGER_TOKEN };
|
|
16
21
|
/**
|
|
@@ -24,10 +29,12 @@ export declare const MONITORING_CLIENT_TOKEN = "OBSERVABILITY_MONITORING_CLIENT"
|
|
|
24
29
|
* - Request ID middleware for tracing
|
|
25
30
|
* - Error capture interceptor (captures ALL errors, even if other filters exist)
|
|
26
31
|
* - Global exception filter for error capture
|
|
27
|
-
* - Logger instance
|
|
32
|
+
* - Logger instance (with automatic remote log capture if monitoring is configured)
|
|
33
|
+
* - Console capture (intercepts console.log/warn/error)
|
|
28
34
|
* - Monitoring client instance
|
|
29
35
|
*/
|
|
30
|
-
export declare class SdkNodeModule implements NestModule {
|
|
36
|
+
export declare class SdkNodeModule implements NestModule, OnModuleDestroy {
|
|
37
|
+
private static remoteLogger;
|
|
31
38
|
/**
|
|
32
39
|
* Register the module with configuration
|
|
33
40
|
*/
|
|
@@ -36,5 +43,9 @@ export declare class SdkNodeModule implements NestModule {
|
|
|
36
43
|
* Configure middleware
|
|
37
44
|
*/
|
|
38
45
|
configure(consumer: MiddlewareConsumer): void;
|
|
46
|
+
/**
|
|
47
|
+
* Cleanup on module destroy
|
|
48
|
+
*/
|
|
49
|
+
onModuleDestroy(): Promise<void>;
|
|
39
50
|
}
|
|
40
51
|
//# sourceMappingURL=sdk-node.module.d.ts.map
|
|
@@ -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,
|
|
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,EACV,eAAe,EAChB,MAAM,gBAAgB,CAAC;AAKxB,OAAO,EAEL,sBAAsB,EAEvB,MAAM,wBAAwB,CAAC;AAGhC,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,0EAA0E;IAC1E,cAAc,CAAC,EAAE,QAAQ,CAAC;IAC1B,gEAAgE;IAChE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAGD,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB;;GAEG;AACH,eAAO,MAAM,uBAAuB,oCAAoC,CAAC;AAEzE;;;;;;;;;;GAUG;AACH,qBAEa,aAAc,YAAW,UAAU,EAAE,eAAe;IAC/D,OAAO,CAAC,MAAM,CAAC,YAAY,CAA6B;IAExD;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,mBAAmB,GAAG,aAAa;IAkE1D;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI;IAK7C;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;CASvC"}
|
|
@@ -5,7 +5,9 @@ import { RequestIdMiddleware } from './request-id.middleware.js';
|
|
|
5
5
|
import { GlobalExceptionFilter } from './global-exception.filter.js';
|
|
6
6
|
import { ErrorCaptureInterceptor } from './error-capture.interceptor.js';
|
|
7
7
|
import { MonitoringClient, createMonitoringClient, } from './monitoring-client.js';
|
|
8
|
-
import {
|
|
8
|
+
import { RemoteLogger, createRemoteLogger } from './remote-logger.js';
|
|
9
|
+
import { startConsoleCapture, stopConsoleCapture } from './console-capture.js';
|
|
10
|
+
import { Logger, LOGGER_TOKEN, } from '@dex-monit/observability-logger';
|
|
9
11
|
// Re-export LOGGER_TOKEN for convenience
|
|
10
12
|
export { LOGGER_TOKEN };
|
|
11
13
|
/**
|
|
@@ -19,7 +21,8 @@ export const MONITORING_CLIENT_TOKEN = 'OBSERVABILITY_MONITORING_CLIENT';
|
|
|
19
21
|
* - Request ID middleware for tracing
|
|
20
22
|
* - Error capture interceptor (captures ALL errors, even if other filters exist)
|
|
21
23
|
* - Global exception filter for error capture
|
|
22
|
-
* - Logger instance
|
|
24
|
+
* - Logger instance (with automatic remote log capture if monitoring is configured)
|
|
25
|
+
* - Console capture (intercepts console.log/warn/error)
|
|
23
26
|
* - Monitoring client instance
|
|
24
27
|
*/
|
|
25
28
|
let SdkNodeModule = (() => {
|
|
@@ -34,16 +37,27 @@ let SdkNodeModule = (() => {
|
|
|
34
37
|
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
35
38
|
SdkNodeModule = _classThis = _classDescriptor.value;
|
|
36
39
|
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
37
|
-
__runInitializers(_classThis, _classExtraInitializers);
|
|
38
40
|
}
|
|
41
|
+
static remoteLogger = null;
|
|
39
42
|
/**
|
|
40
43
|
* Register the module with configuration
|
|
41
44
|
*/
|
|
42
45
|
static forRoot(config) {
|
|
43
|
-
const logger = createLogger(config.logger);
|
|
44
46
|
const monitoringClient = config.monitoring
|
|
45
47
|
? createMonitoringClient(config.monitoring)
|
|
46
48
|
: undefined;
|
|
49
|
+
// Use RemoteLogger if monitoring is configured
|
|
50
|
+
const logger = createRemoteLogger({
|
|
51
|
+
...config.logger,
|
|
52
|
+
monitoringClient,
|
|
53
|
+
remoteLevel: config.remoteLogLevel || 'debug', // Default: capture ALL logs
|
|
54
|
+
});
|
|
55
|
+
// Store reference for cleanup
|
|
56
|
+
SdkNodeModule.remoteLogger = logger;
|
|
57
|
+
// Start console capture if enabled (default: true)
|
|
58
|
+
if (monitoringClient && (config.captureConsole ?? true)) {
|
|
59
|
+
startConsoleCapture(monitoringClient);
|
|
60
|
+
}
|
|
47
61
|
return {
|
|
48
62
|
module: SdkNodeModule,
|
|
49
63
|
providers: [
|
|
@@ -55,6 +69,10 @@ let SdkNodeModule = (() => {
|
|
|
55
69
|
provide: Logger,
|
|
56
70
|
useValue: logger,
|
|
57
71
|
},
|
|
72
|
+
{
|
|
73
|
+
provide: RemoteLogger,
|
|
74
|
+
useValue: logger,
|
|
75
|
+
},
|
|
58
76
|
{
|
|
59
77
|
provide: MONITORING_CLIENT_TOKEN,
|
|
60
78
|
useValue: monitoringClient,
|
|
@@ -79,6 +97,7 @@ let SdkNodeModule = (() => {
|
|
|
79
97
|
exports: [
|
|
80
98
|
LOGGER_TOKEN,
|
|
81
99
|
Logger,
|
|
100
|
+
RemoteLogger,
|
|
82
101
|
MONITORING_CLIENT_TOKEN,
|
|
83
102
|
MonitoringClient,
|
|
84
103
|
],
|
|
@@ -91,6 +110,20 @@ let SdkNodeModule = (() => {
|
|
|
91
110
|
// Apply RequestIdMiddleware to all routes
|
|
92
111
|
consumer.apply(RequestIdMiddleware).forRoutes('*');
|
|
93
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Cleanup on module destroy
|
|
115
|
+
*/
|
|
116
|
+
async onModuleDestroy() {
|
|
117
|
+
// Stop console capture
|
|
118
|
+
stopConsoleCapture();
|
|
119
|
+
// Flush remaining logs
|
|
120
|
+
if (SdkNodeModule.remoteLogger) {
|
|
121
|
+
await SdkNodeModule.remoteLogger.close();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
static {
|
|
125
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
126
|
+
}
|
|
94
127
|
};
|
|
95
128
|
return SdkNodeModule = _classThis;
|
|
96
129
|
})();
|