@logtape/logtape 2.1.0-dev.564 → 2.1.0-dev.600
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/config.cjs +108 -18
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +108 -18
- package/dist/config.js.map +1 -1
- package/dist/filter.cjs +191 -0
- package/dist/filter.d.cts +138 -1
- package/dist/filter.d.cts.map +1 -1
- package/dist/filter.d.ts +138 -1
- package/dist/filter.d.ts.map +1 -1
- package/dist/filter.js +191 -1
- package/dist/filter.js.map +1 -1
- package/dist/formatter.cjs +159 -14
- package/dist/formatter.d.cts +79 -1
- package/dist/formatter.d.cts.map +1 -1
- package/dist/formatter.d.ts +79 -1
- package/dist/formatter.d.ts.map +1 -1
- package/dist/formatter.js +157 -14
- package/dist/formatter.js.map +1 -1
- package/dist/logger.cjs +7 -0
- package/dist/logger.d.cts.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +7 -0
- package/dist/logger.js.map +1 -1
- package/dist/mod.cjs +3 -0
- package/dist/mod.d.cts +3 -3
- package/dist/mod.d.ts +3 -3
- package/dist/mod.js +3 -3
- package/dist/sink.cjs +3 -3
- package/dist/sink.js +3 -3
- package/dist/sink.js.map +1 -1
- package/package.json +1 -1
- package/skills/logtape/SKILL.md +4 -0
package/dist/formatter.cjs
CHANGED
|
@@ -23,7 +23,7 @@ const levelAbbreviations = {
|
|
|
23
23
|
* If `colors` is `true`, the output will be ANSI-colored.
|
|
24
24
|
* @returns The string representation of the value.
|
|
25
25
|
*/
|
|
26
|
-
const
|
|
26
|
+
const platformInspect = typeof document !== "undefined" || typeof navigator !== "undefined" && navigator.product === "ReactNative" ? (v) => JSON.stringify(v) : "Deno" in globalThis && "inspect" in globalThis.Deno && typeof globalThis.Deno.inspect === "function" ? (v, opts) => globalThis.Deno.inspect(v, {
|
|
27
27
|
strAbbreviateSize: Infinity,
|
|
28
28
|
iterableLimit: Infinity,
|
|
29
29
|
...opts
|
|
@@ -32,6 +32,20 @@ const inspect = typeof document !== "undefined" || typeof navigator !== "undefin
|
|
|
32
32
|
maxStringLength: Infinity,
|
|
33
33
|
...opts
|
|
34
34
|
}) : (v) => JSON.stringify(v);
|
|
35
|
+
const inspect = (value, options) => String(platformInspect(value, options));
|
|
36
|
+
const utf8Encoder = new TextEncoder();
|
|
37
|
+
function renderMessageParts(msgParts, valueRenderer) {
|
|
38
|
+
const msgLen = msgParts.length;
|
|
39
|
+
if (msgLen === 1) return msgParts[0];
|
|
40
|
+
if (msgLen <= 6) {
|
|
41
|
+
let message = "";
|
|
42
|
+
for (let i = 0; i < msgLen; i++) message += i % 2 === 0 ? msgParts[i] : valueRenderer(msgParts[i]);
|
|
43
|
+
return message;
|
|
44
|
+
}
|
|
45
|
+
const parts = new Array(msgLen);
|
|
46
|
+
for (let i = 0; i < msgLen; i++) parts[i] = i % 2 === 0 ? msgParts[i] : valueRenderer(msgParts[i]);
|
|
47
|
+
return parts.join("");
|
|
48
|
+
}
|
|
35
49
|
function padZero(num) {
|
|
36
50
|
return num < 10 ? `0${num}` : `${num}`;
|
|
37
51
|
}
|
|
@@ -272,18 +286,7 @@ function getTextFormatter(options = {}) {
|
|
|
272
286
|
const lineEnding = getLineEndingValue(options.lineEnding);
|
|
273
287
|
const formatter = options.format ?? (({ timestamp, level, category, message }) => `${timestamp ? `${timestamp} ` : ""}[${level}] ${category}: ${message}`);
|
|
274
288
|
return (record) => {
|
|
275
|
-
const
|
|
276
|
-
const msgLen = msgParts.length;
|
|
277
|
-
let message;
|
|
278
|
-
if (msgLen === 1) message = msgParts[0];
|
|
279
|
-
else if (msgLen <= 6) {
|
|
280
|
-
message = "";
|
|
281
|
-
for (let i = 0; i < msgLen; i++) message += i % 2 === 0 ? msgParts[i] : valueRenderer(msgParts[i]);
|
|
282
|
-
} else {
|
|
283
|
-
const parts = new Array(msgLen);
|
|
284
|
-
for (let i = 0; i < msgLen; i++) parts[i] = i % 2 === 0 ? msgParts[i] : valueRenderer(msgParts[i]);
|
|
285
|
-
message = parts.join("");
|
|
286
|
-
}
|
|
289
|
+
const message = renderMessageParts(record.message, valueRenderer);
|
|
287
290
|
const timestamp = timestampRenderer(record.timestamp);
|
|
288
291
|
const level = levelRenderer(record.level);
|
|
289
292
|
const category = typeof categorySeparator === "function" ? categorySeparator(record.category) : record.category.join(categorySeparator);
|
|
@@ -492,6 +495,146 @@ function getJsonLinesFormatter(options = {}) {
|
|
|
492
495
|
* @since 0.11.0
|
|
493
496
|
*/
|
|
494
497
|
const jsonLinesFormatter = getJsonLinesFormatter();
|
|
498
|
+
function renderStructuredMessage(record, template) {
|
|
499
|
+
if (template) {
|
|
500
|
+
if (typeof record.rawMessage === "string") return record.rawMessage;
|
|
501
|
+
return record.rawMessage.join("{}");
|
|
502
|
+
}
|
|
503
|
+
return renderMessageParts(record.message, stringifyLogfmtValue);
|
|
504
|
+
}
|
|
505
|
+
function filterLogfmtKey(key) {
|
|
506
|
+
if (key === "") return null;
|
|
507
|
+
let needsEscape = false;
|
|
508
|
+
for (const char of key) {
|
|
509
|
+
const code = char.codePointAt(0);
|
|
510
|
+
if (shouldEscapeLogfmtKeyChar(char, code)) {
|
|
511
|
+
needsEscape = true;
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
if (!needsEscape) return key;
|
|
516
|
+
let result = "";
|
|
517
|
+
for (const char of key) {
|
|
518
|
+
const code = char.codePointAt(0);
|
|
519
|
+
if (shouldEscapeLogfmtKeyChar(char, code)) result += encodeLogfmtKeyChar(char);
|
|
520
|
+
else result += char;
|
|
521
|
+
}
|
|
522
|
+
return result;
|
|
523
|
+
}
|
|
524
|
+
function shouldEscapeLogfmtKeyChar(char, code) {
|
|
525
|
+
return code <= 32 || code === 127 || code === 65533 || char === "=" || char === "\"" || char === "%";
|
|
526
|
+
}
|
|
527
|
+
function encodeLogfmtKeyChar(char) {
|
|
528
|
+
let result = "";
|
|
529
|
+
for (const byte of utf8Encoder.encode(char)) result += `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
|
|
530
|
+
return result;
|
|
531
|
+
}
|
|
532
|
+
function stringifyLogfmtValue(value) {
|
|
533
|
+
if (typeof value === "string") return value;
|
|
534
|
+
if (value === null) return "null";
|
|
535
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "undefined" || typeof value === "symbol" || typeof value === "function") return String(value);
|
|
536
|
+
try {
|
|
537
|
+
const json = JSON.stringify(value, jsonReplacer);
|
|
538
|
+
if (typeof json === "string") return unwrapJsonStringLiteral(json);
|
|
539
|
+
} catch {}
|
|
540
|
+
return inspect(value, { colors: false });
|
|
541
|
+
}
|
|
542
|
+
function unwrapJsonStringLiteral(json) {
|
|
543
|
+
if (json.startsWith("\"") && json.endsWith("\"")) return JSON.parse(json);
|
|
544
|
+
return json;
|
|
545
|
+
}
|
|
546
|
+
function quoteLogfmtValue(value, isString) {
|
|
547
|
+
let needsQuote = value === "" || isString && shouldQuoteStringLiteral(value);
|
|
548
|
+
for (const char of value) {
|
|
549
|
+
const code = char.codePointAt(0);
|
|
550
|
+
if (shouldQuoteLogfmtValueChar(char, code)) {
|
|
551
|
+
needsQuote = true;
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
if (!needsQuote) return value;
|
|
556
|
+
let quoted = "";
|
|
557
|
+
for (const char of value) {
|
|
558
|
+
const code = char.codePointAt(0);
|
|
559
|
+
quoted += escapeLogfmtValueChar(char, code);
|
|
560
|
+
}
|
|
561
|
+
return `"${quoted}"`;
|
|
562
|
+
}
|
|
563
|
+
function shouldQuoteStringLiteral(value) {
|
|
564
|
+
return value === "null" || value === "undefined" || value === "true" || value === "false";
|
|
565
|
+
}
|
|
566
|
+
function shouldQuoteLogfmtValueChar(char, code) {
|
|
567
|
+
return code <= 32 || code === 127 || code === 65533 || char === "=" || char === "\"" || char === "\\";
|
|
568
|
+
}
|
|
569
|
+
function escapeLogfmtValueChar(char, code) {
|
|
570
|
+
switch (char) {
|
|
571
|
+
case " ": return "\\t";
|
|
572
|
+
case "\n": return "\\n";
|
|
573
|
+
case "\r": return "\\r";
|
|
574
|
+
case "\"": return "\\\"";
|
|
575
|
+
case "\\": return "\\\\";
|
|
576
|
+
default: return code <= 31 || code === 127 ? `\\u${code.toString(16).padStart(4, "0")}` : char;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
function formatLogfmtValue(value) {
|
|
580
|
+
const stringified = stringifyLogfmtValue(value);
|
|
581
|
+
return quoteLogfmtValue(stringified, typeof value === "string");
|
|
582
|
+
}
|
|
583
|
+
function pushLogfmtPair(pairs, key, value) {
|
|
584
|
+
const filteredKey = filterLogfmtKey(key);
|
|
585
|
+
if (filteredKey == null) return;
|
|
586
|
+
pairs.push(`${filteredKey}=${formatLogfmtValue(value)}`);
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Get a [logfmt] formatter with the specified options. The log records
|
|
590
|
+
* will be rendered as space-delimited key-value pairs, one record per line.
|
|
591
|
+
* It looks like this:
|
|
592
|
+
*
|
|
593
|
+
* ```text
|
|
594
|
+
* time=2023-11-14T22:13:20.000Z level=info logger=my.logger msg="Hello, world!" key=value
|
|
595
|
+
* ```
|
|
596
|
+
*
|
|
597
|
+
* [logfmt]: https://brandur.org/logfmt
|
|
598
|
+
* @param options The options for the logfmt formatter.
|
|
599
|
+
* @returns The logfmt formatter.
|
|
600
|
+
* @since 2.1.0
|
|
601
|
+
*/
|
|
602
|
+
function getLogfmtFormatter(options = {}) {
|
|
603
|
+
const prependPrefix = "prepend:";
|
|
604
|
+
const lineEnding = getLineEndingValue(options.lineEnding);
|
|
605
|
+
const timestampRenderer = createTimestampFormatter("rfc3339", resolveTimeZone(options.timeZone));
|
|
606
|
+
const isTemplateMessage = options.message === "template";
|
|
607
|
+
const propertiesOption = options.properties ?? "flatten";
|
|
608
|
+
let joinCategory;
|
|
609
|
+
if (typeof options.categorySeparator === "function") joinCategory = options.categorySeparator;
|
|
610
|
+
else {
|
|
611
|
+
const separator = options.categorySeparator ?? ".";
|
|
612
|
+
joinCategory = (category) => category.join(separator);
|
|
613
|
+
}
|
|
614
|
+
let propertyPrefix = "";
|
|
615
|
+
if (propertiesOption === "flatten") propertyPrefix = "";
|
|
616
|
+
else if (propertiesOption.startsWith(prependPrefix)) {
|
|
617
|
+
propertyPrefix = propertiesOption.substring(prependPrefix.length);
|
|
618
|
+
if (propertyPrefix === "") throw new TypeError("Invalid properties option: " + JSON.stringify(propertiesOption) + ". It must be of the form \"prepend:<prefix>\" where <prefix> is a non-empty string.");
|
|
619
|
+
} else throw new TypeError(`Invalid properties option: ${JSON.stringify(propertiesOption)}. It must be "flatten" or "prepend:<prefix>".`);
|
|
620
|
+
return (record) => {
|
|
621
|
+
const pairs = [];
|
|
622
|
+
pushLogfmtPair(pairs, "time", timestampRenderer(record.timestamp));
|
|
623
|
+
pushLogfmtPair(pairs, "level", record.level);
|
|
624
|
+
pushLogfmtPair(pairs, "logger", joinCategory(record.category));
|
|
625
|
+
pushLogfmtPair(pairs, "msg", renderStructuredMessage(record, isTemplateMessage));
|
|
626
|
+
for (const key in record.properties) if (Object.prototype.hasOwnProperty.call(record.properties, key)) pushLogfmtPair(pairs, `${propertyPrefix}${key}`, record.properties[key]);
|
|
627
|
+
return `${pairs.join(" ")}${lineEnding}`;
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* The default [logfmt] formatter. This formatter formats log records as
|
|
632
|
+
* space-delimited key-value pairs, one record per line.
|
|
633
|
+
*
|
|
634
|
+
* [logfmt]: https://brandur.org/logfmt
|
|
635
|
+
* @since 2.1.0
|
|
636
|
+
*/
|
|
637
|
+
const logfmtFormatter = getLogfmtFormatter();
|
|
495
638
|
/**
|
|
496
639
|
* The styles for the log level in the console.
|
|
497
640
|
*/
|
|
@@ -537,5 +680,7 @@ exports.defaultConsoleFormatter = defaultConsoleFormatter;
|
|
|
537
680
|
exports.defaultTextFormatter = defaultTextFormatter;
|
|
538
681
|
exports.getAnsiColorFormatter = getAnsiColorFormatter;
|
|
539
682
|
exports.getJsonLinesFormatter = getJsonLinesFormatter;
|
|
683
|
+
exports.getLogfmtFormatter = getLogfmtFormatter;
|
|
540
684
|
exports.getTextFormatter = getTextFormatter;
|
|
541
|
-
exports.jsonLinesFormatter = jsonLinesFormatter;
|
|
685
|
+
exports.jsonLinesFormatter = jsonLinesFormatter;
|
|
686
|
+
exports.logfmtFormatter = logfmtFormatter;
|
package/dist/formatter.d.cts
CHANGED
|
@@ -372,6 +372,84 @@ declare function getJsonLinesFormatter(options?: JsonLinesFormatterOptions): Tex
|
|
|
372
372
|
* @since 0.11.0
|
|
373
373
|
*/
|
|
374
374
|
declare const jsonLinesFormatter: TextFormatter;
|
|
375
|
+
/**
|
|
376
|
+
* Options for the {@link getLogfmtFormatter} function.
|
|
377
|
+
* @since 2.1.0
|
|
378
|
+
*/
|
|
379
|
+
interface LogfmtFormatterOptions {
|
|
380
|
+
/**
|
|
381
|
+
* The separator between category names. For example, if the separator is
|
|
382
|
+
* `"."`, the category `["a", "b", "c"]` will be formatted as `"a.b.c"`.
|
|
383
|
+
* If this is a function, it will be called with the category array and
|
|
384
|
+
* should return a string, which will be used for rendering the category.
|
|
385
|
+
*
|
|
386
|
+
* @default `"."`
|
|
387
|
+
*/
|
|
388
|
+
readonly categorySeparator?: string | ((category: readonly string[]) => string);
|
|
389
|
+
/**
|
|
390
|
+
* The message format. This can be one of the following:
|
|
391
|
+
*
|
|
392
|
+
* - `"template"`: The raw message template is used as the message.
|
|
393
|
+
* - `"rendered"`: The message is rendered with the values.
|
|
394
|
+
*
|
|
395
|
+
* @default `"rendered"`
|
|
396
|
+
*/
|
|
397
|
+
readonly message?: "template" | "rendered";
|
|
398
|
+
/**
|
|
399
|
+
* The properties format. This can be one of the following:
|
|
400
|
+
*
|
|
401
|
+
* - `"flatten"`: The properties are flattened into logfmt key-value pairs.
|
|
402
|
+
* - `"prepend:<prefix>"`: The properties are prepended with the given prefix
|
|
403
|
+
* (e.g., `"prepend:ctx_"` will prepend `ctx_` to each property key).
|
|
404
|
+
*
|
|
405
|
+
* @default `"flatten"`
|
|
406
|
+
*/
|
|
407
|
+
readonly properties?: "flatten" | `prepend:${string}`;
|
|
408
|
+
/**
|
|
409
|
+
* The timezone used for timestamp rendering.
|
|
410
|
+
*
|
|
411
|
+
* - `undefined` (default): UTC
|
|
412
|
+
* - `null`: System local timezone
|
|
413
|
+
* - IANA timezone name such as `"America/Bogota"` or `"Asia/Seoul"`
|
|
414
|
+
* - Fixed UTC offset string such as `"+09:00"` or `"-05:00"`
|
|
415
|
+
*
|
|
416
|
+
* @since 2.1.0
|
|
417
|
+
*/
|
|
418
|
+
readonly timeZone?: string | null;
|
|
419
|
+
/**
|
|
420
|
+
* Line ending style for formatted output.
|
|
421
|
+
*
|
|
422
|
+
* - `"lf"`: Unix-style line endings (`\n`)
|
|
423
|
+
* - `"crlf"`: Windows-style line endings (`\r\n`)
|
|
424
|
+
*
|
|
425
|
+
* @default "lf"
|
|
426
|
+
* @since 2.1.0
|
|
427
|
+
*/
|
|
428
|
+
readonly lineEnding?: "lf" | "crlf";
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Get a [logfmt] formatter with the specified options. The log records
|
|
432
|
+
* will be rendered as space-delimited key-value pairs, one record per line.
|
|
433
|
+
* It looks like this:
|
|
434
|
+
*
|
|
435
|
+
* ```text
|
|
436
|
+
* time=2023-11-14T22:13:20.000Z level=info logger=my.logger msg="Hello, world!" key=value
|
|
437
|
+
* ```
|
|
438
|
+
*
|
|
439
|
+
* [logfmt]: https://brandur.org/logfmt
|
|
440
|
+
* @param options The options for the logfmt formatter.
|
|
441
|
+
* @returns The logfmt formatter.
|
|
442
|
+
* @since 2.1.0
|
|
443
|
+
*/
|
|
444
|
+
declare function getLogfmtFormatter(options?: LogfmtFormatterOptions): TextFormatter;
|
|
445
|
+
/**
|
|
446
|
+
* The default [logfmt] formatter. This formatter formats log records as
|
|
447
|
+
* space-delimited key-value pairs, one record per line.
|
|
448
|
+
*
|
|
449
|
+
* [logfmt]: https://brandur.org/logfmt
|
|
450
|
+
* @since 2.1.0
|
|
451
|
+
*/
|
|
452
|
+
declare const logfmtFormatter: TextFormatter;
|
|
375
453
|
/**
|
|
376
454
|
* A console formatter is a function that accepts a log record and returns
|
|
377
455
|
* an array of arguments to pass to {@link console.log}.
|
|
@@ -391,5 +469,5 @@ type ConsoleFormatter = (record: LogRecord) => readonly unknown[];
|
|
|
391
469
|
declare function defaultConsoleFormatter(record: LogRecord): readonly unknown[];
|
|
392
470
|
//# sourceMappingURL=formatter.d.ts.map
|
|
393
471
|
//#endregion
|
|
394
|
-
export { AnsiColor, AnsiColorFormatterOptions, AnsiStyle, ConsoleFormatter, FormattedValues, JsonLinesFormatterOptions, TextFormatter, TextFormatterOptions, ansiColorFormatter, defaultConsoleFormatter, defaultTextFormatter, getAnsiColorFormatter, getJsonLinesFormatter, getTextFormatter, jsonLinesFormatter };
|
|
472
|
+
export { AnsiColor, AnsiColorFormatterOptions, AnsiStyle, ConsoleFormatter, FormattedValues, JsonLinesFormatterOptions, LogfmtFormatterOptions, TextFormatter, TextFormatterOptions, ansiColorFormatter, defaultConsoleFormatter, defaultTextFormatter, getAnsiColorFormatter, getJsonLinesFormatter, getLogfmtFormatter, getTextFormatter, jsonLinesFormatter, logfmtFormatter };
|
|
395
473
|
//# sourceMappingURL=formatter.d.cts.map
|
package/dist/formatter.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatter.d.cts","names":[],"sources":["../src/formatter.ts"],"sourcesContent":[],"mappings":";;;;;;;AAWA;
|
|
1
|
+
{"version":3,"file":"formatter.d.cts","names":[],"sources":["../src/formatter.ts"],"sourcesContent":[],"mappings":";;;;;;;AAWA;AAsGA;AA+BA;;;AA6IoB,KAlRR,aAAA,GAkRQ,CAAA,MAAA,EAlRiB,SAkRjB,EAAA,GAAA,MAAA;AAAe;AA+TnC;;;AAEG,UA7ec,eAAA,CA6ed;EAAa;AA0FhB;AAQA;EAyBY,SAAA,EAAA,MAAS,GAAA,IAAA;EA4BJ;;;EA0CW,KAKT,EAAA,MAAA;EAAS;;;EAiBc,QAA1B,EAAA,MAAA;EAAM;;;EAhEiD,OAAA,EAAA,MAAA;EAqFvD;;;EACyB,MACtC,EAnsBO,SAmsBP;AAAa;AAgEhB;AAMA;AAiEA;;AACW,UAp0BM,oBAAA,CAo0BN;EAA8B;AACzB;AA8JhB;AAMA;AAoNA;;;;AAEgB;AA6EhB;AAUA;AAqBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mEAjuCe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAmEK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+TJ,gBAAA,WACL,uBACR;;;;;;;;;;;cA0FU,sBAAsB;;;;;KAQvB,SAAA;;;;;KAyBA,SAAA;;;;;UA4BK,yBAAA,SAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBA0ChC;;;;mBAKA;;;;eAKJ;;;;;;;;;;;gBAYC,OAAO,UAAU;;;;kBAKf;;;;kBAKA;;;;;;;;;;iBAWF,qBAAA,WACL,4BACR;;;;;;;;;;cAgEU,oBAAoB;;;;;UAMhB,yBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiED,qBAAA,WACL,4BACR;;;;;;;;;;;;;;;;;cA8JU,oBAAoB;;;;;UAMhB,sBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoND,kBAAA,WACL,yBACR;;;;;;;;cA6EU,iBAAiB;;;;;;;;;KAUlB,gBAAA,YAA4B;;;;;;;;iBAqBxB,uBAAA,SAAgC"}
|
package/dist/formatter.d.ts
CHANGED
|
@@ -372,6 +372,84 @@ declare function getJsonLinesFormatter(options?: JsonLinesFormatterOptions): Tex
|
|
|
372
372
|
* @since 0.11.0
|
|
373
373
|
*/
|
|
374
374
|
declare const jsonLinesFormatter: TextFormatter;
|
|
375
|
+
/**
|
|
376
|
+
* Options for the {@link getLogfmtFormatter} function.
|
|
377
|
+
* @since 2.1.0
|
|
378
|
+
*/
|
|
379
|
+
interface LogfmtFormatterOptions {
|
|
380
|
+
/**
|
|
381
|
+
* The separator between category names. For example, if the separator is
|
|
382
|
+
* `"."`, the category `["a", "b", "c"]` will be formatted as `"a.b.c"`.
|
|
383
|
+
* If this is a function, it will be called with the category array and
|
|
384
|
+
* should return a string, which will be used for rendering the category.
|
|
385
|
+
*
|
|
386
|
+
* @default `"."`
|
|
387
|
+
*/
|
|
388
|
+
readonly categorySeparator?: string | ((category: readonly string[]) => string);
|
|
389
|
+
/**
|
|
390
|
+
* The message format. This can be one of the following:
|
|
391
|
+
*
|
|
392
|
+
* - `"template"`: The raw message template is used as the message.
|
|
393
|
+
* - `"rendered"`: The message is rendered with the values.
|
|
394
|
+
*
|
|
395
|
+
* @default `"rendered"`
|
|
396
|
+
*/
|
|
397
|
+
readonly message?: "template" | "rendered";
|
|
398
|
+
/**
|
|
399
|
+
* The properties format. This can be one of the following:
|
|
400
|
+
*
|
|
401
|
+
* - `"flatten"`: The properties are flattened into logfmt key-value pairs.
|
|
402
|
+
* - `"prepend:<prefix>"`: The properties are prepended with the given prefix
|
|
403
|
+
* (e.g., `"prepend:ctx_"` will prepend `ctx_` to each property key).
|
|
404
|
+
*
|
|
405
|
+
* @default `"flatten"`
|
|
406
|
+
*/
|
|
407
|
+
readonly properties?: "flatten" | `prepend:${string}`;
|
|
408
|
+
/**
|
|
409
|
+
* The timezone used for timestamp rendering.
|
|
410
|
+
*
|
|
411
|
+
* - `undefined` (default): UTC
|
|
412
|
+
* - `null`: System local timezone
|
|
413
|
+
* - IANA timezone name such as `"America/Bogota"` or `"Asia/Seoul"`
|
|
414
|
+
* - Fixed UTC offset string such as `"+09:00"` or `"-05:00"`
|
|
415
|
+
*
|
|
416
|
+
* @since 2.1.0
|
|
417
|
+
*/
|
|
418
|
+
readonly timeZone?: string | null;
|
|
419
|
+
/**
|
|
420
|
+
* Line ending style for formatted output.
|
|
421
|
+
*
|
|
422
|
+
* - `"lf"`: Unix-style line endings (`\n`)
|
|
423
|
+
* - `"crlf"`: Windows-style line endings (`\r\n`)
|
|
424
|
+
*
|
|
425
|
+
* @default "lf"
|
|
426
|
+
* @since 2.1.0
|
|
427
|
+
*/
|
|
428
|
+
readonly lineEnding?: "lf" | "crlf";
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Get a [logfmt] formatter with the specified options. The log records
|
|
432
|
+
* will be rendered as space-delimited key-value pairs, one record per line.
|
|
433
|
+
* It looks like this:
|
|
434
|
+
*
|
|
435
|
+
* ```text
|
|
436
|
+
* time=2023-11-14T22:13:20.000Z level=info logger=my.logger msg="Hello, world!" key=value
|
|
437
|
+
* ```
|
|
438
|
+
*
|
|
439
|
+
* [logfmt]: https://brandur.org/logfmt
|
|
440
|
+
* @param options The options for the logfmt formatter.
|
|
441
|
+
* @returns The logfmt formatter.
|
|
442
|
+
* @since 2.1.0
|
|
443
|
+
*/
|
|
444
|
+
declare function getLogfmtFormatter(options?: LogfmtFormatterOptions): TextFormatter;
|
|
445
|
+
/**
|
|
446
|
+
* The default [logfmt] formatter. This formatter formats log records as
|
|
447
|
+
* space-delimited key-value pairs, one record per line.
|
|
448
|
+
*
|
|
449
|
+
* [logfmt]: https://brandur.org/logfmt
|
|
450
|
+
* @since 2.1.0
|
|
451
|
+
*/
|
|
452
|
+
declare const logfmtFormatter: TextFormatter;
|
|
375
453
|
/**
|
|
376
454
|
* A console formatter is a function that accepts a log record and returns
|
|
377
455
|
* an array of arguments to pass to {@link console.log}.
|
|
@@ -391,5 +469,5 @@ type ConsoleFormatter = (record: LogRecord) => readonly unknown[];
|
|
|
391
469
|
declare function defaultConsoleFormatter(record: LogRecord): readonly unknown[];
|
|
392
470
|
//# sourceMappingURL=formatter.d.ts.map
|
|
393
471
|
//#endregion
|
|
394
|
-
export { AnsiColor, AnsiColorFormatterOptions, AnsiStyle, ConsoleFormatter, FormattedValues, JsonLinesFormatterOptions, TextFormatter, TextFormatterOptions, ansiColorFormatter, defaultConsoleFormatter, defaultTextFormatter, getAnsiColorFormatter, getJsonLinesFormatter, getTextFormatter, jsonLinesFormatter };
|
|
472
|
+
export { AnsiColor, AnsiColorFormatterOptions, AnsiStyle, ConsoleFormatter, FormattedValues, JsonLinesFormatterOptions, LogfmtFormatterOptions, TextFormatter, TextFormatterOptions, ansiColorFormatter, defaultConsoleFormatter, defaultTextFormatter, getAnsiColorFormatter, getJsonLinesFormatter, getLogfmtFormatter, getTextFormatter, jsonLinesFormatter, logfmtFormatter };
|
|
395
473
|
//# sourceMappingURL=formatter.d.ts.map
|
package/dist/formatter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatter.d.ts","names":[],"sources":["../src/formatter.ts"],"sourcesContent":[],"mappings":";;;;;;;AAWA;
|
|
1
|
+
{"version":3,"file":"formatter.d.ts","names":[],"sources":["../src/formatter.ts"],"sourcesContent":[],"mappings":";;;;;;;AAWA;AAsGA;AA+BA;;;AA6IoB,KAlRR,aAAA,GAkRQ,CAAA,MAAA,EAlRiB,SAkRjB,EAAA,GAAA,MAAA;AAAe;AA+TnC;;;AAEG,UA7ec,eAAA,CA6ed;EAAa;AA0FhB;AAQA;EAyBY,SAAA,EAAA,MAAS,GAAA,IAAA;EA4BJ;;;EA0CW,KAKT,EAAA,MAAA;EAAS;;;EAiBc,QAA1B,EAAA,MAAA;EAAM;;;EAhEiD,OAAA,EAAA,MAAA;EAqFvD;;;EACyB,MACtC,EAnsBO,SAmsBP;AAAa;AAgEhB;AAMA;AAiEA;;AACW,UAp0BM,oBAAA,CAo0BN;EAA8B;AACzB;AA8JhB;AAMA;AAoNA;;;;AAEgB;AA6EhB;AAUA;AAqBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mEAjuCe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAmEK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+TJ,gBAAA,WACL,uBACR;;;;;;;;;;;cA0FU,sBAAsB;;;;;KAQvB,SAAA;;;;;KAyBA,SAAA;;;;;UA4BK,yBAAA,SAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBA0ChC;;;;mBAKA;;;;eAKJ;;;;;;;;;;;gBAYC,OAAO,UAAU;;;;kBAKf;;;;kBAKA;;;;;;;;;;iBAWF,qBAAA,WACL,4BACR;;;;;;;;;;cAgEU,oBAAoB;;;;;UAMhB,yBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiED,qBAAA,WACL,4BACR;;;;;;;;;;;;;;;;;cA8JU,oBAAoB;;;;;UAMhB,sBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoND,kBAAA,WACL,yBACR;;;;;;;;cA6EU,iBAAiB;;;;;;;;;KAUlB,gBAAA,YAA4B;;;;;;;;iBAqBxB,uBAAA,SAAgC"}
|
package/dist/formatter.js
CHANGED
|
@@ -22,7 +22,7 @@ const levelAbbreviations = {
|
|
|
22
22
|
* If `colors` is `true`, the output will be ANSI-colored.
|
|
23
23
|
* @returns The string representation of the value.
|
|
24
24
|
*/
|
|
25
|
-
const
|
|
25
|
+
const platformInspect = typeof document !== "undefined" || typeof navigator !== "undefined" && navigator.product === "ReactNative" ? (v) => JSON.stringify(v) : "Deno" in globalThis && "inspect" in globalThis.Deno && typeof globalThis.Deno.inspect === "function" ? (v, opts) => globalThis.Deno.inspect(v, {
|
|
26
26
|
strAbbreviateSize: Infinity,
|
|
27
27
|
iterableLimit: Infinity,
|
|
28
28
|
...opts
|
|
@@ -31,6 +31,20 @@ const inspect = typeof document !== "undefined" || typeof navigator !== "undefin
|
|
|
31
31
|
maxStringLength: Infinity,
|
|
32
32
|
...opts
|
|
33
33
|
}) : (v) => JSON.stringify(v);
|
|
34
|
+
const inspect = (value, options) => String(platformInspect(value, options));
|
|
35
|
+
const utf8Encoder = new TextEncoder();
|
|
36
|
+
function renderMessageParts(msgParts, valueRenderer) {
|
|
37
|
+
const msgLen = msgParts.length;
|
|
38
|
+
if (msgLen === 1) return msgParts[0];
|
|
39
|
+
if (msgLen <= 6) {
|
|
40
|
+
let message = "";
|
|
41
|
+
for (let i = 0; i < msgLen; i++) message += i % 2 === 0 ? msgParts[i] : valueRenderer(msgParts[i]);
|
|
42
|
+
return message;
|
|
43
|
+
}
|
|
44
|
+
const parts = new Array(msgLen);
|
|
45
|
+
for (let i = 0; i < msgLen; i++) parts[i] = i % 2 === 0 ? msgParts[i] : valueRenderer(msgParts[i]);
|
|
46
|
+
return parts.join("");
|
|
47
|
+
}
|
|
34
48
|
function padZero(num) {
|
|
35
49
|
return num < 10 ? `0${num}` : `${num}`;
|
|
36
50
|
}
|
|
@@ -271,18 +285,7 @@ function getTextFormatter(options = {}) {
|
|
|
271
285
|
const lineEnding = getLineEndingValue(options.lineEnding);
|
|
272
286
|
const formatter = options.format ?? (({ timestamp, level, category, message }) => `${timestamp ? `${timestamp} ` : ""}[${level}] ${category}: ${message}`);
|
|
273
287
|
return (record) => {
|
|
274
|
-
const
|
|
275
|
-
const msgLen = msgParts.length;
|
|
276
|
-
let message;
|
|
277
|
-
if (msgLen === 1) message = msgParts[0];
|
|
278
|
-
else if (msgLen <= 6) {
|
|
279
|
-
message = "";
|
|
280
|
-
for (let i = 0; i < msgLen; i++) message += i % 2 === 0 ? msgParts[i] : valueRenderer(msgParts[i]);
|
|
281
|
-
} else {
|
|
282
|
-
const parts = new Array(msgLen);
|
|
283
|
-
for (let i = 0; i < msgLen; i++) parts[i] = i % 2 === 0 ? msgParts[i] : valueRenderer(msgParts[i]);
|
|
284
|
-
message = parts.join("");
|
|
285
|
-
}
|
|
288
|
+
const message = renderMessageParts(record.message, valueRenderer);
|
|
286
289
|
const timestamp = timestampRenderer(record.timestamp);
|
|
287
290
|
const level = levelRenderer(record.level);
|
|
288
291
|
const category = typeof categorySeparator === "function" ? categorySeparator(record.category) : record.category.join(categorySeparator);
|
|
@@ -491,6 +494,146 @@ function getJsonLinesFormatter(options = {}) {
|
|
|
491
494
|
* @since 0.11.0
|
|
492
495
|
*/
|
|
493
496
|
const jsonLinesFormatter = getJsonLinesFormatter();
|
|
497
|
+
function renderStructuredMessage(record, template) {
|
|
498
|
+
if (template) {
|
|
499
|
+
if (typeof record.rawMessage === "string") return record.rawMessage;
|
|
500
|
+
return record.rawMessage.join("{}");
|
|
501
|
+
}
|
|
502
|
+
return renderMessageParts(record.message, stringifyLogfmtValue);
|
|
503
|
+
}
|
|
504
|
+
function filterLogfmtKey(key) {
|
|
505
|
+
if (key === "") return null;
|
|
506
|
+
let needsEscape = false;
|
|
507
|
+
for (const char of key) {
|
|
508
|
+
const code = char.codePointAt(0);
|
|
509
|
+
if (shouldEscapeLogfmtKeyChar(char, code)) {
|
|
510
|
+
needsEscape = true;
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
if (!needsEscape) return key;
|
|
515
|
+
let result = "";
|
|
516
|
+
for (const char of key) {
|
|
517
|
+
const code = char.codePointAt(0);
|
|
518
|
+
if (shouldEscapeLogfmtKeyChar(char, code)) result += encodeLogfmtKeyChar(char);
|
|
519
|
+
else result += char;
|
|
520
|
+
}
|
|
521
|
+
return result;
|
|
522
|
+
}
|
|
523
|
+
function shouldEscapeLogfmtKeyChar(char, code) {
|
|
524
|
+
return code <= 32 || code === 127 || code === 65533 || char === "=" || char === "\"" || char === "%";
|
|
525
|
+
}
|
|
526
|
+
function encodeLogfmtKeyChar(char) {
|
|
527
|
+
let result = "";
|
|
528
|
+
for (const byte of utf8Encoder.encode(char)) result += `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
|
|
529
|
+
return result;
|
|
530
|
+
}
|
|
531
|
+
function stringifyLogfmtValue(value) {
|
|
532
|
+
if (typeof value === "string") return value;
|
|
533
|
+
if (value === null) return "null";
|
|
534
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "undefined" || typeof value === "symbol" || typeof value === "function") return String(value);
|
|
535
|
+
try {
|
|
536
|
+
const json = JSON.stringify(value, jsonReplacer);
|
|
537
|
+
if (typeof json === "string") return unwrapJsonStringLiteral(json);
|
|
538
|
+
} catch {}
|
|
539
|
+
return inspect(value, { colors: false });
|
|
540
|
+
}
|
|
541
|
+
function unwrapJsonStringLiteral(json) {
|
|
542
|
+
if (json.startsWith("\"") && json.endsWith("\"")) return JSON.parse(json);
|
|
543
|
+
return json;
|
|
544
|
+
}
|
|
545
|
+
function quoteLogfmtValue(value, isString) {
|
|
546
|
+
let needsQuote = value === "" || isString && shouldQuoteStringLiteral(value);
|
|
547
|
+
for (const char of value) {
|
|
548
|
+
const code = char.codePointAt(0);
|
|
549
|
+
if (shouldQuoteLogfmtValueChar(char, code)) {
|
|
550
|
+
needsQuote = true;
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (!needsQuote) return value;
|
|
555
|
+
let quoted = "";
|
|
556
|
+
for (const char of value) {
|
|
557
|
+
const code = char.codePointAt(0);
|
|
558
|
+
quoted += escapeLogfmtValueChar(char, code);
|
|
559
|
+
}
|
|
560
|
+
return `"${quoted}"`;
|
|
561
|
+
}
|
|
562
|
+
function shouldQuoteStringLiteral(value) {
|
|
563
|
+
return value === "null" || value === "undefined" || value === "true" || value === "false";
|
|
564
|
+
}
|
|
565
|
+
function shouldQuoteLogfmtValueChar(char, code) {
|
|
566
|
+
return code <= 32 || code === 127 || code === 65533 || char === "=" || char === "\"" || char === "\\";
|
|
567
|
+
}
|
|
568
|
+
function escapeLogfmtValueChar(char, code) {
|
|
569
|
+
switch (char) {
|
|
570
|
+
case " ": return "\\t";
|
|
571
|
+
case "\n": return "\\n";
|
|
572
|
+
case "\r": return "\\r";
|
|
573
|
+
case "\"": return "\\\"";
|
|
574
|
+
case "\\": return "\\\\";
|
|
575
|
+
default: return code <= 31 || code === 127 ? `\\u${code.toString(16).padStart(4, "0")}` : char;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
function formatLogfmtValue(value) {
|
|
579
|
+
const stringified = stringifyLogfmtValue(value);
|
|
580
|
+
return quoteLogfmtValue(stringified, typeof value === "string");
|
|
581
|
+
}
|
|
582
|
+
function pushLogfmtPair(pairs, key, value) {
|
|
583
|
+
const filteredKey = filterLogfmtKey(key);
|
|
584
|
+
if (filteredKey == null) return;
|
|
585
|
+
pairs.push(`${filteredKey}=${formatLogfmtValue(value)}`);
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Get a [logfmt] formatter with the specified options. The log records
|
|
589
|
+
* will be rendered as space-delimited key-value pairs, one record per line.
|
|
590
|
+
* It looks like this:
|
|
591
|
+
*
|
|
592
|
+
* ```text
|
|
593
|
+
* time=2023-11-14T22:13:20.000Z level=info logger=my.logger msg="Hello, world!" key=value
|
|
594
|
+
* ```
|
|
595
|
+
*
|
|
596
|
+
* [logfmt]: https://brandur.org/logfmt
|
|
597
|
+
* @param options The options for the logfmt formatter.
|
|
598
|
+
* @returns The logfmt formatter.
|
|
599
|
+
* @since 2.1.0
|
|
600
|
+
*/
|
|
601
|
+
function getLogfmtFormatter(options = {}) {
|
|
602
|
+
const prependPrefix = "prepend:";
|
|
603
|
+
const lineEnding = getLineEndingValue(options.lineEnding);
|
|
604
|
+
const timestampRenderer = createTimestampFormatter("rfc3339", resolveTimeZone(options.timeZone));
|
|
605
|
+
const isTemplateMessage = options.message === "template";
|
|
606
|
+
const propertiesOption = options.properties ?? "flatten";
|
|
607
|
+
let joinCategory;
|
|
608
|
+
if (typeof options.categorySeparator === "function") joinCategory = options.categorySeparator;
|
|
609
|
+
else {
|
|
610
|
+
const separator = options.categorySeparator ?? ".";
|
|
611
|
+
joinCategory = (category) => category.join(separator);
|
|
612
|
+
}
|
|
613
|
+
let propertyPrefix = "";
|
|
614
|
+
if (propertiesOption === "flatten") propertyPrefix = "";
|
|
615
|
+
else if (propertiesOption.startsWith(prependPrefix)) {
|
|
616
|
+
propertyPrefix = propertiesOption.substring(prependPrefix.length);
|
|
617
|
+
if (propertyPrefix === "") throw new TypeError("Invalid properties option: " + JSON.stringify(propertiesOption) + ". It must be of the form \"prepend:<prefix>\" where <prefix> is a non-empty string.");
|
|
618
|
+
} else throw new TypeError(`Invalid properties option: ${JSON.stringify(propertiesOption)}. It must be "flatten" or "prepend:<prefix>".`);
|
|
619
|
+
return (record) => {
|
|
620
|
+
const pairs = [];
|
|
621
|
+
pushLogfmtPair(pairs, "time", timestampRenderer(record.timestamp));
|
|
622
|
+
pushLogfmtPair(pairs, "level", record.level);
|
|
623
|
+
pushLogfmtPair(pairs, "logger", joinCategory(record.category));
|
|
624
|
+
pushLogfmtPair(pairs, "msg", renderStructuredMessage(record, isTemplateMessage));
|
|
625
|
+
for (const key in record.properties) if (Object.prototype.hasOwnProperty.call(record.properties, key)) pushLogfmtPair(pairs, `${propertyPrefix}${key}`, record.properties[key]);
|
|
626
|
+
return `${pairs.join(" ")}${lineEnding}`;
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* The default [logfmt] formatter. This formatter formats log records as
|
|
631
|
+
* space-delimited key-value pairs, one record per line.
|
|
632
|
+
*
|
|
633
|
+
* [logfmt]: https://brandur.org/logfmt
|
|
634
|
+
* @since 2.1.0
|
|
635
|
+
*/
|
|
636
|
+
const logfmtFormatter = getLogfmtFormatter();
|
|
494
637
|
/**
|
|
495
638
|
* The styles for the log level in the console.
|
|
496
639
|
*/
|
|
@@ -531,5 +674,5 @@ function defaultConsoleFormatter(record) {
|
|
|
531
674
|
}
|
|
532
675
|
|
|
533
676
|
//#endregion
|
|
534
|
-
export { ansiColorFormatter, defaultConsoleFormatter, defaultTextFormatter, getAnsiColorFormatter, getJsonLinesFormatter, getTextFormatter, jsonLinesFormatter };
|
|
677
|
+
export { ansiColorFormatter, defaultConsoleFormatter, defaultTextFormatter, getAnsiColorFormatter, getJsonLinesFormatter, getLogfmtFormatter, getTextFormatter, jsonLinesFormatter, logfmtFormatter };
|
|
535
678
|
//# sourceMappingURL=formatter.js.map
|