@zudojs/logger 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +74 -5
  2. package/dist/loggerCore/core/loggerCore.context.d.ts +4 -4
  3. package/dist/loggerCore/core/loggerCore.core.d.ts +4 -4
  4. package/dist/loggerCore/core/loggerCore.core.js +8 -8
  5. package/dist/loggerCore/core/loggerCore.type.d.ts +14 -4
  6. package/dist/loggerCore/helpers/loggerCore.helper.d.ts +2 -2
  7. package/dist/loggerCore/helpers/loggerCoreMethods/index.d.ts +1 -1
  8. package/dist/loggerCore/helpers/loggerCoreMethods/index.js +1 -1
  9. package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.child.d.ts +2 -2
  10. package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.dispatch.js +71 -25
  11. package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.entry.d.ts +6 -1
  12. package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.entry.js +8 -3
  13. package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.level.d.ts +7 -0
  14. package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.level.js +35 -2
  15. package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.lifecycle.d.ts +3 -3
  16. package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.lifecycle.js +8 -6
  17. package/dist/loggerEntry/loggerEntryCreate.js +2 -1
  18. package/dist/loggerEntry/loggerEntryHelpers/index.d.ts +1 -1
  19. package/dist/loggerEntry/loggerEntryHelpers/index.js +1 -1
  20. package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.interfaces.d.ts +11 -1
  21. package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.sanitize.d.ts +18 -5
  22. package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.sanitize.js +66 -21
  23. package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.serialize.js +2 -1
  24. package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.valueSerialize.d.ts +11 -2
  25. package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.valueSerialize.js +53 -20
  26. package/dist/loggerFactory/loggerFactory.core.d.ts +9 -2
  27. package/dist/loggerFactory/loggerFactory.core.js +10 -0
  28. package/dist/loggerFormatter/loggerFormatter.core.js +3 -2
  29. package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.json.js +26 -13
  30. package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.metadata.d.ts +5 -2
  31. package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.metadata.js +12 -5
  32. package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.text.js +8 -4
  33. package/dist/loggerLevel/loggerLevel.type.d.ts +13 -0
  34. package/dist/loggerLevel/loggerLevel.type.js +17 -2
  35. package/dist/loggerManager/loggerManager.core.d.ts +10 -0
  36. package/dist/loggerManager/loggerManager.core.js +24 -7
  37. package/dist/loggerOptions/loggerOptions.type.d.ts +14 -3
  38. package/dist/loggerOptions/loggerOptions.type.js +7 -3
  39. package/dist/loggerTransport/loggerTransportComposite/loggerTransportComposite.buffered.js +9 -0
  40. package/dist/loggerTransport/loggerTransportConsole/loggerTransportConsole.core.d.ts +3 -2
  41. package/dist/loggerTransport/loggerTransportConsole/loggerTransportConsole.core.js +9 -4
  42. package/dist/loggerTransport/loggerTransportHelpers/index.d.ts +1 -1
  43. package/dist/loggerTransport/loggerTransportHelpers/index.js +1 -1
  44. package/dist/loggerTransport/loggerTransportHelpers/loggerTransportHelpers.d.ts +15 -0
  45. package/dist/loggerTransport/loggerTransportHelpers/loggerTransportHelpers.js +22 -0
  46. package/package.json +4 -4
@@ -1,25 +1,27 @@
1
1
  /**
2
2
  * ZudojsLogger lifecycle methods.
3
3
  */
4
- import { LoggerLevel } from "../../../loggerLevel/loggerLevel.type.js";
4
+ import { resolveLoggerLevel, } from "../../../loggerLevel/loggerLevel.type.js";
5
5
  import { createLoggerTransport } from "../../../loggerTransport/loggerTransport.core.js";
6
6
  import { isLoggerTransport } from "../../../loggerTransport/loggerTransportGuard.js";
7
7
  import { LoggerConfigurationError } from "../../../loggerErrors/loggerError.base.js";
8
8
  import { throwCollectedFailures } from "../../../loggerErrors/loggerError.helpers.js";
9
9
  /**
10
- * Sets the logger level.
10
+ * Sets the logger level from an enum value or a case-insensitive name.
11
11
  */
