autotel-edge 3.16.0 → 3.16.2
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/{chunk-YWMTEG63.js → chunk-QXL3GNVV.js} +3 -3
- package/dist/{chunk-YWMTEG63.js.map → chunk-QXL3GNVV.js.map} +1 -1
- package/dist/events.d.ts +2 -2
- package/dist/events.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/logger.d.ts +229 -20
- package/dist/logger.js +774 -33
- package/dist/logger.js.map +1 -1
- package/dist/sampling.d.ts +1 -1
- package/dist/{types-BOs00OpG.d.ts → types-CZFcIjJ4.d.ts} +1 -1
- package/package.json +1 -1
- package/src/api/logger.test.ts +1339 -99
- package/src/api/logger.ts +894 -62
- package/src/api/redact.test.ts +432 -0
- package/src/api/redact.ts +402 -0
- package/src/logger.ts +13 -0
package/dist/logger.js
CHANGED
|
@@ -1,7 +1,258 @@
|
|
|
1
1
|
import { createContextKey, context, trace } from '@opentelemetry/api';
|
|
2
2
|
|
|
3
|
+
// src/api/logger.ts
|
|
4
|
+
|
|
5
|
+
// src/api/redact.ts
|
|
6
|
+
var DEFAULT_CENSOR = "[Redacted]";
|
|
7
|
+
function parsePath(path) {
|
|
8
|
+
if (typeof path !== "string" || path.length === 0) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
`redact path must be a non-empty string, got: ${JSON.stringify(path)}`
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
if (path.includes("..")) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
`redact path contains empty segment: ${JSON.stringify(path)}`
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
const openBracket = path.indexOf("[");
|
|
19
|
+
const closeBracket = path.indexOf("]");
|
|
20
|
+
if (openBracket !== -1 && closeBracket === -1 || closeBracket !== -1 && openBracket === -1) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`redact path has unclosed bracket: ${JSON.stringify(path)}`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
if (openBracket !== -1 && closeBracket < openBracket) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`redact path has bracket close before open: ${JSON.stringify(path)}`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
if (/\[\s*\]$/.test(path)) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`redact path has unclosed bracket: ${JSON.stringify(path)}`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
if (/\][^.[\]]/.test(path) && !/\]\[/.test(path)) {
|
|
36
|
+
const afterClose = path.match(/\]([^.[\]]+)/);
|
|
37
|
+
if (afterClose) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`redact path has invalid characters after ']': ${JSON.stringify(path)}`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const segments = [];
|
|
44
|
+
const rx = /[^.[\]]+|\[(\d+|\*)\]/g;
|
|
45
|
+
let match;
|
|
46
|
+
let lastIndex = 0;
|
|
47
|
+
while ((match = rx.exec(path)) !== null) {
|
|
48
|
+
segments.push(match[1] ?? match[0]);
|
|
49
|
+
lastIndex = rx.lastIndex;
|
|
50
|
+
}
|
|
51
|
+
if (lastIndex < path.length) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`redact path has unexpected trailing content: ${JSON.stringify(path)}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (segments.length === 0) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`redact path produced no segments: ${JSON.stringify(path)}`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
return segments;
|
|
62
|
+
}
|
|
63
|
+
function isPlainObject(value) {
|
|
64
|
+
if (value === null || typeof value !== "object") return false;
|
|
65
|
+
const proto = Object.getPrototypeOf(value);
|
|
66
|
+
return proto === Object.prototype || proto === null;
|
|
67
|
+
}
|
|
68
|
+
function deepClone(value) {
|
|
69
|
+
if (value === null || typeof value !== "object") return value;
|
|
70
|
+
if (Array.isArray(value)) return value.map((v) => deepClone(v));
|
|
71
|
+
if (!isPlainObject(value)) return value;
|
|
72
|
+
const result = {};
|
|
73
|
+
for (const key of Object.keys(value)) {
|
|
74
|
+
result[key] = deepClone(value[key]);
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
function buildPathTree(paths) {
|
|
79
|
+
const root = {};
|
|
80
|
+
for (const segments of paths) {
|
|
81
|
+
let node = root;
|
|
82
|
+
for (let i = 0; i < segments.length; i++) {
|
|
83
|
+
const seg = segments[i];
|
|
84
|
+
const isLast = i === segments.length - 1;
|
|
85
|
+
if (!node.children) node.children = {};
|
|
86
|
+
if (!node.children[seg]) node.children[seg] = {};
|
|
87
|
+
if (isLast) {
|
|
88
|
+
node.children[seg].redact = true;
|
|
89
|
+
}
|
|
90
|
+
node = node.children[seg];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return root;
|
|
94
|
+
}
|
|
95
|
+
function redactWithTree(obj, node, censor, remove, path = []) {
|
|
96
|
+
if (!node.children) return;
|
|
97
|
+
const applyCensor = (target, k, currentPath) => {
|
|
98
|
+
if (remove) {
|
|
99
|
+
delete target[k];
|
|
100
|
+
} else {
|
|
101
|
+
target[k] = typeof censor === "function" ? censor(target[k], currentPath) : censor;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
for (const [key, childNode] of Object.entries(node.children)) {
|
|
105
|
+
if (key === "*") {
|
|
106
|
+
for (const k of Object.keys(obj)) {
|
|
107
|
+
const wildcardPath = [...path, k];
|
|
108
|
+
if (childNode.redact && !childNode.children) {
|
|
109
|
+
applyCensor(obj, k, wildcardPath);
|
|
110
|
+
} else if (childNode.redact && childNode.children) {
|
|
111
|
+
const child = obj[k];
|
|
112
|
+
if (child != null && typeof child === "object" && !Array.isArray(child)) {
|
|
113
|
+
redactWithTree(child, childNode, censor, remove, wildcardPath);
|
|
114
|
+
} else {
|
|
115
|
+
applyCensor(obj, k, wildcardPath);
|
|
116
|
+
}
|
|
117
|
+
} else if (childNode.children) {
|
|
118
|
+
const child = obj[k];
|
|
119
|
+
if (child != null && typeof child === "object") {
|
|
120
|
+
redactWithTree(child, childNode, censor, remove, wildcardPath);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
|
|
127
|
+
const val = obj[key];
|
|
128
|
+
const currentPath = [...path, key];
|
|
129
|
+
if (childNode.redact && !childNode.children) {
|
|
130
|
+
applyCensor(obj, key, currentPath);
|
|
131
|
+
} else if (childNode.redact && childNode.children) {
|
|
132
|
+
if (val != null && typeof val === "object") {
|
|
133
|
+
redactWithTree(val, childNode, censor, remove, currentPath);
|
|
134
|
+
}
|
|
135
|
+
} else if (childNode.children && val != null && typeof val === "object") {
|
|
136
|
+
redactWithTree(val, childNode, censor, remove, currentPath);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
var SENSITIVE_KEY_PATHS = [
|
|
141
|
+
"password",
|
|
142
|
+
"passwd",
|
|
143
|
+
"pwd",
|
|
144
|
+
"secret",
|
|
145
|
+
"token",
|
|
146
|
+
"apiKey",
|
|
147
|
+
"api_key",
|
|
148
|
+
"auth",
|
|
149
|
+
"credential",
|
|
150
|
+
"privateKey",
|
|
151
|
+
"private_key",
|
|
152
|
+
"authorization"
|
|
153
|
+
];
|
|
154
|
+
var SENSITIVE_HEADER_PATHS = [
|
|
155
|
+
"req.headers.authorization",
|
|
156
|
+
"req.headers.cookie",
|
|
157
|
+
"headers.authorization",
|
|
158
|
+
"headers.cookie",
|
|
159
|
+
"request.headers.authorization",
|
|
160
|
+
"request.headers.cookie"
|
|
161
|
+
];
|
|
162
|
+
var REDACT_PRESETS = {
|
|
163
|
+
/**
|
|
164
|
+
* Default: covers the same sensitive-key names as the main autotel package's
|
|
165
|
+
* `sensitiveKey` pattern, plus common request header paths.
|
|
166
|
+
*
|
|
167
|
+
* Redacted keys: password, passwd, pwd, secret, token, apiKey, auth,
|
|
168
|
+
* credential, privateKey, authorization
|
|
169
|
+
* Redacted headers: req.headers.authorization, req.headers.cookie, etc.
|
|
170
|
+
*/
|
|
171
|
+
default: {
|
|
172
|
+
paths: [...SENSITIVE_KEY_PATHS, ...SENSITIVE_HEADER_PATHS]
|
|
173
|
+
},
|
|
174
|
+
/**
|
|
175
|
+
* Strict: everything in default, plus nested paths for bearer tokens,
|
|
176
|
+
* JWT fields, and API key fields at common depths.
|
|
177
|
+
*/
|
|
178
|
+
strict: {
|
|
179
|
+
paths: [
|
|
180
|
+
...SENSITIVE_KEY_PATHS,
|
|
181
|
+
...SENSITIVE_HEADER_PATHS,
|
|
182
|
+
// Common nested auth shapes
|
|
183
|
+
"bearer",
|
|
184
|
+
"jwt",
|
|
185
|
+
"apiSecret",
|
|
186
|
+
"api_secret",
|
|
187
|
+
"accessToken",
|
|
188
|
+
"access_token",
|
|
189
|
+
"refreshToken",
|
|
190
|
+
"refresh_token",
|
|
191
|
+
"clientSecret",
|
|
192
|
+
"client_secret"
|
|
193
|
+
]
|
|
194
|
+
},
|
|
195
|
+
/**
|
|
196
|
+
* PCI-DSS: credit card and payment-related fields.
|
|
197
|
+
*/
|
|
198
|
+
"pci-dss": {
|
|
199
|
+
paths: [
|
|
200
|
+
"card",
|
|
201
|
+
"cardNumber",
|
|
202
|
+
"card_number",
|
|
203
|
+
"ccn",
|
|
204
|
+
"pan",
|
|
205
|
+
"cvv",
|
|
206
|
+
"cvc",
|
|
207
|
+
"expirationDate",
|
|
208
|
+
"expiration_date",
|
|
209
|
+
"exp",
|
|
210
|
+
"payment.card",
|
|
211
|
+
"payment.cardNumber",
|
|
212
|
+
"payment.cvv"
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
function resolveConfig(config) {
|
|
217
|
+
if (typeof config === "string") {
|
|
218
|
+
const preset = REDACT_PRESETS[config];
|
|
219
|
+
if (!preset) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
`Unknown redactor preset: "${config}". Available: ${Object.keys(REDACT_PRESETS).join(", ")}`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
return preset;
|
|
225
|
+
}
|
|
226
|
+
return config;
|
|
227
|
+
}
|
|
228
|
+
function createRedactor(config) {
|
|
229
|
+
const { paths, censor = DEFAULT_CENSOR, remove = false } = resolveConfig(config);
|
|
230
|
+
if (paths.length === 0) {
|
|
231
|
+
return (obj) => obj;
|
|
232
|
+
}
|
|
233
|
+
const parsed = paths.map((p) => parsePath(p));
|
|
234
|
+
const tree = buildPathTree(parsed);
|
|
235
|
+
return function redactor(obj) {
|
|
236
|
+
if (obj == null || typeof obj !== "object") return obj;
|
|
237
|
+
const clone = deepClone(obj);
|
|
238
|
+
redactWithTree(clone, tree, censor, remove);
|
|
239
|
+
return clone;
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
3
243
|
// src/api/logger.ts
|
|
4
244
|
var LOG_LEVEL_KEY = createContextKey("autotel-edge-log-level");
|
|
245
|
+
var PINO_LEVELS = {
|
|
246
|
+
values: {
|
|
247
|
+
trace: 10,
|
|
248
|
+
debug: 20,
|
|
249
|
+
info: 30,
|
|
250
|
+
warn: 40,
|
|
251
|
+
error: 50,
|
|
252
|
+
fatal: 60,
|
|
253
|
+
silent: Infinity
|
|
254
|
+
}};
|
|
255
|
+
var LOGGER_VERSION = "autotel-edge";
|
|
5
256
|
function getActiveLogLevel() {
|
|
6
257
|
return context.active().getValue(LOG_LEVEL_KEY);
|
|
7
258
|
}
|
|
@@ -20,62 +271,552 @@ function getTraceContext() {
|
|
|
20
271
|
// First 16 chars for grouping
|
|
21
272
|
};
|
|
22
273
|
}
|
|
274
|
+
function isRecord(value) {
|
|
275
|
+
return typeof value === "object" && value !== null;
|
|
276
|
+
}
|
|
277
|
+
function toErrorAttrs(error) {
|
|
278
|
+
if (error instanceof Error) {
|
|
279
|
+
return {
|
|
280
|
+
error: {
|
|
281
|
+
message: error.message,
|
|
282
|
+
type: error.name,
|
|
283
|
+
stack: error.stack
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
return { error: { message: String(error), type: "Error" } };
|
|
288
|
+
}
|
|
289
|
+
function formatMessage(template, args) {
|
|
290
|
+
if (args.length === 0) return template;
|
|
291
|
+
let argIndex = 0;
|
|
292
|
+
const formatted = template.replaceAll(/%[sdjifoO%]/g, (token) => {
|
|
293
|
+
if (token === "%%") return "%";
|
|
294
|
+
if (argIndex >= args.length) return token;
|
|
295
|
+
const value = args[argIndex++];
|
|
296
|
+
switch (token) {
|
|
297
|
+
case "%d":
|
|
298
|
+
case "%i":
|
|
299
|
+
case "%f": {
|
|
300
|
+
return String(Number(value));
|
|
301
|
+
}
|
|
302
|
+
case "%j": {
|
|
303
|
+
try {
|
|
304
|
+
return JSON.stringify(value);
|
|
305
|
+
} catch {
|
|
306
|
+
return "[Circular]";
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
case "%s": {
|
|
310
|
+
return String(value);
|
|
311
|
+
}
|
|
312
|
+
case "%o":
|
|
313
|
+
case "%O": {
|
|
314
|
+
try {
|
|
315
|
+
return JSON.stringify(value);
|
|
316
|
+
} catch {
|
|
317
|
+
return String(value);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
default: {
|
|
321
|
+
return token;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
return formatted;
|
|
326
|
+
}
|
|
327
|
+
var ANSI_RESET = "\x1B[0m";
|
|
328
|
+
var ANSI_DIM = "\x1B[2m";
|
|
329
|
+
var ANSI_BOLD = "\x1B[1m";
|
|
330
|
+
var ANSI_COLORS = {
|
|
331
|
+
fatal: "\x1B[41m\x1B[37m",
|
|
332
|
+
// white on red bg
|
|
333
|
+
error: "\x1B[31m",
|
|
334
|
+
// red
|
|
335
|
+
warn: "\x1B[33m",
|
|
336
|
+
// yellow
|
|
337
|
+
info: "\x1B[36m",
|
|
338
|
+
// cyan
|
|
339
|
+
debug: "\x1B[34m",
|
|
340
|
+
// blue
|
|
341
|
+
trace: "\x1B[90m"
|
|
342
|
+
// gray
|
|
343
|
+
};
|
|
344
|
+
var LEVEL_SYMBOLS = {
|
|
345
|
+
fatal: "\u2717",
|
|
346
|
+
error: "\u2717",
|
|
347
|
+
warn: "\u26A0",
|
|
348
|
+
info: "\u25CF",
|
|
349
|
+
debug: "\u25E6",
|
|
350
|
+
trace: "\u2026"
|
|
351
|
+
};
|
|
352
|
+
function formatPrettyTimestamp() {
|
|
353
|
+
const d = /* @__PURE__ */ new Date();
|
|
354
|
+
const h = String(d.getHours()).padStart(2, "0");
|
|
355
|
+
const m = String(d.getMinutes()).padStart(2, "0");
|
|
356
|
+
const s = String(d.getSeconds()).padStart(2, "0");
|
|
357
|
+
const ms = String(d.getMilliseconds()).padStart(3, "0");
|
|
358
|
+
return `${h}:${m}:${s}.${ms}`;
|
|
359
|
+
}
|
|
360
|
+
function formatPrettyAttrs(attrs) {
|
|
361
|
+
const keys = Object.keys(attrs);
|
|
362
|
+
if (keys.length === 0) return "";
|
|
363
|
+
return "\n" + JSON.stringify(attrs, null, 2).split("\n").map((line) => ` ${line}`).join("\n");
|
|
364
|
+
}
|
|
365
|
+
function safeStringify(obj, depthLimit, edgeLimit) {
|
|
366
|
+
const ancestors = [];
|
|
367
|
+
return JSON.stringify(obj, function replacer(_key, value) {
|
|
368
|
+
if (typeof value === "object" && value !== null) {
|
|
369
|
+
while (ancestors.length > 0 && ancestors[ancestors.length - 1] !== this) {
|
|
370
|
+
ancestors.pop();
|
|
371
|
+
}
|
|
372
|
+
if (ancestors.includes(value)) return "[Circular]";
|
|
373
|
+
if (ancestors.length >= depthLimit) return "[Object]";
|
|
374
|
+
ancestors.push(value);
|
|
375
|
+
if (!Array.isArray(value)) {
|
|
376
|
+
const keys = Object.keys(value);
|
|
377
|
+
if (keys.length > edgeLimit) {
|
|
378
|
+
const truncated = {};
|
|
379
|
+
for (let i = 0; i < edgeLimit; i++) {
|
|
380
|
+
truncated[keys[i]] = value[keys[i]];
|
|
381
|
+
}
|
|
382
|
+
truncated["..."] = `[${keys.length - edgeLimit} more properties]`;
|
|
383
|
+
return truncated;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return value;
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
function parseLogArgs(args) {
|
|
391
|
+
const [first, ...rest] = args;
|
|
392
|
+
if (typeof first === "string") {
|
|
393
|
+
return {
|
|
394
|
+
msg: formatMessage(first, rest),
|
|
395
|
+
attrs: void 0
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
if (first instanceof Error) {
|
|
399
|
+
return {
|
|
400
|
+
msg: typeof rest[0] === "string" ? formatMessage(rest[0], rest.slice(1)) : first.message,
|
|
401
|
+
attrs: toErrorAttrs(first)
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
if (isRecord(first)) {
|
|
405
|
+
return {
|
|
406
|
+
msg: typeof rest[0] === "string" ? formatMessage(rest[0], rest.slice(1)) : "",
|
|
407
|
+
attrs: first
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
msg: String(first ?? ""),
|
|
412
|
+
attrs: void 0
|
|
413
|
+
};
|
|
414
|
+
}
|
|
23
415
|
function createEdgeLogger(service, options) {
|
|
24
|
-
const
|
|
416
|
+
const customLevels = {
|
|
417
|
+
...options?.customLevels
|
|
418
|
+
};
|
|
419
|
+
const availableLevels = options?.useOnlyCustomLevels ? { ...customLevels, silent: Infinity } : { ...PINO_LEVELS.values, ...customLevels };
|
|
420
|
+
const defaultLevel = options?.level ?? (options?.useOnlyCustomLevels ? Object.keys(customLevels)[0] : "info") ?? "info";
|
|
421
|
+
let currentLevel = defaultLevel;
|
|
25
422
|
const pretty = options?.pretty || false;
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
423
|
+
const currentBindings = { ...options?.bindings };
|
|
424
|
+
const resolvedRedact = Array.isArray(options?.redact) ? { paths: options.redact } : options?.redact;
|
|
425
|
+
const redactor = resolvedRedact ? createRedactor(resolvedRedact) : null;
|
|
426
|
+
const msgPrefix = options?.msgPrefix;
|
|
427
|
+
const levelValues = availableLevels;
|
|
428
|
+
const levelListeners = [];
|
|
429
|
+
const onChildCallback = options?.onChild ?? (() => {
|
|
430
|
+
});
|
|
431
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
432
|
+
let maxListeners = 10;
|
|
433
|
+
const messageKey = options?.messageKey ?? "msg";
|
|
434
|
+
const errorKey = options?.errorKey ?? "err";
|
|
435
|
+
const nestedKey = options?.nestedKey;
|
|
436
|
+
const serializers = { ...options?.serializers };
|
|
437
|
+
const bindingsFormatter = options?.formatters?.bindings;
|
|
438
|
+
const levelFormatter = options?.formatters?.level;
|
|
439
|
+
const logFormatter = options?.formatters?.log;
|
|
440
|
+
const mixin = options?.mixin;
|
|
441
|
+
const levelComparison = options?.levelComparison;
|
|
442
|
+
const mixinMergeStrategy = options?.mixinMergeStrategy ?? ((mergeObject, mixinObject) => ({ ...mergeObject, ...mixinObject }));
|
|
443
|
+
let enabled = options?.enabled ?? true;
|
|
444
|
+
const loggerName = options?.name;
|
|
445
|
+
const base = options?.base;
|
|
446
|
+
const timestamp = options?.timestamp ?? true;
|
|
447
|
+
const safe = options?.safe ?? true;
|
|
448
|
+
const crlf = options?.crlf ?? false;
|
|
449
|
+
const depthLimit = options?.depthLimit ?? 5;
|
|
450
|
+
const edgeLimitOpt = options?.edgeLimit ?? 100;
|
|
451
|
+
const hooks = options?.hooks;
|
|
452
|
+
const writeFn = options?.write;
|
|
453
|
+
const transmit = options?.transmit;
|
|
454
|
+
const bindingsChain = options?._bindingsChain ?? [];
|
|
455
|
+
const compareLevels = (current, expected, comparison) => {
|
|
456
|
+
if (typeof comparison === "function") return comparison(current, expected);
|
|
457
|
+
if (comparison === "DESC") return current <= expected;
|
|
458
|
+
return current >= expected;
|
|
459
|
+
};
|
|
460
|
+
const addListener = (event, listener, prepend = false) => {
|
|
461
|
+
const activeListeners = listeners.get(event) ?? [];
|
|
462
|
+
if (prepend) {
|
|
463
|
+
activeListeners.unshift(listener);
|
|
464
|
+
} else {
|
|
465
|
+
activeListeners.push(listener);
|
|
466
|
+
}
|
|
467
|
+
listeners.set(event, activeListeners);
|
|
468
|
+
if (event === "level-change") {
|
|
469
|
+
levelListeners.length = 0;
|
|
470
|
+
levelListeners.push(
|
|
471
|
+
...listeners.get(event) ?? []
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
const notifyLevelChange = (nextLevel, previousLevel, instance) => {
|
|
476
|
+
const nextValue = levelValues[nextLevel] ?? Infinity;
|
|
477
|
+
const previousValue = levelValues[previousLevel] ?? Infinity;
|
|
478
|
+
const snapshot = [...levelListeners];
|
|
479
|
+
for (const listener of snapshot) {
|
|
480
|
+
listener(nextLevel, nextValue, previousLevel, previousValue, instance);
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
const removeEventListener = (event, listener) => {
|
|
484
|
+
const activeListeners = listeners.get(event);
|
|
485
|
+
if (!activeListeners) {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
const index = activeListeners.indexOf(listener);
|
|
489
|
+
if (index !== -1) {
|
|
490
|
+
activeListeners.splice(index, 1);
|
|
491
|
+
}
|
|
492
|
+
if (activeListeners.length === 0) {
|
|
493
|
+
listeners.delete(event);
|
|
494
|
+
} else {
|
|
495
|
+
listeners.set(event, activeListeners);
|
|
496
|
+
}
|
|
497
|
+
if (event === "level-change") {
|
|
498
|
+
levelListeners.length = 0;
|
|
499
|
+
levelListeners.push(
|
|
500
|
+
...listeners.get(event) ?? []
|
|
501
|
+
);
|
|
502
|
+
}
|
|
32
503
|
};
|
|
33
504
|
const shouldLog = (level) => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
505
|
+
if (!enabled) return false;
|
|
506
|
+
const activeLevel = getActiveLogLevel() ?? currentLevel;
|
|
507
|
+
if (activeLevel === "silent") return false;
|
|
508
|
+
const currentValue = levelValues[activeLevel];
|
|
509
|
+
const expectedValue = levelValues[level];
|
|
510
|
+
if (currentValue === void 0 || expectedValue === void 0) return false;
|
|
511
|
+
return compareLevels(
|
|
512
|
+
expectedValue,
|
|
513
|
+
currentValue,
|
|
514
|
+
levelComparison
|
|
515
|
+
);
|
|
516
|
+
};
|
|
517
|
+
const stringify = (obj) => {
|
|
518
|
+
if (safe) {
|
|
519
|
+
return safeStringify(obj, depthLimit, edgeLimitOpt);
|
|
520
|
+
}
|
|
521
|
+
return JSON.stringify(obj);
|
|
522
|
+
};
|
|
523
|
+
const getTimestamp = () => {
|
|
524
|
+
if (timestamp === false) return void 0;
|
|
525
|
+
if (typeof timestamp === "function") return timestamp();
|
|
526
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
37
527
|
};
|
|
38
|
-
const
|
|
528
|
+
const writeOutput = (level, logObject) => {
|
|
529
|
+
if (writeFn) {
|
|
530
|
+
if (typeof writeFn === "function") {
|
|
531
|
+
writeFn(logObject);
|
|
532
|
+
} else if (typeof writeFn === "object" && writeFn[level]) {
|
|
533
|
+
writeFn[level](logObject);
|
|
534
|
+
} else {
|
|
535
|
+
console.log(stringify(logObject));
|
|
536
|
+
}
|
|
537
|
+
} else if (pretty) {
|
|
538
|
+
return;
|
|
539
|
+
} else {
|
|
540
|
+
const json = stringify(logObject);
|
|
541
|
+
console.log(crlf ? json + "\r\n" : json);
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
const applySerializers = (obj) => {
|
|
545
|
+
if (Object.keys(serializers).length === 0) return obj;
|
|
546
|
+
return Object.fromEntries(
|
|
547
|
+
Object.entries(obj).map(([key, value]) => [
|
|
548
|
+
key,
|
|
549
|
+
key in serializers ? serializers[key](value) : value
|
|
550
|
+
])
|
|
551
|
+
);
|
|
552
|
+
};
|
|
553
|
+
const emitTransmit = (level, rawArgs) => {
|
|
554
|
+
if (!transmit) return;
|
|
555
|
+
if (transmit.level) {
|
|
556
|
+
const transmitValue = levelValues[transmit.level];
|
|
557
|
+
const logValue = levelValues[level];
|
|
558
|
+
if (transmitValue !== void 0 && logValue !== void 0 && logValue < transmitValue) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
const processedMessages = rawArgs.map((m) => {
|
|
563
|
+
if (typeof m !== "object" || m === null) return m;
|
|
564
|
+
const serialized = applySerializers(m);
|
|
565
|
+
return redactor ? redactor(serialized) : serialized;
|
|
566
|
+
});
|
|
567
|
+
const processedBindings = bindingsChain.map((b) => {
|
|
568
|
+
const serialized = applySerializers({ ...b });
|
|
569
|
+
return redactor ? redactor(serialized) : serialized;
|
|
570
|
+
});
|
|
571
|
+
const logEvent = {
|
|
572
|
+
ts: Date.now(),
|
|
573
|
+
messages: processedMessages,
|
|
574
|
+
bindings: processedBindings,
|
|
575
|
+
level: { label: level, value: levelValues[level] }
|
|
576
|
+
};
|
|
577
|
+
transmit.send(level, logEvent);
|
|
578
|
+
};
|
|
579
|
+
const log = (level, msg, attrs, rawArgs) => {
|
|
39
580
|
if (!shouldLog(level)) return;
|
|
40
581
|
const ctx = getTraceContext();
|
|
582
|
+
const message = msgPrefix ? `${msgPrefix}${msg}` : msg;
|
|
583
|
+
const baseBindings = bindingsFormatter ? bindingsFormatter({ ...currentBindings }) : { ...currentBindings };
|
|
584
|
+
const serializedAttrs = attrs ? Object.fromEntries(
|
|
585
|
+
Object.entries(attrs).map(([key, value]) => [
|
|
586
|
+
key,
|
|
587
|
+
key in serializers ? serializers[key](value) : value
|
|
588
|
+
])
|
|
589
|
+
) : void 0;
|
|
590
|
+
const mergeObject = nestedKey ? { [nestedKey]: serializedAttrs ?? {} } : serializedAttrs ?? {};
|
|
591
|
+
const mixinObject = mixin?.(mergeObject, levelValues[level], logger) ?? {};
|
|
592
|
+
const mergedLogObject = mixinMergeStrategy(mergeObject, mixinObject);
|
|
593
|
+
const formattedLogObject = logFormatter ? logFormatter(mergedLogObject) : mergedLogObject;
|
|
594
|
+
const formattedLevel = levelFormatter ? levelFormatter(level, levelValues[level]) : {
|
|
595
|
+
level: logger.useLevelLabels ? level : levelValues[level]
|
|
596
|
+
};
|
|
597
|
+
const ts = getTimestamp();
|
|
41
598
|
const logEntry = {
|
|
42
|
-
|
|
599
|
+
...formattedLevel,
|
|
600
|
+
...loggerName === void 0 ? {} : { name: loggerName },
|
|
43
601
|
service,
|
|
44
|
-
|
|
45
|
-
...
|
|
602
|
+
...message === "" ? {} : { [messageKey]: message },
|
|
603
|
+
...base === null || base === void 0 ? {} : base,
|
|
604
|
+
...baseBindings,
|
|
605
|
+
...formattedLogObject,
|
|
46
606
|
...ctx,
|
|
47
607
|
// Auto-inject traceId, spanId, correlationId
|
|
48
|
-
|
|
608
|
+
...ts === void 0 ? {} : { timestamp: ts }
|
|
49
609
|
};
|
|
50
|
-
if (
|
|
51
|
-
|
|
610
|
+
if (serializedAttrs && !nestedKey && errorKey !== "error" && "error" in logEntry) {
|
|
611
|
+
logEntry[errorKey] = logEntry.error;
|
|
612
|
+
delete logEntry.error;
|
|
613
|
+
}
|
|
614
|
+
if (pretty && !writeFn) {
|
|
615
|
+
const color = ANSI_COLORS[level] ?? ANSI_COLORS.info;
|
|
616
|
+
const symbol = LEVEL_SYMBOLS[level] ?? "\u25CF";
|
|
617
|
+
const time = formatPrettyTimestamp();
|
|
618
|
+
const traceInfo = ctx ? ` ${ANSI_DIM}[${ctx.traceId.slice(0, 8)}/${ctx.spanId.slice(0, 8)}]${ANSI_RESET}` : "";
|
|
619
|
+
const prettyAttrs = {
|
|
620
|
+
...baseBindings,
|
|
621
|
+
...formattedLogObject
|
|
622
|
+
};
|
|
623
|
+
const safeAttrs = redactor ? redactor(prettyAttrs) : prettyAttrs;
|
|
624
|
+
const attrsStr = formatPrettyAttrs(safeAttrs);
|
|
52
625
|
console.log(
|
|
53
|
-
|
|
54
|
-
attrs || ""
|
|
626
|
+
`${ANSI_DIM}${time}${ANSI_RESET} ${color}${ANSI_BOLD}${symbol} ${level.toUpperCase()}${ANSI_RESET}${traceInfo} ${ANSI_DIM}(${service})${ANSI_RESET} ${message}${attrsStr}`
|
|
55
627
|
);
|
|
56
628
|
} else {
|
|
57
|
-
|
|
629
|
+
const safeEntry = redactor ? redactor(logEntry) : logEntry;
|
|
630
|
+
writeOutput(level, safeEntry);
|
|
58
631
|
}
|
|
632
|
+
emitTransmit(level, rawArgs ?? []);
|
|
59
633
|
};
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
634
|
+
const makeLogMethod = (levelName) => {
|
|
635
|
+
return ((...args) => {
|
|
636
|
+
if (hooks?.logMethod) {
|
|
637
|
+
const method = ((...methodArgs) => {
|
|
638
|
+
const { msg: msg2, attrs: attrs2 } = parseLogArgs(methodArgs);
|
|
639
|
+
log(levelName, msg2, attrs2, methodArgs);
|
|
640
|
+
});
|
|
641
|
+
hooks.logMethod.call(logger, args, method, levelValues[levelName]);
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
const { msg, attrs } = parseLogArgs(args);
|
|
645
|
+
log(levelName, msg, attrs, args);
|
|
646
|
+
});
|
|
647
|
+
};
|
|
648
|
+
const logger = {
|
|
649
|
+
version: LOGGER_VERSION,
|
|
650
|
+
get name() {
|
|
651
|
+
return loggerName;
|
|
652
|
+
},
|
|
653
|
+
levels: {
|
|
654
|
+
values: levelValues,
|
|
655
|
+
labels: Object.fromEntries(
|
|
656
|
+
Object.entries(levelValues).map(([label, value]) => [value, label])
|
|
657
|
+
)
|
|
658
|
+
},
|
|
659
|
+
useLevelLabels: options?.useLevelLabels ?? false,
|
|
660
|
+
customLevels: { ...customLevels },
|
|
661
|
+
useOnlyCustomLevels: options?.useOnlyCustomLevels ?? false,
|
|
662
|
+
get level() {
|
|
663
|
+
return currentLevel;
|
|
664
|
+
},
|
|
665
|
+
set level(level) {
|
|
666
|
+
const previousLevel = currentLevel;
|
|
667
|
+
currentLevel = level;
|
|
668
|
+
notifyLevelChange(level, previousLevel, logger);
|
|
669
|
+
},
|
|
670
|
+
get levelVal() {
|
|
671
|
+
return levelValues[currentLevel] ?? Infinity;
|
|
672
|
+
},
|
|
673
|
+
get msgPrefix() {
|
|
674
|
+
return msgPrefix;
|
|
675
|
+
},
|
|
676
|
+
onChild: onChildCallback,
|
|
677
|
+
get enabled() {
|
|
678
|
+
return enabled;
|
|
679
|
+
},
|
|
680
|
+
set enabled(value) {
|
|
681
|
+
enabled = value;
|
|
682
|
+
},
|
|
683
|
+
info: makeLogMethod("info"),
|
|
684
|
+
error: makeLogMethod("error"),
|
|
685
|
+
warn: makeLogMethod("warn"),
|
|
686
|
+
debug: makeLogMethod("debug"),
|
|
687
|
+
trace: makeLogMethod("trace"),
|
|
688
|
+
fatal: makeLogMethod("fatal"),
|
|
689
|
+
silent: (() => {
|
|
690
|
+
}),
|
|
691
|
+
child: (bindings, childOptions) => {
|
|
692
|
+
const childLogger = createEdgeLogger(service, {
|
|
693
|
+
level: childOptions?.level ?? currentLevel,
|
|
694
|
+
pretty,
|
|
695
|
+
bindings: { ...currentBindings, ...bindings },
|
|
696
|
+
redact: childOptions?.redact ?? options?.redact,
|
|
697
|
+
msgPrefix: childOptions?.msgPrefix === void 0 ? msgPrefix : `${msgPrefix ?? ""}${childOptions.msgPrefix}`,
|
|
698
|
+
useLevelLabels: logger.useLevelLabels,
|
|
699
|
+
onChild: onChildCallback,
|
|
700
|
+
enabled,
|
|
701
|
+
messageKey,
|
|
702
|
+
errorKey,
|
|
703
|
+
nestedKey,
|
|
704
|
+
serializers: { ...serializers, ...childOptions?.serializers },
|
|
705
|
+
formatters: {
|
|
706
|
+
level: childOptions?.formatters?.level ?? levelFormatter,
|
|
707
|
+
bindings: childOptions?.formatters?.bindings ?? bindingsFormatter,
|
|
708
|
+
log: childOptions?.formatters?.log ?? logFormatter
|
|
709
|
+
},
|
|
710
|
+
mixin,
|
|
711
|
+
mixinMergeStrategy,
|
|
712
|
+
customLevels,
|
|
713
|
+
useOnlyCustomLevels: options?.useOnlyCustomLevels,
|
|
714
|
+
levelComparison: options?.levelComparison,
|
|
715
|
+
name: loggerName,
|
|
716
|
+
base,
|
|
717
|
+
timestamp,
|
|
718
|
+
safe,
|
|
719
|
+
crlf,
|
|
720
|
+
depthLimit,
|
|
721
|
+
edgeLimit: edgeLimitOpt,
|
|
722
|
+
hooks,
|
|
723
|
+
write: writeFn,
|
|
724
|
+
transmit,
|
|
725
|
+
_bindingsChain: [...bindingsChain, bindings]
|
|
726
|
+
});
|
|
727
|
+
onChildCallback(childLogger);
|
|
728
|
+
return childLogger;
|
|
729
|
+
},
|
|
730
|
+
isLevelEnabled: (level) => shouldLog(level),
|
|
731
|
+
bindings: () => ({ ...currentBindings }),
|
|
732
|
+
setBindings: (bindings) => {
|
|
733
|
+
for (const [key, value] of Object.entries(bindings)) {
|
|
734
|
+
if (!(key in currentBindings)) {
|
|
735
|
+
currentBindings[key] = value;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
},
|
|
739
|
+
flush: (cb) => {
|
|
740
|
+
cb?.();
|
|
70
741
|
},
|
|
71
|
-
|
|
72
|
-
|
|
742
|
+
emit: (event, ...args) => {
|
|
743
|
+
const activeListeners = listeners.get(event);
|
|
744
|
+
if (!activeListeners || activeListeners.length === 0) {
|
|
745
|
+
return false;
|
|
746
|
+
}
|
|
747
|
+
const snapshot = [...activeListeners];
|
|
748
|
+
for (const listener of snapshot) {
|
|
749
|
+
listener(...args);
|
|
750
|
+
}
|
|
751
|
+
return true;
|
|
752
|
+
},
|
|
753
|
+
eventNames: () => [...listeners.keys()],
|
|
754
|
+
getMaxListeners: () => maxListeners,
|
|
755
|
+
listenerCount: (event) => listeners.get(event)?.length ?? 0,
|
|
756
|
+
listeners: (event) => [...listeners.get(event) ?? []],
|
|
757
|
+
on: (event, listener) => {
|
|
758
|
+
addListener(event, listener);
|
|
759
|
+
return logger;
|
|
760
|
+
},
|
|
761
|
+
addListener: (event, listener) => {
|
|
762
|
+
addListener(event, listener);
|
|
763
|
+
return logger;
|
|
764
|
+
},
|
|
765
|
+
once: (event, listener) => {
|
|
766
|
+
const wrapped = (...args) => {
|
|
767
|
+
removeEventListener(event, wrapped);
|
|
768
|
+
listener(...args);
|
|
769
|
+
};
|
|
770
|
+
addListener(event, wrapped);
|
|
771
|
+
return logger;
|
|
772
|
+
},
|
|
773
|
+
prependListener: (event, listener) => {
|
|
774
|
+
addListener(event, listener, true);
|
|
775
|
+
return logger;
|
|
776
|
+
},
|
|
777
|
+
prependOnceListener: (event, listener) => {
|
|
778
|
+
const wrapped = (...args) => {
|
|
779
|
+
removeEventListener(event, wrapped);
|
|
780
|
+
listener(...args);
|
|
781
|
+
};
|
|
782
|
+
addListener(event, wrapped, true);
|
|
783
|
+
return logger;
|
|
784
|
+
},
|
|
785
|
+
rawListeners: (event) => [...listeners.get(event) ?? []],
|
|
786
|
+
off: (event, listener) => {
|
|
787
|
+
removeEventListener(event, listener);
|
|
788
|
+
return logger;
|
|
789
|
+
},
|
|
790
|
+
removeAllListeners: (event) => {
|
|
791
|
+
if (event === void 0) {
|
|
792
|
+
listeners.clear();
|
|
793
|
+
levelListeners.length = 0;
|
|
794
|
+
return logger;
|
|
795
|
+
}
|
|
796
|
+
listeners.delete(event);
|
|
797
|
+
if (event === "level-change") {
|
|
798
|
+
levelListeners.length = 0;
|
|
799
|
+
}
|
|
800
|
+
return logger;
|
|
801
|
+
},
|
|
802
|
+
removeListener: (event, listener) => {
|
|
803
|
+
removeEventListener(event, listener);
|
|
804
|
+
return logger;
|
|
805
|
+
},
|
|
806
|
+
setMaxListeners: (n) => {
|
|
807
|
+
maxListeners = n;
|
|
808
|
+
return logger;
|
|
809
|
+
}
|
|
73
810
|
};
|
|
811
|
+
for (const levelName of Object.keys(customLevels)) {
|
|
812
|
+
logger[levelName] = makeLogMethod(levelName);
|
|
813
|
+
}
|
|
814
|
+
return logger;
|
|
74
815
|
}
|
|
75
816
|
function getEdgeTraceContext() {
|
|
76
817
|
return getTraceContext();
|
|
77
818
|
}
|
|
78
819
|
|
|
79
|
-
export { createEdgeLogger, getActiveLogLevel, getEdgeTraceContext, runWithLogLevel };
|
|
820
|
+
export { REDACT_PRESETS, createEdgeLogger, createRedactor, getActiveLogLevel, getEdgeTraceContext, runWithLogLevel };
|
|
80
821
|
//# sourceMappingURL=logger.js.map
|
|
81
822
|
//# sourceMappingURL=logger.js.map
|