voltlog-io 1.0.2 → 1.0.4
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 +0 -27
- package/dist/chunk-4WQ4K5BM.js +1740 -0
- package/dist/chunk-4WQ4K5BM.js.map +1 -0
- package/dist/chunk-DAFMRCAN.mjs +1746 -0
- package/dist/chunk-DAFMRCAN.mjs.map +1 -0
- package/dist/client.d.mts +1247 -0
- package/dist/client.d.ts +1247 -0
- package/dist/client.js +75 -0
- package/dist/client.js.map +1 -0
- package/dist/client.mjs +75 -0
- package/dist/client.mjs.map +1 -0
- package/dist/index.d.mts +71 -1314
- package/dist/index.d.ts +71 -1314
- package/dist/index.js +36 -1699
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +43 -1712
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -1
|
@@ -0,0 +1,1247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module voltlog-io
|
|
3
|
+
* @description Type definitions for the OCPP-aware structured logger.
|
|
4
|
+
*/
|
|
5
|
+
declare const LogLevel: {
|
|
6
|
+
readonly TRACE: 10;
|
|
7
|
+
readonly DEBUG: 20;
|
|
8
|
+
readonly INFO: 30;
|
|
9
|
+
readonly WARN: 40;
|
|
10
|
+
readonly ERROR: 50;
|
|
11
|
+
readonly FATAL: 60;
|
|
12
|
+
readonly SILENT: number;
|
|
13
|
+
};
|
|
14
|
+
type LogLevelName = keyof typeof LogLevel;
|
|
15
|
+
/** Numeric log level value */
|
|
16
|
+
type LogLevelValue = (typeof LogLevel)[LogLevelName];
|
|
17
|
+
/** Map from level name (lowercase) to numeric value */
|
|
18
|
+
declare const LogLevelNameMap: Record<string, number>;
|
|
19
|
+
/** Map from numeric value to level name */
|
|
20
|
+
declare const LogLevelValueMap: Record<number, LogLevelName>;
|
|
21
|
+
interface LogEntry<TMeta = Record<string, unknown>> {
|
|
22
|
+
/** Unique log ID */
|
|
23
|
+
id: string;
|
|
24
|
+
/** Numeric log level */
|
|
25
|
+
level: number;
|
|
26
|
+
/** Human-readable level name */
|
|
27
|
+
levelName: LogLevelName;
|
|
28
|
+
/** Log message */
|
|
29
|
+
message: string;
|
|
30
|
+
/** Unix epoch timestamp (ms) */
|
|
31
|
+
timestamp: number;
|
|
32
|
+
/** User-defined structured metadata (type-safe via generics) */
|
|
33
|
+
meta: TMeta;
|
|
34
|
+
/** Bound context from child logger (e.g. chargePointId, sessionId) */
|
|
35
|
+
context?: Record<string, unknown>;
|
|
36
|
+
/** Correlation ID for tracing across async operations */
|
|
37
|
+
correlationId?: string;
|
|
38
|
+
/** Error information */
|
|
39
|
+
error?: LogError;
|
|
40
|
+
/** @internal Cached JSON serialization — avoids redundant stringify across transports */
|
|
41
|
+
_json?: string;
|
|
42
|
+
}
|
|
43
|
+
interface LogError {
|
|
44
|
+
message: string;
|
|
45
|
+
stack?: string;
|
|
46
|
+
code?: string;
|
|
47
|
+
name?: string;
|
|
48
|
+
/** Nested cause chain (ES2022 Error.cause support) */
|
|
49
|
+
cause?: LogError;
|
|
50
|
+
}
|
|
51
|
+
interface OcppExchangeMeta {
|
|
52
|
+
/** Charge point / station identity */
|
|
53
|
+
chargePointId?: string;
|
|
54
|
+
/** OCPP message type */
|
|
55
|
+
messageType?: "CALL" | "CALLRESULT" | "CALLERROR";
|
|
56
|
+
/** OCPP action name (e.g. BootNotification) */
|
|
57
|
+
action?: string;
|
|
58
|
+
/** Message direction */
|
|
59
|
+
direction?: "IN" | "OUT";
|
|
60
|
+
/** Correlation ID for request/response matching */
|
|
61
|
+
correlationId?: string;
|
|
62
|
+
/** Negotiated OCPP protocol version */
|
|
63
|
+
protocol?: string;
|
|
64
|
+
/** Serialized payload size in bytes */
|
|
65
|
+
payloadSize?: number;
|
|
66
|
+
/** Latency in milliseconds */
|
|
67
|
+
latencyMs?: number;
|
|
68
|
+
/** Response status (e.g. Accepted, Rejected) */
|
|
69
|
+
status?: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A Transport receives formatted log entries and delivers them
|
|
73
|
+
* to a destination (console, file, webhook, database, etc.).
|
|
74
|
+
*
|
|
75
|
+
* Transports are async-safe — `write()` can return a Promise.
|
|
76
|
+
*/
|
|
77
|
+
interface Transport<TMeta = Record<string, unknown>> {
|
|
78
|
+
/** Unique name for this transport */
|
|
79
|
+
name: string;
|
|
80
|
+
/** Optional per-transport level filter */
|
|
81
|
+
level?: LogLevelName;
|
|
82
|
+
/** Process a log entry */
|
|
83
|
+
write(entry: LogEntry<TMeta>): void | Promise<void>;
|
|
84
|
+
/** Flush any buffered entries */
|
|
85
|
+
flush?(): void | Promise<void>;
|
|
86
|
+
/** Graceful shutdown */
|
|
87
|
+
close?(): void | Promise<void>;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Middleware intercepts log entries before they reach transports.
|
|
91
|
+
* Used for redaction, sampling, enrichment, alerting, etc.
|
|
92
|
+
*
|
|
93
|
+
* Call `next(entry)` to continue the pipeline.
|
|
94
|
+
* Omit `next()` to drop the entry (e.g. sampling).
|
|
95
|
+
*/
|
|
96
|
+
type LogMiddleware<TMeta = Record<string, unknown>> = (entry: LogEntry<TMeta>, next: (entry: LogEntry<TMeta>) => void) => void;
|
|
97
|
+
/**
|
|
98
|
+
* Alert rules evaluate log entries and fire callbacks
|
|
99
|
+
* when configurable conditions are met.
|
|
100
|
+
*/
|
|
101
|
+
interface AlertRule<TMeta = Record<string, unknown>> {
|
|
102
|
+
/** Alert name (for identification) */
|
|
103
|
+
name: string;
|
|
104
|
+
/** Condition — return true if this entry should count toward the alert */
|
|
105
|
+
when: (entry: LogEntry<TMeta>) => boolean;
|
|
106
|
+
/** Number of matching entries required to fire (default: 1) */
|
|
107
|
+
threshold?: number;
|
|
108
|
+
/** Time window in ms for threshold counting */
|
|
109
|
+
windowMs?: number;
|
|
110
|
+
/** Minimum cooldown in ms between alert firings (default: 0) */
|
|
111
|
+
cooldownMs?: number;
|
|
112
|
+
/** Callback fired when alert conditions are met */
|
|
113
|
+
onAlert: (entries: LogEntry<TMeta>[]) => void | Promise<void>;
|
|
114
|
+
}
|
|
115
|
+
interface TimerResult<TMeta = Record<string, unknown>> {
|
|
116
|
+
/** End the timer and log the duration */
|
|
117
|
+
done(message: string, meta?: Partial<TMeta>): void;
|
|
118
|
+
/** Get elapsed time in ms without logging */
|
|
119
|
+
elapsed(): number;
|
|
120
|
+
}
|
|
121
|
+
interface LoggerOptions<TMeta = Record<string, unknown>> {
|
|
122
|
+
/** Minimum log level (default: INFO) */
|
|
123
|
+
level?: LogLevelName;
|
|
124
|
+
/** Transports for log output */
|
|
125
|
+
transports?: Transport<TMeta>[];
|
|
126
|
+
/** Middleware pipeline */
|
|
127
|
+
middleware?: LogMiddleware<TMeta>[];
|
|
128
|
+
/** Alert rules */
|
|
129
|
+
alerts?: AlertRule<TMeta>[];
|
|
130
|
+
/** Default bound context for all log entries */
|
|
131
|
+
context?: Record<string, unknown>;
|
|
132
|
+
/** Field paths to auto-redact (e.g. ['idToken', 'password']) */
|
|
133
|
+
redact?: string[];
|
|
134
|
+
/**
|
|
135
|
+
* When to include error stack traces:
|
|
136
|
+
* - `true` — always include
|
|
137
|
+
* - `false` — never include
|
|
138
|
+
* - `LogLevelName` — include at this level and above
|
|
139
|
+
* Default: 'ERROR'
|
|
140
|
+
*/
|
|
141
|
+
includeStack?: boolean | LogLevelName;
|
|
142
|
+
/**
|
|
143
|
+
* Exchange log mode:
|
|
144
|
+
* - `true` — exchange logs alongside normal logs
|
|
145
|
+
* - `'only'` — only exchange-formatted logs
|
|
146
|
+
* - `false` — disabled (default)
|
|
147
|
+
*/
|
|
148
|
+
exchangeLog?: boolean | "only";
|
|
149
|
+
/** Custom timestamp function (default: Date.now) */
|
|
150
|
+
timestamp?: () => number;
|
|
151
|
+
/**
|
|
152
|
+
* Custom ID generator function.
|
|
153
|
+
* Default: `crypto.randomUUID()` (native, fast).
|
|
154
|
+
* Set to `false` to disable ID generation entirely for max performance.
|
|
155
|
+
*/
|
|
156
|
+
idGenerator?: (() => string) | false;
|
|
157
|
+
}
|
|
158
|
+
interface Logger<TMeta = Record<string, unknown>> {
|
|
159
|
+
trace(message: string, meta?: Partial<TMeta>): void;
|
|
160
|
+
debug(message: string, meta?: Partial<TMeta>): void;
|
|
161
|
+
info(message: string, meta?: Partial<TMeta>): void;
|
|
162
|
+
warn(message: string, meta?: Partial<TMeta>): void;
|
|
163
|
+
error(message: string, metaOrError?: Partial<TMeta> | Error, error?: Error): void;
|
|
164
|
+
fatal(message: string, metaOrError?: Partial<TMeta> | Error, error?: Error): void;
|
|
165
|
+
/** Create a child logger with additional bound context */
|
|
166
|
+
child(context: Record<string, unknown>): Logger<TMeta>;
|
|
167
|
+
/** Add a transport at runtime */
|
|
168
|
+
addTransport(transport: Transport<TMeta>): void;
|
|
169
|
+
/** Remove a transport by name */
|
|
170
|
+
removeTransport(name: string): void;
|
|
171
|
+
/** Add middleware at runtime */
|
|
172
|
+
addMiddleware(middleware: LogMiddleware<TMeta>): void;
|
|
173
|
+
/** Remove middleware by reference */
|
|
174
|
+
removeMiddleware(middleware: LogMiddleware<TMeta>): void;
|
|
175
|
+
/** Change the minimum log level at runtime */
|
|
176
|
+
setLevel(level: LogLevelName): void;
|
|
177
|
+
/** Get the current minimum log level */
|
|
178
|
+
getLevel(): LogLevelName;
|
|
179
|
+
/** Check if a given level would produce output (useful to guard expensive meta computation) */
|
|
180
|
+
isLevelEnabled(level: LogLevelName): boolean;
|
|
181
|
+
/** Start a timer — call `.done(msg)` to log elapsed duration */
|
|
182
|
+
startTimer(level?: LogLevelName): TimerResult<TMeta>;
|
|
183
|
+
/** Flush all transports */
|
|
184
|
+
flush(): Promise<void>;
|
|
185
|
+
/** Close all transports gracefully */
|
|
186
|
+
close(): Promise<void>;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @module voltlog-io
|
|
191
|
+
* @description Log level utilities — filtering, comparison, resolution.
|
|
192
|
+
*/
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Resolve a level name string to its numeric value.
|
|
196
|
+
* Case-insensitive. Returns INFO if unrecognized.
|
|
197
|
+
*/
|
|
198
|
+
declare function resolveLevel(level: string | LogLevelName): number;
|
|
199
|
+
/**
|
|
200
|
+
* Check if a log entry at `entryLevel` passes the `filterLevel`.
|
|
201
|
+
*/
|
|
202
|
+
declare function shouldLog(entryLevel: number, filterLevel: number): boolean;
|
|
203
|
+
/**
|
|
204
|
+
* Determine whether to include stack trace for a given entry level.
|
|
205
|
+
*/
|
|
206
|
+
declare function shouldIncludeStack(entryLevel: number, includeStack: boolean | LogLevelName): boolean;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* @module voltlog-io
|
|
210
|
+
* @description Core Logger class — zero external dependencies (only cuid2), runtime-agnostic.
|
|
211
|
+
*
|
|
212
|
+
* @example Basic usage
|
|
213
|
+
* ```ts
|
|
214
|
+
* import { createLogger, consoleTransport } from 'voltlog-io';
|
|
215
|
+
*
|
|
216
|
+
* const logger = createLogger({
|
|
217
|
+
* level: 'INFO',
|
|
218
|
+
* transports: [consoleTransport()],
|
|
219
|
+
* });
|
|
220
|
+
*
|
|
221
|
+
* logger.info('Server started', { port: 9000 });
|
|
222
|
+
* ```
|
|
223
|
+
*
|
|
224
|
+
* @example Child logger with bound context
|
|
225
|
+
* ```ts
|
|
226
|
+
* const cpLogger = logger.child({ chargePointId: 'CP-101' });
|
|
227
|
+
* cpLogger.info('BootNotification received');
|
|
228
|
+
* // → auto-includes context: { chargePointId: 'CP-101' }
|
|
229
|
+
* ```
|
|
230
|
+
*
|
|
231
|
+
* @example Error with stack trace
|
|
232
|
+
* ```ts
|
|
233
|
+
* logger.error('Connection failed', new Error('ETIMEDOUT'));
|
|
234
|
+
* logger.error('Handler crashed', { action: 'BootNotification' }, new Error('null ref'));
|
|
235
|
+
* ```
|
|
236
|
+
*
|
|
237
|
+
* @example With ocpp-ws-io (if user has both packages)
|
|
238
|
+
* ```ts
|
|
239
|
+
* import { createLogger } from 'ocpp-ws-io/logger'; // re-export
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Create a new logger instance.
|
|
245
|
+
*
|
|
246
|
+
* @example Minimal
|
|
247
|
+
* ```ts
|
|
248
|
+
* const logger = createLogger();
|
|
249
|
+
* logger.info('Hello');
|
|
250
|
+
* ```
|
|
251
|
+
*
|
|
252
|
+
* @example Full options
|
|
253
|
+
* ```ts
|
|
254
|
+
* import { createLogger, consoleTransport, prettyTransport } from 'voltlog-io';
|
|
255
|
+
*
|
|
256
|
+
* const logger = createLogger({
|
|
257
|
+
* level: 'DEBUG',
|
|
258
|
+
* transports: [prettyTransport()],
|
|
259
|
+
* redact: ['password', 'idToken'],
|
|
260
|
+
* includeStack: 'ERROR',
|
|
261
|
+
* });
|
|
262
|
+
* ```
|
|
263
|
+
*
|
|
264
|
+
* @example OCPP-aware with child loggers
|
|
265
|
+
* ```ts
|
|
266
|
+
* import { createLogger, prettyTransport } from 'voltlog-io';
|
|
267
|
+
* import type { OcppExchangeMeta } from 'voltlog-io';
|
|
268
|
+
*
|
|
269
|
+
* const logger = createLogger<OcppExchangeMeta>({
|
|
270
|
+
* level: 'INFO',
|
|
271
|
+
* transports: [prettyTransport()],
|
|
272
|
+
* });
|
|
273
|
+
*
|
|
274
|
+
* // Per-connection child logger
|
|
275
|
+
* const cpLog = logger.child({ chargePointId: 'CP-101' });
|
|
276
|
+
* cpLog.info('OCPP message', {
|
|
277
|
+
* messageType: 'CALL',
|
|
278
|
+
* action: 'BootNotification',
|
|
279
|
+
* direction: 'IN',
|
|
280
|
+
* });
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
declare function createLogger<TMeta = Record<string, unknown>>(options?: LoggerOptions<TMeta>): Logger<TMeta>;
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* @module voltlog-io
|
|
287
|
+
* @description AI Enrichment middleware — enriches logs with AI analysis (e.g. error explanation).
|
|
288
|
+
* @universal Works in all environments (uses `fetch`).
|
|
289
|
+
*
|
|
290
|
+
* > **Security Note**: Requires an API Key. Using this in the browser will expose your key to the client.
|
|
291
|
+
* > Recommended for server-side use only.
|
|
292
|
+
*/
|
|
293
|
+
|
|
294
|
+
interface AiEnrichmentOptions {
|
|
295
|
+
/**
|
|
296
|
+
* Only trigger for logs at or above this level.
|
|
297
|
+
* Default: ERROR
|
|
298
|
+
*/
|
|
299
|
+
level?: LogLevelName;
|
|
300
|
+
/**
|
|
301
|
+
* Field name to store the analysis result in `entry.meta`.
|
|
302
|
+
* Default: 'ai_analysis'
|
|
303
|
+
*/
|
|
304
|
+
targetField?: string;
|
|
305
|
+
/**
|
|
306
|
+
* Custom analyzer function.
|
|
307
|
+
* Return a string (analysis) or object (structured data) to attach to `meta[targetField]`.
|
|
308
|
+
*/
|
|
309
|
+
analyzer: (entry: LogEntry) => Promise<string | Record<string, unknown> | null>;
|
|
310
|
+
/**
|
|
311
|
+
* Timeout in milliseconds for the AI call.
|
|
312
|
+
* Default: 2000ms (fail fast to avoid blocking for too long)
|
|
313
|
+
*/
|
|
314
|
+
timeout?: number;
|
|
315
|
+
/**
|
|
316
|
+
* If true, errors in the analyzer are swallowed (logged to console.error but don't crash).
|
|
317
|
+
* Default: true
|
|
318
|
+
*/
|
|
319
|
+
swallowErrors?: boolean;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Enriches log entries by calling an asynchronous AI analyzer.
|
|
323
|
+
* Appends result to `entry.meta[targetField]`.
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* ```ts
|
|
327
|
+
* const ai = aiEnrichmentMiddleware({
|
|
328
|
+
* analyzer: createOpenAiErrorAnalyzer(process.env.OPENAI_API_KEY!),
|
|
329
|
+
* level: "ERROR",
|
|
330
|
+
* targetField: "error_explanation"
|
|
331
|
+
* });
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
declare function aiEnrichmentMiddleware<TMeta = Record<string, unknown>>(options: AiEnrichmentOptions): LogMiddleware<TMeta>;
|
|
335
|
+
/**
|
|
336
|
+
* Helper to create an OpenAI-compatible analyzer specifically for Error explanation.
|
|
337
|
+
*/
|
|
338
|
+
declare function createOpenAiErrorAnalyzer(apiKey: string, model?: string, systemPrompt?: string): (entry: LogEntry) => Promise<string | null>;
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* @module voltlog-io
|
|
342
|
+
* @description Alert middleware — checks logs against rules and triggers alerts.
|
|
343
|
+
* @universal Works in all environments.
|
|
344
|
+
* @example
|
|
345
|
+
* ```ts
|
|
346
|
+
* import { createLogger, consoleTransport, alertMiddleware } from 'voltlog-io';
|
|
347
|
+
*
|
|
348
|
+
* const logger = createLogger({
|
|
349
|
+
* transports: [consoleTransport()],
|
|
350
|
+
* middleware: [
|
|
351
|
+
* alertMiddleware([
|
|
352
|
+
* {
|
|
353
|
+
* name: 'error-spike',
|
|
354
|
+
* when: (entry) => entry.level >= 50, // ERROR+
|
|
355
|
+
* threshold: 10,
|
|
356
|
+
* windowMs: 60_000,
|
|
357
|
+
* cooldownMs: 300_000,
|
|
358
|
+
* onAlert: async (entries) => {
|
|
359
|
+
* await sendEmail({ subject: `${entries.length} errors in 1 min` });
|
|
360
|
+
* },
|
|
361
|
+
* },
|
|
362
|
+
* {
|
|
363
|
+
* name: 'callerror-alert',
|
|
364
|
+
* when: (entry) => entry.meta.messageType === 'CALLERROR',
|
|
365
|
+
* threshold: 5,
|
|
366
|
+
* windowMs: 60_000,
|
|
367
|
+
* onAlert: (entries) => sendSlackNotification(entries),
|
|
368
|
+
* },
|
|
369
|
+
* ]),
|
|
370
|
+
* ],
|
|
371
|
+
* });
|
|
372
|
+
* ```
|
|
373
|
+
*/
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Create an alert middleware that evaluates rules against every log entry.
|
|
377
|
+
*
|
|
378
|
+
* Alerts are non-blocking — `onAlert` failures are silently caught
|
|
379
|
+
* to never interfere with the logging pipeline.
|
|
380
|
+
*/
|
|
381
|
+
declare function alertMiddleware<TMeta = Record<string, unknown>>(rules: AlertRule<TMeta>[]): LogMiddleware<TMeta>;
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Helper to create a type-safe middleware function.
|
|
385
|
+
*
|
|
386
|
+
* @example
|
|
387
|
+
* const myMiddleware = createMiddleware((entry, next) => {
|
|
388
|
+
* entry.meta.foo = 'bar';
|
|
389
|
+
* next(entry);
|
|
390
|
+
* });
|
|
391
|
+
*/
|
|
392
|
+
declare function createMiddleware<TMeta = Record<string, unknown>>(fn: LogMiddleware<TMeta>): LogMiddleware<TMeta>;
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* @module voltlog-io
|
|
396
|
+
* @description Deduplication middleware — groups identical logs in a time window.
|
|
397
|
+
* @universal Works in all environments.
|
|
398
|
+
*/
|
|
399
|
+
|
|
400
|
+
interface DeduplicationOptions<TMeta = Record<string, unknown>> {
|
|
401
|
+
/**
|
|
402
|
+
* Time window in ms to group logs.
|
|
403
|
+
* Logs matching the key within this window will be aggregated.
|
|
404
|
+
* Default: 1000ms
|
|
405
|
+
*/
|
|
406
|
+
windowMs?: number;
|
|
407
|
+
/**
|
|
408
|
+
* Function to generate a unique key for deduplication.
|
|
409
|
+
* Default: uses `entry.message` + `entry.level`
|
|
410
|
+
*/
|
|
411
|
+
keyFn?: (entry: LogEntry<TMeta>) => string;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Deduplication middleware.
|
|
415
|
+
* Buffers logs for `windowMs`. If identical logs arrive, increments a counter.
|
|
416
|
+
* Emits the log once at the end of the window with `meta.duplicateCount`.
|
|
417
|
+
*/
|
|
418
|
+
declare function deduplicationMiddleware<TMeta = Record<string, unknown>>(options?: DeduplicationOptions<TMeta>): LogMiddleware<TMeta>;
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* @module voltlog-io
|
|
422
|
+
* @description Heap usage middleware — adds memory stats to logs.
|
|
423
|
+
* @universal Works in all environments, but only adds data in Node.js/Bun/Deno.
|
|
424
|
+
* Checks for `process.memoryUsage` presence before execution.
|
|
425
|
+
*/
|
|
426
|
+
|
|
427
|
+
interface HeapUsageOptions {
|
|
428
|
+
/**
|
|
429
|
+
* Field name to store memory stats in `entry.meta`.
|
|
430
|
+
* Default: 'memory'
|
|
431
|
+
*/
|
|
432
|
+
fieldName?: string;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Adds `rss`, `heapTotal`, and `heapUsed` to log metadata.
|
|
436
|
+
* Only works in environments where `process.memoryUsage` is available (Node.js/Bun/Deno).
|
|
437
|
+
*/
|
|
438
|
+
declare function heapUsageMiddleware<TMeta = Record<string, unknown>>(options?: HeapUsageOptions): LogMiddleware<TMeta>;
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* @module voltlog-io
|
|
442
|
+
* @description Universal HTTP middleware builder. Allows integration with Express, Fastify, exact node:http, etc.
|
|
443
|
+
* without introducing third-party framework dependencies.
|
|
444
|
+
*/
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Extracts essential logic from a given raw HTTP Request object
|
|
448
|
+
* regardless of the underlying framework (Express, Fastify, etc.)
|
|
449
|
+
*/
|
|
450
|
+
interface HttpRequestMapper<TReq = any> {
|
|
451
|
+
getMethod: (req: TReq) => string;
|
|
452
|
+
getUrl: (req: TReq) => string;
|
|
453
|
+
getIp?: (req: TReq) => string | undefined;
|
|
454
|
+
getUserAgent?: (req: TReq) => string | undefined;
|
|
455
|
+
/** Extract a particular header value */
|
|
456
|
+
getHeader?: (req: TReq, name: string) => string | undefined;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Extracts essential logic from a given raw HTTP Response object
|
|
460
|
+
*/
|
|
461
|
+
interface HttpResponseMapper<TRes = any> {
|
|
462
|
+
getStatusCode: (res: TRes) => number;
|
|
463
|
+
/** Execute callback when response is fully sent to the client */
|
|
464
|
+
onFinish: (res: TRes, callback: () => void) => void;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Options for configuring the HTTP access logger
|
|
468
|
+
*/
|
|
469
|
+
interface HttpLoggerOptions<TReq = any, TRes = any> {
|
|
470
|
+
reqMapper: HttpRequestMapper<TReq>;
|
|
471
|
+
resMapper: HttpResponseMapper<TRes>;
|
|
472
|
+
/** Log level to use for HTTP requests (default: INFO) */
|
|
473
|
+
level?: "TRACE" | "DEBUG" | "INFO" | "WARN" | "ERROR" | "FATAL";
|
|
474
|
+
/** Extract specific dynamic context values from the request to add to meta */
|
|
475
|
+
extractContext?: (req: TReq, res: TRes) => Record<string, unknown>;
|
|
476
|
+
/** Function to skip logging certain requests (e.g., /health) */
|
|
477
|
+
skip?: (req: TReq) => boolean;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Common mappers for the raw Node.js `http.IncomingMessage` and `http.ServerResponse`.
|
|
481
|
+
* Since Express and many others extend these objects, they work universally.
|
|
482
|
+
*/
|
|
483
|
+
declare const nodeHttpMappers: {
|
|
484
|
+
req: HttpRequestMapper;
|
|
485
|
+
res: HttpResponseMapper;
|
|
486
|
+
};
|
|
487
|
+
/**
|
|
488
|
+
* Creates a generic HTTP logging middleware function.
|
|
489
|
+
* Use this inside Express, Fastify, or custom routers.
|
|
490
|
+
*
|
|
491
|
+
* @example
|
|
492
|
+
* ```ts
|
|
493
|
+
* import express from 'express';
|
|
494
|
+
* import { createHttpLogger, nodeHttpMappers, createLogger } from 'voltlog-io';
|
|
495
|
+
*
|
|
496
|
+
* const app = express();
|
|
497
|
+
* const logger = createLogger();
|
|
498
|
+
*
|
|
499
|
+
* const httpLogger = createHttpLogger(logger, {
|
|
500
|
+
* reqMapper: nodeHttpMappers.req,
|
|
501
|
+
* resMapper: nodeHttpMappers.res,
|
|
502
|
+
* });
|
|
503
|
+
*
|
|
504
|
+
* app.use((req, res, next) => {
|
|
505
|
+
* httpLogger(req, res);
|
|
506
|
+
* next();
|
|
507
|
+
* });
|
|
508
|
+
* ```
|
|
509
|
+
*/
|
|
510
|
+
declare function createHttpLogger<TReq = any, TRes = any>(logger: Logger, options: HttpLoggerOptions<TReq, TRes>): (req: TReq, res: TRes) => void;
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* @module voltlog-io
|
|
514
|
+
* @description IP middleware — extracts client IP from headers (x-forwarded-for, etc.).
|
|
515
|
+
* @universal Works in any environment where headers are available in metadata.
|
|
516
|
+
*/
|
|
517
|
+
|
|
518
|
+
interface IpMiddlewareOptions {
|
|
519
|
+
/**
|
|
520
|
+
* Field name to store the extracted IP in `entry.meta`.
|
|
521
|
+
* Default: 'ip'
|
|
522
|
+
*/
|
|
523
|
+
fieldName?: string;
|
|
524
|
+
/**
|
|
525
|
+
* Custom list of headers/keys to check for IP address.
|
|
526
|
+
* Default: ['x-forwarded-for', 'x-real-ip', 'req.ip', 'ip']
|
|
527
|
+
*/
|
|
528
|
+
headerKeys?: string[];
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Extracts IP address from commonly used headers in `entry.meta`.
|
|
532
|
+
*/
|
|
533
|
+
declare function ipMiddleware<TMeta = Record<string, unknown>>(options?: IpMiddlewareOptions): LogMiddleware<TMeta>;
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* @module voltlog-io
|
|
537
|
+
* @description Level Override middleware — allows forcing a log level for specific requests.
|
|
538
|
+
* @universal Works in all environments.
|
|
539
|
+
* Useful for debugging specific users/requests in production without changing global config.
|
|
540
|
+
*/
|
|
541
|
+
|
|
542
|
+
interface LevelOverrideOptions {
|
|
543
|
+
/**
|
|
544
|
+
* Header name or meta key to trigger override.
|
|
545
|
+
* Default: 'x-log-level'
|
|
546
|
+
*/
|
|
547
|
+
key?: string;
|
|
548
|
+
/**
|
|
549
|
+
* If true, removes the trigger key from metadata before logging.
|
|
550
|
+
* Default: true
|
|
551
|
+
*/
|
|
552
|
+
cleanup?: boolean;
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Dynamically changes the log entry level if a specific key is found in meta/context.
|
|
556
|
+
* E.g. passing `x-log-level: DEBUG` allows a specific request to bypass INFO filters.
|
|
557
|
+
*/
|
|
558
|
+
declare function levelOverrideMiddleware<TMeta = Record<string, unknown>>(options?: LevelOverrideOptions): LogMiddleware<TMeta>;
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* @module voltlog-io
|
|
562
|
+
* @description OCPP middleware — masks sensitive fields and calculates latency for OCPP messages.
|
|
563
|
+
* @universal Works in all environments.
|
|
564
|
+
*
|
|
565
|
+
* @example
|
|
566
|
+
* ```ts
|
|
567
|
+
* import { createLogger, consoleTransport, ocppMiddleware } from 'voltlog-io';
|
|
568
|
+
* import type { OcppExchangeMeta } from 'voltlog-io';
|
|
569
|
+
*
|
|
570
|
+
* const logger = createLogger<OcppExchangeMeta>({
|
|
571
|
+
* transports: [consoleTransport()],
|
|
572
|
+
* middleware: [ocppMiddleware()],
|
|
573
|
+
* });
|
|
574
|
+
*
|
|
575
|
+
* // The middleware auto-computes payloadSize and adds correlationId to the entry
|
|
576
|
+
* logger.info('Message received', {
|
|
577
|
+
* action: 'BootNotification',
|
|
578
|
+
* messageType: 'CALL',
|
|
579
|
+
* direction: 'IN',
|
|
580
|
+
* });
|
|
581
|
+
* ```
|
|
582
|
+
*/
|
|
583
|
+
|
|
584
|
+
interface OcppMiddlewareOptions {
|
|
585
|
+
/**
|
|
586
|
+
* Automatically compute payload size from meta if not set.
|
|
587
|
+
* Default: true
|
|
588
|
+
*/
|
|
589
|
+
autoPayloadSize?: boolean;
|
|
590
|
+
/**
|
|
591
|
+
* Propagate correlationId from meta to entry.correlationId.
|
|
592
|
+
* Default: true
|
|
593
|
+
*/
|
|
594
|
+
propagateCorrelationId?: boolean;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Create an OCPP enrichment middleware.
|
|
598
|
+
* Enriches log entries with computed OCPP metadata.
|
|
599
|
+
*/
|
|
600
|
+
declare function ocppMiddleware(options?: OcppMiddlewareOptions): LogMiddleware<OcppExchangeMeta>;
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* @module voltlog-io
|
|
604
|
+
* @description OpenTelemetry middleware — automatically picks up the active trace context.
|
|
605
|
+
* Works like Winston's @opentelemetry/instrumentation-winston but as a simple middleware.
|
|
606
|
+
*
|
|
607
|
+
* @server-only Requires `@opentelemetry/api` as an optional peer dependency.
|
|
608
|
+
*
|
|
609
|
+
* When the OpenTelemetry SDK is active (e.g., with SigNoz, Jaeger, Grafana Tempo),
|
|
610
|
+
* this middleware automatically injects traceId, spanId, and traceFlags into every log entry.
|
|
611
|
+
*
|
|
612
|
+
* @example
|
|
613
|
+
* ```ts
|
|
614
|
+
* import { createLogger, otelTraceMiddleware, otelTransport } from 'voltlog-io';
|
|
615
|
+
*
|
|
616
|
+
* const logger = createLogger({
|
|
617
|
+
* middleware: [otelTraceMiddleware()],
|
|
618
|
+
* transports: [
|
|
619
|
+
* otelTransport({
|
|
620
|
+
* endpoint: 'http://localhost:4318',
|
|
621
|
+
* serviceName: 'my-app',
|
|
622
|
+
* }),
|
|
623
|
+
* ],
|
|
624
|
+
* });
|
|
625
|
+
*
|
|
626
|
+
* // If an OTel span is active, every log automatically gets:
|
|
627
|
+
* // { traceId: "abc123...", spanId: "def456...", traceFlags: 1 }
|
|
628
|
+
* logger.info("Processing request");
|
|
629
|
+
* ```
|
|
630
|
+
*/
|
|
631
|
+
|
|
632
|
+
interface OtelTraceMiddlewareOptions {
|
|
633
|
+
/**
|
|
634
|
+
* Custom reference to `@opentelemetry/api` trace module.
|
|
635
|
+
* If not provided, we attempt `require('@opentelemetry/api')` or dynamic import.
|
|
636
|
+
* This allows the middleware to work without a hard dependency.
|
|
637
|
+
*/
|
|
638
|
+
traceApi?: {
|
|
639
|
+
trace: {
|
|
640
|
+
getActiveSpan: () => unknown;
|
|
641
|
+
};
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Middleware that automatically injects OpenTelemetry trace context into log entries.
|
|
646
|
+
*
|
|
647
|
+
* How it works:
|
|
648
|
+
* 1. On each log call, checks for an active OTel span
|
|
649
|
+
* 2. If found, extracts traceId, spanId, and traceFlags
|
|
650
|
+
* 3. Injects them into the log entry's metadata
|
|
651
|
+
*
|
|
652
|
+
* This is the equivalent of Winston's @opentelemetry/instrumentation-winston
|
|
653
|
+
* but implemented as a zero-dependency middleware.
|
|
654
|
+
*/
|
|
655
|
+
declare function otelTraceMiddleware<TMeta = Record<string, unknown>>(options?: OtelTraceMiddlewareOptions): LogMiddleware<TMeta>;
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* @module voltlog-io
|
|
659
|
+
* @description Redaction middleware — masks sensitive data in log entries.
|
|
660
|
+
* @universal Works in all environments.
|
|
661
|
+
*
|
|
662
|
+
* @example
|
|
663
|
+
* ```ts
|
|
664
|
+
* import { createLogger, consoleTransport, redactionMiddleware } from 'voltlog-io';
|
|
665
|
+
*
|
|
666
|
+
* const logger = createLogger({
|
|
667
|
+
* transports: [consoleTransport()],
|
|
668
|
+
* middleware: [redactionMiddleware({ paths: ['password', 'idToken', 'authorization'] })],
|
|
669
|
+
* });
|
|
670
|
+
*
|
|
671
|
+
* logger.info('Auth attempt', { password: 's3cret', user: 'admin' });
|
|
672
|
+
* // → { password: '[REDACTED]', user: 'admin' }
|
|
673
|
+
* ```
|
|
674
|
+
*/
|
|
675
|
+
|
|
676
|
+
interface RedactionOptions {
|
|
677
|
+
/** Field paths to redact (case-insensitive matching) */
|
|
678
|
+
paths: string[];
|
|
679
|
+
/** Replacement value (default: '[REDACTED]') */
|
|
680
|
+
replacement?: string;
|
|
681
|
+
/** Also redact matching keys in nested objects (default: true) */
|
|
682
|
+
deep?: boolean;
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Create a redaction middleware that replaces sensitive field values.
|
|
686
|
+
*/
|
|
687
|
+
declare function redactionMiddleware<TMeta = Record<string, unknown>>(options: RedactionOptions): LogMiddleware<TMeta>;
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* @module voltlog-io
|
|
691
|
+
* @description Sampling middleware — rate limits or probabilistically samples logs.
|
|
692
|
+
* @universal Works in all environments.
|
|
693
|
+
* @example
|
|
694
|
+
* ```ts
|
|
695
|
+
* import { createLogger, consoleTransport, samplingMiddleware } from 'voltlog-io';
|
|
696
|
+
*
|
|
697
|
+
* const logger = createLogger({
|
|
698
|
+
* transports: [consoleTransport()],
|
|
699
|
+
* middleware: [
|
|
700
|
+
* samplingMiddleware({
|
|
701
|
+
* keyFn: (entry) => `${entry.meta.action}:${entry.meta.chargePointId}`,
|
|
702
|
+
* maxPerWindow: 10,
|
|
703
|
+
* windowMs: 60_000, // 10 logs per minute per action+CP combo
|
|
704
|
+
* }),
|
|
705
|
+
* ],
|
|
706
|
+
* });
|
|
707
|
+
* ```
|
|
708
|
+
*/
|
|
709
|
+
|
|
710
|
+
interface SamplingOptions<TMeta = Record<string, unknown>> {
|
|
711
|
+
/**
|
|
712
|
+
* Function to extract a sampling key from a log entry.
|
|
713
|
+
* Entries with the same key share a rate limit.
|
|
714
|
+
* Default: uses `entry.message`
|
|
715
|
+
*/
|
|
716
|
+
keyFn?: (entry: LogEntry<TMeta>) => string;
|
|
717
|
+
/** Maximum entries allowed per key per window (default: 100) */
|
|
718
|
+
maxPerWindow?: number;
|
|
719
|
+
/** Time window in ms (default: 60000 = 1 minute) */
|
|
720
|
+
windowMs?: number;
|
|
721
|
+
/** Probability to keep a log (0 to 1). Default: 1 (keep all) */
|
|
722
|
+
sampleRate?: number;
|
|
723
|
+
/** Logs at this level or higher always pass. Default: 40 (WARN) */
|
|
724
|
+
priorityLevel?: number;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Create a sampling middleware that drops logs exceeding the rate limit.
|
|
728
|
+
*/
|
|
729
|
+
declare function samplingMiddleware<TMeta = Record<string, unknown>>(options?: SamplingOptions<TMeta>): LogMiddleware<TMeta>;
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* @module voltlog-io
|
|
733
|
+
* @description User Agent middleware — parses UA strings into browser/os/device info.
|
|
734
|
+
* @universal Works in all environments (Server/Browser).
|
|
735
|
+
* Lightweight regex-based parsing to avoid heavy dependencies.
|
|
736
|
+
*/
|
|
737
|
+
|
|
738
|
+
interface UserAgentOptions {
|
|
739
|
+
/**
|
|
740
|
+
* Field to look for user agent string (source).
|
|
741
|
+
* Default checks `entry.meta.userAgent`, `entry.meta['user-agent']`, or context.
|
|
742
|
+
*/
|
|
743
|
+
sourceField?: string;
|
|
744
|
+
/**
|
|
745
|
+
* Field to store the parsed info (target).
|
|
746
|
+
* Default: 'client'
|
|
747
|
+
*/
|
|
748
|
+
targetField?: string;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Parses user-agent string into structured data (browser, os).
|
|
752
|
+
*/
|
|
753
|
+
declare function userAgentMiddleware<TMeta = Record<string, unknown>>(options?: UserAgentOptions): LogMiddleware<TMeta>;
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* @module voltlog-io
|
|
757
|
+
* @description Batch transformer — wraps another transformer and buffers logs.
|
|
758
|
+
* @universal Works in all environments.
|
|
759
|
+
*
|
|
760
|
+
* @example
|
|
761
|
+
* ```ts
|
|
762
|
+
* import { createLogger, batchTransport, consoleTransport } from 'voltlog-io';
|
|
763
|
+
*
|
|
764
|
+
* // Batch console output: flush every 100 entries or every 2 seconds
|
|
765
|
+
* const logger = createLogger({
|
|
766
|
+
* transports: [
|
|
767
|
+
* batchTransport(consoleTransport(), { batchSize: 100, flushIntervalMs: 2000 }),
|
|
768
|
+
* ],
|
|
769
|
+
* });
|
|
770
|
+
* ```
|
|
771
|
+
*/
|
|
772
|
+
|
|
773
|
+
interface BatchTransportOptions {
|
|
774
|
+
/** Number of entries to buffer before flushing (default: 100) */
|
|
775
|
+
batchSize?: number;
|
|
776
|
+
/** Max time in ms to wait before flushing a partial batch (default: 5000) */
|
|
777
|
+
flushIntervalMs?: number;
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Wrap any transport with batching. Entries are buffered and flushed
|
|
781
|
+
* either when the batch is full or the timer fires.
|
|
782
|
+
*/
|
|
783
|
+
declare function batchTransport(inner: Transport, options?: BatchTransportOptions): Transport;
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* @module voltlog-io
|
|
787
|
+
* @description Browser stream transformer — writes newline-delimited JSON to a WHATWG WritableStream.
|
|
788
|
+
* @browser-only Depends on WHATWG Streams API (typical in Browsers/Edge).
|
|
789
|
+
* Useful for streaming logs in browser environments (e.g. to `fetch` streams or ServiceWorkers).
|
|
790
|
+
*/
|
|
791
|
+
|
|
792
|
+
interface BrowserJsonStreamTransportOptions {
|
|
793
|
+
/**
|
|
794
|
+
* Writable stream to output to.
|
|
795
|
+
* Must be a standard WHATWG WritableStream (available in modern browsers).
|
|
796
|
+
*/
|
|
797
|
+
stream: WritableStream<string>;
|
|
798
|
+
/** Per-transport level filter */
|
|
799
|
+
level?: LogLevelName;
|
|
800
|
+
/**
|
|
801
|
+
* Custom serializer. Return the string to write.
|
|
802
|
+
* Default: JSON.stringify(entry) + '\n'
|
|
803
|
+
*/
|
|
804
|
+
serializer?: (entry: LogEntry) => string;
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Create a JSON stream transport for browsers that writes to a WritableStream.
|
|
808
|
+
*/
|
|
809
|
+
declare function browserJsonStreamTransport(options: BrowserJsonStreamTransportOptions): Transport;
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* @module voltlog-io
|
|
813
|
+
* @description Console transformer — writes to `console.log`, `console.error`, etc.
|
|
814
|
+
* @universal Works in all environments. Works in Node.js, browsers, and all runtimes.
|
|
815
|
+
*
|
|
816
|
+
* @example
|
|
817
|
+
* ```ts
|
|
818
|
+
* import { createLogger, consoleTransport } from 'voltlog-io';
|
|
819
|
+
*
|
|
820
|
+
* const logger = createLogger({
|
|
821
|
+
* transports: [consoleTransport()],
|
|
822
|
+
* });
|
|
823
|
+
* ```
|
|
824
|
+
*/
|
|
825
|
+
|
|
826
|
+
interface ConsoleTransportOptions {
|
|
827
|
+
/** Per-transport level filter */
|
|
828
|
+
level?: LogLevelName;
|
|
829
|
+
/**
|
|
830
|
+
* Use appropriate console method per level (console.warn, console.error, etc.).
|
|
831
|
+
* Default: true
|
|
832
|
+
*/
|
|
833
|
+
useConsoleLevels?: boolean;
|
|
834
|
+
/**
|
|
835
|
+
* Format the entry before outputting. Return any value that console.log can handle.
|
|
836
|
+
* Default: serializes to JSON string.
|
|
837
|
+
*/
|
|
838
|
+
formatter?: (entry: LogEntry) => unknown;
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* Create a console transport. Outputs structured JSON to the console.
|
|
842
|
+
*/
|
|
843
|
+
declare function consoleTransport(options?: ConsoleTransportOptions): Transport;
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* @module voltlog-io
|
|
847
|
+
* @description Helper to create custom transports easily.
|
|
848
|
+
* @universal Works in all environments.
|
|
849
|
+
*/
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* Helper to build a transport without manually defining the object structure.
|
|
853
|
+
*
|
|
854
|
+
* @example
|
|
855
|
+
* const myTransport = createTransport('my-api', async (entry) => {
|
|
856
|
+
* await fetch('https://api.example.com/logs', { body: JSON.stringify(entry) });
|
|
857
|
+
* });
|
|
858
|
+
*
|
|
859
|
+
* @param name - Unique name for the transport
|
|
860
|
+
* @param write - Function to process log entries
|
|
861
|
+
* @param options - Optional overrides (level, flush, close)
|
|
862
|
+
*/
|
|
863
|
+
declare function createTransport(name: string, write: (entry: LogEntry) => void | Promise<void>, options?: Partial<Omit<Transport, "name" | "write">>): Transport;
|
|
864
|
+
|
|
865
|
+
/**
|
|
866
|
+
* @module voltlog-io
|
|
867
|
+
* @description Datadog transformer — sends logs directly to Datadog Intake API.
|
|
868
|
+
* @universal Works in all environments (uses `fetch`).
|
|
869
|
+
*
|
|
870
|
+
* > **Security Note**: Using this in the browser will expose your API Key.
|
|
871
|
+
* > Recommended for server-side use only.
|
|
872
|
+
*/
|
|
873
|
+
|
|
874
|
+
interface DatadogTransportOptions {
|
|
875
|
+
/** Datadog API Key */
|
|
876
|
+
apiKey: string;
|
|
877
|
+
/** Datadog Site (default: datadoghq.com) */
|
|
878
|
+
site?: string;
|
|
879
|
+
/** Service name tag */
|
|
880
|
+
service?: string;
|
|
881
|
+
/** Source tag (default: nodejs) */
|
|
882
|
+
ddSource?: string;
|
|
883
|
+
/** Hostname override */
|
|
884
|
+
hostname?: string;
|
|
885
|
+
/** Tags (comma separated: env:prod,version:1.0) */
|
|
886
|
+
tags?: string;
|
|
887
|
+
/** Transport level filter */
|
|
888
|
+
level?: LogLevelName;
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Sends logs to Datadog via HTTP POST.
|
|
892
|
+
* Uses built-in batching (not implemented here for brevity, typically would use batchTransport wrapper).
|
|
893
|
+
* This implementation sends immediately for simplicity or relies on `batchTransport` composition.
|
|
894
|
+
*/
|
|
895
|
+
declare function datadogTransport(options: DatadogTransportOptions): Transport;
|
|
896
|
+
|
|
897
|
+
/**
|
|
898
|
+
* @module voltlog-io
|
|
899
|
+
* @description Discord transformer — sends logs to Discord via Webhook.
|
|
900
|
+
* @universal Works in all environments (uses `fetch`).
|
|
901
|
+
*
|
|
902
|
+
* > **Security Note**: Using this in the browser will expose your Webhook URL.
|
|
903
|
+
* > Recommended for server-side use only.
|
|
904
|
+
*/
|
|
905
|
+
|
|
906
|
+
interface DiscordTransportOptions {
|
|
907
|
+
/** Discord Webhook URL */
|
|
908
|
+
webhookUrl: string;
|
|
909
|
+
/** Minimum log level (default: ERROR) */
|
|
910
|
+
level?: LogLevelName;
|
|
911
|
+
/** Username override */
|
|
912
|
+
username?: string;
|
|
913
|
+
/** Avatar URL override */
|
|
914
|
+
avatarUrl?: string;
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Sends formatted embeds to Discord.
|
|
918
|
+
* Best used for Alerts/Errors.
|
|
919
|
+
*/
|
|
920
|
+
declare function discordTransport(options: DiscordTransportOptions): Transport;
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* @module voltlog-io
|
|
924
|
+
* @description Loki transformer — pushes logs to Grafana Loki.
|
|
925
|
+
* @universal Works in all environments (uses `fetch`).
|
|
926
|
+
*
|
|
927
|
+
* > **Security Note**: Using this in the browser exposes auth details and may face CORS issues.
|
|
928
|
+
* > Recommended for server-side use.
|
|
929
|
+
*/
|
|
930
|
+
|
|
931
|
+
interface LokiTransportOptions {
|
|
932
|
+
/** Loki URL (e.g. http://localhost:3100) */
|
|
933
|
+
host: string;
|
|
934
|
+
/** Basic Auth username */
|
|
935
|
+
basicAuthUser?: string;
|
|
936
|
+
/** Basic Auth password/token */
|
|
937
|
+
basicAuthPassword?: string;
|
|
938
|
+
/** Tenant ID (header X-Scope-OrgID) */
|
|
939
|
+
tenantId?: string;
|
|
940
|
+
/** Static labels to attach to every stream (e.g. { app: 'volt-server' }) */
|
|
941
|
+
labels?: Record<string, string>;
|
|
942
|
+
/**
|
|
943
|
+
* Extract dynamic labels from each log entry.
|
|
944
|
+
* These are merged with static labels and used as Loki stream labels.
|
|
945
|
+
* Keep cardinality low — use only a few well-known values.
|
|
946
|
+
* @example (entry) => ({ level: entry.levelName, service: entry.context?.service })
|
|
947
|
+
*/
|
|
948
|
+
dynamicLabels?: (entry: LogEntry) => Record<string, string | undefined>;
|
|
949
|
+
/**
|
|
950
|
+
* Include context, error, and correlationId in the Loki log payload.
|
|
951
|
+
* Default: true
|
|
952
|
+
*/
|
|
953
|
+
includeMetadata?: boolean;
|
|
954
|
+
/** Transport level filter */
|
|
955
|
+
level?: LogLevelName;
|
|
956
|
+
/** Batch size (default: 10) */
|
|
957
|
+
batchSize?: number;
|
|
958
|
+
/** Batch interval ms (default: 5000) */
|
|
959
|
+
interval?: number;
|
|
960
|
+
/** Enable retry on push failure (default: false) */
|
|
961
|
+
retry?: boolean;
|
|
962
|
+
/** Maximum retries (default: 3) */
|
|
963
|
+
maxRetries?: number;
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* Pushes logs to Grafana Loki via HTTP API.
|
|
967
|
+
* Uses batching to improve performance.
|
|
968
|
+
*/
|
|
969
|
+
declare function lokiTransport(options: LokiTransportOptions): Transport;
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* @module voltlog-io
|
|
973
|
+
* @description OpenTelemetry OTLP transport — sends logs via OTLP HTTP/JSON protocol.
|
|
974
|
+
* @server-only Uses `fetch()` to send logs to an OTLP-compatible collector.
|
|
975
|
+
*
|
|
976
|
+
* Compatible with: SigNoz, Jaeger, Grafana Alloy, OpenTelemetry Collector, and any OTLP endpoint.
|
|
977
|
+
*
|
|
978
|
+
* @example
|
|
979
|
+
* ```ts
|
|
980
|
+
* import { createLogger, otelTraceMiddleware, otelTransport } from 'voltlog-io';
|
|
981
|
+
*
|
|
982
|
+
* // SigNoz Cloud
|
|
983
|
+
* const logger = createLogger({
|
|
984
|
+
* middleware: [otelTraceMiddleware()],
|
|
985
|
+
* transports: [
|
|
986
|
+
* otelTransport({
|
|
987
|
+
* endpoint: 'https://ingest.signoz.io',
|
|
988
|
+
* headers: { 'signoz-access-token': 'YOUR_TOKEN' },
|
|
989
|
+
* serviceName: 'my-app',
|
|
990
|
+
* }),
|
|
991
|
+
* ],
|
|
992
|
+
* });
|
|
993
|
+
*
|
|
994
|
+
* // Self-hosted OTel Collector
|
|
995
|
+
* const logger2 = createLogger({
|
|
996
|
+
* transports: [
|
|
997
|
+
* otelTransport({
|
|
998
|
+
* endpoint: 'http://localhost:4318', // OTLP HTTP port
|
|
999
|
+
* serviceName: 'volt-csms',
|
|
1000
|
+
* resource: {
|
|
1001
|
+
* 'deployment.environment': 'production',
|
|
1002
|
+
* 'service.version': '2.0.0',
|
|
1003
|
+
* },
|
|
1004
|
+
* }),
|
|
1005
|
+
* ],
|
|
1006
|
+
* });
|
|
1007
|
+
*
|
|
1008
|
+
* logger.info('Request processed', { userId: 'u-42' });
|
|
1009
|
+
* // → Sent to SigNoz with traceId, spanId, severity, resource attributes
|
|
1010
|
+
* ```
|
|
1011
|
+
*/
|
|
1012
|
+
|
|
1013
|
+
interface OtelTransportOptions {
|
|
1014
|
+
/**
|
|
1015
|
+
* OTLP HTTP endpoint (e.g. http://localhost:4318 or https://ingest.signoz.io).
|
|
1016
|
+
* The `/v1/logs` path is appended automatically.
|
|
1017
|
+
*/
|
|
1018
|
+
endpoint: string;
|
|
1019
|
+
/** Service name reported to the collector */
|
|
1020
|
+
serviceName: string;
|
|
1021
|
+
/** Additional resource attributes (e.g. deployment.environment, service.version) */
|
|
1022
|
+
resource?: Record<string, string>;
|
|
1023
|
+
/** Extra headers (e.g. signoz-access-token, Authorization) */
|
|
1024
|
+
headers?: Record<string, string>;
|
|
1025
|
+
/** Transport level filter */
|
|
1026
|
+
level?: LogLevelName;
|
|
1027
|
+
/** Batch size before flush (default: 20) */
|
|
1028
|
+
batchSize?: number;
|
|
1029
|
+
/** Batch interval in ms (default: 5000) */
|
|
1030
|
+
interval?: number;
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Creates an OTLP HTTP/JSON transport for OpenTelemetry-compatible backends.
|
|
1034
|
+
*
|
|
1035
|
+
* Sends logs using the OTLP Logs protocol:
|
|
1036
|
+
* https://opentelemetry.io/docs/specs/otlp/#otlphttp-request
|
|
1037
|
+
*/
|
|
1038
|
+
declare function otelTransport(options: OtelTransportOptions): Transport;
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* @module voltlog-io
|
|
1042
|
+
* @description Pretty transformer — human-readable colored output with OCPP exchange formatting.
|
|
1043
|
+
* @universal Works in all environments (uses ANSI codes, supported by many browser consoles or stripped).
|
|
1044
|
+
*
|
|
1045
|
+
* @example Dev mode
|
|
1046
|
+
* ```ts
|
|
1047
|
+
* import { createLogger, prettyTransport } from 'voltlog-io';
|
|
1048
|
+
*
|
|
1049
|
+
* const logger = createLogger({
|
|
1050
|
+
* transports: [prettyTransport()],
|
|
1051
|
+
* });
|
|
1052
|
+
*
|
|
1053
|
+
* logger.info('Server started', { port: 9000 });
|
|
1054
|
+
* // → ℹ 2024-01-15T10:30:00.000Z INFO Server started { port: 9000 }
|
|
1055
|
+
* ```
|
|
1056
|
+
*
|
|
1057
|
+
* @example OCPP exchange logs
|
|
1058
|
+
* ```ts
|
|
1059
|
+
* logger.info('OCPP exchange', {
|
|
1060
|
+
* chargePointId: 'CP-101',
|
|
1061
|
+
* action: 'BootNotification',
|
|
1062
|
+
* messageType: 'CALL',
|
|
1063
|
+
* direction: 'IN',
|
|
1064
|
+
* latencyMs: 34,
|
|
1065
|
+
* status: 'Accepted',
|
|
1066
|
+
* });
|
|
1067
|
+
* // → ⚡ CP-101 → BootNotification [IN] CALL
|
|
1068
|
+
* // → ✔ Accepted (34ms)
|
|
1069
|
+
* ```
|
|
1070
|
+
*/
|
|
1071
|
+
|
|
1072
|
+
interface PrettyTransportOptions {
|
|
1073
|
+
/** Per-transport level filter */
|
|
1074
|
+
level?: LogLevelName;
|
|
1075
|
+
/** Show timestamps (default: true) */
|
|
1076
|
+
timestamps?: boolean;
|
|
1077
|
+
/** Use colors in output (default: true) */
|
|
1078
|
+
colors?: boolean;
|
|
1079
|
+
/** Hide meta object in output (default: false) */
|
|
1080
|
+
hideMeta?: boolean;
|
|
1081
|
+
/** Pretty-print meta object with indentation (default: false) */
|
|
1082
|
+
prettyMeta?: boolean;
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Create a pretty-print transport for dev/debug use.
|
|
1086
|
+
* Includes OCPP exchange log prettification.
|
|
1087
|
+
*/
|
|
1088
|
+
declare function prettyTransport(options?: PrettyTransportOptions): Transport;
|
|
1089
|
+
|
|
1090
|
+
/**
|
|
1091
|
+
* @module voltlog-io
|
|
1092
|
+
* @description Ring buffer transport — stores last N log entries in memory for on-demand retrieval.
|
|
1093
|
+
* @universal Works in all environments.
|
|
1094
|
+
*
|
|
1095
|
+
* Useful for debugging, in-app log viewers, and diagnostic endpoints.
|
|
1096
|
+
*
|
|
1097
|
+
* @example
|
|
1098
|
+
* ```ts
|
|
1099
|
+
* import { createLogger, ringBufferTransport } from 'voltlog-io';
|
|
1100
|
+
*
|
|
1101
|
+
* const ring = ringBufferTransport({ maxSize: 500 });
|
|
1102
|
+
* const logger = createLogger({ transports: [ring] });
|
|
1103
|
+
*
|
|
1104
|
+
* logger.info('Hello');
|
|
1105
|
+
* logger.error('Oops', new Error('fail'));
|
|
1106
|
+
*
|
|
1107
|
+
* // Retrieve buffered entries
|
|
1108
|
+
* const entries = ring.getEntries();
|
|
1109
|
+
* const errors = ring.getEntries({ level: 'ERROR' });
|
|
1110
|
+
* ```
|
|
1111
|
+
*/
|
|
1112
|
+
|
|
1113
|
+
interface RingBufferTransportOptions {
|
|
1114
|
+
/** Maximum number of entries to keep (default: 1000) */
|
|
1115
|
+
maxSize?: number;
|
|
1116
|
+
/** Per-transport level filter */
|
|
1117
|
+
level?: LogLevelName;
|
|
1118
|
+
}
|
|
1119
|
+
interface RingBufferQueryOptions {
|
|
1120
|
+
/** Filter by minimum level */
|
|
1121
|
+
level?: LogLevelName;
|
|
1122
|
+
/** Return only entries after this timestamp */
|
|
1123
|
+
since?: number;
|
|
1124
|
+
/** Maximum entries to return (default: all) */
|
|
1125
|
+
limit?: number;
|
|
1126
|
+
}
|
|
1127
|
+
interface RingBufferTransport extends Transport {
|
|
1128
|
+
/** Retrieve buffered log entries with optional filtering */
|
|
1129
|
+
getEntries(query?: RingBufferQueryOptions): LogEntry[];
|
|
1130
|
+
/** Clear all buffered entries */
|
|
1131
|
+
clear(): void;
|
|
1132
|
+
/** Get current buffer size */
|
|
1133
|
+
size: number;
|
|
1134
|
+
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Creates a ring buffer (circular buffer) transport.
|
|
1137
|
+
* Stores the most recent `maxSize` entries in memory.
|
|
1138
|
+
* Oldest entries are evicted when the buffer is full.
|
|
1139
|
+
*/
|
|
1140
|
+
declare function ringBufferTransport(options?: RingBufferTransportOptions): RingBufferTransport;
|
|
1141
|
+
|
|
1142
|
+
/**
|
|
1143
|
+
* @module voltlog-io
|
|
1144
|
+
* @description Sentry transformer — pushes errors to Sentry.
|
|
1145
|
+
* @universal Works in both Server and Browser (depends on provided Sentry instance).
|
|
1146
|
+
*/
|
|
1147
|
+
|
|
1148
|
+
/**
|
|
1149
|
+
* Minimal Sentry client interface to avoid hard dependency on @sentry/* packages.
|
|
1150
|
+
* Users pass their Sentry instance (e.g. `import * as Sentry from '@sentry/node'`).
|
|
1151
|
+
*/
|
|
1152
|
+
interface SentryInstance {
|
|
1153
|
+
captureException(exception: unknown, hint?: unknown): string;
|
|
1154
|
+
captureMessage(message: string, level?: unknown): string;
|
|
1155
|
+
addBreadcrumb(breadcrumb: unknown): void;
|
|
1156
|
+
Severity?: unknown;
|
|
1157
|
+
}
|
|
1158
|
+
interface SentryTransportOptions {
|
|
1159
|
+
/** Configured Sentry instance/hub */
|
|
1160
|
+
sentry: SentryInstance;
|
|
1161
|
+
/** Minimum level to trigger captureException (default: ERROR) */
|
|
1162
|
+
errorLevel?: LogLevelName;
|
|
1163
|
+
/** Minimum level to send breadcrumbs (default: INFO) */
|
|
1164
|
+
breadcrumbLevel?: LogLevelName;
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Integrates with an existing Sentry instance.
|
|
1168
|
+
* - Logs >= errorLevel -> sent as Exceptions
|
|
1169
|
+
* - Logs >= breadcrumbLevel -> sent as Breadcrumbs
|
|
1170
|
+
*/
|
|
1171
|
+
declare function sentryTransport(options: SentryTransportOptions): Transport;
|
|
1172
|
+
|
|
1173
|
+
/**
|
|
1174
|
+
* @module voltlog-io
|
|
1175
|
+
* @description Slack transformer — sends rich notifications to Slack via Incoming Webhook.
|
|
1176
|
+
* @universal Works in all environments (uses `fetch`).
|
|
1177
|
+
*
|
|
1178
|
+
* > **Security Note**: Using this in the browser will expose your Webhook URL.
|
|
1179
|
+
* > Recommended for server-side use only.
|
|
1180
|
+
* Best used with a filter (e.g. ERROR only) or alert middleware.
|
|
1181
|
+
*/
|
|
1182
|
+
|
|
1183
|
+
interface SlackTransportOptions {
|
|
1184
|
+
/** Slack Incoming Webhook URL */
|
|
1185
|
+
webhookUrl: string;
|
|
1186
|
+
/** Filter level (default: ERROR) */
|
|
1187
|
+
level?: LogLevelName;
|
|
1188
|
+
/** Custom username (overrides webhook default) */
|
|
1189
|
+
username?: string;
|
|
1190
|
+
/** Custom icon emoji (overrides webhook default) */
|
|
1191
|
+
iconEmoji?: string;
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
* Sends logs to Slack with formatting.
|
|
1195
|
+
* Best suited for ERROR/FATAL logs or specific alerts.
|
|
1196
|
+
*/
|
|
1197
|
+
declare function slackTransport(options: SlackTransportOptions): Transport;
|
|
1198
|
+
|
|
1199
|
+
/**
|
|
1200
|
+
* @module voltlog-io
|
|
1201
|
+
* @description Webhook transformer — sends logs via HTTP POST.
|
|
1202
|
+
* @universal Works in all environments (uses `fetch`).
|
|
1203
|
+
* Supports batching for performance.
|
|
1204
|
+
*
|
|
1205
|
+
* @example
|
|
1206
|
+
* ```ts
|
|
1207
|
+
* import { createLogger, webhookTransport } from 'voltlog-io';
|
|
1208
|
+
*
|
|
1209
|
+
* const logger = createLogger({
|
|
1210
|
+
* transports: [
|
|
1211
|
+
* webhookTransport({
|
|
1212
|
+
* url: 'https://my-api.com/logs',
|
|
1213
|
+
* headers: { Authorization: 'Bearer token123' },
|
|
1214
|
+
* batchSize: 50,
|
|
1215
|
+
* flushIntervalMs: 5000,
|
|
1216
|
+
* }),
|
|
1217
|
+
* ],
|
|
1218
|
+
* });
|
|
1219
|
+
* ```
|
|
1220
|
+
*/
|
|
1221
|
+
|
|
1222
|
+
interface WebhookTransportOptions {
|
|
1223
|
+
/** Target URL to POST log entries to */
|
|
1224
|
+
url: string;
|
|
1225
|
+
/** HTTP method (default: POST) */
|
|
1226
|
+
method?: string;
|
|
1227
|
+
/** Additional HTTP headers */
|
|
1228
|
+
headers?: Record<string, string>;
|
|
1229
|
+
/** Number of entries to batch before sending (default: 1 = no batching) */
|
|
1230
|
+
batchSize?: number;
|
|
1231
|
+
/** Max time in ms to wait before flushing a partial batch (default: 5000) */
|
|
1232
|
+
flushIntervalMs?: number;
|
|
1233
|
+
/** Per-transport level filter */
|
|
1234
|
+
level?: LogLevelName;
|
|
1235
|
+
/** Custom body serializer. Default: JSON.stringify({ entries: [...] }) */
|
|
1236
|
+
serializer?: (entries: LogEntry[]) => string;
|
|
1237
|
+
/** Retry failed requests (default: false) */
|
|
1238
|
+
retry?: boolean;
|
|
1239
|
+
/** Max retries (default: 3) */
|
|
1240
|
+
maxRetries?: number;
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Create a webhook transport that POSTs log entries to an HTTP endpoint.
|
|
1244
|
+
*/
|
|
1245
|
+
declare function webhookTransport(options: WebhookTransportOptions): Transport;
|
|
1246
|
+
|
|
1247
|
+
export { type AiEnrichmentOptions, type AlertRule, type BatchTransportOptions, type BrowserJsonStreamTransportOptions, type ConsoleTransportOptions, type DatadogTransportOptions, type DeduplicationOptions, type DiscordTransportOptions, type HttpLoggerOptions, type HttpRequestMapper, type HttpResponseMapper, type LevelOverrideOptions, type LogEntry, type LogError, LogLevel, type LogLevelName, LogLevelNameMap, type LogLevelValue, LogLevelValueMap, type LogMiddleware, type Logger, type LoggerOptions, type LokiTransportOptions, type OcppExchangeMeta, type OcppMiddlewareOptions, type OtelTraceMiddlewareOptions, type OtelTransportOptions, type PrettyTransportOptions, type RedactionOptions, type RingBufferQueryOptions, type RingBufferTransport, type RingBufferTransportOptions, type SamplingOptions, type SentryInstance, type SentryTransportOptions, type SlackTransportOptions, type TimerResult, type Transport, type UserAgentOptions, type WebhookTransportOptions, aiEnrichmentMiddleware, alertMiddleware, batchTransport, browserJsonStreamTransport, consoleTransport, createHttpLogger, createLogger, createMiddleware, createOpenAiErrorAnalyzer, createTransport, datadogTransport, deduplicationMiddleware, discordTransport, heapUsageMiddleware, ipMiddleware, levelOverrideMiddleware, lokiTransport, nodeHttpMappers, ocppMiddleware, otelTraceMiddleware, otelTransport, prettyTransport, redactionMiddleware, resolveLevel, ringBufferTransport, samplingMiddleware, sentryTransport, shouldIncludeStack, shouldLog, slackTransport, userAgentMiddleware, webhookTransport };
|