12
12
  export function setLoggerLevel(ctx, level) {
13
13
  ctx.assertActive();
14
- if (!Number.isInteger(level) ||
15
- level < LoggerLevel.FATAL ||
16
- level > LoggerLevel.TRACE) {
14
+ let resolved;
15
+ try {
16
+ resolved = resolveLoggerLevel(level);
17
+ }
18
+ catch {
17
19
  throw new LoggerConfigurationError(`Invalid logger level: ${String(level)}.`);
18
20
  }
19
21
  ctx.assertMutable();
20
22
  ctx.updateConfiguration({
21
23
  ...ctx.configuration,
22
- level,
24
+ level: resolved,
23
25
  });
24
26
  }
25
27
  /**
@@ -2,6 +2,7 @@
2
2
  * Logger entry creation from input.
3
3
  */
4
4
  import { createLoggerEntryId } from "./loggerEntry.core.js";
5
+ import { InvalidLoggerEntryError } from "../loggerErrors/loggerError.base.js";
5
6
  import { loggerLevelNameFallback } from "./loggerEntryHelpers/loggerEntryHelpers.serialize.js";
6
7
  /**
7
8
  * Creates a normalized LoggerEntry.
@@ -10,7 +11,7 @@ export function createLoggerEntry(input) {
10
11
  const timestamp = input.timestamp ?? new Date();
11
12
  const timestampMs = timestamp.getTime();
12
13
  if (!Number.isFinite(timestampMs)) {
13
- throw new RangeError("Logger entry timestamp must be a valid date.");
14
+ throw new InvalidLoggerEntryError("Logger entry timestamp must be a valid date.");
14
15
  }
15
16
  const levelName = input.levelName ?? loggerLevelNameFallback(input.level);
16
17
  return Object.freeze({
@@ -6,6 +6,6 @@
6
6
  export type { LogValue, LogMetadata, LoggerSource, LoggerEntryContext, } from "./loggerEntryHelpers.types.js";
7
7
  export type { LoggerEntry, LoggerEntryInput, } from "./loggerEntryHelpers.interfaces.js";
8
8
  export { serializeLoggerEntry, serializeLoggerError, serializeLoggerValue, loggerLevelNameFallback, } from "./loggerEntryHelpers.serialize.js";
9
- export { LOGGER_REDACTION_TOKEN, DEFAULT_LOGGER_SECRET_PATTERN, escapeLogText, hasLogControlCharacters, createSecretMatcher, redactLogValue, } from "./loggerEntryHelpers.sanitize.js";
9
+ export { LOGGER_REDACTION_TOKEN, LOGGER_UNREADABLE_TOKEN, DEFAULT_LOGGER_SECRET_PATTERN, escapeLogText, hasLogControlCharacters, createSecretMatcher, redactLogValue, } from "./loggerEntryHelpers.sanitize.js";
10
10
  export type { LoggerRedactionOptions } from "./loggerEntryHelpers.sanitize.js";
11
11
  //# sourceMappingURL=index.d.ts.map
@@ -4,5 +4,5 @@
4
4
  * Logger entry helper types and utilities.
5
5
  */
6
6
  export { serializeLoggerEntry, serializeLoggerError, serializeLoggerValue, loggerLevelNameFallback, } from "./loggerEntryHelpers.serialize.js";
7
- export { LOGGER_REDACTION_TOKEN, DEFAULT_LOGGER_SECRET_PATTERN, escapeLogText, hasLogControlCharacters, createSecretMatcher, redactLogValue, } from "./loggerEntryHelpers.sanitize.js";
7
+ export { LOGGER_REDACTION_TOKEN, LOGGER_UNREADABLE_TOKEN, DEFAULT_LOGGER_SECRET_PATTERN, escapeLogText, hasLogControlCharacters, createSecretMatcher, redactLogValue, } from "./loggerEntryHelpers.sanitize.js";
8
8
  //# sourceMappingURL=index.js.map
@@ -20,9 +20,19 @@ export interface LoggerEntry {
20
20
  */
21
21
  readonly levelName: LoggerLevelName;
22
22
  /**
23
- * Human-readable message.
23
+ * The message as the caller logged it. A transport receives it unchanged;
24
+ * the formatter's rendering of the whole record is in `formatted`.
24
25
  */
25
26
  readonly message: string;
27
+ /**
28
+ * The record rendered as one line by the logger's formatter: the string a
29
+ * text or JSON formatter returned, or the JSON line of the record an
30
+ * object formatter (`createStructuredLoggerFormatter`) returned. Set on
31
+ * entries a logger hands to its transports. Line-oriented transports
32
+ * print `entry.formatted ?? entry.message`; `message` is always the raw
33
+ * message the caller logged.
34
+ */
35
+ readonly formatted?: string;
26
36
  /**
27
37
  * Structured metadata.
28
38
  */
@@ -12,6 +12,14 @@
12
12
  * Replacement token written in place of a redacted value.
13
13
  */
14
14
  export declare const LOGGER_REDACTION_TOKEN = "[REDACTED]";
15
+ /**
16
+ * Replacement token written in place of a property whose getter threw.
17
+ *
18
+ * A throwing accessor used to propagate out of `logger.info(...)` and
19
+ * abort the caller. The field is marked instead, the rest of the entry
20
+ * is logged, and the failure is reported as an infrastructure error.
21
+ */
22
+ export declare const LOGGER_UNREADABLE_TOKEN = "[Unreadable]";
15
23
  /**
16
24
  * Legacy substring pattern for secret field names.
17
25
  *
@@ -64,10 +72,15 @@ export declare function createSecretMatcher(options?: LoggerRedactionOptions): (
64
72
  /**
65
73
  * Recursively replaces secret-named fields with a redaction token.
66
74
  *
67
- * Nesting, arrays and getters are all covered: the walk descends into
68
- * every enumerable own property, and a getter is read here — once,
69
- * before the value can reach a transport. Cycles resolve to
70
- * "[Circular]" rather than recursing forever.
75
+ * Nesting, arrays, `Map`, `Set` and getters are all covered: the walk
76
+ * descends into every enumerable own property, and a getter is read
77
+ * here — once, before the value can reach a transport. A getter that
78
+ * throws yields {@link LOGGER_UNREADABLE_TOKEN} and is reported through
79
+ * `onReadError` instead of aborting the caller's log statement.
80
+ *
81
+ * `seen` tracks the ANCESTOR PATH only (each object is unmarked as the
82
+ * walk ascends), so a back-edge resolves to "[Circular]" while an
83
+ * object merely referenced twice in one payload is logged both times.
71
84
  */
72
- export declare function redactLogValue(value: unknown, isSecret: (key: string) => boolean, replacement?: string, seen?: WeakSet<object>): unknown;
85
+ export declare function redactLogValue(value: unknown, isSecret: (key: string) => boolean, replacement?: string, seen?: WeakSet<object>, onReadError?: (key: string, error: unknown) => void): unknown;
73
86
  //# sourceMappingURL=loggerEntryHelpers.sanitize.d.ts.map
@@ -19,6 +19,14 @@ const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f\u2028\u2029]/g;
19
19
  * Replacement token written in place of a redacted value.
20
20
  */
21
21
  export const LOGGER_REDACTION_TOKEN = "[REDACTED]";
22
+ /**
23
+ * Replacement token written in place of a property whose getter threw.
24
+ *
25
+ * A throwing accessor used to propagate out of `logger.info(...)` and
26
+ * abort the caller. The field is marked instead, the rest of the entry
27
+ * is logged, and the failure is reported as an infrastructure error.
28
+ */
29
+ export const LOGGER_UNREADABLE_TOKEN = "[Unreadable]";
22
30
  /**
23
31
  * Legacy substring pattern for secret field names.
24
32
  *
@@ -85,15 +93,32 @@ export function createSecretMatcher(options = {}) {
85
93
  : configured;
86
94
  return (key) => exact.has(key.toLowerCase()) || pattern.test(key);
87
95
  }
96
+ /** Defines an own, enumerable property without touching a setter. */
97
+ function defineLogProperty(target, key, value) {
98
+ // defineProperty, never assignment: a "__proto__" key coming from
99
+ // JSON.parse of untrusted input would otherwise reach the inherited
100
+ // setter and replace this object's prototype.
101
+ Object.defineProperty(target, key, {
102
+ value,
103
+ enumerable: true,
104
+ writable: true,
105
+ configurable: true,
106
+ });
107
+ }
88
108
  /**
89
109
  * Recursively replaces secret-named fields with a redaction token.
90
110
  *
91
- * Nesting, arrays and getters are all covered: the walk descends into
92
- * every enumerable own property, and a getter is read here — once,
93
- * before the value can reach a transport. Cycles resolve to
94
- * "[Circular]" rather than recursing forever.
111
+ * Nesting, arrays, `Map`, `Set` and getters are all covered: the walk
112
+ * descends into every enumerable own property, and a getter is read
113
+ * here — once, before the value can reach a transport. A getter that
114
+ * throws yields {@link LOGGER_UNREADABLE_TOKEN} and is reported through
115
+ * `onReadError` instead of aborting the caller's log statement.
116
+ *
117
+ * `seen` tracks the ANCESTOR PATH only (each object is unmarked as the
118
+ * walk ascends), so a back-edge resolves to "[Circular]" while an
119
+ * object merely referenced twice in one payload is logged both times.
95
120
  */
96
- export function redactLogValue(value, isSecret, replacement = LOGGER_REDACTION_TOKEN, seen = new WeakSet()) {
121
+ export function redactLogValue(value, isSecret, replacement = LOGGER_REDACTION_TOKEN, seen = new WeakSet(), onReadError) {
97
122
  if (value === null || typeof value !== "object") {
98
123
  return value;
99
124
  }
@@ -104,23 +129,43 @@ export function redactLogValue(value, isSecret, replacement = LOGGER_REDACTION_T
104
129
  return "[Circular]";
105
130
  }
106
131
  seen.add(value);
107
- if (Array.isArray(value)) {
108
- return value.map((item) => redactLogValue(item, isSecret, replacement, seen));
132
+ try {
133
+ const descend = (item) => redactLogValue(item, isSecret, replacement, seen, onReadError);
134
+ if (Array.isArray(value)) {
135
+ return value.map(descend);
136
+ }
137
+ if (value instanceof Set) {
138
+ return Array.from(value, descend);
139
+ }
140
+ const result = {};
141
+ if (value instanceof Map) {
142
+ for (const [key, item] of value.entries()) {
143
+ const name = typeof key === "string" ? key : String(key);
144
+ defineLogProperty(result, name, isSecret(name) ? replacement : descend(item));
145
+ }
146
+ return result;
147
+ }
148
+ for (const key of Object.keys(value)) {
149
+ if (isSecret(key)) {
150
+ // Never even read a secret-named accessor.
151
+ defineLogProperty(result, key, replacement);
152
+ continue;
153
+ }
154
+ let item;
155
+ try {
156
+ item = value[key];
157
+ }
158
+ catch (error) {
159
+ onReadError?.(key, error);
160
+ defineLogProperty(result, key, LOGGER_UNREADABLE_TOKEN);
161
+ continue;
162
+ }
163
+ defineLogProperty(result, key, descend(item));
164
+ }
165
+ return result;
109
166
  }
110
- const result = {};
111
- for (const [key, item] of Object.entries(value)) {
112
- // defineProperty, never assignment: a "__proto__" key coming from
113
- // JSON.parse of untrusted input would otherwise reach the inherited
114
- // setter and replace this object's prototype.
115
- Object.defineProperty(result, key, {
116
- value: isSecret(key)
117
- ? replacement
118
- : redactLogValue(item, isSecret, replacement, seen),
119
- enumerable: true,
120
- writable: true,
121
- configurable: true,
122
- });
167
+ finally {
168
+ seen.delete(value);
123
169
  }
124
- return result;
125
170
  }
126
171
  //# sourceMappingURL=loggerEntryHelpers.sanitize.js.map
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Logger entry serialization helpers.
3
3
  */
4
+ import { InvalidLoggerLevelError } from "../../loggerErrors/loggerError.base.js";
4
5
  import { serializeLoggerError, serializeLoggerValue, } from "./loggerEntryHelpers.valueSerialize.js";
5
6
  /**
6
7
  * Returns a plain serializable representation of an entry.
@@ -49,7 +50,7 @@ export function loggerLevelNameFallback(level) {
49
50
  case 5:
50
51
  return "trace";
51
52
  default:
52
- throw new RangeError(`Unknown logger level: ${String(level)}`);
53
+ throw new InvalidLoggerLevelError(level);
53
54
  }
54
55
  }
55
56
  //# sourceMappingURL=loggerEntryHelpers.serialize.js.map
@@ -3,14 +3,23 @@
3
3
  */
4
4
  /**
5
5
  * Serializes an error-like object into a plain object.
6
+ *
7
+ * @param includeStack - `false` leaves the stack out (its frames carry
8
+ * absolute file paths). Defaults to `true`.
6
9
  */
7
10
  export declare function serializeLoggerError(error: {
8
11
  name?: string;
9
12
  message: string;
10
13
  stack?: string;
11
- }): Record<string, unknown>;
14
+ }, includeStack?: boolean): Record<string, unknown>;
12
15
  /**
13
16
  * Converts arbitrary values into safer serializable values.
17
+ *
18
+ * `seen` tracks the ANCESTOR PATH only, so only a genuine back-edge
19
+ * becomes "[Circular]"; `Map` and `Set` keep their contents; and a
20
+ * property whose getter throws becomes "[Unreadable]" rather than
21
+ * taking the whole log line down. `includeErrorStack: false` leaves the
22
+ * stack out of every Error found in the value.
14
23
  */
15
- export declare function serializeLoggerValue(value: unknown, seen?: WeakSet<object>): unknown;
24
+ export declare function serializeLoggerValue(value: unknown, seen?: WeakSet<object>, includeErrorStack?: boolean): unknown;
16
25
  //# sourceMappingURL=loggerEntryHelpers.valueSerialize.d.ts.map
@@ -1,20 +1,28 @@
1
1
  /**
2
2
  * Logger entry value serialization.
3
3
  */
4
+ import { LOGGER_UNREADABLE_TOKEN } from "./loggerEntryHelpers.sanitize.js";
4
5
  /**
5
6
  * Serializes an error-like object into a plain object.
7
+ *
8
+ * @param includeStack - `false` leaves the stack out (its frames carry
9
+ * absolute file paths). Defaults to `true`.
6
10
  */
7
- export function serializeLoggerError(error) {
8
- return {
9
- name: error.name,
10
- message: error.message,
11
- stack: error.stack,
12
- };
11
+ export function serializeLoggerError(error, includeStack = true) {
12
+ return includeStack
13
+ ? { name: error.name, message: error.message, stack: error.stack }
14
+ : { name: error.name, message: error.message };
13
15
  }
14
16
  /**
15
17
  * Converts arbitrary values into safer serializable values.
18
+ *
19
+ * `seen` tracks the ANCESTOR PATH only, so only a genuine back-edge
20
+ * becomes "[Circular]"; `Map` and `Set` keep their contents; and a
21
+ * property whose getter throws becomes "[Unreadable]" rather than
22
+ * taking the whole log line down. `includeErrorStack: false` leaves the
23
+ * stack out of every Error found in the value.
16
24
  */
17
- export function serializeLoggerValue(value, seen = new WeakSet()) {
25
+ export function serializeLoggerValue(value, seen = new WeakSet(), includeErrorStack = true) {
18
26
  if (value === null ||
19
27
  value === undefined ||
20
28
  typeof value === "string" ||
@@ -29,7 +37,7 @@ export function serializeLoggerValue(value, seen = new WeakSet()) {
29
37
  return value.toISOString();
30
38
  }
31
39
  if (value instanceof Error) {
32
- return serializeLoggerError(value);
40
+ return serializeLoggerError(value, includeErrorStack);
33
41
  }
34
42
  if (typeof value === "function") {
35
43
  return `[Function ${value.name || "anonymous"}]`;
@@ -44,21 +52,46 @@ export function serializeLoggerValue(value, seen = new WeakSet()) {
44
52
  return "[Circular]";
45
53
  }
46
54
  seen.add(value);
47
- if (Array.isArray(value)) {
48
- return value.map((item) => serializeLoggerValue(item, seen));
49
- }
50
- const result = {};
51
- for (const [key, item] of Object.entries(value)) {
55
+ try {
56
+ if (Array.isArray(value)) {
57
+ return value.map((item) => serializeLoggerValue(item, seen, includeErrorStack));
58
+ }
59
+ if (value instanceof Set) {
60
+ return Array.from(value, (item) => serializeLoggerValue(item, seen, includeErrorStack));
61
+ }
62
+ const result = {};
52
63
  // defineProperty, never assignment: a "__proto__" key from an
53
64
  // untrusted payload would otherwise reach the inherited setter and
54
65
  // replace the serialized object's prototype.
55
- Object.defineProperty(result, key, {
56
- value: serializeLoggerValue(item, seen),
57
- enumerable: true,
58
- writable: true,
59
- configurable: true,
60
- });
66
+ const define = (key, item) => {
67
+ Object.defineProperty(result, key, {
68
+ value: item,
69
+ enumerable: true,
70
+ writable: true,
71
+ configurable: true,
72
+ });
73
+ };
74
+ if (value instanceof Map) {
75
+ for (const [key, item] of value.entries()) {
76
+ define(typeof key === "string" ? key : String(key), serializeLoggerValue(item, seen, includeErrorStack));
77
+ }
78
+ return result;
79
+ }
80
+ for (const key of Object.keys(value)) {
81
+ let item;
82
+ try {
83
+ item = value[key];
84
+ }
85
+ catch {
86
+ define(key, LOGGER_UNREADABLE_TOKEN);
87
+ continue;
88
+ }
89
+ define(key, serializeLoggerValue(item, seen, includeErrorStack));
90
+ }
91
+ return result;
92
+ }
93
+ finally {
94
+ seen.delete(value);
61
95
  }
62
- return result;
63
96
  }
64
97
  //# sourceMappingURL=loggerEntryHelpers.valueSerialize.js.map
@@ -1,5 +1,5 @@
1
1
  import type { Logger } from "../loggerCore/core/loggerCore.type.js";
2
- import type { LoggerOptions, ChildLoggerOptions } from "../loggerOptions/loggerOptions.type.js";
2
+ import type { LoggerOptions, ChildLoggerOptionsInput } from "../loggerOptions/loggerOptions.type.js";
3
3
  /**
4
4
  * Factory responsible for creating and managing Zudojs loggers.
5
5
  *
@@ -17,12 +17,19 @@ export declare class LoggerFactory {
17
17
  * instance is returned unless `forceNew` is enabled.
18
18
  */
19
19
  create(name?: string, options?: LoggerOptions, forceNew?: boolean): Logger;
20
+ /**
21
+ * Registers an existing logger under its own name (or `name`).
22
+ *
23
+ * An adopted logger is then covered by `flushAll()`, `disposeAll()`,
24
+ * `getAll()` and `size` exactly like one the factory created itself.
25
+ */
26
+ register(logger: Logger, name?: string): Logger;
20
27
  /** Returns an existing logger. */
21
28
  get(name: string): Logger | undefined;
22
29
  /** Gets an existing logger or creates it. */
23
30
  getOrCreate(name: string, options?: LoggerOptions): Logger;
24
31
  /** Creates a child logger from an existing logger. */
25
- child(parent: Logger, options?: ChildLoggerOptions): Logger;
32
+ child(parent: Logger, options?: ChildLoggerOptionsInput): Logger;
26
33
  /**
27
34
  * Removes a logger from the factory registry.
28
35
  *
@@ -31,6 +31,16 @@ export class LoggerFactory {
31
31
  this.loggers.set(loggerName, logger);
32
32
  return logger;
33
33
  }
34
+ /**
35
+ * Registers an existing logger under its own name (or `name`).
36
+ *
37
+ * An adopted logger is then covered by `flushAll()`, `disposeAll()`,
38
+ * `getAll()` and `size` exactly like one the factory created itself.
39
+ */
40
+ register(logger, name) {
41
+ this.loggers.set(name ?? logger.name, logger);
42
+ return logger;
43
+ }
34
44
  /** Returns an existing logger. */
35
45
  get(name) {
36
46
  return this.loggers.get(name);
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Core logger formatter functions.
3
3
  */
4
+ import { LoggerFormatterNotFoundError } from "../loggerErrors/loggerError.base.js";
4
5
  import { createLoggerFormatterId, isLoggerFormatterFunction, isLoggerFormatterObject, isLoggerFormatter, } from "./loggerFormatterGuard.js";
5
6
  /**
6
7
  * Creates a function-backed formatter.
@@ -20,7 +21,7 @@ export function createLoggerFormatter(formatter, options = {}) {
20
21
  return formatter(entry, context);
21
22
  }
22
23
  if (typeof formatter === "string") {
23
- throw new Error(`Cannot format with string identifier "${formatter}" directly. Resolve the formatter first.`);
24
+ throw new LoggerFormatterNotFoundError(formatter);
24
25
  }
25
26
  return formatter.format(entry, context);
26
27
  },
@@ -34,7 +35,7 @@ export function formatLoggerEntry(formatter, entry, context = {}) {
34
35
  return formatter(entry, context);
35
36
  }
36
37
  if (typeof formatter === "string") {
37
- throw new Error(`Cannot format with string identifier "${formatter}" directly. Resolve the formatter first.`);
38
+ throw new LoggerFormatterNotFoundError(formatter);
38
39
  }
39
40
  return formatter.format(entry, context);
40
41
  }
@@ -12,29 +12,42 @@ function removeUndefinedValues(value, seen = new WeakSet()) {
12
12
  return "[Circular]";
13
13
  }
14
14
  seen.add(value);
15
- return value.map((item) => removeUndefinedValues(item, seen));
15
+ try {
16
+ return value.map((item) => removeUndefinedValues(item, seen));
17
+ }
18
+ finally {
19
+ seen.delete(value);
20
+ }
16
21
  }
17
22
  if (value && typeof value === "object" && !(value instanceof Date)) {
18
23
  // Without a cycle guard this walk recursed until the stack blew,
19
24
  // and the resulting RangeError was swallowed by dispatch — losing
20
- // the log line rather than reporting the offending value.
25
+ // the log line rather than reporting the offending value. `seen`
26
+ // tracks the ancestor path only (unmarked on ascent), so an object
27
+ // referenced twice in one payload is kept rather than collapsing
28
+ // to "[Circular]" from its second occurrence on.
21
29
  if (seen.has(value)) {
22
30
  return "[Circular]";
23
31
  }
24
32
  seen.add(value);
25
- const result = {};
26
- for (const [key, item] of Object.entries(value)) {
27
- if (item === undefined) {
28
- continue;
33
+ try {
34
+ const result = {};
35
+ for (const [key, item] of Object.entries(value)) {
36
+ if (item === undefined) {
37
+ continue;
38
+ }
39
+ Object.defineProperty(result, key, {
40
+ value: removeUndefinedValues(item, seen),
41
+ enumerable: true,
42
+ writable: true,
43
+ configurable: true,
44
+ });
29
45
  }
30
- Object.defineProperty(result, key, {
31
- value: removeUndefinedValues(item, seen),
32
- enumerable: true,
33
- writable: true,
34
- configurable: true,
35
- });
46
+ return result;
47
+ }
48
+ finally {
49
+ seen.delete(value);
36
50
  }
37
- return result;
38
51
  }
39
52
  return value;
40
53
  }
@@ -3,12 +3,15 @@
3
3
  */
4
4
  /**
5
5
  * Formats metadata as key=value pairs.
6
+ *
7
+ * @param includeStackTrace - `false` leaves the stack out of any Error in
8
+ * the metadata, as the text formatter's option of the same name promises.
6
9
  */
7
- export declare function formatMetadata(metadata: Record<string, unknown>, separator: string): string;
10
+ export declare function formatMetadata(metadata: Record<string, unknown>, separator: string, includeStackTrace?: boolean): string;
8
11
  /**
9
12
  * Formats an arbitrary metadata value.
10
13
  */
11
- export declare function formatValue(value: unknown): string;
14
+ export declare function formatValue(value: unknown, includeStackTrace?: boolean): string;
12
15
  /**
13
16
  * Formats an Error.
14
17
  *
@@ -5,19 +5,22 @@ import { serializeLoggerError, serializeLoggerValue, } from "../../loggerEntry/l
5
5
  import { escapeLogText } from "../../loggerEntry/loggerEntryHelpers/loggerEntryHelpers.sanitize.js";
6
6
  /**
7
7
  * Formats metadata as key=value pairs.
8
+ *
9
+ * @param includeStackTrace - `false` leaves the stack out of any Error in
10
+ * the metadata, as the text formatter's option of the same name promises.
8
11
  */
9
- export function formatMetadata(metadata, separator) {
12
+ export function formatMetadata(metadata, separator, includeStackTrace = true) {
10
13
  return (Object.entries(metadata)
11
14
  .filter(([, value]) => value !== undefined)
12
15
  // Metadata KEYS are as attacker-influenceable as values (a header
13
16
  // name, a form field) and were previously interpolated raw.
14
- .map(([key, value]) => `${escapeLogText(key)}=${formatValue(value)}`)
17
+ .map(([key, value]) => `${escapeLogText(key)}=${formatValue(value, includeStackTrace)}`)
15
18
  .join(separator));
16
19
  }
17
20
  /**
18
21
  * Formats an arbitrary metadata value.
19
22
  */
20
- export function formatValue(value) {
23
+ export function formatValue(value, includeStackTrace = true) {
21
24
  if (value === null) {
22
25
  return "null";
23
26
  }
@@ -31,7 +34,8 @@ export function formatValue(value) {
31
34
  return escapeLogText(value);
32
35
  }
33
36
  if (typeof value === "object") {
34
- return escapeLogText(JSON.stringify(serializeLoggerValue(value)));
37
+ const serialized = serializeLoggerValue(value, new WeakSet(), includeStackTrace);
38
+ return escapeLogText(JSON.stringify(serialized));
35
39
  }
36
40
  return escapeLogText(String(value));
37
41
  }
@@ -79,7 +83,10 @@ export function formatError(error, includeStackTrace) {
79
83
  }
80
84
  return `\n${lines.join("\n")}`;
81
85
  }
82
- const serialized = serializeLoggerError(error);
86
+ // Name and message only: the fallback used to serialize the stack as
87
+ // well, so `includeStackTrace: false` still printed every frame (and the
88
+ // absolute paths in them) inside the JSON.
89
+ const serialized = serializeLoggerError(error, false);
83
90
  return `error=${escapeLogText(JSON.stringify(serialized))}`;
84
91
  }
85
92
  //# sourceMappingURL=loggerFormatterFormatters.metadata.js.map
@@ -65,12 +65,16 @@ export function createTextLoggerFormatter(options = {}) {
65
65
  }
66
66
  }
67
67
  if (includeMetadata && Object.keys(entry.metadata).length > 0) {
68
- parts.push(formatMetadata(entry.metadata, metadataSeparator));
68
+ parts.push(formatMetadata(entry.metadata, metadataSeparator, includeStackTrace));
69
69
  }
70
- if (entry.error) {
71
- parts.push(formatError(entry.error, includeStackTrace));
70
+ const line = parts.join(" ");
71
+ if (!entry.error) {
72
+ return line;
72
73
  }
73
- return parts.join(" ");
74
+ // A stack starts on its own line; joining it with " " left a trailing
75
+ // space after the message.
76
+ const error = formatError(entry.error, includeStackTrace);
77
+ return error.startsWith("\n") ? `${line}${error}` : `${line} ${error}`;
74
78
  }, { name: options.name ?? "text" });
75
79
  }
76
80
  //# sourceMappingURL=loggerFormatterFormatters.text.js.map
@@ -14,10 +14,23 @@ export declare enum LoggerLevel {
14
14
  }
15
15
  /** String representation of supported logger levels. */
16
16
  export type LoggerLevelName = "fatal" | "error" | "warn" | "info" | "debug" | "trace";
17
+ /**
18
+ * A level as configuration accepts it: the enum value, or its name in any
19
+ * case (`"error"`, `"ERROR"`). `"warning"` and `"information"` are accepted
20
+ * at runtime as aliases of `"warn"` and `"info"`.
21
+ */
22
+ export type LoggerLevelLike = LoggerLevel | LoggerLevelName | Uppercase<LoggerLevelName>;
17
23
  /** Converts a logger level into its canonical name. */
18
24
  export declare function loggerLevelToName(level: LoggerLevel): LoggerLevelName;
19
25
  /** Converts a logger level name into its enum value. */
20
26
  export declare function loggerLevelFromName(name: LoggerLevelName | string): LoggerLevel;
27
+ /**
28
+ * Resolves a configured level (enum value or case-insensitive name) to its
29
+ * enum value.
30
+ *
31
+ * @throws InvalidLoggerLevelError when the value is neither.
32
+ */
33
+ export declare function resolveLoggerLevel(level: LoggerLevelLike): LoggerLevel;
21
34
  /** Checks whether a value is a valid LoggerLevel. */
22
35
  export declare function isLoggerLevel(value: unknown): value is LoggerLevel;
23
36
  /** Checks whether a value is a valid logger level name. */