@zudojs/logger 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -12
- package/dist/loggerCore/core/loggerCore.core.d.ts +7 -1
- package/dist/loggerCore/core/loggerCore.core.js +18 -4
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.dispatch.js +62 -17
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.entry.d.ts +6 -1
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.entry.js +8 -3
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.level.js +23 -2
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.lifecycle.d.ts +9 -0
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.lifecycle.js +51 -49
- package/dist/loggerEntry/index.d.ts +1 -0
- package/dist/loggerEntry/index.js +1 -0
- package/dist/loggerEntry/loggerEntry.secretFields.d.ts +19 -0
- package/dist/loggerEntry/loggerEntry.secretFields.js +90 -0
- package/dist/loggerEntry/loggerEntryCreate.js +2 -1
- package/dist/loggerEntry/loggerEntryHelpers/index.d.ts +1 -1
- package/dist/loggerEntry/loggerEntryHelpers/index.js +1 -1
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.sanitize.d.ts +29 -12
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.sanitize.js +89 -31
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.serialize.js +2 -1
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.valueSerialize.d.ts +5 -0
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.valueSerialize.js +43 -12
- package/dist/loggerErrors/loggerError.helpers.d.ts +11 -0
- package/dist/loggerErrors/loggerError.helpers.js +21 -0
- package/dist/loggerFactory/loggerFactory.core.d.ts +7 -0
- package/dist/loggerFactory/loggerFactory.core.js +10 -0
- package/dist/loggerFormatter/loggerFormatter.core.js +3 -2
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.js +1 -1
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.json.js +26 -13
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.metadata.d.ts +6 -0
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.metadata.js +43 -8
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.text.js +1 -1
- package/dist/loggerLevel/loggerLevel.type.js +3 -2
- package/dist/loggerManager/loggerManager.core.d.ts +10 -0
- package/dist/loggerManager/loggerManager.core.js +24 -7
- package/dist/loggerTransport/loggerTransportComposite/loggerTransportComposite.buffered.js +66 -10
- package/dist/loggerTransport/loggerTransportComposite/loggerTransportComposite.d.ts +9 -0
- package/dist/loggerTransport/loggerTransportComposite/loggerTransportComposite.js +29 -8
- package/package.json +2 -2
package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.metadata.d.ts
CHANGED
|
@@ -11,6 +11,12 @@ export declare function formatMetadata(metadata: Record<string, unknown>, separa
|
|
|
11
11
|
export declare function formatValue(value: unknown): string;
|
|
12
12
|
/**
|
|
13
13
|
* Formats an Error.
|
|
14
|
+
*
|
|
15
|
+
* With a stack trace the output is intentionally multi-line, but only
|
|
16
|
+
* the frame lines break the line: the header (which carries the
|
|
17
|
+
* attacker-influenceable message) is escaped as a single line, and every
|
|
18
|
+
* frame line is escaped and indented so none of them can start at
|
|
19
|
+
* column 0 and parse as a record of its own.
|
|
14
20
|
*/
|
|
15
21
|
export declare function formatError(error: Error, includeStackTrace: boolean): string;
|
|
16
22
|
//# sourceMappingURL=loggerFormatterFormatters.metadata.d.ts.map
|
package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.metadata.js
CHANGED
|
@@ -23,28 +23,63 @@ export function formatValue(value) {
|
|
|
23
23
|
}
|
|
24
24
|
if (typeof value === "string") {
|
|
25
25
|
if (/\s/.test(value)) {
|
|
26
|
-
|
|
26
|
+
// JSON escapes C0 controls but not DEL, C1 or U+2028/U+2029.
|
|
27
|
+
return escapeLogText(JSON.stringify(value));
|
|
27
28
|
}
|
|
28
29
|
// A value with no whitespace can still carry ANSI escapes or other
|
|
29
30
|
// C0 controls, which used to reach the sink verbatim.
|
|
30
31
|
return escapeLogText(value);
|
|
31
32
|
}
|
|
32
33
|
if (typeof value === "object") {
|
|
33
|
-
return JSON.stringify(serializeLoggerValue(value));
|
|
34
|
+
return escapeLogText(JSON.stringify(serializeLoggerValue(value)));
|
|
34
35
|
}
|
|
35
36
|
return escapeLogText(String(value));
|
|
36
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Splits an error's stack into its header (name and message) and its
|
|
40
|
+
* frame lines.
|
|
41
|
+
*
|
|
42
|
+
* The header is derived from the error itself rather than from the first
|
|
43
|
+
* stack line, because the message can contain newlines of its own and
|
|
44
|
+
* would otherwise spill across several "lines" of the stack.
|
|
45
|
+
*/
|
|
46
|
+
function splitStack(error, stack) {
|
|
47
|
+
const message = String(error.message ?? "");
|
|
48
|
+
const header = message ? `${error.name}: ${message}` : String(error.name);
|
|
49
|
+
if (stack.startsWith(header)) {
|
|
50
|
+
const rest = stack.slice(header.length).replace(/^\r?\n/u, "");
|
|
51
|
+
return { header, frames: rest ? rest.split("\n") : [] };
|
|
52
|
+
}
|
|
53
|
+
const lines = stack.split("\n");
|
|
54
|
+
const firstFrame = lines.findIndex((line) => /^\s+at\s/u.test(line));
|
|
55
|
+
if (firstFrame === -1) {
|
|
56
|
+
return { header: stack, frames: [] };
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
header: lines.slice(0, firstFrame).join("\n"),
|
|
60
|
+
frames: lines.slice(firstFrame),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
37
63
|
/**
|
|
38
64
|
* Formats an Error.
|
|
65
|
+
*
|
|
66
|
+
* With a stack trace the output is intentionally multi-line, but only
|
|
67
|
+
* the frame lines break the line: the header (which carries the
|
|
68
|
+
* attacker-influenceable message) is escaped as a single line, and every
|
|
69
|
+
* frame line is escaped and indented so none of them can start at
|
|
70
|
+
* column 0 and parse as a record of its own.
|
|
39
71
|
*/
|
|
40
72
|
export function formatError(error, includeStackTrace) {
|
|
41
|
-
if (includeStackTrace && error.stack) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
73
|
+
if (includeStackTrace && typeof error.stack === "string" && error.stack) {
|
|
74
|
+
const { header, frames } = splitStack(error, error.stack);
|
|
75
|
+
const lines = [escapeLogText(header)];
|
|
76
|
+
for (const frame of frames) {
|
|
77
|
+
const escaped = escapeLogText(frame);
|
|
78
|
+
lines.push(/^\s/u.test(escaped) ? escaped : ` ${escaped}`);
|
|
79
|
+
}
|
|
80
|
+
return `\n${lines.join("\n")}`;
|
|
46
81
|
}
|
|
47
82
|
const serialized = serializeLoggerError(error);
|
|
48
|
-
return `error=${JSON.stringify(serialized)}`;
|
|
83
|
+
return `error=${escapeLogText(JSON.stringify(serialized))}`;
|
|
49
84
|
}
|
|
50
85
|
//# sourceMappingURL=loggerFormatterFormatters.metadata.js.map
|
|
@@ -43,7 +43,7 @@ export function createTextLoggerFormatter(options = {}) {
|
|
|
43
43
|
// output. Colour codes are emitted only on explicit opt-in, and
|
|
44
44
|
// only around the fixed level name — never around user text,
|
|
45
45
|
// which stays escaped.
|
|
46
|
-
const levelTag = `[${entry.levelName.toUpperCase()}]`;
|
|
46
|
+
const levelTag = `[${escapeLogText(entry.levelName.toUpperCase())}]`;
|
|
47
47
|
parts.push(context.colors ? colorizeLevel(entry.levelName, levelTag) : levelTag);
|
|
48
48
|
if (includeLogger && entry.logger) {
|
|
49
49
|
parts.push(`[${escapeLogText(entry.logger)}]`);
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Lower numeric values represent more severe messages.
|
|
5
5
|
* Higher numeric values represent more verbose messages.
|
|
6
6
|
*/
|
|
7
|
+
import { InvalidLoggerLevelError } from "../loggerErrors/loggerError.base.js";
|
|
7
8
|
export var LoggerLevel;
|
|
8
9
|
(function (LoggerLevel) {
|
|
9
10
|
LoggerLevel[LoggerLevel["FATAL"] = 0] = "FATAL";
|
|
@@ -29,7 +30,7 @@ export function loggerLevelToName(level) {
|
|
|
29
30
|
case LoggerLevel.TRACE:
|
|
30
31
|
return "trace";
|
|
31
32
|
default:
|
|
32
|
-
throw new
|
|
33
|
+
throw new InvalidLoggerLevelError(level);
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
/** Converts a logger level name into its enum value. */
|
|
@@ -50,7 +51,7 @@ export function loggerLevelFromName(name) {
|
|
|
50
51
|
case "trace":
|
|
51
52
|
return LoggerLevel.TRACE;
|
|
52
53
|
default:
|
|
53
|
-
throw new
|
|
54
|
+
throw new InvalidLoggerLevelError(name);
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
/** Checks whether a value is a valid LoggerLevel. */
|
|
@@ -16,6 +16,16 @@ export declare class LoggerManager {
|
|
|
16
16
|
constructor(options?: LoggerOptions);
|
|
17
17
|
/** Initializes the logger manager. Initialization is idempotent. */
|
|
18
18
|
initialize(options?: LoggerOptions): Logger;
|
|
19
|
+
/**
|
|
20
|
+
* Adopts an existing logger as the manager's default logger.
|
|
21
|
+
*
|
|
22
|
+
* The logger is REGISTERED with the underlying factory, so it is
|
|
23
|
+
* covered by flush(), close(), getAll() and size. Assigning it to the
|
|
24
|
+
* private default field alone (as createLoggerManagerFromLogger used
|
|
25
|
+
* to) left the registry empty, making flush()/close() no-ops for the
|
|
26
|
+
* only logger the manager owned.
|
|
27
|
+
*/
|
|
28
|
+
adopt(logger: Logger): Logger;
|
|
19
29
|
/** Returns the default application logger. Lazily initializes when necessary. */
|
|
20
30
|
getLogger(): Logger;
|
|
21
31
|
/** Returns a named logger. Named loggers are managed by the underlying factory. */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { LoggerDisposedError } from "../loggerErrors/loggerError.base.js";
|
|
1
2
|
import { createLogger } from "../loggerCore/core/loggerCore.core.js";
|
|
2
3
|
import { createDefaultLogger } from "../loggerCore/helpers/loggerCore.helper.js";
|
|
3
4
|
import { createLoggerFactory } from "../loggerFactory/loggerFactory.core.js";
|
|
@@ -19,7 +20,7 @@ export class LoggerManager {
|
|
|
19
20
|
/** Initializes the logger manager. Initialization is idempotent. */
|
|
20
21
|
initialize(options = {}) {
|
|
21
22
|
if (this.closed)
|
|
22
|
-
throw new
|
|
23
|
+
throw new LoggerDisposedError("LoggerManager");
|
|
23
24
|
if (this.initialized && this.defaultLogger)
|
|
24
25
|
return this.defaultLogger;
|
|
25
26
|
const logger = this.factory.create(options.name ?? "zudojs", options);
|
|
@@ -27,10 +28,27 @@ export class LoggerManager {
|
|
|
27
28
|
this.initialized = true;
|
|
28
29
|
return logger;
|
|
29
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Adopts an existing logger as the manager's default logger.
|
|
33
|
+
*
|
|
34
|
+
* The logger is REGISTERED with the underlying factory, so it is
|
|
35
|
+
* covered by flush(), close(), getAll() and size. Assigning it to the
|
|
36
|
+
* private default field alone (as createLoggerManagerFromLogger used
|
|
37
|
+
* to) left the registry empty, making flush()/close() no-ops for the
|
|
38
|
+
* only logger the manager owned.
|
|
39
|
+
*/
|
|
40
|
+
adopt(logger) {
|
|
41
|
+
if (this.closed)
|
|
42
|
+
throw new LoggerDisposedError("LoggerManager");
|
|
43
|
+
this.factory.register(logger);
|
|
44
|
+
this.defaultLogger = logger;
|
|
45
|
+
this.initialized = true;
|
|
46
|
+
return logger;
|
|
47
|
+
}
|
|
30
48
|
/** Returns the default application logger. Lazily initializes when necessary. */
|
|
31
49
|
getLogger() {
|
|
32
50
|
if (this.closed)
|
|
33
|
-
throw new
|
|
51
|
+
throw new LoggerDisposedError("LoggerManager");
|
|
34
52
|
if (!this.defaultLogger)
|
|
35
53
|
return this.initialize();
|
|
36
54
|
return this.defaultLogger;
|
|
@@ -38,13 +56,13 @@ export class LoggerManager {
|
|
|
38
56
|
/** Returns a named logger. Named loggers are managed by the underlying factory. */
|
|
39
57
|
get(name, options = {}) {
|
|
40
58
|
if (this.closed)
|
|
41
|
-
throw new
|
|
59
|
+
throw new LoggerDisposedError("LoggerManager");
|
|
42
60
|
return this.factory.getOrCreate(name, options);
|
|
43
61
|
}
|
|
44
62
|
/** Creates a new logger even when another logger with the same name already exists. */
|
|
45
63
|
create(name, options = {}) {
|
|
46
64
|
if (this.closed)
|
|
47
|
-
throw new
|
|
65
|
+
throw new LoggerDisposedError("LoggerManager");
|
|
48
66
|
return this.factory.create(name, options, true);
|
|
49
67
|
}
|
|
50
68
|
/** Checks whether a named logger exists. */
|
|
@@ -99,7 +117,7 @@ export class LoggerManager {
|
|
|
99
117
|
/** Provides direct access to the underlying factory. */
|
|
100
118
|
getFactory() {
|
|
101
119
|
if (this.closed)
|
|
102
|
-
throw new
|
|
120
|
+
throw new LoggerDisposedError("LoggerManager");
|
|
103
121
|
return this.factory;
|
|
104
122
|
}
|
|
105
123
|
}
|
|
@@ -120,8 +138,7 @@ export function createManagedDefaultLogger(name = "zudojs") {
|
|
|
120
138
|
/** Creates a logger manager from an existing logger. */
|
|
121
139
|
export function createLoggerManagerFromLogger(logger) {
|
|
122
140
|
const manager = new LoggerManager();
|
|
123
|
-
manager
|
|
124
|
-
manager["initialized"] = true;
|
|
141
|
+
manager.adopt(logger);
|
|
125
142
|
return manager;
|
|
126
143
|
}
|
|
127
144
|
/** Returns a logger from a manager or creates a fallback logger when no manager is supplied. */
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
* Buffered logger transport.
|
|
3
3
|
*/
|
|
4
4
|
import { createLoggerTransport, writeLoggerTransport, } from "../loggerTransport.core.js";
|
|
5
|
-
import {
|
|
5
|
+
import { closeLoggerTransport, flushLoggerTransport, } from "../loggerTransportHelpers/loggerTransportHelpers.js";
|
|
6
|
+
import { throwCollectedFailures } from "../../loggerErrors/loggerError.helpers.js";
|
|
7
|
+
import { LoggerTransportClosedError } from "../../loggerErrors/loggerError.base.js";
|
|
6
8
|
/**
|
|
7
9
|
* Creates a transport that buffers entries before forwarding
|
|
8
10
|
* them to another transport.
|
|
@@ -12,14 +14,51 @@ export function createBufferedLoggerTransport(transport, options = {}) {
|
|
|
12
14
|
const maxSize = options.maxSize ?? 100;
|
|
13
15
|
const flushInterval = options.flushInterval ?? 0;
|
|
14
16
|
let timer;
|
|
15
|
-
|
|
17
|
+
let deferredFailure;
|
|
18
|
+
let closed = false;
|
|
19
|
+
// Each entry is written on its own: one failing write used to abort the
|
|
20
|
+
// loop after the whole batch had already been spliced out, losing every
|
|
21
|
+
// entry behind it. Only the entries that actually failed are dropped.
|
|
22
|
+
const drain = async () => {
|
|
16
23
|
if (buffer.length === 0) {
|
|
17
24
|
return;
|
|
18
25
|
}
|
|
19
26
|
const entries = buffer.splice(0, buffer.length);
|
|
27
|
+
const failures = [];
|
|
20
28
|
for (const entry of entries) {
|
|
21
|
-
|
|
29
|
+
try {
|
|
30
|
+
await writeLoggerTransport(transport, entry);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
failures.push(error);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
throwCollectedFailures(failures, `${failures.length} buffered log entries failed to write.`);
|
|
37
|
+
};
|
|
38
|
+
// A failure from a timer-triggered drain has no caller to reach, so it
|
|
39
|
+
// is kept and rethrown by the next explicit flush()/close().
|
|
40
|
+
const takeDeferredFailure = () => {
|
|
41
|
+
if (!deferredFailure)
|
|
42
|
+
return [];
|
|
43
|
+
const { error } = deferredFailure;
|
|
44
|
+
deferredFailure = undefined;
|
|
45
|
+
return [error];
|
|
46
|
+
};
|
|
47
|
+
const flush = async () => {
|
|
48
|
+
const failures = takeDeferredFailure();
|
|
49
|
+
try {
|
|
50
|
+
await drain();
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
failures.push(error);
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
await flushLoggerTransport(transport);
|
|
22
57
|
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
failures.push(error);
|
|
60
|
+
}
|
|
61
|
+
throwCollectedFailures(failures, "Buffered logger transport flush failed.");
|
|
23
62
|
};
|
|
24
63
|
const scheduleFlush = () => {
|
|
25
64
|
if (flushInterval <= 0 || timer) {
|
|
@@ -28,10 +67,10 @@ export function createBufferedLoggerTransport(transport, options = {}) {
|
|
|
28
67
|
timer = setTimeout(async () => {
|
|
29
68
|
timer = undefined;
|
|
30
69
|
try {
|
|
31
|
-
await
|
|
70
|
+
await drain();
|
|
32
71
|
}
|
|
33
|
-
catch {
|
|
34
|
-
|
|
72
|
+
catch (error) {
|
|
73
|
+
deferredFailure ??= { error };
|
|
35
74
|
}
|
|
36
75
|
}, flushInterval);
|
|
37
76
|
// A pending flush is housekeeping, not work: left referenced it kept a
|
|
@@ -43,9 +82,15 @@ export function createBufferedLoggerTransport(transport, options = {}) {
|
|
|
43
82
|
name: options.name ?? "buffered",
|
|
44
83
|
enabled: options.enabled ?? true,
|
|
45
84
|
async write(entry) {
|
|
85
|
+
// A write after close() used to be buffered and then silently
|
|
86
|
+
// dropped: nothing drains the buffer again. Refuse it instead so
|
|
87
|
+
// the caller learns the entry was not accepted.
|
|
88
|
+
if (closed) {
|
|
89
|
+
throw new LoggerTransportClosedError(options.name ?? "buffered");
|
|
90
|
+
}
|
|
46
91
|
buffer.push(entry);
|
|
47
92
|
if (buffer.length >= maxSize) {
|
|
48
|
-
await
|
|
93
|
+
await drain();
|
|
49
94
|
}
|
|
50
95
|
else {
|
|
51
96
|
scheduleFlush();
|
|
@@ -53,14 +98,25 @@ export function createBufferedLoggerTransport(transport, options = {}) {
|
|
|
53
98
|
},
|
|
54
99
|
flush,
|
|
55
100
|
async close() {
|
|
101
|
+
closed = true;
|
|
56
102
|
if (timer) {
|
|
57
103
|
clearTimeout(timer);
|
|
58
104
|
timer = undefined;
|
|
59
105
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
await
|
|
106
|
+
const failures = [];
|
|
107
|
+
try {
|
|
108
|
+
await flush();
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
failures.push(error);
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
await closeLoggerTransport(transport);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
failures.push(error);
|
|
63
118
|
}
|
|
119
|
+
throwCollectedFailures(failures, "Buffered logger transport close failed.");
|
|
64
120
|
},
|
|
65
121
|
};
|
|
66
122
|
return createLoggerTransport(buffered, options);
|
|
@@ -7,11 +7,20 @@ import { createBufferedLoggerTransport } from "./loggerTransportComposite.buffer
|
|
|
7
7
|
/**
|
|
8
8
|
* Creates a transport that forwards entries to another
|
|
9
9
|
* transport only when a predicate passes.
|
|
10
|
+
*
|
|
11
|
+
* `flush()` and `close()` are forwarded to the inner transport, so a
|
|
12
|
+
* buffered or file transport nested inside is drained and released by
|
|
13
|
+
* the logger's own `flush()`/`close()`.
|
|
10
14
|
*/
|
|
11
15
|
export declare function createConditionalLoggerTransport(transport: LoggerTransportLike, predicate: (entry: LoggerEntry) => boolean | Promise<boolean>, options?: LoggerTransportOptions): RegisteredLoggerTransport;
|
|
12
16
|
/**
|
|
13
17
|
* Creates a transport that forwards entries to multiple
|
|
14
18
|
* transports.
|
|
19
|
+
*
|
|
20
|
+
* Every sink receives every entry even when another sink throws: writes
|
|
21
|
+
* are settled independently and the failures are rethrown afterwards
|
|
22
|
+
* (one failure as itself, several as an AggregateError). `flush()` and
|
|
23
|
+
* `close()` fan out to every inner transport the same way.
|
|
15
24
|
*/
|
|
16
25
|
export declare function createMultiLoggerTransport(transports: readonly LoggerTransportLike[], options?: LoggerTransportOptions): RegisteredLoggerTransport;
|
|
17
26
|
export { createBufferedLoggerTransport };
|
|
@@ -2,27 +2,48 @@
|
|
|
2
2
|
* Composite logger transports.
|
|
3
3
|
*/
|
|
4
4
|
import { createLoggerTransport, writeLoggerTransport, } from "../loggerTransport.core.js";
|
|
5
|
+
import { createLoggerTransportId } from "../loggerTransportGuard.js";
|
|
6
|
+
import { closeLoggerTransport, flushLoggerTransport, } from "../loggerTransportHelpers/loggerTransportHelpers.js";
|
|
7
|
+
import { settleAllOrThrow } from "../../loggerErrors/loggerError.helpers.js";
|
|
5
8
|
import { createBufferedLoggerTransport } from "./loggerTransportComposite.buffered.js";
|
|
6
9
|
/**
|
|
7
10
|
* Creates a transport that forwards entries to another
|
|
8
11
|
* transport only when a predicate passes.
|
|
12
|
+
*
|
|
13
|
+
* `flush()` and `close()` are forwarded to the inner transport, so a
|
|
14
|
+
* buffered or file transport nested inside is drained and released by
|
|
15
|
+
* the logger's own `flush()`/`close()`.
|
|
9
16
|
*/
|
|
10
17
|
export function createConditionalLoggerTransport(transport, predicate, options = {}) {
|
|
11
|
-
return createLoggerTransport(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
return createLoggerTransport({
|
|
19
|
+
name: options.name ?? createLoggerTransportId(),
|
|
20
|
+
enabled: options.enabled ?? true,
|
|
21
|
+
async write(entry, context) {
|
|
22
|
+
if (await predicate(entry)) {
|
|
23
|
+
await writeLoggerTransport(transport, entry, context);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
flush: () => flushLoggerTransport(transport),
|
|
27
|
+
close: () => closeLoggerTransport(transport),
|
|
15
28
|
}, options);
|
|
16
29
|
}
|
|
17
30
|
/**
|
|
18
31
|
* Creates a transport that forwards entries to multiple
|
|
19
32
|
* transports.
|
|
33
|
+
*
|
|
34
|
+
* Every sink receives every entry even when another sink throws: writes
|
|
35
|
+
* are settled independently and the failures are rethrown afterwards
|
|
36
|
+
* (one failure as itself, several as an AggregateError). `flush()` and
|
|
37
|
+
* `close()` fan out to every inner transport the same way.
|
|
20
38
|
*/
|
|
21
39
|
export function createMultiLoggerTransport(transports, options = {}) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
40
|
+
const sinks = [...transports];
|
|
41
|
+
return createLoggerTransport({
|
|
42
|
+
name: options.name ?? createLoggerTransportId(),
|
|
43
|
+
enabled: options.enabled ?? true,
|
|
44
|
+
write: (entry, context) => settleAllOrThrow(sinks.map((sink) => () => writeLoggerTransport(sink, entry, context)), "Multiple logger transports failed to write an entry."),
|
|
45
|
+
flush: () => settleAllOrThrow(sinks.map((sink) => () => flushLoggerTransport(sink)), "Multiple logger transports failed to flush."),
|
|
46
|
+
close: () => settleAllOrThrow(sinks.map((sink) => () => closeLoggerTransport(sink)), "Multiple logger transports failed to close."),
|
|
26
47
|
}, options);
|
|
27
48
|
}
|
|
28
49
|
export { createBufferedLoggerTransport };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zudojs/logger",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Structured logging with transports, log levels, and context propagation for Zudojs applications.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"node": ">=24.0.0"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@zudojs/errors": "1.0
|
|
31
|
+
"@zudojs/errors": "1.2.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"typescript": "7.0.2",
|