voltlog-io 1.0.3 → 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/dist/index.mjs CHANGED
@@ -1,430 +1,40 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
- // node_modules/tsup/assets/esm_shims.js
9
- import path from "path";
10
- import { fileURLToPath } from "url";
11
- var getFilename = () => fileURLToPath(import.meta.url);
12
- var __filename = /* @__PURE__ */ getFilename();
13
-
14
- // src/core/types.ts
15
- var LogLevel = {
16
- TRACE: 10,
17
- DEBUG: 20,
18
- INFO: 30,
19
- WARN: 40,
20
- ERROR: 50,
21
- FATAL: 60,
22
- SILENT: Infinity
23
- };
24
- var LogLevelNameMap = Object.fromEntries(
25
- Object.entries(LogLevel).map(([k, v]) => [k.toLowerCase(), v])
26
- );
27
- var LogLevelValueMap = Object.fromEntries(
28
- Object.entries(LogLevel).filter(([, v]) => Number.isFinite(v)).map(([k, v]) => [v, k])
29
- );
30
-
31
- // src/core/levels.ts
32
- function resolveLevel(level) {
33
- const n = LogLevelNameMap[level.toLowerCase()];
34
- return n !== void 0 ? n : LogLevel.INFO;
35
- }
36
- function shouldLog(entryLevel, filterLevel) {
37
- return entryLevel >= filterLevel;
38
- }
39
- function shouldIncludeStack(entryLevel, includeStack) {
40
- if (typeof includeStack === "boolean") return includeStack;
41
- return entryLevel >= resolveLevel(includeStack);
42
- }
43
-
44
- // src/core/logger.ts
45
- import { randomUUID } from "crypto";
46
-
47
- // src/core/pipeline.ts
48
- function composeMiddleware(middleware, final) {
49
- if (middleware.length === 0) return final;
50
- return (entry) => {
51
- let index = 0;
52
- const next = (e) => {
53
- if (index < middleware.length) {
54
- const mw = middleware[index++];
55
- mw(e, next);
56
- } else {
57
- final(e);
58
- }
59
- };
60
- next(entry);
61
- };
62
- }
63
- function fanOutToTransports(entry, transports, loggerLevel) {
64
- for (const t of transports) {
65
- const tLevel = t.level ? resolveLevel(t.level) : loggerLevel;
66
- if (!shouldLog(entry.level, tLevel)) continue;
67
- try {
68
- const result = t.write(entry);
69
- if (result && typeof result.catch === "function") {
70
- result.catch(() => {
71
- });
72
- }
73
- } catch {
74
- }
75
- }
76
- }
77
-
78
- // src/core/logger.ts
79
- var LoggerImpl = class {
80
- _level;
81
- _transports;
82
- _middlewareList;
83
- _pipeline;
84
- _context;
85
- _includeStack;
86
- _timestampFn;
87
- _idFn;
88
- constructor(options = {}) {
89
- this._level = resolveLevel(options.level ?? "INFO");
90
- this._transports = [...options.transports ?? []];
91
- this._middlewareList = [...options.middleware ?? []];
92
- this._context = options.context ? { ...options.context } : {};
93
- this._includeStack = options.includeStack ?? "ERROR";
94
- this._timestampFn = options.timestamp ?? Date.now;
95
- this._idFn = options.idGenerator !== void 0 ? options.idGenerator : randomUUID;
96
- this._pipeline = this._buildPipeline();
97
- }
98
- // ─── Log Methods ────────────────────────────────────────────
99
- trace(message, meta) {
100
- if (10 < this._level) return;
101
- this._log(10, "TRACE", message, meta);
102
- }
103
- debug(message, meta) {
104
- if (20 < this._level) return;
105
- this._log(20, "DEBUG", message, meta);
106
- }
107
- info(message, meta) {
108
- if (30 < this._level) return;
109
- this._log(30, "INFO", message, meta);
110
- }
111
- warn(message, meta) {
112
- if (40 < this._level) return;
113
- this._log(40, "WARN", message, meta);
114
- }
115
- error(message, metaOrError, error) {
116
- if (50 < this._level) return;
117
- if (metaOrError instanceof Error) {
118
- this._log(50, "ERROR", message, void 0, metaOrError);
119
- } else {
120
- this._log(50, "ERROR", message, metaOrError, error);
121
- }
122
- }
123
- fatal(message, metaOrError, error) {
124
- if (metaOrError instanceof Error) {
125
- this._log(60, "FATAL", message, void 0, metaOrError);
126
- } else {
127
- this._log(60, "FATAL", message, metaOrError, error);
128
- }
129
- }
130
- // ─── Child Logger ───────────────────────────────────────────
131
- child(context) {
132
- return new ChildLoggerImpl(this, { ...this._context, ...context });
133
- }
134
- // ─── Dynamic Configuration ─────────────────────────────────
135
- addTransport(transport) {
136
- this._transports.push(transport);
137
- }
138
- removeTransport(name) {
139
- this._transports = this._transports.filter((t) => t.name !== name);
140
- }
141
- addMiddleware(middleware) {
142
- this._middlewareList.push(middleware);
143
- this._pipeline = this._buildPipeline();
144
- }
145
- removeMiddleware(middleware) {
146
- this._middlewareList = this._middlewareList.filter((m) => m !== middleware);
147
- this._pipeline = this._buildPipeline();
148
- }
149
- // ─── Level Control ─────────────────────────────────────────
150
- setLevel(level) {
151
- this._level = resolveLevel(level);
152
- }
153
- getLevel() {
154
- return LogLevelValueMap[this._level] ?? "INFO";
155
- }
156
- isLevelEnabled(level) {
157
- return resolveLevel(level) >= this._level;
158
- }
159
- // ─── Timer ─────────────────────────────────────────────────
160
- startTimer(level) {
161
- const start = performance.now();
162
- const logLevel = level ?? "INFO";
163
- return {
164
- done: (message, meta) => {
165
- const durationMs = Math.round(performance.now() - start);
166
- const merged = { ...meta, durationMs };
167
- const methodKey = logLevel.toLowerCase();
168
- this[methodKey](message, merged);
169
- },
170
- elapsed: () => Math.round(performance.now() - start)
171
- };
172
- }
173
- // ─── Lifecycle ──────────────────────────────────────────────
174
- async flush() {
175
- await Promise.all(this._transports.map((t) => t.flush?.()).filter(Boolean));
176
- }
177
- async close() {
178
- await this.flush();
179
- await Promise.all(this._transports.map((t) => t.close?.()).filter(Boolean));
180
- }
181
- // ─── Internal ───────────────────────────────────────────────
182
- /** @internal */
183
- _log(level, levelName, message, meta, error) {
184
- this._logWithContext(level, levelName, message, this._context, meta, error);
185
- }
186
- /** @internal — used by child loggers to inject bound context */
187
- _logWithContext(level, levelName, message, context, meta, error) {
188
- if (!shouldLog(level, this._level)) return;
189
- const entry = {
190
- id: this._idFn ? this._idFn() : "",
191
- level,
192
- levelName,
193
- message,
194
- timestamp: this._timestampFn(),
195
- meta: meta ?? {},
196
- context: Object.keys(context).length > 0 ? context : void 0
197
- };
198
- if (error) {
199
- entry.error = serializeError(
200
- error,
201
- shouldIncludeStack(level, this._includeStack)
202
- );
203
- }
204
- this._pipeline(entry);
205
- }
206
- _buildPipeline() {
207
- return composeMiddleware(this._middlewareList, (entry) => {
208
- fanOutToTransports(entry, this._transports, this._level);
209
- });
210
- }
211
- };
212
- function serializeError(error, includeStack, depth = 0) {
213
- const logError = {
214
- message: error.message,
215
- name: error.name,
216
- code: error.code
217
- };
218
- if (includeStack) {
219
- logError.stack = error.stack;
220
- }
221
- if (error.cause instanceof Error && depth < 5) {
222
- logError.cause = serializeError(error.cause, includeStack, depth + 1);
223
- }
224
- return logError;
225
- }
226
- var ChildLoggerImpl = class _ChildLoggerImpl {
227
- constructor(_parent, _context) {
228
- this._parent = _parent;
229
- this._context = _context;
230
- }
231
- trace(message, meta) {
232
- this._parent._logWithContext(10, "TRACE", message, this._context, meta);
233
- }
234
- debug(message, meta) {
235
- this._parent._logWithContext(20, "DEBUG", message, this._context, meta);
236
- }
237
- info(message, meta) {
238
- this._parent._logWithContext(30, "INFO", message, this._context, meta);
239
- }
240
- warn(message, meta) {
241
- this._parent._logWithContext(40, "WARN", message, this._context, meta);
242
- }
243
- error(message, metaOrError, error) {
244
- if (metaOrError instanceof Error) {
245
- this._parent._logWithContext(
246
- 50,
247
- "ERROR",
248
- message,
249
- this._context,
250
- void 0,
251
- metaOrError
252
- );
253
- } else {
254
- this._parent._logWithContext(
255
- 50,
256
- "ERROR",
257
- message,
258
- this._context,
259
- metaOrError,
260
- error
261
- );
262
- }
263
- }
264
- fatal(message, metaOrError, error) {
265
- if (metaOrError instanceof Error) {
266
- this._parent._logWithContext(
267
- 60,
268
- "FATAL",
269
- message,
270
- this._context,
271
- void 0,
272
- metaOrError
273
- );
274
- } else {
275
- this._parent._logWithContext(
276
- 60,
277
- "FATAL",
278
- message,
279
- this._context,
280
- metaOrError,
281
- error
282
- );
283
- }
284
- }
285
- child(context) {
286
- return new _ChildLoggerImpl(this._parent, {
287
- ...this._context,
288
- ...context
289
- });
290
- }
291
- addTransport(transport) {
292
- this._parent.addTransport(transport);
293
- }
294
- removeTransport(name) {
295
- this._parent.removeTransport(name);
296
- }
297
- addMiddleware(middleware) {
298
- this._parent.addMiddleware(middleware);
299
- }
300
- removeMiddleware(middleware) {
301
- this._parent.removeMiddleware(middleware);
302
- }
303
- setLevel(level) {
304
- this._parent.setLevel(level);
305
- }
306
- getLevel() {
307
- return this._parent.getLevel();
308
- }
309
- isLevelEnabled(level) {
310
- return this._parent.isLevelEnabled(level);
311
- }
312
- startTimer(level) {
313
- return this._parent.startTimer(level);
314
- }
315
- flush() {
316
- return this._parent.flush();
317
- }
318
- close() {
319
- return this._parent.close();
320
- }
321
- };
322
- function createLogger(options) {
323
- return new LoggerImpl(options);
324
- }
325
-
326
- // src/middleware/ai-enrichment.ts
327
- function aiEnrichmentMiddleware(options) {
328
- const minLevel = resolveLevel(options.level ?? "ERROR");
329
- const timeoutMs = options.timeout ?? 2e3;
330
- const swallow = options.swallowErrors ?? true;
331
- const fieldName = options.targetField ?? "ai_analysis";
332
- return async (entry, next) => {
333
- if (entry.level < minLevel) {
334
- next(entry);
335
- return;
336
- }
337
- try {
338
- const analysisPromise = options.analyzer(entry);
339
- const timeoutPromise = new Promise(
340
- (_, reject) => setTimeout(() => reject(new Error("AI Analysis Timeout")), timeoutMs)
341
- );
342
- const result = await Promise.race([analysisPromise, timeoutPromise]);
343
- if (result) {
344
- entry.meta = {
345
- ...entry.meta,
346
- [fieldName]: result
347
- };
348
- }
349
- } catch (err) {
350
- if (!swallow) {
351
- throw err;
352
- }
353
- }
354
- next(entry);
355
- };
356
- }
357
- function createOpenAiErrorAnalyzer(apiKey, model = "gpt-3.5-turbo", systemPrompt = "You are a log analyzer. Explain this error briefly and suggest a fix in 1 sentence.") {
358
- return async (entry) => {
359
- try {
360
- const response = await fetch(
361
- "https://api.openai.com/v1/chat/completions",
362
- {
363
- method: "POST",
364
- headers: {
365
- "Content-Type": "application/json",
366
- Authorization: `Bearer ${apiKey}`
367
- },
368
- body: JSON.stringify({
369
- model,
370
- messages: [
371
- { role: "system", content: systemPrompt },
372
- {
373
- role: "user",
374
- content: `Error Message: ${entry.message}
375
- Context: ${JSON.stringify(entry.meta)}`
376
- }
377
- ],
378
- max_tokens: 150
379
- })
380
- }
381
- );
382
- if (!response.ok) return null;
383
- const data = await response.json();
384
- return data.choices?.[0]?.message?.content ?? null;
385
- } catch {
386
- return null;
387
- }
388
- };
389
- }
390
-
391
- // src/middleware/alert.ts
392
- function alertMiddleware(rules) {
393
- const states = /* @__PURE__ */ new Map();
394
- for (const rule of rules) {
395
- states.set(rule.name, { entries: [], lastFired: -Infinity });
396
- }
397
- return (entry, next) => {
398
- const now = entry.timestamp;
399
- for (const rule of rules) {
400
- if (!rule.when(entry)) continue;
401
- const state = states.get(rule.name);
402
- const windowMs = rule.windowMs ?? Infinity;
403
- const threshold = rule.threshold ?? 1;
404
- const cooldownMs = rule.cooldownMs ?? 0;
405
- if (Number.isFinite(windowMs)) {
406
- state.entries = state.entries.filter(
407
- (e) => now - e.timestamp < windowMs
408
- );
409
- }
410
- state.entries.push(entry);
411
- if (state.entries.length >= threshold && now - state.lastFired >= cooldownMs) {
412
- const alertEntries = [...state.entries];
413
- state.entries = [];
414
- state.lastFired = now;
415
- try {
416
- const result = rule.onAlert(alertEntries);
417
- if (result && typeof result.catch === "function") {
418
- result.catch(() => {
419
- });
420
- }
421
- } catch {
422
- }
423
- }
424
- }
425
- next(entry);
426
- };
427
- }
1
+ import {
2
+ LogLevel,
3
+ LogLevelNameMap,
4
+ LogLevelValueMap,
5
+ aiEnrichmentMiddleware,
6
+ alertMiddleware,
7
+ batchTransport,
8
+ browserJsonStreamTransport,
9
+ consoleTransport,
10
+ createHttpLogger,
11
+ createLogger,
12
+ createMiddleware,
13
+ createOpenAiErrorAnalyzer,
14
+ createTransport,
15
+ datadogTransport,
16
+ deduplicationMiddleware,
17
+ discordTransport,
18
+ heapUsageMiddleware,
19
+ ipMiddleware,
20
+ levelOverrideMiddleware,
21
+ lokiTransport,
22
+ nodeHttpMappers,
23
+ ocppMiddleware,
24
+ otelTraceMiddleware,
25
+ otelTransport,
26
+ prettyTransport,
27
+ redactionMiddleware,
28
+ resolveLevel,
29
+ ringBufferTransport,
30
+ samplingMiddleware,
31
+ sentryTransport,
32
+ shouldIncludeStack,
33
+ shouldLog,
34
+ slackTransport,
35
+ userAgentMiddleware,
36
+ webhookTransport
37
+ } from "./chunk-DAFMRCAN.mjs";
428
38
 
