@oxog/log 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +297 -0
- package/dist/index.cjs +1893 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +1784 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins/index.cjs +854 -0
- package/dist/plugins/index.cjs.map +1 -0
- package/dist/plugins/index.js +782 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/transports/index.cjs +853 -0
- package/dist/transports/index.cjs.map +1 -0
- package/dist/transports/index.js +809 -0
- package/dist/transports/index.js.map +1 -0
- package/package.json +96 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1784 @@
|
|
|
1
|
+
// src/logger.ts
|
|
2
|
+
import { createKernel } from "@oxog/plugin";
|
|
3
|
+
import { createEmitter } from "@oxog/emitter";
|
|
4
|
+
import { pigment } from "@oxog/pigment";
|
|
5
|
+
|
|
6
|
+
// src/types.ts
|
|
7
|
+
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
8
|
+
LogLevel2[LogLevel2["Trace"] = 10] = "Trace";
|
|
9
|
+
LogLevel2[LogLevel2["Debug"] = 20] = "Debug";
|
|
10
|
+
LogLevel2[LogLevel2["Info"] = 30] = "Info";
|
|
11
|
+
LogLevel2[LogLevel2["Warn"] = 40] = "Warn";
|
|
12
|
+
LogLevel2[LogLevel2["Error"] = 50] = "Error";
|
|
13
|
+
LogLevel2[LogLevel2["Fatal"] = 60] = "Fatal";
|
|
14
|
+
return LogLevel2;
|
|
15
|
+
})(LogLevel || {});
|
|
16
|
+
|
|
17
|
+
// src/constants.ts
|
|
18
|
+
var LOG_LEVELS = {
|
|
19
|
+
trace: 10 /* Trace */,
|
|
20
|
+
debug: 20 /* Debug */,
|
|
21
|
+
info: 30 /* Info */,
|
|
22
|
+
warn: 40 /* Warn */,
|
|
23
|
+
error: 50 /* Error */,
|
|
24
|
+
fatal: 60 /* Fatal */
|
|
25
|
+
};
|
|
26
|
+
var LEVEL_NAMES = {
|
|
27
|
+
[10 /* Trace */]: "trace",
|
|
28
|
+
[20 /* Debug */]: "debug",
|
|
29
|
+
[30 /* Info */]: "info",
|
|
30
|
+
[40 /* Warn */]: "warn",
|
|
31
|
+
[50 /* Error */]: "error",
|
|
32
|
+
[60 /* Fatal */]: "fatal"
|
|
33
|
+
};
|
|
34
|
+
var LEVEL_ORDER = [
|
|
35
|
+
"trace",
|
|
36
|
+
"debug",
|
|
37
|
+
"info",
|
|
38
|
+
"warn",
|
|
39
|
+
"error",
|
|
40
|
+
"fatal"
|
|
41
|
+
];
|
|
42
|
+
var DEFAULT_NAME = "app";
|
|
43
|
+
var DEFAULT_LEVEL = "info";
|
|
44
|
+
var DEFAULT_FORMAT = "auto";
|
|
45
|
+
var DEFAULT_COLORS = true;
|
|
46
|
+
var DEFAULT_TIMESTAMP = true;
|
|
47
|
+
var DEFAULT_SOURCE = false;
|
|
48
|
+
var DEFAULT_SYNC_LEVELS = {
|
|
49
|
+
trace: false,
|
|
50
|
+
debug: false,
|
|
51
|
+
info: false,
|
|
52
|
+
warn: false,
|
|
53
|
+
error: true,
|
|
54
|
+
fatal: true
|
|
55
|
+
};
|
|
56
|
+
var DEFAULT_BUFFER_OPTIONS = {
|
|
57
|
+
size: 100,
|
|
58
|
+
flushInterval: 1e3
|
|
59
|
+
};
|
|
60
|
+
var LEVEL_COLORS = {
|
|
61
|
+
trace: "gray",
|
|
62
|
+
debug: "cyan",
|
|
63
|
+
info: "blue",
|
|
64
|
+
warn: "yellow",
|
|
65
|
+
error: "red",
|
|
66
|
+
fatal: "magenta"
|
|
67
|
+
};
|
|
68
|
+
var LEVEL_LABELS = {
|
|
69
|
+
trace: "TRACE",
|
|
70
|
+
debug: "DEBUG",
|
|
71
|
+
info: "INFO ",
|
|
72
|
+
warn: "WARN ",
|
|
73
|
+
error: "ERROR",
|
|
74
|
+
fatal: "FATAL"
|
|
75
|
+
};
|
|
76
|
+
var REDACTED_VALUE = "[REDACTED]";
|
|
77
|
+
var COMMON_SENSITIVE_FIELDS = [
|
|
78
|
+
"password",
|
|
79
|
+
"token",
|
|
80
|
+
"secret",
|
|
81
|
+
"apiKey",
|
|
82
|
+
"api_key",
|
|
83
|
+
"apikey",
|
|
84
|
+
"authorization",
|
|
85
|
+
"auth",
|
|
86
|
+
"credential",
|
|
87
|
+
"credentials",
|
|
88
|
+
"ssn",
|
|
89
|
+
"creditCard",
|
|
90
|
+
"credit_card",
|
|
91
|
+
"creditcard",
|
|
92
|
+
"cvv",
|
|
93
|
+
"pin"
|
|
94
|
+
];
|
|
95
|
+
function parseSize(size) {
|
|
96
|
+
const match = size.match(/^(\d+(?:\.\d+)?)\s*(B|KB|MB|GB|TB)?$/i);
|
|
97
|
+
if (!match) {
|
|
98
|
+
throw new Error(`Invalid size format: ${size}`);
|
|
99
|
+
}
|
|
100
|
+
const value = parseFloat(match[1]);
|
|
101
|
+
const unit = (match[2] || "B").toUpperCase();
|
|
102
|
+
const units = {
|
|
103
|
+
B: 1,
|
|
104
|
+
KB: 1024,
|
|
105
|
+
MB: 1024 * 1024,
|
|
106
|
+
GB: 1024 * 1024 * 1024,
|
|
107
|
+
TB: 1024 * 1024 * 1024 * 1024
|
|
108
|
+
};
|
|
109
|
+
return Math.floor(value * (units[unit] ?? 1));
|
|
110
|
+
}
|
|
111
|
+
function parseRotation(rotation) {
|
|
112
|
+
const match = rotation.match(/^(\d+)\s*(s|m|h|d|w)?$/i);
|
|
113
|
+
if (!match) {
|
|
114
|
+
throw new Error(`Invalid rotation format: ${rotation}`);
|
|
115
|
+
}
|
|
116
|
+
const value = parseInt(match[1], 10);
|
|
117
|
+
const unit = (match[2] || "s").toLowerCase();
|
|
118
|
+
const units = {
|
|
119
|
+
s: 1e3,
|
|
120
|
+
m: 60 * 1e3,
|
|
121
|
+
h: 60 * 60 * 1e3,
|
|
122
|
+
d: 24 * 60 * 60 * 1e3,
|
|
123
|
+
w: 7 * 24 * 60 * 60 * 1e3
|
|
124
|
+
};
|
|
125
|
+
return value * (units[unit] ?? 1e3);
|
|
126
|
+
}
|
|
127
|
+
var IS_NODE = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
|
|
128
|
+
var IS_BROWSER = typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
129
|
+
var IS_DEV = IS_NODE && process.env["NODE_ENV"] !== "production";
|
|
130
|
+
var IS_TTY = IS_NODE && process.stdout?.isTTY === true;
|
|
131
|
+
|
|
132
|
+
// src/utils/env.ts
|
|
133
|
+
function isNode() {
|
|
134
|
+
return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
|
|
135
|
+
}
|
|
136
|
+
function isBrowser() {
|
|
137
|
+
return typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
138
|
+
}
|
|
139
|
+
function isDev() {
|
|
140
|
+
if (!isNode()) return false;
|
|
141
|
+
return process.env["NODE_ENV"] !== "production";
|
|
142
|
+
}
|
|
143
|
+
function isTTY() {
|
|
144
|
+
if (!isNode()) return false;
|
|
145
|
+
return process.stdout?.isTTY === true;
|
|
146
|
+
}
|
|
147
|
+
function shouldUseColors() {
|
|
148
|
+
if (isBrowser()) return true;
|
|
149
|
+
if (!isNode()) return false;
|
|
150
|
+
if (process.env["NO_COLOR"] !== void 0) return false;
|
|
151
|
+
if (process.env["FORCE_COLOR"] !== void 0) return true;
|
|
152
|
+
if (process.env["CI"] !== void 0) return true;
|
|
153
|
+
return isTTY();
|
|
154
|
+
}
|
|
155
|
+
function getEnvironment() {
|
|
156
|
+
if (!isNode()) return "browser";
|
|
157
|
+
return process.env["NODE_ENV"] || "development";
|
|
158
|
+
}
|
|
159
|
+
function getCwd() {
|
|
160
|
+
if (!isNode()) return void 0;
|
|
161
|
+
return process.cwd();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/utils/format.ts
|
|
165
|
+
function formatJson(entry) {
|
|
166
|
+
return JSON.stringify(entry, jsonReplacer);
|
|
167
|
+
}
|
|
168
|
+
function jsonReplacer(_key, value) {
|
|
169
|
+
if (typeof value === "bigint") {
|
|
170
|
+
return value.toString();
|
|
171
|
+
}
|
|
172
|
+
if (value instanceof Error) {
|
|
173
|
+
const errorObj = value;
|
|
174
|
+
const result = {
|
|
175
|
+
name: value.name,
|
|
176
|
+
message: value.message,
|
|
177
|
+
stack: value.stack
|
|
178
|
+
};
|
|
179
|
+
for (const key of Object.keys(errorObj)) {
|
|
180
|
+
if (!(key in result)) {
|
|
181
|
+
result[key] = errorObj[key];
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
if (typeof value === "object" && value !== null) {
|
|
187
|
+
if (value instanceof RegExp) {
|
|
188
|
+
return value.toString();
|
|
189
|
+
}
|
|
190
|
+
if (value instanceof Date) {
|
|
191
|
+
return value.toISOString();
|
|
192
|
+
}
|
|
193
|
+
if (value instanceof Map) {
|
|
194
|
+
return Object.fromEntries(value);
|
|
195
|
+
}
|
|
196
|
+
if (value instanceof Set) {
|
|
197
|
+
return Array.from(value);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return value;
|
|
201
|
+
}
|
|
202
|
+
function safeStringify(value, indent2) {
|
|
203
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
204
|
+
return JSON.stringify(
|
|
205
|
+
value,
|
|
206
|
+
(_key, val) => {
|
|
207
|
+
const replaced = jsonReplacer(_key, val);
|
|
208
|
+
if (typeof replaced === "object" && replaced !== null) {
|
|
209
|
+
if (seen.has(replaced)) {
|
|
210
|
+
return "[Circular]";
|
|
211
|
+
}
|
|
212
|
+
seen.add(replaced);
|
|
213
|
+
}
|
|
214
|
+
return replaced;
|
|
215
|
+
},
|
|
216
|
+
indent2
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
function formatPretty(entry, pigment2, options = {}) {
|
|
220
|
+
const parts = [];
|
|
221
|
+
const { timestamp = true, source = true } = options;
|
|
222
|
+
if (timestamp && entry.time) {
|
|
223
|
+
const time = formatTime(entry.time);
|
|
224
|
+
parts.push(pigment2 ? pigment2.gray(`[${time}]`) : `[${time}]`);
|
|
225
|
+
}
|
|
226
|
+
const levelLabel = LEVEL_LABELS[entry.levelName] || entry.levelName.toUpperCase();
|
|
227
|
+
const levelColor = LEVEL_COLORS[entry.levelName] || "white";
|
|
228
|
+
if (pigment2) {
|
|
229
|
+
const colorFn = pigment2[levelColor];
|
|
230
|
+
parts.push(colorFn ? colorFn(levelLabel) : levelLabel);
|
|
231
|
+
} else {
|
|
232
|
+
parts.push(levelLabel);
|
|
233
|
+
}
|
|
234
|
+
if (source && entry.file) {
|
|
235
|
+
const location = entry.line ? `${entry.file}:${entry.line}` : entry.file;
|
|
236
|
+
parts.push(pigment2 ? pigment2.dim(`(${location})`) : `(${location})`);
|
|
237
|
+
}
|
|
238
|
+
if (entry.msg) {
|
|
239
|
+
parts.push(entry.msg);
|
|
240
|
+
}
|
|
241
|
+
const extra = getExtraFields(entry);
|
|
242
|
+
if (Object.keys(extra).length > 0) {
|
|
243
|
+
const extraStr = safeStringify(extra);
|
|
244
|
+
parts.push(pigment2 ? pigment2.dim(extraStr) : extraStr);
|
|
245
|
+
}
|
|
246
|
+
if (entry.err?.stack) {
|
|
247
|
+
parts.push("\n" + (pigment2 ? pigment2.red(entry.err.stack) : entry.err.stack));
|
|
248
|
+
}
|
|
249
|
+
return parts.join(" ");
|
|
250
|
+
}
|
|
251
|
+
function getExtraFields(entry) {
|
|
252
|
+
const standardFields = /* @__PURE__ */ new Set([
|
|
253
|
+
"level",
|
|
254
|
+
"levelName",
|
|
255
|
+
"time",
|
|
256
|
+
"msg",
|
|
257
|
+
"file",
|
|
258
|
+
"line",
|
|
259
|
+
"column",
|
|
260
|
+
"correlationId",
|
|
261
|
+
"duration",
|
|
262
|
+
"err",
|
|
263
|
+
"name"
|
|
264
|
+
]);
|
|
265
|
+
const extra = {};
|
|
266
|
+
for (const [key, value] of Object.entries(entry)) {
|
|
267
|
+
if (!standardFields.has(key)) {
|
|
268
|
+
extra[key] = value;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return extra;
|
|
272
|
+
}
|
|
273
|
+
function formatTime(timestamp) {
|
|
274
|
+
const date = new Date(timestamp);
|
|
275
|
+
const hours = date.getHours().toString().padStart(2, "0");
|
|
276
|
+
const minutes = date.getMinutes().toString().padStart(2, "0");
|
|
277
|
+
const seconds = date.getSeconds().toString().padStart(2, "0");
|
|
278
|
+
return `${hours}:${minutes}:${seconds}`;
|
|
279
|
+
}
|
|
280
|
+
function formatIso(timestamp) {
|
|
281
|
+
return new Date(timestamp).toISOString();
|
|
282
|
+
}
|
|
283
|
+
function formatDuration(ms) {
|
|
284
|
+
if (ms < 1e3) {
|
|
285
|
+
return `${ms}ms`;
|
|
286
|
+
}
|
|
287
|
+
if (ms < 6e4) {
|
|
288
|
+
return `${(ms / 1e3).toFixed(2)}s`;
|
|
289
|
+
}
|
|
290
|
+
const minutes = Math.floor(ms / 6e4);
|
|
291
|
+
const seconds = Math.round(ms % 6e4 / 1e3);
|
|
292
|
+
return `${minutes}m ${seconds}s`;
|
|
293
|
+
}
|
|
294
|
+
function formatBytes(bytes) {
|
|
295
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
296
|
+
let unitIndex = 0;
|
|
297
|
+
let value = bytes;
|
|
298
|
+
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
299
|
+
value /= 1024;
|
|
300
|
+
unitIndex++;
|
|
301
|
+
}
|
|
302
|
+
return `${value.toFixed(unitIndex > 0 ? 2 : 0)} ${units[unitIndex]}`;
|
|
303
|
+
}
|
|
304
|
+
function truncate(str, maxLength, suffix = "...") {
|
|
305
|
+
if (str.length <= maxLength) return str;
|
|
306
|
+
return str.slice(0, maxLength - suffix.length) + suffix;
|
|
307
|
+
}
|
|
308
|
+
function indent(str, spaces = 2) {
|
|
309
|
+
const pad = " ".repeat(spaces);
|
|
310
|
+
return str.split("\n").map((line) => pad + line).join("\n");
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// src/transports/console.ts
|
|
314
|
+
var DEFAULT_LEVEL_COLORS = {
|
|
315
|
+
trace: "gray",
|
|
316
|
+
debug: "cyan",
|
|
317
|
+
info: "blue",
|
|
318
|
+
warn: "yellow",
|
|
319
|
+
error: "red",
|
|
320
|
+
fatal: "magenta"
|
|
321
|
+
};
|
|
322
|
+
function consoleTransport(options = {}) {
|
|
323
|
+
const {
|
|
324
|
+
colors = shouldUseColors(),
|
|
325
|
+
timestamp = true,
|
|
326
|
+
levelColors = {}
|
|
327
|
+
} = options;
|
|
328
|
+
const mergedColors = { ...DEFAULT_LEVEL_COLORS, ...levelColors };
|
|
329
|
+
let pigment2;
|
|
330
|
+
return {
|
|
331
|
+
name: "console",
|
|
332
|
+
write(entry) {
|
|
333
|
+
let output;
|
|
334
|
+
if (isBrowser()) {
|
|
335
|
+
writeToBrowserConsole(entry, colors);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (colors && pigment2) {
|
|
339
|
+
output = formatPretty(entry, pigment2, { timestamp, source: true });
|
|
340
|
+
} else if (colors) {
|
|
341
|
+
output = formatWithAnsi(entry, mergedColors, timestamp);
|
|
342
|
+
} else {
|
|
343
|
+
output = formatJson(entry);
|
|
344
|
+
}
|
|
345
|
+
if (entry.level >= 50) {
|
|
346
|
+
process.stderr.write(output + "\n");
|
|
347
|
+
} else {
|
|
348
|
+
process.stdout.write(output + "\n");
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
flush() {
|
|
352
|
+
},
|
|
353
|
+
close() {
|
|
354
|
+
},
|
|
355
|
+
supports(env) {
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
function formatWithAnsi(entry, levelColors, showTimestamp) {
|
|
361
|
+
const parts = [];
|
|
362
|
+
if (showTimestamp && entry.time) {
|
|
363
|
+
const time = formatTime2(entry.time);
|
|
364
|
+
parts.push(`\x1B[90m[${time}]\x1B[0m`);
|
|
365
|
+
}
|
|
366
|
+
const color = getAnsiColor(levelColors[entry.levelName] || "white");
|
|
367
|
+
const levelLabel = entry.levelName.toUpperCase().padEnd(5);
|
|
368
|
+
parts.push(`${color}${levelLabel}\x1B[0m`);
|
|
369
|
+
if (entry.file) {
|
|
370
|
+
const location = entry.line ? `${entry.file}:${entry.line}` : entry.file;
|
|
371
|
+
parts.push(`\x1B[90m(${location})\x1B[0m`);
|
|
372
|
+
}
|
|
373
|
+
if (entry.msg) {
|
|
374
|
+
parts.push(entry.msg);
|
|
375
|
+
}
|
|
376
|
+
const extra = getExtraFields2(entry);
|
|
377
|
+
if (Object.keys(extra).length > 0) {
|
|
378
|
+
parts.push(`\x1B[90m${JSON.stringify(extra)}\x1B[0m`);
|
|
379
|
+
}
|
|
380
|
+
if (entry.err?.stack) {
|
|
381
|
+
parts.push("\n\x1B[31m" + entry.err.stack + "\x1B[0m");
|
|
382
|
+
}
|
|
383
|
+
return parts.join(" ");
|
|
384
|
+
}
|
|
385
|
+
function getAnsiColor(colorName) {
|
|
386
|
+
const colors = {
|
|
387
|
+
black: "\x1B[30m",
|
|
388
|
+
red: "\x1B[31m",
|
|
389
|
+
green: "\x1B[32m",
|
|
390
|
+
yellow: "\x1B[33m",
|
|
391
|
+
blue: "\x1B[34m",
|
|
392
|
+
magenta: "\x1B[35m",
|
|
393
|
+
cyan: "\x1B[36m",
|
|
394
|
+
white: "\x1B[37m",
|
|
395
|
+
gray: "\x1B[90m",
|
|
396
|
+
grey: "\x1B[90m"
|
|
397
|
+
};
|
|
398
|
+
return colors[colorName] || "\x1B[0m";
|
|
399
|
+
}
|
|
400
|
+
function formatTime2(timestamp) {
|
|
401
|
+
const date = new Date(timestamp);
|
|
402
|
+
return date.toTimeString().slice(0, 8);
|
|
403
|
+
}
|
|
404
|
+
function getExtraFields2(entry) {
|
|
405
|
+
const standardFields = /* @__PURE__ */ new Set([
|
|
406
|
+
"level",
|
|
407
|
+
"levelName",
|
|
408
|
+
"time",
|
|
409
|
+
"msg",
|
|
410
|
+
"file",
|
|
411
|
+
"line",
|
|
412
|
+
"column",
|
|
413
|
+
"correlationId",
|
|
414
|
+
"duration",
|
|
415
|
+
"err",
|
|
416
|
+
"name"
|
|
417
|
+
]);
|
|
418
|
+
const extra = {};
|
|
419
|
+
for (const [key, value] of Object.entries(entry)) {
|
|
420
|
+
if (!standardFields.has(key)) {
|
|
421
|
+
extra[key] = value;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return extra;
|
|
425
|
+
}
|
|
426
|
+
function writeToBrowserConsole(entry, styled) {
|
|
427
|
+
const method = getConsoleMethod(entry.levelName);
|
|
428
|
+
const consoleObj = console;
|
|
429
|
+
if (styled) {
|
|
430
|
+
const styles = getBrowserStyles(entry.levelName);
|
|
431
|
+
const label = `%c[${entry.levelName.toUpperCase()}]`;
|
|
432
|
+
const extra = getExtraFields2(entry);
|
|
433
|
+
if (Object.keys(extra).length > 0) {
|
|
434
|
+
consoleObj[method](label, styles, entry.msg, extra);
|
|
435
|
+
} else {
|
|
436
|
+
consoleObj[method](label, styles, entry.msg);
|
|
437
|
+
}
|
|
438
|
+
} else {
|
|
439
|
+
consoleObj[method](`[${entry.levelName.toUpperCase()}]`, entry.msg, entry);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
function getConsoleMethod(levelName) {
|
|
443
|
+
switch (levelName) {
|
|
444
|
+
case "trace":
|
|
445
|
+
case "debug":
|
|
446
|
+
return "debug";
|
|
447
|
+
case "info":
|
|
448
|
+
return "info";
|
|
449
|
+
case "warn":
|
|
450
|
+
return "warn";
|
|
451
|
+
case "error":
|
|
452
|
+
case "fatal":
|
|
453
|
+
return "error";
|
|
454
|
+
default:
|
|
455
|
+
return "log";
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
function getBrowserStyles(levelName) {
|
|
459
|
+
const colors = {
|
|
460
|
+
trace: "#888888",
|
|
461
|
+
debug: "#00bcd4",
|
|
462
|
+
info: "#2196f3",
|
|
463
|
+
warn: "#ff9800",
|
|
464
|
+
error: "#f44336",
|
|
465
|
+
fatal: "#9c27b0"
|
|
466
|
+
};
|
|
467
|
+
return `color: ${colors[levelName]}; font-weight: bold;`;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// src/plugins/core/level.ts
|
|
471
|
+
function levelPlugin() {
|
|
472
|
+
return {
|
|
473
|
+
name: "level",
|
|
474
|
+
version: "1.0.0",
|
|
475
|
+
install(kernel) {
|
|
476
|
+
const ctx = kernel.getContext();
|
|
477
|
+
if (ctx.level === void 0) {
|
|
478
|
+
ctx.level = LOG_LEVELS.info;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// src/plugins/core/format.ts
|
|
485
|
+
function formatPlugin() {
|
|
486
|
+
return {
|
|
487
|
+
name: "format",
|
|
488
|
+
version: "1.0.0",
|
|
489
|
+
install(kernel) {
|
|
490
|
+
const ctx = kernel.getContext();
|
|
491
|
+
if (ctx.format === void 0) {
|
|
492
|
+
ctx.format = detectFormat();
|
|
493
|
+
} else if (ctx.format === "auto") {
|
|
494
|
+
ctx.format = detectFormat();
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
function detectFormat() {
|
|
500
|
+
if (isDev() && isTTY()) {
|
|
501
|
+
return "pretty";
|
|
502
|
+
}
|
|
503
|
+
return "json";
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// src/plugins/core/timestamp.ts
|
|
507
|
+
function timestampPlugin() {
|
|
508
|
+
return {
|
|
509
|
+
name: "timestamp",
|
|
510
|
+
version: "1.0.0",
|
|
511
|
+
install(kernel) {
|
|
512
|
+
const ctx = kernel.getContext();
|
|
513
|
+
if (ctx.timestamp === void 0) {
|
|
514
|
+
ctx.timestamp = true;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// src/errors.ts
|
|
521
|
+
var LogError = class extends Error {
|
|
522
|
+
/** Error code for programmatic handling */
|
|
523
|
+
code;
|
|
524
|
+
/** Original error if wrapping another error */
|
|
525
|
+
cause;
|
|
526
|
+
constructor(message, code, cause) {
|
|
527
|
+
super(message);
|
|
528
|
+
this.name = "LogError";
|
|
529
|
+
this.code = code;
|
|
530
|
+
this.cause = cause;
|
|
531
|
+
if (Error.captureStackTrace) {
|
|
532
|
+
Error.captureStackTrace(this, this.constructor);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
var PluginError = class extends LogError {
|
|
537
|
+
/** Name of the plugin that caused the error */
|
|
538
|
+
pluginName;
|
|
539
|
+
constructor(message, pluginName, cause) {
|
|
540
|
+
super(`[Plugin: ${pluginName}] ${message}`, "PLUGIN_ERROR", cause);
|
|
541
|
+
this.name = "PluginError";
|
|
542
|
+
this.pluginName = pluginName;
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
var TransportError = class extends LogError {
|
|
546
|
+
/** Name of the transport that caused the error */
|
|
547
|
+
transportName;
|
|
548
|
+
constructor(message, transportName, cause) {
|
|
549
|
+
super(`[Transport: ${transportName}] ${message}`, "TRANSPORT_ERROR", cause);
|
|
550
|
+
this.name = "TransportError";
|
|
551
|
+
this.transportName = transportName;
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
var ConfigError = class extends LogError {
|
|
555
|
+
/** Configuration option that caused the error */
|
|
556
|
+
option;
|
|
557
|
+
constructor(message, option, cause) {
|
|
558
|
+
super(message, "CONFIG_ERROR", cause);
|
|
559
|
+
this.name = "ConfigError";
|
|
560
|
+
this.option = option;
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
var SerializationError = class extends LogError {
|
|
564
|
+
constructor(message, cause) {
|
|
565
|
+
super(message, "SERIALIZATION_ERROR", cause);
|
|
566
|
+
this.name = "SerializationError";
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
var EnvironmentError = class extends LogError {
|
|
570
|
+
/** Required environment */
|
|
571
|
+
requiredEnv;
|
|
572
|
+
constructor(message, requiredEnv, cause) {
|
|
573
|
+
super(message, "ENVIRONMENT_ERROR", cause);
|
|
574
|
+
this.name = "EnvironmentError";
|
|
575
|
+
this.requiredEnv = requiredEnv;
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
var BufferError = class extends LogError {
|
|
579
|
+
constructor(message, cause) {
|
|
580
|
+
super(message, "BUFFER_ERROR", cause);
|
|
581
|
+
this.name = "BufferError";
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
function ensureError(value) {
|
|
585
|
+
if (value instanceof Error) {
|
|
586
|
+
return value;
|
|
587
|
+
}
|
|
588
|
+
if (typeof value === "string") {
|
|
589
|
+
return new Error(value);
|
|
590
|
+
}
|
|
591
|
+
if (typeof value === "object" && value !== null) {
|
|
592
|
+
const obj = value;
|
|
593
|
+
if (typeof obj["message"] === "string") {
|
|
594
|
+
const err = new Error(obj["message"]);
|
|
595
|
+
if (typeof obj["name"] === "string") {
|
|
596
|
+
err.name = obj["name"];
|
|
597
|
+
}
|
|
598
|
+
return err;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return new Error(String(value));
|
|
602
|
+
}
|
|
603
|
+
function wrapError(error, message) {
|
|
604
|
+
const cause = ensureError(error);
|
|
605
|
+
return new LogError(`${message}: ${cause.message}`, "WRAPPED_ERROR", cause);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// src/logger.ts
|
|
609
|
+
function createLogger(options = {}) {
|
|
610
|
+
const {
|
|
611
|
+
name = DEFAULT_NAME,
|
|
612
|
+
level = DEFAULT_LEVEL,
|
|
613
|
+
format = DEFAULT_FORMAT,
|
|
614
|
+
colors = true,
|
|
615
|
+
transports = [],
|
|
616
|
+
redact = [],
|
|
617
|
+
source = false,
|
|
618
|
+
timestamp = true,
|
|
619
|
+
sync = { fatal: true, error: true },
|
|
620
|
+
buffer,
|
|
621
|
+
context: initialContext = {},
|
|
622
|
+
plugins = []
|
|
623
|
+
} = options;
|
|
624
|
+
if (!(level in LOG_LEVELS)) {
|
|
625
|
+
throw new ConfigError(`Invalid log level: ${level}`, "level");
|
|
626
|
+
}
|
|
627
|
+
const emitter = createEmitter();
|
|
628
|
+
const activeTransports = [];
|
|
629
|
+
const env = getEnvironment();
|
|
630
|
+
if (transports.length === 0) {
|
|
631
|
+
activeTransports.push(consoleTransport({ colors }));
|
|
632
|
+
} else {
|
|
633
|
+
for (const transport of transports) {
|
|
634
|
+
if (!transport.supports || transport.supports(env)) {
|
|
635
|
+
activeTransports.push(transport);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
const kernelContext = {
|
|
640
|
+
name,
|
|
641
|
+
level: LOG_LEVELS[level],
|
|
642
|
+
format: resolveFormat(format),
|
|
643
|
+
colors,
|
|
644
|
+
timestamp,
|
|
645
|
+
source,
|
|
646
|
+
transports: activeTransports,
|
|
647
|
+
redactPaths: redact,
|
|
648
|
+
bindings: { ...initialContext },
|
|
649
|
+
correlationId: void 0,
|
|
650
|
+
pigment,
|
|
651
|
+
emitter,
|
|
652
|
+
syncLevels: {
|
|
653
|
+
trace: sync.trace ?? false,
|
|
654
|
+
debug: sync.debug ?? false,
|
|
655
|
+
info: sync.info ?? false,
|
|
656
|
+
warn: sync.warn ?? false,
|
|
657
|
+
error: sync.error ?? true,
|
|
658
|
+
fatal: sync.fatal ?? true
|
|
659
|
+
},
|
|
660
|
+
bufferOptions: buffer ?? {},
|
|
661
|
+
timers: /* @__PURE__ */ new Map(),
|
|
662
|
+
buffer: [],
|
|
663
|
+
flushTimerId: void 0
|
|
664
|
+
};
|
|
665
|
+
const kernel = createKernel({ context: kernelContext });
|
|
666
|
+
kernel.use(levelPlugin());
|
|
667
|
+
kernel.use(formatPlugin());
|
|
668
|
+
kernel.use(timestampPlugin());
|
|
669
|
+
for (const plugin of plugins) {
|
|
670
|
+
kernel.use(plugin);
|
|
671
|
+
}
|
|
672
|
+
let closed = false;
|
|
673
|
+
async function writeToTransports(entry) {
|
|
674
|
+
if (closed) return;
|
|
675
|
+
const promises = [];
|
|
676
|
+
for (const transport of activeTransports) {
|
|
677
|
+
try {
|
|
678
|
+
const result = transport.write(entry);
|
|
679
|
+
if (result instanceof Promise) {
|
|
680
|
+
promises.push(result);
|
|
681
|
+
}
|
|
682
|
+
} catch (err) {
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
if (promises.length > 0) {
|
|
686
|
+
await Promise.all(promises).catch(() => {
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
function createEntry(levelNum, levelName, msg, data, error) {
|
|
691
|
+
const ctx = kernel.getContext();
|
|
692
|
+
const entry = {
|
|
693
|
+
level: levelNum,
|
|
694
|
+
levelName,
|
|
695
|
+
time: Date.now(),
|
|
696
|
+
msg,
|
|
697
|
+
...ctx.bindings,
|
|
698
|
+
...data
|
|
699
|
+
};
|
|
700
|
+
if (error) {
|
|
701
|
+
entry.err = {
|
|
702
|
+
name: error.name,
|
|
703
|
+
message: error.message,
|
|
704
|
+
stack: error.stack
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
if (ctx.correlationId) {
|
|
708
|
+
entry.correlationId = ctx.correlationId;
|
|
709
|
+
}
|
|
710
|
+
return entry;
|
|
711
|
+
}
|
|
712
|
+
function isEnabled(levelNum) {
|
|
713
|
+
const ctx = kernel.getContext();
|
|
714
|
+
return levelNum >= ctx.level;
|
|
715
|
+
}
|
|
716
|
+
function shouldSync(levelName) {
|
|
717
|
+
const ctx = kernel.getContext();
|
|
718
|
+
return ctx.syncLevels[levelName] ?? false;
|
|
719
|
+
}
|
|
720
|
+
function log(levelNum, levelName, msgOrObj, msgOrUndefined) {
|
|
721
|
+
if (!isEnabled(levelNum)) return;
|
|
722
|
+
let msg;
|
|
723
|
+
let data;
|
|
724
|
+
let error;
|
|
725
|
+
if (typeof msgOrObj === "string") {
|
|
726
|
+
msg = msgOrObj;
|
|
727
|
+
} else if (msgOrObj instanceof Error) {
|
|
728
|
+
msg = msgOrUndefined ?? msgOrObj.message;
|
|
729
|
+
error = msgOrObj;
|
|
730
|
+
} else {
|
|
731
|
+
msg = msgOrUndefined ?? "";
|
|
732
|
+
data = msgOrObj;
|
|
733
|
+
}
|
|
734
|
+
const entry = createEntry(levelNum, levelName, msg, data, error);
|
|
735
|
+
emitter.emit("log", entry);
|
|
736
|
+
emitter.emit(`log:${levelName}`, entry);
|
|
737
|
+
if (shouldSync(levelName)) {
|
|
738
|
+
writeToTransports(entry).catch(() => {
|
|
739
|
+
});
|
|
740
|
+
} else {
|
|
741
|
+
writeToTransports(entry).catch(() => {
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
const logger = {
|
|
746
|
+
// Log methods with overloads
|
|
747
|
+
trace(msgOrObj, msg) {
|
|
748
|
+
log(LOG_LEVELS.trace, "trace", msgOrObj, msg);
|
|
749
|
+
},
|
|
750
|
+
debug(msgOrObj, msg) {
|
|
751
|
+
log(LOG_LEVELS.debug, "debug", msgOrObj, msg);
|
|
752
|
+
},
|
|
753
|
+
info(msgOrObj, msg) {
|
|
754
|
+
log(LOG_LEVELS.info, "info", msgOrObj, msg);
|
|
755
|
+
},
|
|
756
|
+
warn(msgOrObj, msg) {
|
|
757
|
+
log(LOG_LEVELS.warn, "warn", msgOrObj, msg);
|
|
758
|
+
},
|
|
759
|
+
error(msgOrObj, msg) {
|
|
760
|
+
log(LOG_LEVELS.error, "error", msgOrObj, msg);
|
|
761
|
+
},
|
|
762
|
+
fatal(msgOrObj, msg) {
|
|
763
|
+
log(LOG_LEVELS.fatal, "fatal", msgOrObj, msg);
|
|
764
|
+
},
|
|
765
|
+
// Level management
|
|
766
|
+
getLevel() {
|
|
767
|
+
const ctx = kernel.getContext();
|
|
768
|
+
return LEVEL_NAMES[ctx.level] ?? "info";
|
|
769
|
+
},
|
|
770
|
+
setLevel(newLevel) {
|
|
771
|
+
if (!(newLevel in LOG_LEVELS)) {
|
|
772
|
+
throw new ConfigError(`Invalid log level: ${newLevel}`, "level");
|
|
773
|
+
}
|
|
774
|
+
const ctx = kernel.getContext();
|
|
775
|
+
ctx.level = LOG_LEVELS[newLevel];
|
|
776
|
+
},
|
|
777
|
+
isLevelEnabled(levelName) {
|
|
778
|
+
return isEnabled(LOG_LEVELS[levelName]);
|
|
779
|
+
},
|
|
780
|
+
// Child logger
|
|
781
|
+
child(bindings) {
|
|
782
|
+
const ctx = kernel.getContext();
|
|
783
|
+
return createLogger({
|
|
784
|
+
...options,
|
|
785
|
+
context: { ...ctx.bindings, ...bindings },
|
|
786
|
+
transports: activeTransports
|
|
787
|
+
});
|
|
788
|
+
},
|
|
789
|
+
// Correlation
|
|
790
|
+
withCorrelation(id) {
|
|
791
|
+
const ctx = kernel.getContext();
|
|
792
|
+
const correlationId = id ?? generateCorrelationId();
|
|
793
|
+
const childLogger = createLogger({
|
|
794
|
+
...options,
|
|
795
|
+
context: { ...ctx.bindings },
|
|
796
|
+
transports: activeTransports
|
|
797
|
+
});
|
|
798
|
+
const childCtx = childLogger._getContext?.();
|
|
799
|
+
if (childCtx) {
|
|
800
|
+
childCtx.correlationId = correlationId;
|
|
801
|
+
}
|
|
802
|
+
return childLogger;
|
|
803
|
+
},
|
|
804
|
+
// Timing
|
|
805
|
+
time(label) {
|
|
806
|
+
const ctx = kernel.getContext();
|
|
807
|
+
ctx.timers.set(label, performance.now());
|
|
808
|
+
},
|
|
809
|
+
timeEnd(label) {
|
|
810
|
+
const ctx = kernel.getContext();
|
|
811
|
+
const start = ctx.timers.get(label);
|
|
812
|
+
if (start === void 0) {
|
|
813
|
+
logger.warn(`Timer '${label}' does not exist`);
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
const duration = performance.now() - start;
|
|
817
|
+
ctx.timers.delete(label);
|
|
818
|
+
logger.debug({ label, duration }, `${label}: ${duration.toFixed(2)}ms`);
|
|
819
|
+
},
|
|
820
|
+
async timeAsync(label, fn) {
|
|
821
|
+
const start = performance.now();
|
|
822
|
+
try {
|
|
823
|
+
return await fn();
|
|
824
|
+
} finally {
|
|
825
|
+
const duration = performance.now() - start;
|
|
826
|
+
logger.debug({ label, duration }, `${label}: ${duration.toFixed(2)}ms`);
|
|
827
|
+
}
|
|
828
|
+
},
|
|
829
|
+
startTimer(label) {
|
|
830
|
+
const start = performance.now();
|
|
831
|
+
return () => {
|
|
832
|
+
const duration = performance.now() - start;
|
|
833
|
+
logger.debug({ label, duration }, `${label}: ${duration.toFixed(2)}ms`);
|
|
834
|
+
};
|
|
835
|
+
},
|
|
836
|
+
// Plugin management
|
|
837
|
+
use(plugin) {
|
|
838
|
+
kernel.use(plugin);
|
|
839
|
+
return logger;
|
|
840
|
+
},
|
|
841
|
+
unregister(pluginName) {
|
|
842
|
+
return kernel.unregister(pluginName);
|
|
843
|
+
},
|
|
844
|
+
hasPlugin(pluginName) {
|
|
845
|
+
return kernel.has(pluginName);
|
|
846
|
+
},
|
|
847
|
+
listPlugins() {
|
|
848
|
+
return kernel.list();
|
|
849
|
+
},
|
|
850
|
+
// Event handling
|
|
851
|
+
on(event, handler) {
|
|
852
|
+
return emitter.on(event, handler);
|
|
853
|
+
},
|
|
854
|
+
// Lifecycle
|
|
855
|
+
async flush() {
|
|
856
|
+
const promises = [];
|
|
857
|
+
for (const transport of activeTransports) {
|
|
858
|
+
if (transport.flush) {
|
|
859
|
+
try {
|
|
860
|
+
const result = transport.flush();
|
|
861
|
+
if (result instanceof Promise) {
|
|
862
|
+
promises.push(result);
|
|
863
|
+
}
|
|
864
|
+
} catch {
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
await Promise.all(promises);
|
|
869
|
+
emitter.emit("flush", void 0);
|
|
870
|
+
},
|
|
871
|
+
async close() {
|
|
872
|
+
if (closed) return;
|
|
873
|
+
closed = true;
|
|
874
|
+
await logger.flush();
|
|
875
|
+
const promises = [];
|
|
876
|
+
for (const transport of activeTransports) {
|
|
877
|
+
if (transport.close) {
|
|
878
|
+
try {
|
|
879
|
+
const result = transport.close();
|
|
880
|
+
if (result instanceof Promise) {
|
|
881
|
+
promises.push(result);
|
|
882
|
+
}
|
|
883
|
+
} catch {
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
await Promise.all(promises);
|
|
888
|
+
kernel.destroy();
|
|
889
|
+
emitter.emit("close", void 0);
|
|
890
|
+
},
|
|
891
|
+
async destroy() {
|
|
892
|
+
return logger.close();
|
|
893
|
+
},
|
|
894
|
+
// Internal method for getting context
|
|
895
|
+
_getContext() {
|
|
896
|
+
return kernel.getContext();
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
return logger;
|
|
900
|
+
}
|
|
901
|
+
function resolveFormat(format) {
|
|
902
|
+
if (format !== "auto") {
|
|
903
|
+
return format;
|
|
904
|
+
}
|
|
905
|
+
if (isNode()) {
|
|
906
|
+
return process.stdout?.isTTY ? "pretty" : "json";
|
|
907
|
+
}
|
|
908
|
+
return "pretty";
|
|
909
|
+
}
|
|
910
|
+
function generateCorrelationId() {
|
|
911
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
912
|
+
const r = Math.random() * 16 | 0;
|
|
913
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
914
|
+
return v.toString(16);
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// src/transports/file.ts
|
|
919
|
+
function fileTransport(options) {
|
|
920
|
+
if (!isNode()) {
|
|
921
|
+
throw new EnvironmentError("File transport is only available in Node.js", "node");
|
|
922
|
+
}
|
|
923
|
+
const {
|
|
924
|
+
path: filePath,
|
|
925
|
+
rotate,
|
|
926
|
+
maxSize,
|
|
927
|
+
maxFiles = 5,
|
|
928
|
+
compress = false
|
|
929
|
+
} = options;
|
|
930
|
+
const maxSizeBytes = maxSize ? parseSize(maxSize) : void 0;
|
|
931
|
+
const rotationMs = rotate ? parseRotation(rotate) : void 0;
|
|
932
|
+
let currentSize = 0;
|
|
933
|
+
let lastRotation = Date.now();
|
|
934
|
+
let writeStream = null;
|
|
935
|
+
let fs = null;
|
|
936
|
+
let path = null;
|
|
937
|
+
let zlib = null;
|
|
938
|
+
async function ensureModules() {
|
|
939
|
+
if (!fs) {
|
|
940
|
+
fs = await import("fs");
|
|
941
|
+
path = await import("path");
|
|
942
|
+
if (compress) {
|
|
943
|
+
zlib = await import("zlib");
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
async function ensureStream() {
|
|
948
|
+
await ensureModules();
|
|
949
|
+
if (!writeStream) {
|
|
950
|
+
const dir = path.dirname(filePath);
|
|
951
|
+
if (!fs.existsSync(dir)) {
|
|
952
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
953
|
+
}
|
|
954
|
+
if (fs.existsSync(filePath)) {
|
|
955
|
+
const stats = fs.statSync(filePath);
|
|
956
|
+
currentSize = stats.size;
|
|
957
|
+
}
|
|
958
|
+
writeStream = fs.createWriteStream(filePath, { flags: "a" });
|
|
959
|
+
}
|
|
960
|
+
return writeStream;
|
|
961
|
+
}
|
|
962
|
+
function shouldRotate() {
|
|
963
|
+
if (maxSizeBytes && currentSize >= maxSizeBytes) {
|
|
964
|
+
return true;
|
|
965
|
+
}
|
|
966
|
+
if (rotationMs && Date.now() - lastRotation >= rotationMs) {
|
|
967
|
+
return true;
|
|
968
|
+
}
|
|
969
|
+
return false;
|
|
970
|
+
}
|
|
971
|
+
async function performRotation() {
|
|
972
|
+
await ensureModules();
|
|
973
|
+
if (!writeStream) return;
|
|
974
|
+
await new Promise((resolve, reject) => {
|
|
975
|
+
writeStream.once("finish", resolve);
|
|
976
|
+
writeStream.once("error", reject);
|
|
977
|
+
writeStream.end();
|
|
978
|
+
});
|
|
979
|
+
writeStream = null;
|
|
980
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
981
|
+
const ext = path.extname(filePath);
|
|
982
|
+
const base = filePath.slice(0, -ext.length);
|
|
983
|
+
const rotatedPath = `${base}.${timestamp}${ext}`;
|
|
984
|
+
fs.renameSync(filePath, rotatedPath);
|
|
985
|
+
if (compress && zlib) {
|
|
986
|
+
const gzPath = `${rotatedPath}.gz`;
|
|
987
|
+
const input = fs.createReadStream(rotatedPath);
|
|
988
|
+
const output = fs.createWriteStream(gzPath);
|
|
989
|
+
const gzip = zlib.createGzip();
|
|
990
|
+
await new Promise((resolve, reject) => {
|
|
991
|
+
input.pipe(gzip).pipe(output);
|
|
992
|
+
output.on("finish", () => {
|
|
993
|
+
fs.unlinkSync(rotatedPath);
|
|
994
|
+
resolve();
|
|
995
|
+
});
|
|
996
|
+
output.on("error", reject);
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
await cleanupOldFiles();
|
|
1000
|
+
currentSize = 0;
|
|
1001
|
+
lastRotation = Date.now();
|
|
1002
|
+
}
|
|
1003
|
+
async function cleanupOldFiles() {
|
|
1004
|
+
await ensureModules();
|
|
1005
|
+
const dir = path.dirname(filePath);
|
|
1006
|
+
const baseName = path.basename(filePath);
|
|
1007
|
+
const ext = path.extname(filePath);
|
|
1008
|
+
const prefix = baseName.slice(0, -ext.length);
|
|
1009
|
+
const files = fs.readdirSync(dir).filter((f) => f.startsWith(prefix) && f !== baseName).map((f) => ({
|
|
1010
|
+
name: f,
|
|
1011
|
+
path: path.join(dir, f),
|
|
1012
|
+
mtime: fs.statSync(path.join(dir, f)).mtime.getTime()
|
|
1013
|
+
})).sort((a, b) => b.mtime - a.mtime);
|
|
1014
|
+
while (files.length > maxFiles) {
|
|
1015
|
+
const file = files.pop();
|
|
1016
|
+
fs.unlinkSync(file.path);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
return {
|
|
1020
|
+
name: "file",
|
|
1021
|
+
async write(entry) {
|
|
1022
|
+
if (shouldRotate()) {
|
|
1023
|
+
await performRotation();
|
|
1024
|
+
}
|
|
1025
|
+
const stream = await ensureStream();
|
|
1026
|
+
const line = formatJson(entry) + "\n";
|
|
1027
|
+
const bytes = Buffer.byteLength(line, "utf8");
|
|
1028
|
+
return new Promise((resolve, reject) => {
|
|
1029
|
+
stream.write(line, (err) => {
|
|
1030
|
+
if (err) {
|
|
1031
|
+
reject(new TransportError(`Failed to write to file: ${err.message}`, "file", err));
|
|
1032
|
+
} else {
|
|
1033
|
+
currentSize += bytes;
|
|
1034
|
+
resolve();
|
|
1035
|
+
}
|
|
1036
|
+
});
|
|
1037
|
+
});
|
|
1038
|
+
},
|
|
1039
|
+
async flush() {
|
|
1040
|
+
if (writeStream && "flush" in writeStream) {
|
|
1041
|
+
return new Promise((resolve) => {
|
|
1042
|
+
writeStream.flush(resolve);
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
},
|
|
1046
|
+
async close() {
|
|
1047
|
+
if (writeStream) {
|
|
1048
|
+
await new Promise((resolve, reject) => {
|
|
1049
|
+
writeStream.once("finish", resolve);
|
|
1050
|
+
writeStream.once("error", reject);
|
|
1051
|
+
writeStream.end();
|
|
1052
|
+
});
|
|
1053
|
+
writeStream = null;
|
|
1054
|
+
}
|
|
1055
|
+
},
|
|
1056
|
+
supports(env) {
|
|
1057
|
+
return env === "node";
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// src/transports/stream.ts
|
|
1063
|
+
function streamTransport(options) {
|
|
1064
|
+
if (!isNode()) {
|
|
1065
|
+
throw new EnvironmentError("Stream transport is only available in Node.js", "node");
|
|
1066
|
+
}
|
|
1067
|
+
const { stream } = options;
|
|
1068
|
+
if (!stream) {
|
|
1069
|
+
throw new TransportError("Stream is required", "stream");
|
|
1070
|
+
}
|
|
1071
|
+
let closed = false;
|
|
1072
|
+
return {
|
|
1073
|
+
name: "stream",
|
|
1074
|
+
write(entry) {
|
|
1075
|
+
if (closed) {
|
|
1076
|
+
return Promise.reject(new TransportError("Stream is closed", "stream"));
|
|
1077
|
+
}
|
|
1078
|
+
const line = formatJson(entry) + "\n";
|
|
1079
|
+
return new Promise((resolve, reject) => {
|
|
1080
|
+
const canWrite = stream.write(line, (err) => {
|
|
1081
|
+
if (err) {
|
|
1082
|
+
reject(new TransportError(`Failed to write to stream: ${err.message}`, "stream", err));
|
|
1083
|
+
} else {
|
|
1084
|
+
resolve();
|
|
1085
|
+
}
|
|
1086
|
+
});
|
|
1087
|
+
if (!canWrite) {
|
|
1088
|
+
stream.once("drain", () => {
|
|
1089
|
+
resolve();
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
});
|
|
1093
|
+
},
|
|
1094
|
+
flush() {
|
|
1095
|
+
return Promise.resolve();
|
|
1096
|
+
},
|
|
1097
|
+
async close() {
|
|
1098
|
+
if (closed) return;
|
|
1099
|
+
closed = true;
|
|
1100
|
+
return new Promise((resolve, reject) => {
|
|
1101
|
+
stream.once("finish", resolve);
|
|
1102
|
+
stream.once("error", (err) => {
|
|
1103
|
+
reject(new TransportError(`Failed to close stream: ${err.message}`, "stream", err));
|
|
1104
|
+
});
|
|
1105
|
+
stream.end();
|
|
1106
|
+
});
|
|
1107
|
+
},
|
|
1108
|
+
supports(env) {
|
|
1109
|
+
return env === "node";
|
|
1110
|
+
}
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
// src/transports/http.ts
|
|
1115
|
+
function httpTransport(options) {
|
|
1116
|
+
const {
|
|
1117
|
+
url,
|
|
1118
|
+
method = "POST",
|
|
1119
|
+
headers = {},
|
|
1120
|
+
batch = 100,
|
|
1121
|
+
interval = 5e3,
|
|
1122
|
+
retry = 3
|
|
1123
|
+
} = options;
|
|
1124
|
+
if (!url) {
|
|
1125
|
+
throw new TransportError("URL is required", "http");
|
|
1126
|
+
}
|
|
1127
|
+
let buffer = [];
|
|
1128
|
+
let flushTimer = null;
|
|
1129
|
+
let isFlushing = false;
|
|
1130
|
+
let closed = false;
|
|
1131
|
+
function startFlushInterval() {
|
|
1132
|
+
if (flushTimer) return;
|
|
1133
|
+
if (interval > 0) {
|
|
1134
|
+
flushTimer = setInterval(() => {
|
|
1135
|
+
flushBuffer2().catch(() => {
|
|
1136
|
+
});
|
|
1137
|
+
}, interval);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
function stopFlushInterval() {
|
|
1141
|
+
if (flushTimer) {
|
|
1142
|
+
clearInterval(flushTimer);
|
|
1143
|
+
flushTimer = null;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
async function flushBuffer2() {
|
|
1147
|
+
if (isFlushing || buffer.length === 0) return;
|
|
1148
|
+
isFlushing = true;
|
|
1149
|
+
const entries = buffer;
|
|
1150
|
+
buffer = [];
|
|
1151
|
+
try {
|
|
1152
|
+
await sendWithRetry(entries);
|
|
1153
|
+
} catch (err) {
|
|
1154
|
+
buffer = [...entries, ...buffer].slice(0, batch * 2);
|
|
1155
|
+
throw err;
|
|
1156
|
+
} finally {
|
|
1157
|
+
isFlushing = false;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
async function sendWithRetry(entries) {
|
|
1161
|
+
let lastError = null;
|
|
1162
|
+
for (let attempt = 0; attempt <= retry; attempt++) {
|
|
1163
|
+
try {
|
|
1164
|
+
await sendEntries(entries);
|
|
1165
|
+
return;
|
|
1166
|
+
} catch (err) {
|
|
1167
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
1168
|
+
if (attempt < retry) {
|
|
1169
|
+
const delay = Math.min(1e3 * Math.pow(2, attempt), 3e4);
|
|
1170
|
+
await sleep(delay);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
throw new TransportError(
|
|
1175
|
+
`Failed to send logs after ${retry + 1} attempts: ${lastError?.message}`,
|
|
1176
|
+
"http",
|
|
1177
|
+
lastError || void 0
|
|
1178
|
+
);
|
|
1179
|
+
}
|
|
1180
|
+
async function sendEntries(entries) {
|
|
1181
|
+
const body = JSON.stringify(entries);
|
|
1182
|
+
const response = await fetch(url, {
|
|
1183
|
+
method,
|
|
1184
|
+
headers: {
|
|
1185
|
+
"Content-Type": "application/json",
|
|
1186
|
+
...headers
|
|
1187
|
+
},
|
|
1188
|
+
body
|
|
1189
|
+
});
|
|
1190
|
+
if (!response.ok) {
|
|
1191
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
function sleep(ms) {
|
|
1195
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1196
|
+
}
|
|
1197
|
+
let started = false;
|
|
1198
|
+
return {
|
|
1199
|
+
name: "http",
|
|
1200
|
+
async write(entry) {
|
|
1201
|
+
if (closed) {
|
|
1202
|
+
throw new TransportError("Transport is closed", "http");
|
|
1203
|
+
}
|
|
1204
|
+
if (!started) {
|
|
1205
|
+
started = true;
|
|
1206
|
+
startFlushInterval();
|
|
1207
|
+
}
|
|
1208
|
+
buffer.push(entry);
|
|
1209
|
+
if (buffer.length >= batch) {
|
|
1210
|
+
await flushBuffer2();
|
|
1211
|
+
}
|
|
1212
|
+
},
|
|
1213
|
+
async flush() {
|
|
1214
|
+
await flushBuffer2();
|
|
1215
|
+
},
|
|
1216
|
+
async close() {
|
|
1217
|
+
if (closed) return;
|
|
1218
|
+
closed = true;
|
|
1219
|
+
stopFlushInterval();
|
|
1220
|
+
if (buffer.length > 0) {
|
|
1221
|
+
try {
|
|
1222
|
+
await flushBuffer2();
|
|
1223
|
+
} catch {
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
},
|
|
1227
|
+
supports(_env) {
|
|
1228
|
+
return typeof fetch !== "undefined";
|
|
1229
|
+
}
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// src/transports/localStorage.ts
|
|
1234
|
+
function localStorageTransport(options) {
|
|
1235
|
+
if (!isBrowser()) {
|
|
1236
|
+
throw new EnvironmentError("LocalStorage transport is only available in browser", "browser");
|
|
1237
|
+
}
|
|
1238
|
+
const {
|
|
1239
|
+
key,
|
|
1240
|
+
maxSize = "1MB",
|
|
1241
|
+
levels
|
|
1242
|
+
} = options;
|
|
1243
|
+
if (!key) {
|
|
1244
|
+
throw new TransportError("Key is required", "localStorage");
|
|
1245
|
+
}
|
|
1246
|
+
const maxBytes = parseSize(maxSize);
|
|
1247
|
+
const allowedLevels = levels ? new Set(levels.map((l) => LOG_LEVELS[l])) : null;
|
|
1248
|
+
function getStorage() {
|
|
1249
|
+
return window.localStorage;
|
|
1250
|
+
}
|
|
1251
|
+
function loadLogs() {
|
|
1252
|
+
try {
|
|
1253
|
+
const data = getStorage().getItem(key);
|
|
1254
|
+
if (!data) return [];
|
|
1255
|
+
return JSON.parse(data);
|
|
1256
|
+
} catch {
|
|
1257
|
+
return [];
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
function saveLogs(logs) {
|
|
1261
|
+
const data = JSON.stringify(logs);
|
|
1262
|
+
while (data.length > maxBytes && logs.length > 0) {
|
|
1263
|
+
logs.shift();
|
|
1264
|
+
}
|
|
1265
|
+
try {
|
|
1266
|
+
getStorage().setItem(key, JSON.stringify(logs));
|
|
1267
|
+
} catch (err) {
|
|
1268
|
+
if (logs.length > 10) {
|
|
1269
|
+
logs.splice(0, Math.floor(logs.length / 2));
|
|
1270
|
+
saveLogs(logs);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
function isLevelAllowed(level) {
|
|
1275
|
+
if (!allowedLevels) return true;
|
|
1276
|
+
return allowedLevels.has(level);
|
|
1277
|
+
}
|
|
1278
|
+
return {
|
|
1279
|
+
name: "localStorage",
|
|
1280
|
+
write(entry) {
|
|
1281
|
+
if (!isLevelAllowed(entry.level)) {
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
const logs = loadLogs();
|
|
1285
|
+
logs.push(entry);
|
|
1286
|
+
saveLogs(logs);
|
|
1287
|
+
},
|
|
1288
|
+
flush() {
|
|
1289
|
+
},
|
|
1290
|
+
close() {
|
|
1291
|
+
},
|
|
1292
|
+
supports(env) {
|
|
1293
|
+
return env === "browser";
|
|
1294
|
+
}
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
function readLogs(key) {
|
|
1298
|
+
if (!isBrowser()) return [];
|
|
1299
|
+
try {
|
|
1300
|
+
const data = window.localStorage.getItem(key);
|
|
1301
|
+
if (!data) return [];
|
|
1302
|
+
return JSON.parse(data);
|
|
1303
|
+
} catch {
|
|
1304
|
+
return [];
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
function clearLogs(key) {
|
|
1308
|
+
if (!isBrowser()) return;
|
|
1309
|
+
window.localStorage.removeItem(key);
|
|
1310
|
+
}
|
|
1311
|
+
function getStorageUsage(key) {
|
|
1312
|
+
if (!isBrowser()) return 0;
|
|
1313
|
+
const data = window.localStorage.getItem(key);
|
|
1314
|
+
if (!data) return 0;
|
|
1315
|
+
return data.length * 2;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
// src/utils/redact.ts
|
|
1319
|
+
function redactFields(obj, paths, placeholder = REDACTED_VALUE) {
|
|
1320
|
+
if (!obj || typeof obj !== "object" || paths.length === 0) {
|
|
1321
|
+
return obj;
|
|
1322
|
+
}
|
|
1323
|
+
const result = deepClone(obj);
|
|
1324
|
+
for (const path of paths) {
|
|
1325
|
+
redactPath(result, path.split("."), placeholder);
|
|
1326
|
+
}
|
|
1327
|
+
return result;
|
|
1328
|
+
}
|
|
1329
|
+
function deepClone(obj) {
|
|
1330
|
+
if (obj === null || typeof obj !== "object") {
|
|
1331
|
+
return obj;
|
|
1332
|
+
}
|
|
1333
|
+
if (Array.isArray(obj)) {
|
|
1334
|
+
return obj.map((item) => deepClone(item));
|
|
1335
|
+
}
|
|
1336
|
+
if (obj instanceof Date) {
|
|
1337
|
+
return new Date(obj.getTime());
|
|
1338
|
+
}
|
|
1339
|
+
if (obj instanceof RegExp) {
|
|
1340
|
+
return new RegExp(obj.source, obj.flags);
|
|
1341
|
+
}
|
|
1342
|
+
if (obj instanceof Map) {
|
|
1343
|
+
const result2 = /* @__PURE__ */ new Map();
|
|
1344
|
+
for (const [key, value] of obj) {
|
|
1345
|
+
result2.set(key, deepClone(value));
|
|
1346
|
+
}
|
|
1347
|
+
return result2;
|
|
1348
|
+
}
|
|
1349
|
+
if (obj instanceof Set) {
|
|
1350
|
+
const result2 = /* @__PURE__ */ new Set();
|
|
1351
|
+
for (const value of obj) {
|
|
1352
|
+
result2.add(deepClone(value));
|
|
1353
|
+
}
|
|
1354
|
+
return result2;
|
|
1355
|
+
}
|
|
1356
|
+
const result = {};
|
|
1357
|
+
for (const key of Object.keys(obj)) {
|
|
1358
|
+
result[key] = deepClone(obj[key]);
|
|
1359
|
+
}
|
|
1360
|
+
return result;
|
|
1361
|
+
}
|
|
1362
|
+
function redactPath(obj, pathParts, placeholder) {
|
|
1363
|
+
if (pathParts.length === 0 || !obj || typeof obj !== "object") {
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
const [current, ...rest] = pathParts;
|
|
1367
|
+
if (!current) return;
|
|
1368
|
+
if (current === "*") {
|
|
1369
|
+
for (const key of Object.keys(obj)) {
|
|
1370
|
+
if (rest.length === 0) {
|
|
1371
|
+
obj[key] = placeholder;
|
|
1372
|
+
} else {
|
|
1373
|
+
const value = obj[key];
|
|
1374
|
+
if (value && typeof value === "object") {
|
|
1375
|
+
redactPath(value, rest, placeholder);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
if (current === "[*]" && Array.isArray(obj)) {
|
|
1382
|
+
for (let i = 0; i < obj.length; i++) {
|
|
1383
|
+
if (rest.length === 0) {
|
|
1384
|
+
obj[i] = placeholder;
|
|
1385
|
+
} else {
|
|
1386
|
+
const value = obj[i];
|
|
1387
|
+
if (value && typeof value === "object") {
|
|
1388
|
+
redactPath(value, rest, placeholder);
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
if (!(current in obj)) {
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
if (rest.length === 0) {
|
|
1398
|
+
obj[current] = placeholder;
|
|
1399
|
+
} else {
|
|
1400
|
+
const value = obj[current];
|
|
1401
|
+
if (value && typeof value === "object") {
|
|
1402
|
+
redactPath(value, rest, placeholder);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
function isSensitive(fieldName, sensitivePatterns) {
|
|
1407
|
+
const lowerField = fieldName.toLowerCase();
|
|
1408
|
+
return sensitivePatterns.some((pattern) => {
|
|
1409
|
+
const lowerPattern = pattern.toLowerCase();
|
|
1410
|
+
if (lowerField === lowerPattern) return true;
|
|
1411
|
+
if (lowerField.includes(lowerPattern)) return true;
|
|
1412
|
+
if (pattern.startsWith("/") && pattern.endsWith("/")) {
|
|
1413
|
+
try {
|
|
1414
|
+
const regex = new RegExp(pattern.slice(1, -1), "i");
|
|
1415
|
+
return regex.test(fieldName);
|
|
1416
|
+
} catch {
|
|
1417
|
+
return false;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
return false;
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
function createRedactor(paths, placeholder = REDACTED_VALUE) {
|
|
1424
|
+
return (obj) => redactFields(obj, paths, placeholder);
|
|
1425
|
+
}
|
|
1426
|
+
function autoRedact(obj, additionalPatterns = [], placeholder = REDACTED_VALUE) {
|
|
1427
|
+
if (!obj || typeof obj !== "object") {
|
|
1428
|
+
return obj;
|
|
1429
|
+
}
|
|
1430
|
+
const defaultPatterns = [
|
|
1431
|
+
"password",
|
|
1432
|
+
"passwd",
|
|
1433
|
+
"pwd",
|
|
1434
|
+
"secret",
|
|
1435
|
+
"token",
|
|
1436
|
+
"apikey",
|
|
1437
|
+
"api_key",
|
|
1438
|
+
"apiKey",
|
|
1439
|
+
"auth",
|
|
1440
|
+
"authorization",
|
|
1441
|
+
"credential",
|
|
1442
|
+
"credentials",
|
|
1443
|
+
"private",
|
|
1444
|
+
"privateKey",
|
|
1445
|
+
"private_key"
|
|
1446
|
+
];
|
|
1447
|
+
const patterns = [...defaultPatterns, ...additionalPatterns];
|
|
1448
|
+
const result = deepClone(obj);
|
|
1449
|
+
autoRedactRecursive(result, patterns, placeholder);
|
|
1450
|
+
return result;
|
|
1451
|
+
}
|
|
1452
|
+
function autoRedactRecursive(obj, patterns, placeholder) {
|
|
1453
|
+
for (const key of Object.keys(obj)) {
|
|
1454
|
+
const value = obj[key];
|
|
1455
|
+
if (isSensitive(key, patterns)) {
|
|
1456
|
+
obj[key] = placeholder;
|
|
1457
|
+
} else if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1458
|
+
autoRedactRecursive(value, patterns, placeholder);
|
|
1459
|
+
} else if (Array.isArray(value)) {
|
|
1460
|
+
for (const item of value) {
|
|
1461
|
+
if (item && typeof item === "object") {
|
|
1462
|
+
autoRedactRecursive(item, patterns, placeholder);
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
// src/plugins/optional/redact.ts
|
|
1470
|
+
function redactPlugin(options = {}) {
|
|
1471
|
+
return {
|
|
1472
|
+
name: "redact",
|
|
1473
|
+
version: "1.0.0",
|
|
1474
|
+
install(kernel) {
|
|
1475
|
+
const ctx = kernel.getContext();
|
|
1476
|
+
const paths = options.paths || ctx.redactPaths || [];
|
|
1477
|
+
const placeholder = options.placeholder || REDACTED_VALUE;
|
|
1478
|
+
ctx.redactPaths = paths;
|
|
1479
|
+
kernel.redact = (entry) => {
|
|
1480
|
+
return redactEntry(entry, paths, placeholder);
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
function redactEntry(entry, paths, placeholder = REDACTED_VALUE) {
|
|
1486
|
+
if (paths.length === 0) {
|
|
1487
|
+
return entry;
|
|
1488
|
+
}
|
|
1489
|
+
return redactFields(entry, paths, placeholder);
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
// src/utils/source.ts
|
|
1493
|
+
function getSourceLocation(depth = 0) {
|
|
1494
|
+
const err = new Error();
|
|
1495
|
+
const stack = err.stack;
|
|
1496
|
+
if (!stack) {
|
|
1497
|
+
return void 0;
|
|
1498
|
+
}
|
|
1499
|
+
const frames = parseStack(stack);
|
|
1500
|
+
const targetIndex = 2 + depth;
|
|
1501
|
+
if (targetIndex >= frames.length) {
|
|
1502
|
+
return void 0;
|
|
1503
|
+
}
|
|
1504
|
+
return frames[targetIndex];
|
|
1505
|
+
}
|
|
1506
|
+
function parseStack(stack) {
|
|
1507
|
+
const lines = stack.split("\n");
|
|
1508
|
+
const frames = [];
|
|
1509
|
+
for (const line of lines) {
|
|
1510
|
+
const trimmed = line.trim();
|
|
1511
|
+
if (!trimmed.startsWith("at ")) {
|
|
1512
|
+
continue;
|
|
1513
|
+
}
|
|
1514
|
+
const frame = parseV8Frame(trimmed);
|
|
1515
|
+
if (frame) {
|
|
1516
|
+
frames.push(frame);
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
return frames;
|
|
1520
|
+
}
|
|
1521
|
+
function parseV8Frame(line) {
|
|
1522
|
+
let content = line.slice(3);
|
|
1523
|
+
if (content.startsWith("async ")) {
|
|
1524
|
+
content = content.slice(6);
|
|
1525
|
+
}
|
|
1526
|
+
const parenMatch = content.match(/^(.+?)\s+\((.+):(\d+):(\d+)\)$/);
|
|
1527
|
+
if (parenMatch) {
|
|
1528
|
+
const [, fn, path, lineStr, colStr] = parenMatch;
|
|
1529
|
+
return {
|
|
1530
|
+
fn: fn?.trim() || void 0,
|
|
1531
|
+
path,
|
|
1532
|
+
file: extractFileName(path || ""),
|
|
1533
|
+
line: parseInt(lineStr || "0", 10),
|
|
1534
|
+
column: parseInt(colStr || "0", 10)
|
|
1535
|
+
};
|
|
1536
|
+
}
|
|
1537
|
+
const simpleMatch = content.match(/^(.+):(\d+):(\d+)$/);
|
|
1538
|
+
if (simpleMatch) {
|
|
1539
|
+
const [, path, lineStr, colStr] = simpleMatch;
|
|
1540
|
+
return {
|
|
1541
|
+
path,
|
|
1542
|
+
file: extractFileName(path || ""),
|
|
1543
|
+
line: parseInt(lineStr || "0", 10),
|
|
1544
|
+
column: parseInt(colStr || "0", 10)
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
const noColMatch = content.match(/^(.+):(\d+)$/);
|
|
1548
|
+
if (noColMatch) {
|
|
1549
|
+
const [, path, lineStr] = noColMatch;
|
|
1550
|
+
return {
|
|
1551
|
+
path,
|
|
1552
|
+
file: extractFileName(path || ""),
|
|
1553
|
+
line: parseInt(lineStr || "0", 10)
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
return void 0;
|
|
1557
|
+
}
|
|
1558
|
+
function extractFileName(path) {
|
|
1559
|
+
let cleanPath = path.replace(/^file:\/\//, "");
|
|
1560
|
+
cleanPath = cleanPath.replace(/\\/g, "/");
|
|
1561
|
+
cleanPath = cleanPath.split("?")[0]?.split("#")[0] || cleanPath;
|
|
1562
|
+
const parts = cleanPath.split("/");
|
|
1563
|
+
return parts[parts.length - 1] || cleanPath;
|
|
1564
|
+
}
|
|
1565
|
+
function isInternalFrame(location, internalPatterns = []) {
|
|
1566
|
+
const defaultPatterns = [
|
|
1567
|
+
"@oxog/log",
|
|
1568
|
+
"node_modules",
|
|
1569
|
+
"internal/",
|
|
1570
|
+
"<anonymous>"
|
|
1571
|
+
];
|
|
1572
|
+
const patterns = [...defaultPatterns, ...internalPatterns];
|
|
1573
|
+
const pathToCheck = location.path || location.file;
|
|
1574
|
+
return patterns.some((pattern) => {
|
|
1575
|
+
if (pattern.includes("/") || pattern.includes("\\")) {
|
|
1576
|
+
return pathToCheck.includes(pattern);
|
|
1577
|
+
}
|
|
1578
|
+
return location.file === pattern || location.file.includes(pattern);
|
|
1579
|
+
});
|
|
1580
|
+
}
|
|
1581
|
+
function getCallerLocation(depth = 0, internalPatterns = []) {
|
|
1582
|
+
const err = new Error();
|
|
1583
|
+
const stack = err.stack;
|
|
1584
|
+
if (!stack) {
|
|
1585
|
+
return void 0;
|
|
1586
|
+
}
|
|
1587
|
+
const frames = parseStack(stack);
|
|
1588
|
+
let skipped = 0;
|
|
1589
|
+
for (const frame of frames) {
|
|
1590
|
+
if (skipped < 2 + depth) {
|
|
1591
|
+
skipped++;
|
|
1592
|
+
continue;
|
|
1593
|
+
}
|
|
1594
|
+
if (!isInternalFrame(frame, internalPatterns)) {
|
|
1595
|
+
return frame;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
return void 0;
|
|
1599
|
+
}
|
|
1600
|
+
function formatLocation(location) {
|
|
1601
|
+
let result = location.file;
|
|
1602
|
+
if (location.line) {
|
|
1603
|
+
result += `:${location.line}`;
|
|
1604
|
+
}
|
|
1605
|
+
if (location.column) {
|
|
1606
|
+
result += `:${location.column}`;
|
|
1607
|
+
}
|
|
1608
|
+
return result;
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
// src/plugins/optional/source.ts
|
|
1612
|
+
function sourcePlugin(options = {}) {
|
|
1613
|
+
return {
|
|
1614
|
+
name: "source",
|
|
1615
|
+
version: "1.0.0",
|
|
1616
|
+
install(kernel) {
|
|
1617
|
+
const ctx = kernel.getContext();
|
|
1618
|
+
ctx.source = true;
|
|
1619
|
+
const depth = options.depth ?? 4;
|
|
1620
|
+
kernel.getSource = () => {
|
|
1621
|
+
return getSourceLocation(depth);
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
// src/plugins/optional/timing.ts
|
|
1628
|
+
function timingPlugin() {
|
|
1629
|
+
return {
|
|
1630
|
+
name: "timing",
|
|
1631
|
+
version: "1.0.0",
|
|
1632
|
+
install(kernel) {
|
|
1633
|
+
const ctx = kernel.getContext();
|
|
1634
|
+
if (!ctx.timers) {
|
|
1635
|
+
ctx.timers = /* @__PURE__ */ new Map();
|
|
1636
|
+
}
|
|
1637
|
+
},
|
|
1638
|
+
onDestroy() {
|
|
1639
|
+
}
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
// src/plugins/optional/correlation.ts
|
|
1644
|
+
var correlationOptions = {};
|
|
1645
|
+
function correlationPlugin(options = {}) {
|
|
1646
|
+
return {
|
|
1647
|
+
name: "correlation",
|
|
1648
|
+
version: "1.0.0",
|
|
1649
|
+
install(_kernel) {
|
|
1650
|
+
correlationOptions = options;
|
|
1651
|
+
}
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
// src/plugins/optional/buffer.ts
|
|
1656
|
+
function bufferPlugin(options = {}) {
|
|
1657
|
+
return {
|
|
1658
|
+
name: "buffer",
|
|
1659
|
+
version: "1.0.0",
|
|
1660
|
+
install(kernel) {
|
|
1661
|
+
const ctx = kernel.getContext();
|
|
1662
|
+
ctx.buffer = [];
|
|
1663
|
+
ctx.bufferOptions = {
|
|
1664
|
+
size: options.size ?? DEFAULT_BUFFER_OPTIONS.size,
|
|
1665
|
+
flushInterval: options.flushInterval ?? DEFAULT_BUFFER_OPTIONS.flushInterval
|
|
1666
|
+
};
|
|
1667
|
+
const flushInterval = ctx.bufferOptions.flushInterval ?? DEFAULT_BUFFER_OPTIONS.flushInterval;
|
|
1668
|
+
if (flushInterval > 0) {
|
|
1669
|
+
ctx.flushTimerId = setInterval(() => {
|
|
1670
|
+
flushBuffer(ctx).catch(() => {
|
|
1671
|
+
});
|
|
1672
|
+
}, flushInterval);
|
|
1673
|
+
}
|
|
1674
|
+
},
|
|
1675
|
+
async onDestroy() {
|
|
1676
|
+
}
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1679
|
+
async function flushBuffer(ctx) {
|
|
1680
|
+
if (ctx.buffer.length === 0) {
|
|
1681
|
+
return [];
|
|
1682
|
+
}
|
|
1683
|
+
const entries = ctx.buffer;
|
|
1684
|
+
ctx.buffer = [];
|
|
1685
|
+
await Promise.all(
|
|
1686
|
+
entries.flatMap(
|
|
1687
|
+
(entry) => ctx.transports.map(
|
|
1688
|
+
(transport) => Promise.resolve(transport.write(entry)).catch(() => {
|
|
1689
|
+
})
|
|
1690
|
+
)
|
|
1691
|
+
)
|
|
1692
|
+
);
|
|
1693
|
+
ctx.emitter.emit("flush", void 0);
|
|
1694
|
+
return entries;
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
// src/plugins/optional/browser.ts
|
|
1698
|
+
var browserOptions = {};
|
|
1699
|
+
function browserPlugin(options = {}) {
|
|
1700
|
+
return {
|
|
1701
|
+
name: "browser",
|
|
1702
|
+
version: "1.0.0",
|
|
1703
|
+
install(_kernel) {
|
|
1704
|
+
browserOptions = options;
|
|
1705
|
+
}
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
export {
|
|
1709
|
+
BufferError,
|
|
1710
|
+
COMMON_SENSITIVE_FIELDS,
|
|
1711
|
+
ConfigError,
|
|
1712
|
+
DEFAULT_BUFFER_OPTIONS,
|
|
1713
|
+
DEFAULT_COLORS,
|
|
1714
|
+
DEFAULT_FORMAT,
|
|
1715
|
+
DEFAULT_LEVEL,
|
|
1716
|
+
DEFAULT_NAME,
|
|
1717
|
+
DEFAULT_SOURCE,
|
|
1718
|
+
DEFAULT_SYNC_LEVELS,
|
|
1719
|
+
DEFAULT_TIMESTAMP,
|
|
1720
|
+
EnvironmentError,
|
|
1721
|
+
IS_BROWSER,
|
|
1722
|
+
IS_DEV,
|
|
1723
|
+
IS_NODE,
|
|
1724
|
+
IS_TTY,
|
|
1725
|
+
LEVEL_COLORS,
|
|
1726
|
+
LEVEL_LABELS,
|
|
1727
|
+
LEVEL_NAMES,
|
|
1728
|
+
LEVEL_ORDER,
|
|
1729
|
+
LOG_LEVELS,
|
|
1730
|
+
LogError,
|
|
1731
|
+
LogLevel,
|
|
1732
|
+
PluginError,
|
|
1733
|
+
REDACTED_VALUE,
|
|
1734
|
+
SerializationError,
|
|
1735
|
+
TransportError,
|
|
1736
|
+
autoRedact,
|
|
1737
|
+
browserPlugin,
|
|
1738
|
+
bufferPlugin,
|
|
1739
|
+
clearLogs,
|
|
1740
|
+
consoleTransport,
|
|
1741
|
+
correlationPlugin,
|
|
1742
|
+
createLogger,
|
|
1743
|
+
createRedactor,
|
|
1744
|
+
createLogger as default,
|
|
1745
|
+
ensureError,
|
|
1746
|
+
extractFileName,
|
|
1747
|
+
fileTransport,
|
|
1748
|
+
formatBytes,
|
|
1749
|
+
formatDuration,
|
|
1750
|
+
formatIso,
|
|
1751
|
+
formatJson,
|
|
1752
|
+
formatLocation,
|
|
1753
|
+
formatPlugin,
|
|
1754
|
+
formatPretty,
|
|
1755
|
+
formatTime,
|
|
1756
|
+
getCallerLocation,
|
|
1757
|
+
getCwd,
|
|
1758
|
+
getEnvironment,
|
|
1759
|
+
getSourceLocation,
|
|
1760
|
+
getStorageUsage,
|
|
1761
|
+
httpTransport,
|
|
1762
|
+
indent,
|
|
1763
|
+
isBrowser,
|
|
1764
|
+
isDev,
|
|
1765
|
+
isNode,
|
|
1766
|
+
isSensitive,
|
|
1767
|
+
isTTY,
|
|
1768
|
+
levelPlugin,
|
|
1769
|
+
localStorageTransport,
|
|
1770
|
+
parseRotation,
|
|
1771
|
+
parseSize,
|
|
1772
|
+
readLogs,
|
|
1773
|
+
redactFields,
|
|
1774
|
+
redactPlugin,
|
|
1775
|
+
safeStringify,
|
|
1776
|
+
shouldUseColors,
|
|
1777
|
+
sourcePlugin,
|
|
1778
|
+
streamTransport,
|
|
1779
|
+
timestampPlugin,
|
|
1780
|
+
timingPlugin,
|
|
1781
|
+
truncate,
|
|
1782
|
+
wrapError
|
|
1783
|
+
};
|
|
1784
|
+
//# sourceMappingURL=index.js.map
|