loggily 0.8.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -221
- package/dist/context.d.mts +2 -2
- package/dist/context.d.mts.map +1 -1
- package/dist/context.mjs +3 -3
- package/dist/context.mjs.map +1 -1
- package/dist/{core-Dm2PQUoS.d.mts → core-DHB4KaG1.d.mts} +74 -5
- package/dist/core-DHB4KaG1.d.mts.map +1 -0
- package/dist/{core-B3pox577.mjs → core-DKwMMOZw.mjs} +344 -47
- package/dist/core-DKwMMOZw.mjs.map +1 -0
- package/dist/{file-writer-DtaY8Njt.d.mts → file-writer-BhJzFNKE.d.mts} +10 -3
- package/dist/file-writer-BhJzFNKE.d.mts.map +1 -0
- package/dist/index.browser.d.mts +4 -4
- package/dist/index.browser.d.mts.map +1 -1
- package/dist/index.browser.mjs +1 -1
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/metrics.d.mts +1 -1
- package/dist/metrics.mjs.map +1 -1
- package/dist/otel.d.mts +1 -1
- package/dist/otel.mjs.map +1 -1
- package/dist/{pipeline-Cl9-wCmt.d.mts → pipeline-D0omS7eF.d.mts} +14 -2
- package/dist/pipeline-D0omS7eF.d.mts.map +1 -0
- package/dist/worker.d.mts +2 -2
- package/dist/worker.d.mts.map +1 -1
- package/dist/worker.mjs +1 -1
- package/dist/worker.mjs.map +1 -1
- package/package.json +7 -3
- package/dist/core-B3pox577.mjs.map +0 -1
- package/dist/core-Dm2PQUoS.d.mts.map +0 -1
- package/dist/file-writer-DtaY8Njt.d.mts.map +0 -1
- package/dist/pipeline-Cl9-wCmt.d.mts.map +0 -1
|
@@ -49,15 +49,21 @@ const colors = {
|
|
|
49
49
|
function createFileWriter(filePath, options = {}) {
|
|
50
50
|
const bufferSize = options.bufferSize ?? 4096;
|
|
51
51
|
const flushInterval = options.flushInterval ?? 100;
|
|
52
|
+
const fs = options.__fs ?? {
|
|
53
|
+
openSync,
|
|
54
|
+
writeSync,
|
|
55
|
+
closeSync
|
|
56
|
+
};
|
|
52
57
|
let buffer = "";
|
|
53
58
|
let fd = null;
|
|
54
59
|
let timer = null;
|
|
55
60
|
let closed = false;
|
|
56
|
-
fd = openSync(filePath, "a");
|
|
61
|
+
fd = fs.openSync(filePath, "a");
|
|
57
62
|
/** Flush buffer contents to disk synchronously */
|
|
58
63
|
function flush() {
|
|
59
64
|
if (buffer.length === 0 || fd === null) return;
|
|
60
|
-
|
|
65
|
+
const data = buffer;
|
|
66
|
+
fs.writeSync(fd, data);
|
|
61
67
|
buffer = "";
|
|
62
68
|
}
|
|
63
69
|
timer = setInterval(flush, flushInterval);
|
|
@@ -82,7 +88,7 @@ function createFileWriter(filePath, options = {}) {
|
|
|
82
88
|
flush();
|
|
83
89
|
} catch {} finally {
|
|
84
90
|
if (fd !== null) {
|
|
85
|
-
closeSync(fd);
|
|
91
|
+
fs.closeSync(fd);
|
|
86
92
|
fd = null;
|
|
87
93
|
}
|
|
88
94
|
process.removeListener("exit", exitHandler);
|
|
@@ -149,7 +155,8 @@ function resetIdCounters() {
|
|
|
149
155
|
*
|
|
150
156
|
* @example
|
|
151
157
|
* ```typescript
|
|
152
|
-
* const span = log.span("http-request")
|
|
158
|
+
* const span = log.span?.("http-request")
|
|
159
|
+
* if (!span) return
|
|
153
160
|
* const header = traceparent(span.spanData)
|
|
154
161
|
* // → "00-a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6-1a2b3c4d5e6f7a8b-01"
|
|
155
162
|
* fetch(url, { headers: { traceparent: header } })
|
|
@@ -195,6 +202,211 @@ function shouldSample() {
|
|
|
195
202
|
return Math.random() < sampleRate;
|
|
196
203
|
}
|
|
197
204
|
//#endregion
|
|
205
|
+
//#region src/console-sinks.ts
|
|
206
|
+
/**
|
|
207
|
+
* Console sinks — runtime-aware structured output to console.
|
|
208
|
+
*
|
|
209
|
+
* Two sinks, one contract: `(event: Event) => void`. They do NOT pre-format
|
|
210
|
+
* events into a single ANSI string. Instead they spread structured arguments
|
|
211
|
+
* to `console.*` so the platform can render rich output:
|
|
212
|
+
*
|
|
213
|
+
* Terminal (Node / Bun):
|
|
214
|
+
* console.info(ansiPrefix, message, ...userArgs)
|
|
215
|
+
* — util.format keeps objects inspectable in devtools
|
|
216
|
+
*
|
|
217
|
+
* Browser (Chrome / Firefox / Safari DevTools):
|
|
218
|
+
* console.info("%c<level> %c<namespace>", levelCss, nsCss, message, ...userArgs)
|
|
219
|
+
* — DevTools renders colored prefix and keeps every user arg as an
|
|
220
|
+
* expandable, clickable object.
|
|
221
|
+
*
|
|
222
|
+
* Source-location preservation:
|
|
223
|
+
* We call `console.<level>(...)` from arrow-function sinks. DevTools attribute
|
|
224
|
+
* the log line to the caller's frame, not this file, as long as no wrapper
|
|
225
|
+
* closes over a bound console reference. `Function.prototype.bind.call(console.info, ...)`
|
|
226
|
+
* works too but defeats `vi.spyOn(console, 'info')` because bind captures
|
|
227
|
+
* the function at bind time. Plain arrow functions re-read `console.info`
|
|
228
|
+
* on each call — mockable AND location-preserving.
|
|
229
|
+
*
|
|
230
|
+
* The sinks consume `event.userArgs` (raw user-supplied data/errors) when
|
|
231
|
+
* present, falling back to `event.props` so they still work with code paths
|
|
232
|
+
* that have not yet been migrated.
|
|
233
|
+
*/
|
|
234
|
+
/**
|
|
235
|
+
* True when running inside a browser-like environment. We check for the
|
|
236
|
+
* presence of a `window` with `document` — the Node DOM test env (jsdom)
|
|
237
|
+
* also matches, but that is the correct behaviour for DevTools-style output.
|
|
238
|
+
*/
|
|
239
|
+
function isBrowserRuntime() {
|
|
240
|
+
return typeof globalThis?.window !== "undefined" && typeof globalThis.document !== "undefined";
|
|
241
|
+
}
|
|
242
|
+
function timeStr(time) {
|
|
243
|
+
return new Date(time).toISOString().split("T")[1]?.split(".")[0] ?? "";
|
|
244
|
+
}
|
|
245
|
+
function levelLabel(level) {
|
|
246
|
+
switch (level) {
|
|
247
|
+
case "trace": return "TRACE";
|
|
248
|
+
case "debug": return "DEBUG";
|
|
249
|
+
case "info": return "INFO";
|
|
250
|
+
case "warn": return "WARN";
|
|
251
|
+
case "error": return "ERROR";
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Return the raw user-supplied data attached to the event, in call order.
|
|
256
|
+
* Prefers the explicit `userArgs` field (new path). Falls back to `props`
|
|
257
|
+
* (coerced to a single trailing object) for events emitted by older code or
|
|
258
|
+
* by `writeSpan`. Filters out `undefined` entries.
|
|
259
|
+
*/
|
|
260
|
+
function userArgsOf(event) {
|
|
261
|
+
if ("userArgs" in event && Array.isArray(event.userArgs)) return event.userArgs.filter((v) => v !== void 0);
|
|
262
|
+
if (event.props && Object.keys(event.props).length > 0) return [event.props];
|
|
263
|
+
return [];
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Terminal (ANSI) sink. Emits `[ansiPrefix, message, ...userArgs]` so that:
|
|
267
|
+
* - stdout gets a colored prefix
|
|
268
|
+
* - util.format leaves objects inspectable
|
|
269
|
+
* - vi.spyOn(console, 'info') intercepts calls (arrows re-read console)
|
|
270
|
+
*
|
|
271
|
+
* Spans and JSON format still go through the pre-formatted single-arg path —
|
|
272
|
+
* their consumers are log aggregators, not humans.
|
|
273
|
+
*/
|
|
274
|
+
function createTerminalConsoleSink(format = "console") {
|
|
275
|
+
if (format === "json") return (event) => routeSingle(event, formatJSONEvent(event));
|
|
276
|
+
return (event) => {
|
|
277
|
+
if (event.kind === "span") {
|
|
278
|
+
writeStderrLine(formatConsoleEvent(event));
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const prefix = `${colors.dim(timeStr(event.time))} ${levelAnsi(event.level)} ${colors.cyan(event.namespace)}`;
|
|
282
|
+
const args = userArgsOf(event);
|
|
283
|
+
invokeForLevel(event.level, prefix, event.message, ...args);
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
/** Emit one line to process.stderr when available (no-op in browser). */
|
|
287
|
+
function writeStderrLine(text) {
|
|
288
|
+
const p = typeof process !== "undefined" ? process : void 0;
|
|
289
|
+
if (p?.stderr && typeof p.stderr.write === "function") {
|
|
290
|
+
p.stderr.write(text + "\n");
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
console.info(text);
|
|
294
|
+
}
|
|
295
|
+
function levelAnsi(level) {
|
|
296
|
+
switch (level) {
|
|
297
|
+
case "trace": return colors.dim("TRACE");
|
|
298
|
+
case "debug": return colors.dim("DEBUG");
|
|
299
|
+
case "info": return colors.blue("INFO");
|
|
300
|
+
case "warn": return colors.yellow("WARN");
|
|
301
|
+
case "error": return colors.red("ERROR");
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Browser (DevTools) sink. Uses `%c` CSS format specifiers so DevTools
|
|
306
|
+
* renders a colored level + namespace prefix, then spreads user args raw so
|
|
307
|
+
* DevTools keeps them as expandable object references (clickable source
|
|
308
|
+
* locations for Error, drill-in for plain objects).
|
|
309
|
+
*
|
|
310
|
+
* For JSON format we still emit a single string — consumers that explicitly
|
|
311
|
+
* asked for JSON want machine-readable output, not DevTools theatrics.
|
|
312
|
+
*/
|
|
313
|
+
function createBrowserConsoleSink(format = "console") {
|
|
314
|
+
if (format === "json") return (event) => routeSingle(event, formatJSONEvent(event));
|
|
315
|
+
return (event) => {
|
|
316
|
+
if (event.kind === "span") {
|
|
317
|
+
const spanTemplate = `%c%s %cSPAN %c%s %c(%sms)`;
|
|
318
|
+
const args = userArgsOf(event);
|
|
319
|
+
const spanPropsString = args.length > 0 ? ` ${safeStringify(Object.assign({}, ...args.filter(isPlainRecord)))}` : "";
|
|
320
|
+
const { durationLabel } = { durationLabel: String(event.duration) };
|
|
321
|
+
invokeForLevel("info", spanTemplate + (spanPropsString ? "%s" : ""), cssDim(), timeStr(event.time), cssSpan(), cssNamespace(), event.namespace, cssDim(), durationLabel, ...spanPropsString ? [cssDim(), spanPropsString] : []);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const template = `%c%s %c%s %c%s`;
|
|
325
|
+
const args = userArgsOf(event);
|
|
326
|
+
invokeForLevel(event.level, template, cssDim(), timeStr(event.time), cssLevel(event.level), levelLabel(event.level), cssNamespace(), event.namespace, event.message, ...args);
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
function isPlainRecord(v) {
|
|
330
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
331
|
+
}
|
|
332
|
+
function cssDim() {
|
|
333
|
+
return "color: #888";
|
|
334
|
+
}
|
|
335
|
+
function cssNamespace() {
|
|
336
|
+
return "color: #0aa; font-weight: bold";
|
|
337
|
+
}
|
|
338
|
+
function cssSpan() {
|
|
339
|
+
return "color: #a0a; font-weight: bold";
|
|
340
|
+
}
|
|
341
|
+
function cssLevel(level) {
|
|
342
|
+
switch (level) {
|
|
343
|
+
case "trace":
|
|
344
|
+
case "debug": return "color: #888; font-weight: bold";
|
|
345
|
+
case "info": return "color: #36f; font-weight: bold";
|
|
346
|
+
case "warn": return "color: #b80; font-weight: bold";
|
|
347
|
+
case "error": return "color: #c33; font-weight: bold";
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Dispatch to the right console method for the event's level.
|
|
352
|
+
* We intentionally use arrow-style invocation rather than
|
|
353
|
+
* `Function.prototype.bind.call(console.info, console, ...)` so that:
|
|
354
|
+
* 1. Tests can `vi.spyOn(console, 'info')` AFTER the sink is created.
|
|
355
|
+
* 2. DevTools attribute the log line to the caller's source location
|
|
356
|
+
* (arrows don't appear in the stack between the call and `console.*`).
|
|
357
|
+
*/
|
|
358
|
+
function invokeForLevel(level, ...args) {
|
|
359
|
+
switch (level) {
|
|
360
|
+
case "trace":
|
|
361
|
+
case "debug":
|
|
362
|
+
console.debug(...args);
|
|
363
|
+
return;
|
|
364
|
+
case "info":
|
|
365
|
+
console.info(...args);
|
|
366
|
+
return;
|
|
367
|
+
case "warn":
|
|
368
|
+
console.warn(...args);
|
|
369
|
+
return;
|
|
370
|
+
case "error":
|
|
371
|
+
console.error(...args);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Route an already-formatted single string to the correct console level.
|
|
377
|
+
* Used for JSON format and for span events where we preserve the pre-formatted
|
|
378
|
+
* shape.
|
|
379
|
+
*/
|
|
380
|
+
function routeSingle(event, text) {
|
|
381
|
+
if (event.kind === "span") {
|
|
382
|
+
console.info(text);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
switch (event.level) {
|
|
386
|
+
case "trace":
|
|
387
|
+
case "debug":
|
|
388
|
+
console.debug(text);
|
|
389
|
+
return;
|
|
390
|
+
case "info":
|
|
391
|
+
console.info(text);
|
|
392
|
+
return;
|
|
393
|
+
case "warn":
|
|
394
|
+
console.warn(text);
|
|
395
|
+
return;
|
|
396
|
+
case "error":
|
|
397
|
+
console.error(text);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Pick the right sink for the current runtime. Browsers get `%c` CSS, Node
|
|
403
|
+
* gets ANSI. Both emit multi-arg spreads so the platform can render objects
|
|
404
|
+
* as expandable references.
|
|
405
|
+
*/
|
|
406
|
+
function createConsoleSink$1(format = "console") {
|
|
407
|
+
return isBrowserRuntime() ? createBrowserConsoleSink(format) : createTerminalConsoleSink(format);
|
|
408
|
+
}
|
|
409
|
+
//#endregion
|
|
198
410
|
//#region src/pipeline.ts
|
|
199
411
|
const LOG_LEVEL_PRIORITY = {
|
|
200
412
|
trace: 0,
|
|
@@ -324,38 +536,12 @@ function parseNsFilter(ns) {
|
|
|
324
536
|
};
|
|
325
537
|
}
|
|
326
538
|
/**
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
* browser DevTools. When you click a log line in DevTools, it shows where
|
|
331
|
-
* YOUR code called log.info?.(), not where pipeline.ts called console.info().
|
|
332
|
-
* DO NOT replace bind() with direct console.info(text) calls — it breaks
|
|
333
|
-
* source location tracking in browsers.
|
|
539
|
+
* Console sink used inside the pipeline builder. Delegates to the structured
|
|
540
|
+
* console sink (browser %c or terminal multi-arg) so the pipeline preserves
|
|
541
|
+
* expandable user args end-to-end.
|
|
334
542
|
*/
|
|
335
|
-
function writeToConsole(text, event) {
|
|
336
|
-
if (event.kind === "span") {
|
|
337
|
-
writeStderr(text);
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
|
-
switch (event.level) {
|
|
341
|
-
case "trace":
|
|
342
|
-
case "debug":
|
|
343
|
-
Function.prototype.bind.call(console.debug, console, text)();
|
|
344
|
-
break;
|
|
345
|
-
case "info":
|
|
346
|
-
Function.prototype.bind.call(console.info, console, text)();
|
|
347
|
-
break;
|
|
348
|
-
case "warn":
|
|
349
|
-
Function.prototype.bind.call(console.warn, console, text)();
|
|
350
|
-
break;
|
|
351
|
-
case "error":
|
|
352
|
-
Function.prototype.bind.call(console.error, console, text)();
|
|
353
|
-
break;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
543
|
function createConsoleSink(format) {
|
|
357
|
-
|
|
358
|
-
return (event) => writeToConsole(formatter(event), event);
|
|
544
|
+
return createConsoleSink$1(format);
|
|
359
545
|
}
|
|
360
546
|
function createFileSink(path, format) {
|
|
361
547
|
const writer = createFileWriter(path);
|
|
@@ -375,6 +561,19 @@ function createWritableSink(writable, format) {
|
|
|
375
561
|
}
|
|
376
562
|
return (event) => writable.write(event);
|
|
377
563
|
}
|
|
564
|
+
/** Consecutive failures before a throwing output sink is disabled. */
|
|
565
|
+
const OUTPUT_MAX_STRIKES = 3;
|
|
566
|
+
/**
|
|
567
|
+
* Report a dispatch-guard failure WITHOUT going through any pipeline —
|
|
568
|
+
* the error channel for the logger itself must not recurse into the logger.
|
|
569
|
+
* Routes through writeStderr (the `_process` guard) for browser safety.
|
|
570
|
+
*/
|
|
571
|
+
function reportGuard(what, err) {
|
|
572
|
+
const detail = err instanceof Error ? err.stack ?? err.message : String(err);
|
|
573
|
+
try {
|
|
574
|
+
writeStderr(`loggily: ${what}: ${detail}`);
|
|
575
|
+
} catch {}
|
|
576
|
+
}
|
|
378
577
|
const VALID_CONFIG_KEYS = new Set([
|
|
379
578
|
"level",
|
|
380
579
|
"ns",
|
|
@@ -478,23 +677,59 @@ function buildPipeline(elements, parentConfig) {
|
|
|
478
677
|
}
|
|
479
678
|
throw new Error(`loggily: unsupported config element of type "${typeof element}". Config arrays accept: objects (config), arrays (branches), functions (stages), console, "console", or writables ({ write }).`);
|
|
480
679
|
}
|
|
680
|
+
const stageReported = [];
|
|
681
|
+
const branchReported = [];
|
|
481
682
|
const dispatch = (event) => {
|
|
482
683
|
if (event.kind === "span" && !spansEnabled) return;
|
|
483
684
|
let e = event;
|
|
484
|
-
for (
|
|
485
|
-
|
|
685
|
+
for (let i = 0; i < stages.length; i++) {
|
|
686
|
+
let result;
|
|
687
|
+
try {
|
|
688
|
+
result = stages[i](e);
|
|
689
|
+
} catch (err) {
|
|
690
|
+
if (!stageReported[i]) {
|
|
691
|
+
stageReported[i] = true;
|
|
692
|
+
reportGuard(`stage #${i} threw — dropping event, fail-closed (reported once)`, err);
|
|
693
|
+
}
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
486
696
|
if (result === null) return;
|
|
487
697
|
if (result !== void 0) e = result;
|
|
488
698
|
}
|
|
489
699
|
for (const output of outputs) {
|
|
700
|
+
if (output.disabled) continue;
|
|
490
701
|
if (e.kind === "log" && LOG_LEVEL_PRIORITY[e.level] < output.levelPriority) continue;
|
|
491
702
|
if (output.nsFilter && !output.nsFilter(e.namespace)) continue;
|
|
492
|
-
|
|
703
|
+
try {
|
|
704
|
+
output.write(e);
|
|
705
|
+
if (output.failures) output.failures = 0;
|
|
706
|
+
} catch (err) {
|
|
707
|
+
output.failures = (output.failures ?? 0) + 1;
|
|
708
|
+
if (output.failures === 1) reportGuard("output write threw — event lost for this sink", err);
|
|
709
|
+
if (output.failures >= OUTPUT_MAX_STRIKES) {
|
|
710
|
+
output.disabled = true;
|
|
711
|
+
reportGuard(`output disabled after ${output.failures} consecutive failures`, err);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
493
714
|
}
|
|
494
|
-
for (
|
|
715
|
+
for (let i = 0; i < branches.length; i++) try {
|
|
716
|
+
branches[i].dispatch(e);
|
|
717
|
+
} catch (err) {
|
|
718
|
+
if (!branchReported[i]) {
|
|
719
|
+
branchReported[i] = true;
|
|
720
|
+
reportGuard(`branch #${i} dispatch threw (reported once)`, err);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
const spanEnabledForNamespace = (namespace) => {
|
|
725
|
+
if (!spansEnabled) return false;
|
|
726
|
+
if (outputs.some((output) => !output.nsFilter || output.nsFilter(namespace))) return true;
|
|
727
|
+
if (branches.some((branch) => branch.spanEnabled(namespace))) return true;
|
|
728
|
+
return stages.length > 0;
|
|
495
729
|
};
|
|
496
730
|
return {
|
|
497
731
|
dispatch,
|
|
732
|
+
spanEnabled: spanEnabledForNamespace,
|
|
498
733
|
level: config.level,
|
|
499
734
|
dispose: () => {
|
|
500
735
|
for (const d of disposables) d();
|
|
@@ -573,6 +808,12 @@ function readEnvTrace() {
|
|
|
573
808
|
* ])
|
|
574
809
|
* log.info?.('server started', { port: 3000 })
|
|
575
810
|
*/
|
|
811
|
+
const SPAN_ENABLED = Symbol("loggily.spanEnabled");
|
|
812
|
+
function spanIsEnabled(logger, namespace) {
|
|
813
|
+
if (collectSpans) return true;
|
|
814
|
+
const checker = logger[SPAN_ENABLED];
|
|
815
|
+
return checker ? checker(namespace) : true;
|
|
816
|
+
}
|
|
576
817
|
function resetIds() {
|
|
577
818
|
resetIdCounters();
|
|
578
819
|
}
|
|
@@ -638,6 +879,7 @@ function createLoggerImpl(name, props, pipeline) {
|
|
|
638
879
|
const emitLog = (level, msgOrError, dataOrMsg, extraData) => {
|
|
639
880
|
let message;
|
|
640
881
|
let data;
|
|
882
|
+
const userArgs = [];
|
|
641
883
|
if (msgOrError instanceof Error) {
|
|
642
884
|
const err = msgOrError;
|
|
643
885
|
const contextTags = _getContextTags?.() ?? {};
|
|
@@ -665,6 +907,8 @@ function createLoggerImpl(name, props, pipeline) {
|
|
|
665
907
|
error_cause: err.cause !== void 0 ? serializeCause(err.cause) : void 0
|
|
666
908
|
};
|
|
667
909
|
}
|
|
910
|
+
userArgs.push(err);
|
|
911
|
+
if (data && Object.keys(data).length > 0) userArgs.push(data);
|
|
668
912
|
} else {
|
|
669
913
|
message = resolveMessage(msgOrError);
|
|
670
914
|
const contextTags = _getContextTags?.();
|
|
@@ -676,6 +920,7 @@ function createLoggerImpl(name, props, pipeline) {
|
|
|
676
920
|
...props,
|
|
677
921
|
...dataOrMsg
|
|
678
922
|
} : void 0;
|
|
923
|
+
if (data && Object.keys(data).length > 0) userArgs.push(data);
|
|
679
924
|
}
|
|
680
925
|
const event = {
|
|
681
926
|
kind: "log",
|
|
@@ -683,7 +928,8 @@ function createLoggerImpl(name, props, pipeline) {
|
|
|
683
928
|
namespace: name,
|
|
684
929
|
level,
|
|
685
930
|
message,
|
|
686
|
-
props: data
|
|
931
|
+
props: data,
|
|
932
|
+
userArgs
|
|
687
933
|
};
|
|
688
934
|
pipeline.dispatch(event);
|
|
689
935
|
};
|
|
@@ -693,6 +939,9 @@ function createLoggerImpl(name, props, pipeline) {
|
|
|
693
939
|
get level() {
|
|
694
940
|
return pipeline.level;
|
|
695
941
|
},
|
|
942
|
+
[SPAN_ENABLED](namespace) {
|
|
943
|
+
return pipeline.spanEnabled(namespace);
|
|
944
|
+
},
|
|
696
945
|
dispatch(event) {
|
|
697
946
|
pipeline.dispatch(event);
|
|
698
947
|
},
|
|
@@ -704,6 +953,7 @@ function createLoggerImpl(name, props, pipeline) {
|
|
|
704
953
|
info: (msg, data) => emitLog("info", msg, data),
|
|
705
954
|
warn: (msg, data) => emitLog("warn", msg, data),
|
|
706
955
|
error: (msgOrError, dataOrMsg, extraData) => emitLog("error", msgOrError, dataOrMsg, extraData),
|
|
956
|
+
/** @deprecated Use .child() instead */
|
|
707
957
|
logger(namespace, childProps) {
|
|
708
958
|
return this.child(namespace ?? "", childProps);
|
|
709
959
|
},
|
|
@@ -757,8 +1007,9 @@ function augmentWithSpans(logger, parentSpanId, traceId, traceSampled) {
|
|
|
757
1007
|
traceId,
|
|
758
1008
|
traceSampled
|
|
759
1009
|
};
|
|
1010
|
+
const spanMethod = spanIsEnabled(logger, logger.name) ? createSpanMethod(logger, spanState) : void 0;
|
|
760
1011
|
return new Proxy(logger, { get(target, prop) {
|
|
761
|
-
if (prop === "span") return
|
|
1012
|
+
if (prop === "span") return spanMethod;
|
|
762
1013
|
if (prop === "child") return function child(namespaceOrContext, childProps) {
|
|
763
1014
|
return augmentWithSpans(target.child(namespaceOrContext, childProps), spanState.parentSpanId, spanState.traceId, spanState.traceSampled);
|
|
764
1015
|
};
|
|
@@ -796,7 +1047,8 @@ function createSpanMethod(logger, spanState) {
|
|
|
796
1047
|
startTime: Date.now(),
|
|
797
1048
|
endTime: null,
|
|
798
1049
|
duration: null,
|
|
799
|
-
attrs: {}
|
|
1050
|
+
attrs: {},
|
|
1051
|
+
laps: []
|
|
800
1052
|
};
|
|
801
1053
|
const spanAugmented = augmentWithSpans(logger.child(namespace ?? "", resolvedChildProps), newSpanId, finalTraceId, sampled);
|
|
802
1054
|
_enterContext?.(newSpanId, finalTraceId, resolvedParentId);
|
|
@@ -814,6 +1066,7 @@ function createSpanMethod(logger, spanState) {
|
|
|
814
1066
|
}), { ...newSpanData.attrs }));
|
|
815
1067
|
_exitContext?.(newSpanId);
|
|
816
1068
|
if (sampled) {
|
|
1069
|
+
const lapsProp = newSpanData.laps.length > 0 ? Object.fromEntries(newSpanData.laps.map((l) => [l.name, l.deltaMs])) : void 0;
|
|
817
1070
|
const spanEvent = {
|
|
818
1071
|
kind: "span",
|
|
819
1072
|
time: newSpanData.endTime,
|
|
@@ -822,7 +1075,8 @@ function createSpanMethod(logger, spanState) {
|
|
|
822
1075
|
duration: newSpanData.duration,
|
|
823
1076
|
props: {
|
|
824
1077
|
...mergedProps,
|
|
825
|
-
...newSpanData.attrs
|
|
1078
|
+
...newSpanData.attrs,
|
|
1079
|
+
...lapsProp ? { laps: lapsProp } : {}
|
|
826
1080
|
},
|
|
827
1081
|
spanId: newSpanData.id,
|
|
828
1082
|
traceId: newSpanData.traceId,
|
|
@@ -831,6 +1085,16 @@ function createSpanMethod(logger, spanState) {
|
|
|
831
1085
|
logger.dispatch(spanEvent);
|
|
832
1086
|
}
|
|
833
1087
|
};
|
|
1088
|
+
const lapImpl = (name) => {
|
|
1089
|
+
const elapsedMs = Date.now() - newSpanData.startTime;
|
|
1090
|
+
const lastLap = newSpanData.laps[newSpanData.laps.length - 1];
|
|
1091
|
+
const deltaMs = lastLap ? elapsedMs - lastLap.elapsedMs : elapsedMs;
|
|
1092
|
+
newSpanData.laps.push({
|
|
1093
|
+
name,
|
|
1094
|
+
elapsedMs,
|
|
1095
|
+
deltaMs
|
|
1096
|
+
});
|
|
1097
|
+
};
|
|
834
1098
|
const spanDataProxy = createSpanDataProxy(() => ({
|
|
835
1099
|
id: newSpanData.id,
|
|
836
1100
|
traceId: newSpanData.traceId,
|
|
@@ -847,6 +1111,7 @@ function createSpanMethod(logger, spanState) {
|
|
|
847
1111
|
if (prop === "end") return () => {
|
|
848
1112
|
if (newSpanData.endTime === null) currentDispose();
|
|
849
1113
|
};
|
|
1114
|
+
if (prop === "lap") return lapImpl;
|
|
850
1115
|
if (prop === "name") return childName;
|
|
851
1116
|
if (prop === "props") return Object.freeze({ ...mergedProps });
|
|
852
1117
|
return target[prop];
|
|
@@ -938,6 +1203,15 @@ function withEnvDefaults() {
|
|
|
938
1203
|
*/
|
|
939
1204
|
function applyNamespaceGating(logger) {
|
|
940
1205
|
return new Proxy(logger, { get(target, prop) {
|
|
1206
|
+
if (prop === SPAN_ENABLED) return (namespace) => {
|
|
1207
|
+
if (collectSpans) return true;
|
|
1208
|
+
const trace = currentTrace();
|
|
1209
|
+
if (!trace.enabled) return false;
|
|
1210
|
+
if (trace.filter && !trace.filter(namespace)) return false;
|
|
1211
|
+
const ns = currentNs();
|
|
1212
|
+
if (ns && !ns(namespace)) return false;
|
|
1213
|
+
return true;
|
|
1214
|
+
};
|
|
941
1215
|
if (typeof prop === "string" && prop in LOG_LEVEL_PRIORITY && prop !== "silent") {
|
|
942
1216
|
const nsLevel = readEnvLevelForNamespace(target.name);
|
|
943
1217
|
if (LOG_LEVEL_PRIORITY[prop] < LOG_LEVEL_PRIORITY[nsLevel]) return;
|
|
@@ -966,14 +1240,23 @@ function createEnvPipeline() {
|
|
|
966
1240
|
}
|
|
967
1241
|
const ns = currentNs();
|
|
968
1242
|
if (ns && !ns(event.namespace)) return;
|
|
969
|
-
const
|
|
1243
|
+
const format = currentFormat();
|
|
1244
|
+
const text = (format === "json" ? formatJSONEvent : formatConsoleEvent)(event);
|
|
970
1245
|
const lvl = event.kind === "log" ? event.level : "span";
|
|
971
|
-
for (const w of _writers) w(text, lvl);
|
|
972
|
-
if (!_suppressConsole)
|
|
1246
|
+
for (const w of _writers) w(text, lvl, event.namespace, event);
|
|
1247
|
+
if (!_suppressConsole) createConsoleSink$1(format)(event);
|
|
973
1248
|
fileSink?.(event);
|
|
974
1249
|
};
|
|
975
1250
|
return {
|
|
976
1251
|
dispatch,
|
|
1252
|
+
spanEnabled(namespace) {
|
|
1253
|
+
const trace = currentTrace();
|
|
1254
|
+
if (!trace.enabled) return false;
|
|
1255
|
+
if (trace.filter && !trace.filter(namespace)) return false;
|
|
1256
|
+
const ns = currentNs();
|
|
1257
|
+
if (ns && !ns(namespace)) return false;
|
|
1258
|
+
return true;
|
|
1259
|
+
},
|
|
977
1260
|
get level() {
|
|
978
1261
|
return currentLevel();
|
|
979
1262
|
},
|
|
@@ -1048,7 +1331,21 @@ function setOutputMode(_mode) {
|
|
|
1048
1331
|
function getOutputMode() {
|
|
1049
1332
|
return "console";
|
|
1050
1333
|
}
|
|
1051
|
-
function addWriter(
|
|
1334
|
+
function addWriter(arg1, arg2) {
|
|
1335
|
+
if (typeof arg1 === "function") return _addRawWriter(arg1);
|
|
1336
|
+
if (!arg2) throw new Error("addWriter: writer fn required when first argument is a config object");
|
|
1337
|
+
const config = arg1;
|
|
1338
|
+
const matches = config.ns ? parseNsFilter(config.ns) : null;
|
|
1339
|
+
const minPriority = config.level ? LOG_LEVEL_PRIORITY[config.level] : null;
|
|
1340
|
+
return _addRawWriter((formatted, level, namespace, event) => {
|
|
1341
|
+
if (matches && !matches(namespace)) return;
|
|
1342
|
+
if (minPriority !== null) {
|
|
1343
|
+
if ((LOG_LEVEL_PRIORITY[level] ?? LOG_LEVEL_PRIORITY.info) < minPriority) return;
|
|
1344
|
+
}
|
|
1345
|
+
arg2(formatted, level, namespace, event);
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
function _addRawWriter(writer) {
|
|
1052
1349
|
_writers.push(writer);
|
|
1053
1350
|
return () => {
|
|
1054
1351
|
const i = _writers.indexOf(writer);
|
|
@@ -1071,4 +1368,4 @@ function writeSpan(namespace, duration, attrs) {
|
|
|
1071
1368
|
//#endregion
|
|
1072
1369
|
export { withEnvDefaults as A, setSampleRate as B, setOutputMode as C, startCollecting as D, spansAreEnabled as E, safeStringify as F, createFileWriter as H, serializeCause as I, getIdFormat as L, writeSpan as M, LOG_LEVEL_PRIORITY as N, stopCollecting as O, buildPipeline as P, getSampleRate as R, setLogLevel as S, setTraceFilter as T, traceparent as V, getTraceFilter as _, baseCreateLogger as a, setDebugFilter as b, createSpanDataProxy as c, enableSpans as d, getCollectedSpans as f, getOutputMode as g, getLogLevel as h, addWriter as i, withSpans as j, withConfigMetrics as k, createTestLogger as l, getLogFormat as m, _setContextHooks as n, clearCollectedSpans as o, getDebugFilter as p, _setLogFileWriterFactory as r, createLogger as s, _clearContextHooks as t, disableSpans as u, pipe as v, setSuppressConsole as w, setLogFormat as x, resetIds as y, setIdFormat as z };
|
|
1073
1370
|
|
|
1074
|
-
//# sourceMappingURL=core-
|
|
1371
|
+
//# sourceMappingURL=core-DKwMMOZw.mjs.map
|