429
39
  // src/middleware/async-context.ts
430
40
  import { AsyncLocalStorage } from "async_hooks";
@@ -465,10 +75,10 @@ function asyncContextMiddleware() {
465
75
  }
466
76
 
467
77
  // src/middleware/correlation-id.ts
468
- import { randomUUID as randomUUID2 } from "crypto";
78
+ import { randomUUID } from "crypto";
469
79
  function correlationIdMiddleware(options = {}) {
470
80
  const header = options.header ?? "x-correlation-id";
471
- const generate = options.generator ?? randomUUID2;
81
+ const generate = options.generator ?? randomUUID;
472
82
  return (entry, next) => {
473
83
  if (entry.correlationId) {
474
84
  return next(entry);
@@ -486,586 +96,9 @@ function correlationIdMiddleware(options = {}) {
486
96
  };
487
97
  }
488
98
 
489
- // src/middleware/create-middleware.ts
490
- function createMiddleware(fn) {
491
- return fn;
492
- }
493
-
494
- // src/middleware/deduplication.ts
495
- function deduplicationMiddleware(options = {}) {
496
- const windowMs = options.windowMs ?? 1e3;
497
- const keyFn = options.keyFn ?? ((e) => `${e.level}:${e.message}:${e.error?.message ?? ""}`);
498
- const buffer = /* @__PURE__ */ new Map();
499
- return (entry, next) => {
500
- const key = keyFn(entry);
501
- if (buffer.has(key)) {
502
- const state = buffer.get(key);
503
- state.count++;
504
- return;
505
- }
506
- const timer = setTimeout(() => {
507
- const state = buffer.get(key);
508
- if (state) {
509
- buffer.delete(key);
510
- if (state.count > 1) {
511
- state.entry.meta = {
512
- ...state.entry.meta,
513
- duplicateCount: state.count
514
- };
515
- }
516
- next(state.entry);
517
- }
518
- }, windowMs);
519
- buffer.set(key, {
520
- entry,
521
- count: 1,
522
- timer
523
- });
524
- };
525
- }
526
-
527
- // src/middleware/heap-usage.ts
528
- function heapUsageMiddleware(options = {}) {
529
- const fieldName = options.fieldName ?? "memory";
530
- return (entry, next) => {
531
- if (typeof process !== "undefined" && process.memoryUsage) {
532
- const memory = process.memoryUsage();
533
- entry.meta = {
534
- ...entry.meta,
535
- [fieldName]: {
536
- rss: memory.rss,
537
- heapTotal: memory.heapTotal,
538
- heapUsed: memory.heapUsed
539
- }
540
- };
541
- }
542
- next(entry);
543
- };
544
- }
545
-
546
- // src/middleware/http.ts
547
- var nodeHttpMappers = {
548
- req: {
549
- getMethod: (req) => req.method || "UNKNOWN",
550
- getUrl: (req) => req.originalUrl || req.url || "/",
551
- getIp: (req) => req.ip || req.socket?.remoteAddress || req.headers?.["x-forwarded-for"] || void 0,
552
- getUserAgent: (req) => req.headers?.["user-agent"] || void 0,
553
- getHeader: (req, name) => req.headers?.[name] || void 0
554
- },
555
- res: {
556
- getStatusCode: (res) => res.statusCode || 200,
557
- onFinish: (res, callback) => {
558
- if (typeof res.on === "function") {
559
- res.on("finish", callback);
560
- res.on("close", callback);
561
- } else {
562
- callback();
563
- }
564
- }
565
- }
566
- };
567
- function createHttpLogger(logger, options) {
568
- const {
569
- reqMapper,
570
- resMapper,
571
- level = "INFO",
572
- skip,
573
- extractContext
574
- } = options;
575
- return (req, res) => {
576
- if (skip?.(req)) {
577
- return;
578
- }
579
- const startTime = performance.now();
580
- let finished = false;
581
- resMapper.onFinish(res, () => {
582
- if (finished) return;
583
- finished = true;
584
- const durationMs = Math.round(performance.now() - startTime);
585
- const statusCode = resMapper.getStatusCode(res);
586
- const method = reqMapper.getMethod(req);
587
- const url = reqMapper.getUrl(req);
588
- const meta = {
589
- method,
590
- url,
591
- statusCode,
592
- durationMs,
593
- ip: reqMapper.getIp ? reqMapper.getIp(req) : void 0,
594
- userAgent: reqMapper.getUserAgent ? reqMapper.getUserAgent(req) : void 0
595
- };
596
- if (extractContext) {
597
- Object.assign(meta, extractContext(req, res));
598
- }
599
- let finalLevel = level;
600
- if (statusCode >= 500) finalLevel = "ERROR";
601
- else if (statusCode >= 400 && level === "INFO") finalLevel = "WARN";
602
- const methodKey = finalLevel.toLowerCase();
603
- logger[methodKey](
604
- `${method} ${url} - ${statusCode} (${durationMs}ms)`,
605
- meta
606
- );
607
- });
608
- };
609
- }
610
-
611
- // src/middleware/ip.ts
612
- function ipMiddleware(options = {}) {
613
- const targetField = options.fieldName ?? "ip";
614
- const keysToCheck = options.headerKeys ?? [
615
- "x-forwarded-for",
616
- "x-real-ip",
617
- "req.ip",
618
- "ip",
619
- "x-client-ip"
620
- ];
621
- return (entry, next) => {
622
- const meta = entry.meta;
623
- const headers = meta.headers || {};
624
- const req = meta.req || {};
625
- let foundIp;
626
- for (const key of keysToCheck) {
627
- if (typeof meta[key] === "string") {
628
- foundIp = meta[key];
629
- break;
630
- }
631
- if (typeof headers[key] === "string") {
632
- foundIp = headers[key];
633
- break;
634
- }
635
- if (typeof req[key] === "string") {
636
- foundIp = req[key];
637
- break;
638
- }
639
- if (key === "req.ip" && typeof req.ip === "string") {
640
- foundIp = req.ip;
641
- break;
642
- }
643
- }
644
- if (foundIp) {
645
- const firstIp = typeof foundIp === "string" ? foundIp.split(",")[0].trim() : String(foundIp);
646
- entry.meta = {
647
- ...entry.meta,
648
- [targetField]: firstIp
649
- };
650
- }
651
- next(entry);
652
- };
653
- }
654
-
655
- // src/middleware/level-override.ts
656
- function levelOverrideMiddleware(options = {}) {
657
- const key = options.key ?? "x-log-level";
658
- const cleanup = options.cleanup ?? true;
659
- return (entry, next) => {
660
- const meta = entry.meta;
661
- const context = entry.context;
662
- const levelName = meta[key] || context?.[key] || meta.headers?.[key];
663
- if (levelName && typeof levelName === "string") {
664
- const upperName = levelName.toUpperCase();
665
- if (LogLevel[upperName]) {
666
- entry.level = LogLevel[upperName];
667
- entry.levelName = upperName;
668
- if (cleanup) {
669
- delete meta[key];
670
- if (meta.headers) {
671
- delete meta.headers[key];
672
- }
673
- }
674
- }
675
- }
676
- next(entry);
677
- };
678
- }
679
-
680
- // src/middleware/ocpp.ts
681
- function ocppMiddleware(options = {}) {
682
- const autoPayloadSize = options.autoPayloadSize ?? true;
683
- const propagateCorrelationId = options.propagateCorrelationId ?? true;
684
- return (entry, next) => {
685
- const enriched = { ...entry, meta: { ...entry.meta } };
686
- if (autoPayloadSize && enriched.meta.payloadSize === void 0 && enriched.meta.action) {
687
- try {
688
- enriched.meta.payloadSize = JSON.stringify(enriched.meta).length;
689
- } catch {
690
- }
691
- }
692
- if (propagateCorrelationId && enriched.meta.correlationId && !enriched.correlationId) {
693
- enriched.correlationId = enriched.meta.correlationId;
694
- }
695
- next(enriched);
696
- };
697
- }
698
-
699
- // src/middleware/otel-trace.ts
700
- function otelTraceMiddleware(options = {}) {
701
- let traceApi = options.traceApi ?? null;
702
- let resolved = !!traceApi;
703
- if (!resolved) {
704
- try {
705
- const { createRequire } = __require("module");
706
- const dynamicRequire = createRequire(__filename);
707
- const api = dynamicRequire("@opentelemetry/api");
708
- traceApi = api;
709
- resolved = true;
710
- } catch {
711
- }
712
- }
713
- return (entry, next) => {
714
- if (resolved && traceApi?.trace) {
715
- try {
716
- const activeSpan = traceApi.trace.getActiveSpan?.();
717
- if (activeSpan) {
718
- const spanContext = activeSpan.spanContext?.();
719
- if (spanContext) {
720
- const meta = entry.meta;
721
- meta.traceId = spanContext.traceId;
722
- meta.spanId = spanContext.spanId;
723
- meta.traceFlags = spanContext.traceFlags;
724
- if (!entry.correlationId) {
725
- entry.correlationId = spanContext.traceId;
726
- }
727
- }
728
- }
729
- } catch {
730
- }
731
- }
732
- next(entry);
733
- };
734
- }
735
-
736
- // src/middleware/redaction.ts
737
- var DEFAULT_REDACT_VALUE = "[REDACTED]";
738
- function redactionMiddleware(options) {
739
- const paths = new Set(options.paths.map((p) => p.toLowerCase()));
740
- const replacement = options.replacement ?? DEFAULT_REDACT_VALUE;
741
- const deep = options.deep ?? true;
742
- function redactObject(obj) {
743
- const result = {};
744
- for (const [key, value] of Object.entries(obj)) {
745
- if (paths.has(key.toLowerCase())) {
746
- result[key] = replacement;
747
- } else if (deep && value !== null && typeof value === "object" && !Array.isArray(value)) {
748
- result[key] = redactObject(value);
749
- } else {
750
- result[key] = value;
751
- }
752
- }
753
- return result;
754
- }
755
- return (entry, next) => {
756
- const redacted = { ...entry };
757
- if (entry.meta && typeof entry.meta === "object") {
758
- redacted.meta = redactObject(
759
- entry.meta
760
- );
761
- }
762
- if (entry.context && typeof entry.context === "object") {
763
- redacted.context = redactObject(entry.context);
764
- }
765
- next(redacted);
766
- };
767
- }
768
-
769
- // src/middleware/sampling.ts
770
- function samplingMiddleware(options = {}) {
771
- const keyFn = options.keyFn ?? ((entry) => entry.message);
772
- const maxPerWindow = options.maxPerWindow ?? 100;
773
- const windowMs = options.windowMs ?? 6e4;
774
- const sampleRate = options.sampleRate ?? 1;
775
- const priorityLevel = options.priorityLevel ?? 40;
776
- const buckets = /* @__PURE__ */ new Map();
777
- return (entry, next) => {
778
- if (entry.level >= priorityLevel) {
779
- return next(entry);
780
- }
781
- if (sampleRate < 1 && Math.random() > sampleRate) {
782
- return;
783
- }
784
- const key = keyFn(entry);
785
- const now = entry.timestamp;
786
- let bucket = buckets.get(key);
787
- if (!bucket || now - bucket.windowStart >= windowMs) {
788
- bucket = { count: 0, windowStart: now };
789
- buckets.set(key, bucket);
790
- }
791
- if (bucket.count < maxPerWindow) {
792
- bucket.count++;
793
- next(entry);
794
- }
795
- if (buckets.size > 2e3) {
796
- const expireBefore = now - windowMs * 2;
797
- for (const [k, b] of buckets) {
798
- if (b.windowStart < expireBefore) {
799
- buckets.delete(k);
800
- }
801
- }
802
- }
803
- };
804
- }
805
-
806
- // src/middleware/user-agent.ts
807
- function userAgentMiddleware(options = {}) {
808
- const sourceField = options.sourceField;
809
- const targetField = options.targetField ?? "client";
810
- return (entry, next) => {
811
- const meta = entry.meta;
812
- const context = entry.context;
813
- const ua = (sourceField ? meta[sourceField] : void 0) || meta.userAgent || meta["user-agent"] || context?.userAgent || context?.["user-agent"];
814
- if (ua) {
815
- const info = parseUserAgent(ua);
816
- entry.meta = {
817
- ...entry.meta,
818
- [targetField]: info
819
- };
820
- }
821
- next(entry);
822
- };
823
- }
824
- function parseUserAgent(ua) {
825
- const browser = /(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i.exec(ua) || [];
826
- let name = browser[1] ? browser[1].toLowerCase() : "unknown";
827
- let version = browser[2] || "unknown";
828
- if (/trident/i.test(name)) {
829
- name = "ie";
830
- } else if (name === "chrome") {
831
- const edge = /edg(e)?\/(\d+)/i.exec(ua);
832
- if (edge) {
833
- name = "edge";
834
- version = edge[2];
835
- }
836
- }
837
- const osResult = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.exec(
838
- ua
839
- );
840
- const os = osResult ? osResult[0].toLowerCase() : "desktop";
841
- return { browser: name, version, os };
842
- }
843
-
844
- // src/transports/batch.ts
845
- function batchTransport(inner, options = {}) {
846
- const batchSize = options.batchSize ?? 100;
847
- const flushIntervalMs = options.flushIntervalMs ?? 5e3;
848
- let buffer = [];
849
- let flushTimer = null;
850
- function scheduleFlush() {
851
- if (flushTimer) return;
852
- flushTimer = setTimeout(() => {
853
- flushTimer = null;
854
- doFlush();
855
- }, flushIntervalMs);
856
- }
857
- function doFlush() {
858
- if (buffer.length === 0) return;
859
- const batch = buffer;
860
- buffer = [];
861
- for (const entry of batch) {
862
- try {
863
- const result = inner.write(entry);
864
- if (result && typeof result.catch === "function") {
865
- result.catch(() => {
866
- });
867
- }
868
- } catch {
869
- }
870
- }
871
- }
872
- return {
873
- name: `batch(${inner.name})`,
874
- level: inner.level,
875
- write(entry) {
876
- buffer.push(entry);
877
- if (buffer.length >= batchSize) {
878
- doFlush();
879
- } else {
880
- scheduleFlush();
881
- }
882
- },
883
- async flush() {
884
- if (flushTimer) {
885
- clearTimeout(flushTimer);
886
- flushTimer = null;
887
- }
888
- doFlush();
889
- await inner.flush?.();
890
- },
891
- async close() {
892
- await this.flush?.();
893
- await inner.close?.();
894
- }
895
- };
896
- }
897
-
898
- // src/transports/browser-json-stream.ts
899
- function browserJsonStreamTransport(options) {
900
- const stream = options.stream;
901
- const writer = stream.getWriter();
902
- const serialize = options.serializer ?? ((entry) => `${JSON.stringify(entry)}
903
- `);
904
- return {
905
- name: "browser-stream",
906
- level: options.level,
907
- async write(entry) {
908
- try {
909
- const data = serialize(entry);
910
- await writer.ready;
911
- await writer.write(data);
912
- } catch (err) {
913
- console.error("[voltlog] Failed to write to browser stream", err);
914
- }
915
- },
916
- async close() {
917
- try {
918
- await writer.close();
919
- } catch (_err) {
920
- }
921
- }
922
- };
923
- }
924
-
925
- // src/transports/console.ts
926
- function consoleTransport(options = {}) {
927
- const useConsoleLevels = options.useConsoleLevels ?? true;
928
- const formatter = options.formatter ?? ((entry) => JSON.stringify(entry));
929
- return {
930
- name: "console",
931
- level: options.level,
932
- write(entry) {
933
- const output = formatter(entry);
934
- if (!useConsoleLevels) {
935
- console.log(output);
936
- return;
937
- }
938
- if (entry.level >= LogLevel.FATAL) {
939
- console.error(output);
940
- } else if (entry.level >= LogLevel.ERROR) {
941
- console.error(output);
942
- } else if (entry.level >= LogLevel.WARN) {
943
- console.warn(output);
944
- } else if (entry.level >= LogLevel.INFO) {
945
- console.info(output);
946
- } else if (entry.level >= LogLevel.DEBUG) {
947
- console.debug(output);
948
- } else {
949
- console.log(output);
950
- }
951
- }
952
- };
953
- }
954
-
955
- // src/transports/create-transport.ts
956
- function createTransport(name, write, options = {}) {
957
- return {
958
- name,
959
- write,
960
- ...options
961
- };
962
- }
963
-
964
- // src/transports/datadog.ts
965
- function datadogTransport(options) {
966
- const {
967
- apiKey,
968
- site = "datadoghq.com",
969
- service,
970
- ddSource = "nodejs",
971
- hostname,
972
- tags,
973
- level
974
- } = options;
975
- const url = `https://http-intake.logs.${site}/api/v2/logs`;
976
- return {
977
- name: "datadog",
978
- level,
979
- async write(entry) {
980
- const payload = {
981
- ddsource: ddSource,
982
- ddtags: tags,
983
- hostname,
984
- service,
985
- message: entry.message,
986
- status: entry.levelName.toLowerCase(),
987
- // Datadog uses lowercase status
988
- ...entry.meta,
989
- timestamp: entry.timestamp
990
- // Datadog auto-parses this
991
- };
992
- try {
993
- await fetch(url, {
994
- method: "POST",
995
- headers: {
996
- "Content-Type": "application/json",
997
- "DD-API-KEY": apiKey
998
- },
999
- body: JSON.stringify(payload)
1000
- });
1001
- } catch (err) {
1002
- console.error("[voltlog] Datadog push failed", err);
1003
- }
1004
- }
1005
- };
1006
- }
1007
-
1008
- // src/transports/discord.ts
1009
- function discordTransport(options) {
1010
- const { webhookUrl, username, avatarUrl, level = "ERROR" } = options;
1011
- return {
1012
- name: "discord",
1013
- level,
1014
- async write(entry) {
1015
- try {
1016
- await fetch(webhookUrl, {
1017
- method: "POST",
1018
- headers: { "Content-Type": "application/json" },
1019
- body: JSON.stringify(
1020
- formatDiscordPayload(entry, username, avatarUrl)
1021
- )
1022
- });
1023
- } catch (_err) {
1024
- }
1025
- }
1026
- };
1027
- }
1028
- function formatDiscordPayload(entry, username, avatar_url) {
1029
- const color = getLevelColor(entry.level);
1030
- return {
1031
- username: username || "VoltLog",
1032
- avatar_url,
1033
- embeds: [
1034
- {
1035
- title: `${entry.levelName} - ${entry.message}`,
1036
- color,
1037
- timestamp: new Date(entry.timestamp).toISOString(),
1038
- fields: [
1039
- {
1040
- name: "Meta",
1041
- value: `\`\`\`json
1042
- ${JSON.stringify(entry.meta, null, 2).slice(
1043
- 0,
1044
- 1e3
1045
- )}
1046
- \`\`\``
1047
- },
1048
- entry.error?.stack ? {
1049
- name: "Stack",
1050
- value: `\`\`\`js
1051
- ${entry.error.stack.slice(0, 1e3)}
1052
- \`\`\``
1053
- } : null
1054
- ].filter(Boolean)
1055
- }
1056
- ]
1057
- };
1058
- }
1059
- function getLevelColor(level) {
1060
- if (level >= 50) return 15158332;
1061
- if (level >= 40) return 16776960;
1062
- if (level >= 30) return 3447003;
1063
- return 9807270;
1064
- }
1065
-
1066
99
  // src/transports/file.ts
1067
100
  import fs from "fs";
1068
- import path2 from "path";
101
+ import path from "path";
1069
102
  function fileTransport(options) {
1070
103
  const { dir, level, maxSize } = options;
1071
104
  const filenamePattern = options.filename ?? "app-%DATE%.log";
@@ -1092,11 +125,11 @@ function fileTransport(options) {
1092
125
  const dateStr = getCachedDate();
1093
126
  const filename = filenamePattern.replace("%DATE%", dateStr);
1094
127
  if (sizeRotation && rotationIndex > 0) {
1095
- const ext = path2.extname(filename);
128
+ const ext = path.extname(filename);
1096
129
  const base = filename.slice(0, -ext.length || void 0);
1097
- return path2.join(dir, `${base}.${rotationIndex}${ext}`);
130
+ return path.join(dir, `${base}.${rotationIndex}${ext}`);
1098
131
  }
1099
- return path2.join(dir, filename);
132
+ return path.join(dir, filename);
1100
133
  }
1101
134
  function openStream(filePath) {
1102
135
  if (currentStream) {
@@ -1174,431 +207,6 @@ function jsonStreamTransport(options) {
1174
207
  };
1175
208
  }
1176
209
 
1177
- // src/transports/loki.ts
1178
- function lokiTransport(options) {
1179
- const { host, level } = options;
1180
- const staticLabels = options.labels ?? { app: "voltlog" };
1181
- const batchSize = options.batchSize ?? 10;
1182
- const interval = options.interval ?? 5e3;
1183
- const includeMetadata = options.includeMetadata !== false;
1184
- const retryEnabled = options.retry ?? false;
1185
- const maxRetries = options.maxRetries ?? 3;
1186
- const url = `${host.replace(/\/$/, "")}/loki/api/v1/push`;
1187
- const headers = {
1188
- "Content-Type": "application/json"
1189
- };
1190
- if (options.basicAuthUser && options.basicAuthPassword) {
1191
- const creds = btoa(`${options.basicAuthUser}:${options.basicAuthPassword}`);
1192
- headers.Authorization = `Basic ${creds}`;
1193
- }
1194
- if (options.tenantId) {
1195
- headers["X-Scope-OrgID"] = options.tenantId;
1196
- }
1197
- let buffer = [];
1198
- let timer = null;
1199
- function buildLogLine(entry) {
1200
- const payload = {
1201
- level: entry.levelName,
1202
- message: entry.message,
1203
- ...entry.meta
1204
- };
1205
- if (includeMetadata) {
1206
- if (entry.correlationId) {
1207
- payload.correlationId = entry.correlationId;
1208
- }
1209
- if (entry.context) {
1210
- payload.context = entry.context;
1211
- }
1212
- if (entry.error) {
1213
- payload.error = entry.error;
1214
- }
1215
- }
1216
- return JSON.stringify(payload);
1217
- }
1218
- function buildStreams(batch) {
1219
- if (!options.dynamicLabels) {
1220
- return [
1221
- {
1222
- stream: staticLabels,
1223
- values: batch.map((e) => [
1224
- String(e.timestamp * 1e6),
1225
- // Loki wants nanoseconds
1226
- buildLogLine(e)
1227
- ])
1228
- }
1229
- ];
1230
- }
1231
- const grouped = /* @__PURE__ */ new Map();
1232
- for (const entry of batch) {
1233
- const dynamic = options.dynamicLabels(entry);
1234
- const merged = { ...staticLabels };
1235
- for (const [k, v] of Object.entries(dynamic)) {
1236
- if (v !== void 0) merged[k] = v;
1237
- }
1238
- const key = JSON.stringify(merged);
1239
- let group = grouped.get(key);
1240
- if (!group) {
1241
- group = { labels: merged, values: [] };
1242
- grouped.set(key, group);
1243
- }
1244
- group.values.push([
1245
- String(entry.timestamp * 1e6),
1246
- buildLogLine(entry)
1247
- ]);
1248
- }
1249
- return Array.from(grouped.values()).map((g) => ({
1250
- stream: g.labels,
1251
- values: g.values
1252
- }));
1253
- }
1254
- async function pushWithRetry(batch) {
1255
- const body = JSON.stringify({ streams: buildStreams(batch) });
1256
- let lastError;
1257
- const attempts = retryEnabled ? maxRetries : 1;
1258
- for (let attempt = 0; attempt < attempts; attempt++) {
1259
- try {
1260
- const response = await fetch(url, { method: "POST", headers, body });
1261
- if (response.ok) return;
1262
- if (response.status >= 400 && response.status < 500) {
1263
- console.error(`[voltlog] Loki push failed: ${response.status}`);
1264
- return;
1265
- }
1266
- lastError = new Error(`Loki HTTP ${response.status}`);
1267
- } catch (err) {
1268
- lastError = err;
1269
- }
1270
- if (attempt < attempts - 1) {
1271
- await new Promise((r) => setTimeout(r, 100 * 2 ** attempt));
1272
- }
1273
- }
1274
- console.error("[voltlog] Loki push failed after retries", lastError);
1275
- }
1276
- const doFlush = async () => {
1277
- if (buffer.length === 0) return;
1278
- const batch = buffer;
1279
- buffer = [];
1280
- await pushWithRetry(batch);
1281
- };
1282
- const schedule = () => {
1283
- if (!timer) {
1284
- timer = setTimeout(() => {
1285
- timer = null;
1286
- doFlush();
1287
- }, interval);
1288
- }
1289
- };
1290
- return {
1291
- name: "loki",
1292
- level,
1293
- write(entry) {
1294
- buffer.push(entry);
1295
- if (buffer.length >= batchSize) {
1296
- if (timer) clearTimeout(timer);
1297
- timer = null;
1298
- doFlush();
1299
- } else {
1300
- schedule();
1301
- }
1302
- },
1303
- async flush() {
1304
- if (timer) clearTimeout(timer);
1305
- await doFlush();
1306
- },
1307
- async close() {
1308
- await this.flush?.();
1309
- }
1310
- };
1311
- }
1312
-
1313
- // src/transports/otel.ts
1314
- var OTEL_SEVERITY_MAP = {
1315
- TRACE: { number: 1, text: "TRACE" },
1316
- DEBUG: { number: 5, text: "DEBUG" },
1317
- INFO: { number: 9, text: "INFO" },
1318
- WARN: { number: 13, text: "WARN" },
1319
- ERROR: { number: 17, text: "ERROR" },
1320
- FATAL: { number: 21, text: "FATAL" }
1321
- };
1322
- function otelTransport(options) {
1323
- const { endpoint, serviceName, level, resource = {} } = options;
1324
- const batchSize = options.batchSize ?? 20;
1325
- const interval = options.interval ?? 5e3;
1326
- const url = `${endpoint.replace(/\/$/, "")}/v1/logs`;
1327
- const headers = {
1328
- "Content-Type": "application/json",
1329
- ...options.headers
1330
- };
1331
- const resourceAttributes = [
1332
- { key: "service.name", value: { stringValue: serviceName } },
1333
- ...Object.entries(resource).map(([key, val]) => ({
1334
- key,
1335
- value: { stringValue: val }
1336
- }))
1337
- ];
1338
- let buffer = [];
1339
- let timer = null;
1340
- function toOtlpLogRecord(entry) {
1341
- const severity = OTEL_SEVERITY_MAP[entry.levelName] ?? OTEL_SEVERITY_MAP.INFO;
1342
- const attributes = [];
1343
- if (entry.meta && typeof entry.meta === "object") {
1344
- for (const [key, val] of Object.entries(entry.meta)) {
1345
- if (key === "traceId" || key === "spanId" || key === "traceFlags")
1346
- continue;
1347
- if (val !== void 0 && val !== null) {
1348
- attributes.push({
1349
- key,
1350
- value: typeof val === "number" ? { intValue: val } : { stringValue: String(val) }
1351
- });
1352
- }
1353
- }
1354
- }
1355
- if (entry.context) {
1356
- for (const [key, val] of Object.entries(entry.context)) {
1357
- if (val !== void 0 && val !== null) {
1358
- attributes.push({
1359
- key: `context.${key}`,
1360
- value: { stringValue: String(val) }
1361
- });
1362
- }
1363
- }
1364
- }
1365
- if (entry.error) {
1366
- attributes.push({
1367
- key: "error.message",
1368
- value: { stringValue: entry.error.message }
1369
- });
1370
- if (entry.error.name) {
1371
- attributes.push({
1372
- key: "error.type",
1373
- value: { stringValue: entry.error.name }
1374
- });
1375
- }
1376
- if (entry.error.stack) {
1377
- attributes.push({
1378
- key: "error.stack",
1379
- value: { stringValue: entry.error.stack }
1380
- });
1381
- }
1382
- }
1383
- const record = {
1384
- timeUnixNano: String(entry.timestamp * 1e6),
1385
- // ms → ns
1386
- severityNumber: severity?.number,
1387
- severityText: severity?.text,
1388
- body: { stringValue: entry.message },
1389
- attributes
1390
- };
1391
- const meta = entry.meta;
1392
- if (meta?.traceId) {
1393
- record.traceId = meta.traceId;
1394
- }
1395
- if (meta?.spanId) {
1396
- record.spanId = meta.spanId;
1397
- }
1398
- if (meta?.traceFlags !== void 0) {
1399
- record.flags = meta.traceFlags;
1400
- }
1401
- return record;
1402
- }
1403
- async function sendBatch(batch) {
1404
- const payload = {
1405
- resourceLogs: [
1406
- {
1407
- resource: { attributes: resourceAttributes },
1408
- scopeLogs: [
1409
- {
1410
- scope: { name: "voltlog-io" },
1411
- logRecords: batch.map(toOtlpLogRecord)
1412
- }
1413
- ]
1414
- }
1415
- ]
1416
- };
1417
- try {
1418
- const response = await fetch(url, {
1419
- method: "POST",
1420
- headers,
1421
- body: JSON.stringify(payload)
1422
- });
1423
- if (!response.ok) {
1424
- console.error(
1425
- `[voltlog] OTLP push failed: ${response.status} ${response.statusText}`
1426
- );
1427
- }
1428
- } catch (err) {
1429
- console.error("[voltlog] OTLP push failed", err);
1430
- }
1431
- }
1432
- function flush() {
1433
- if (buffer.length === 0) return;
1434
- const batch = buffer;
1435
- buffer = [];
1436
- sendBatch(batch).catch(() => {
1437
- });
1438
- }
1439
- function schedule() {
1440
- if (!timer) {
1441
- timer = setTimeout(() => {
1442
- timer = null;
1443
- flush();
1444
- }, interval);
1445
- }
1446
- }
1447
- return {
1448
- name: "otel",
1449
- level,
1450
- write(entry) {
1451
- buffer.push(entry);
1452
- if (buffer.length >= batchSize) {
1453
- if (timer) clearTimeout(timer);
1454
- timer = null;
1455
- flush();
1456
- } else {
1457
- schedule();
1458
- }
1459
- },
1460
- async flush() {
1461
- if (timer) clearTimeout(timer);
1462
- timer = null;
1463
- if (buffer.length === 0) return;
1464
- const batch = buffer;
1465
- buffer = [];
1466
- await sendBatch(batch);
1467
- },
1468
- async close() {
1469
- await this.flush?.();
1470
- }
1471
- };
1472
- }
1473
-
1474
- // src/transports/pretty.ts
1475
- var RESET = "\x1B[0m";
1476
- var DIM = "\x1B[2m";
1477
- var BOLD = "\x1B[1m";
1478
- var COLORS = {
1479
- TRACE: "\x1B[90m",
1480
- // gray
1481
- DEBUG: "\x1B[36m",
1482
- // cyan
1483
- INFO: "\x1B[32m",
1484
- // green
1485
- WARN: "\x1B[33m",
1486
- // yellow
1487
- ERROR: "\x1B[31m",
1488
- // red
1489
- FATAL: "\x1B[35;1m"
1490
- // bold magenta
1491
- };
1492
- var ICONS = {
1493
- TRACE: "\u{1F50D}",
1494
- DEBUG: "\u{1F41B}",
1495
- INFO: "\u2139",
1496
- WARN: "\u26A0",
1497
- ERROR: "\u2716",
1498
- FATAL: "\u{1F480}"
1499
- };
1500
- var EXCHANGE_ICONS = {
1501
- CALL: "\u26A1",
1502
- CALLRESULT: "\u2714",
1503
- CALLERROR: "\u{1F6A8}"
1504
- };
1505
- var DIRECTION_ARROWS = {
1506
- IN: "\u2192",
1507
- OUT: "\u2190"
1508
- };
1509
- function prettyTransport(options = {}) {
1510
- const showTimestamps = options.timestamps ?? true;
1511
- const useColors = options.colors ?? true;
1512
- const hideMeta = options.hideMeta ?? false;
1513
- const prettyMeta = options.prettyMeta ?? false;
1514
- function colorize(text, color) {
1515
- return useColors ? `${color}${text}${RESET}` : text;
1516
- }
1517
- function formatExchange(entry) {
1518
- const meta = entry.meta;
1519
- if (!meta || !meta.action || !meta.messageType) return null;
1520
- const icon = EXCHANGE_ICONS[meta.messageType] ?? "\u2022";
1521
- const arrow = DIRECTION_ARROWS[meta.direction ?? "IN"] ?? "\u2192";
1522
- const cpId = meta.chargePointId ?? "unknown";
1523
- const action = meta.action;
1524
- const msgType = meta.messageType;
1525
- const dir = meta.direction ?? "";
1526
- let line = `${icon} ${colorize(cpId, BOLD)} ${arrow} ${colorize(
1527
- action,
1528
- BOLD
1529
- )} [${dir}] ${colorize(msgType, DIM)}`;
1530
- if (meta.status || meta.latencyMs !== void 0) {
1531
- const statusIcon = meta.messageType === "CALLERROR" ? "\u274C" : "\u2714";
1532
- const status = meta.status ?? "";
1533
- const latency = meta.latencyMs !== void 0 ? `(${meta.latencyMs}ms)` : "";
1534
- line += `
1535
- ${statusIcon} ${status} ${colorize(latency, DIM)}`;
1536
- }
1537
- return line;
1538
- }
1539
- function formatStandard(entry) {
1540
- const icon = ICONS[entry.levelName] ?? "\u2022";
1541
- const levelColor = COLORS[entry.levelName] ?? "";
1542
- const level = colorize(entry.levelName.padEnd(5), levelColor);
1543
- const ts = showTimestamps ? `${colorize(new Date(entry.timestamp).toISOString(), DIM)} ` : "";
1544
- let line = `${icon} ${ts}${level} ${entry.message}`;
1545
- if (entry.context && Object.keys(entry.context).length > 0) {
1546
- const kvStr = Object.entries(entry.context).map(
1547
- ([k, v]) => `${k}:${typeof v === "string" ? v : JSON.stringify(v)}`
1548
- ).join(" ");
1549
- line += ` ${colorize(kvStr, DIM)}`;
1550
- }
1551
- if (!hideMeta && entry.meta && Object.keys(entry.meta).length > 0) {
1552
- if (prettyMeta) {
1553
- const metaEntries = Object.entries(
1554
- entry.meta
1555
- );
1556
- const parts = metaEntries.map(([key, value]) => {
1557
- const valStr = typeof value === "string" ? value : JSON.stringify(value);
1558
- return `${colorize(`${key}:`, DIM)}${valStr}`;
1559
- });
1560
- line += ` ${parts.join(" ")}`;
1561
- } else {
1562
- const kvStr = Object.entries(entry.meta).map(
1563
- ([k, v]) => `${k}:${typeof v === "string" ? v : JSON.stringify(v)}`
1564
- ).join(" ");
1565
- line += ` ${colorize(kvStr, DIM)}`;
1566
- }
1567
- }
1568
- if (entry.error) {
1569
- line += `
1570
- ${colorize(
1571
- `${entry.error.name ?? "Error"}: ${entry.error.message}`,
1572
- COLORS.ERROR ?? ""
1573
- )}`;
1574
- if (entry.error.stack) {
1575
- line += `
1576
- ${colorize(entry.error.stack, DIM)}`;
1577
- }
1578
- }
1579
- return line;
1580
- }
1581
- return {
1582
- name: "pretty",
1583
- level: options.level,
1584
- write(entry) {
1585
- const exchangeOutput = formatExchange(entry);
1586
- if (exchangeOutput) {
1587
- console.log(exchangeOutput);
1588
- return;
1589
- }
1590
- const output = formatStandard(entry);
1591
- if (entry.level >= LogLevel.ERROR) {
1592
- console.error(output);
1593
- } else if (entry.level >= LogLevel.WARN) {
1594
- console.warn(output);
1595
- } else {
1596
- console.log(output);
1597
- }
1598
- }
1599
- };
1600
- }
1601
-
1602
210
  // src/transports/redis.ts
1603
211
  function redisTransport(options) {
1604
212
  const { client, streamKey = "logs", maxLen, level } = options;
@@ -1638,283 +246,6 @@ function redisTransport(options) {
1638
246
  }
1639
247
  };
1640
248
  }
1641
-
1642
- // src/transports/ring-buffer.ts
1643
- function ringBufferTransport(options = {}) {
1644
- const maxSize = options.maxSize ?? 1e3;
1645
- const buffer = [];
1646
- let head = 0;
1647
- let count = 0;
1648
- return {
1649
- name: "ring-buffer",
1650
- level: options.level,
1651
- write(entry) {
1652
- if (count < maxSize) {
1653
- buffer.push(entry);
1654
- count++;
1655
- } else {
1656
- buffer[head] = entry;
1657
- }
1658
- head = (head + 1) % maxSize;
1659
- },
1660
- getEntries(query) {
1661
- let entries;
1662
- if (count < maxSize) {
1663
- entries = buffer.slice();
1664
- } else {
1665
- entries = [...buffer.slice(head), ...buffer.slice(0, head)];
1666
- }
1667
- if (query?.level) {
1668
- const minLevel = resolveLevel(query.level);
1669
- entries = entries.filter((e) => e.level >= minLevel);
1670
- }
1671
- if (query?.since) {
1672
- const since = query.since;
1673
- entries = entries.filter((e) => e.timestamp >= since);
1674
- }
1675
- if (query?.limit) {
1676
- entries = entries.slice(-query.limit);
1677
- }
1678
- return entries;
1679
- },
1680
- clear() {
1681
- buffer.length = 0;
1682
- head = 0;
1683
- count = 0;
1684
- },
1685
- get size() {
1686
- return count;
1687
- }
1688
- };
1689
- }
1690
-
1691
- // src/transports/sentry.ts
1692
- function sentryTransport(options) {
1693
- const { sentry } = options;
1694
- const errorLevelValue = resolveLevel(options.errorLevel ?? "ERROR");
1695
- const breadcrumbLevelValue = resolveLevel(options.breadcrumbLevel ?? "INFO");
1696
- return {
1697
- name: "sentry",
1698
- write(entry) {
1699
- if (entry.level >= errorLevelValue) {
1700
- if (entry.error) {
1701
- sentry.captureException(entry.error, {
1702
- extra: {
1703
- ...entry.meta,
1704
- context: entry.context
1705
- },
1706
- level: "error"
1707
- });
1708
- } else {
1709
- sentry.captureMessage(entry.message, "error");
1710
- }
1711
- }
1712
- if (entry.level >= breadcrumbLevelValue) {
1713
- sentry.addBreadcrumb({
1714
- category: "log",
1715
- message: entry.message,
1716
- level: mapLevelToSentry(entry.level),
1717
- data: { ...entry.meta, ...entry.context },
1718
- timestamp: entry.timestamp / 1e3
1719
- });
1720
- }
1721
- }
1722
- };
1723
- }
1724
- function mapLevelToSentry(level) {
1725
- if (level >= 60) return "fatal";
1726
- if (level >= 50) return "error";
1727
- if (level >= 40) return "warning";
1728
- if (level >= 30) return "info";
1729
- return "debug";
1730
- }
1731
-
1732
- // src/transports/slack.ts
1733
- function slackTransport(options) {
1734
- const { webhookUrl, username, iconEmoji, level } = options;
1735
- return {
1736
- name: "slack",
1737
- level: level ?? "ERROR",
1738
- // Default to ERROR to prevent spamming
1739
- async write(entry) {
1740
- try {
1741
- const payload = formatSlackMessage(entry, username, iconEmoji);
1742
- const response = await fetch(webhookUrl, {
1743
- method: "POST",
1744
- headers: { "Content-Type": "application/json" },
1745
- body: JSON.stringify(payload)
1746
- });
1747
- if (!response.ok) {
1748
- }
1749
- } catch (_err) {
1750
- }
1751
- }
1752
- };
1753
- }
1754
- function formatSlackMessage(entry, username, icon_emoji) {
1755
- const levelEmoji = getLevelEmoji(entry.level);
1756
- const color = getLevelColor2(entry.level);
1757
- const blocks = [
1758
- {
1759
- type: "header",
1760
- text: {
1761
- type: "plain_text",
1762
- text: `${levelEmoji} ${entry.levelName}: ${entry.message}`,
1763
- emoji: true
1764
- }
1765
- },
1766
- {
1767
- type: "context",
1768
- elements: [
1769
- {
1770
- type: "mrkdwn",
1771
- text: `*Time:* ${new Date(entry.timestamp).toISOString()}`
1772
- },
1773
- {
1774
- type: "mrkdwn",
1775
- text: `*ID:* \`${entry.id}\``
1776
- }
1777
- ]
1778
- }
1779
- ];
1780
- if (entry.correlationId) {
1781
- blocks[1].elements.push({
1782
- type: "mrkdwn",
1783
- text: `*Trace:* \`${entry.correlationId}\``
1784
- });
1785
- }
1786
- if (Object.keys(entry.meta).length > 0) {
1787
- blocks.push({
1788
- type: "section",
1789
- text: {
1790
- type: "mrkdwn",
1791
- text: `*Metadata:*
1792
- \`\`\`${JSON.stringify(entry.meta, null, 2)}\`\`\``,
1793
- emoji: true
1794
- }
1795
- });
1796
- }
1797
- if (entry.error?.stack) {
1798
- blocks.push({
1799
- type: "section",
1800
- text: {
1801
- type: "mrkdwn",
1802
- text: `*Error Stack:*
1803
- \`\`\`${entry.error.stack}\`\`\``,
1804
- emoji: true
1805
- }
1806
- });
1807
- }
1808
- return {
1809
- username,
1810
- icon_emoji,
1811
- attachments: [
1812
- {
1813
- color,
1814
- blocks
1815
- }
1816
- ]
1817
- };
1818
- }
1819
- function getLevelEmoji(level) {
1820
- if (level >= 60) return "\u{1F525}";
1821
- if (level >= 50) return "\u{1F6A8}";
1822
- if (level >= 40) return "\u26A0\uFE0F";
1823
- if (level >= 30) return "\u2139\uFE0F";
1824
- if (level >= 20) return "\u{1F41B}";
1825
- return "\u{1F50D}";
1826
- }
1827
- function getLevelColor2(level) {
1828
- if (level >= 60) return "#ff0000";
1829
- if (level >= 50) return "#ff4444";
1830
- if (level >= 40) return "#ffbb33";
1831
- if (level >= 30) return "#33b5e5";
1832
- if (level >= 20) return "#99cc00";
1833
- return "#aa66cc";
1834
- }
1835
-
1836
- // src/transports/webhook.ts
1837
- function webhookTransport(options) {
1838
- const {
1839
- url,
1840
- method = "POST",
1841
- headers = {},
1842
- batchSize = 1,
1843
- flushIntervalMs = 5e3,
1844
- retry = false,
1845
- maxRetries = 3
1846
- } = options;
1847
- const serialize = options.serializer ?? ((entries) => JSON.stringify({
1848
- entries,
1849
- count: entries.length,
1850
- timestamp: Date.now()
1851
- }));
1852
- let buffer = [];
1853
- let flushTimer = null;
1854
- async function sendBatch(entries, attempt = 0) {
1855
- try {
1856
- const response = await fetch(url, {
1857
- method,
1858
- headers: {
1859
- "Content-Type": "application/json",
1860
- ...headers
1861
- },
1862
- body: serialize(entries)
1863
- });
1864
- if (!response.ok && retry && attempt < maxRetries) {
1865
- const delay = Math.min(1e3 * 2 ** attempt, 3e4);
1866
- await new Promise((r) => setTimeout(r, delay));
1867
- return sendBatch(entries, attempt + 1);
1868
- }
1869
- } catch {
1870
- if (retry && attempt < maxRetries) {
1871
- const delay = Math.min(1e3 * 2 ** attempt, 3e4);
1872
- await new Promise((r) => setTimeout(r, delay));
1873
- return sendBatch(entries, attempt + 1);
1874
- }
1875
- }
1876
- }
1877
- function scheduleFlush() {
1878
- if (flushTimer) return;
1879
- flushTimer = setTimeout(() => {
1880
- flushTimer = null;
1881
- doFlush();
1882
- }, flushIntervalMs);
1883
- }
1884
- function doFlush() {
1885
- if (buffer.length === 0) return;
1886
- const batch = buffer;
1887
- buffer = [];
1888
- sendBatch(batch).catch(() => {
1889
- });
1890
- }
1891
- return {
1892
- name: "webhook",
1893
- level: options.level,
1894
- write(entry) {
1895
- buffer.push(entry);
1896
- if (buffer.length >= batchSize) {
1897
- doFlush();
1898
- } else {
1899
- scheduleFlush();
1900
- }
1901
- },
1902
- async flush() {
1903
- if (flushTimer) {
1904
- clearTimeout(flushTimer);
1905
- flushTimer = null;
1906
- }
1907
- if (buffer.length > 0) {
1908
- const batch = buffer;
1909
- buffer = [];
1910
- await sendBatch(batch);
1911
- }
1912
- },
1913
- async close() {
1914
- await this.flush?.();
1915
- }
1916
- };
1917
- }
1918
249
  export {
1919
250
  LogLevel,
1920
251
  LogLevelNameMap,