@zudojs/logger 0.1.0 → 1.1.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/LICENSE +21 -0
- package/README.md +98 -8
- package/dist/loggerCore/core/loggerCore.context.d.ts +9 -2
- package/dist/loggerCore/core/loggerCore.context.js +12 -6
- package/dist/loggerCore/core/loggerCore.core.d.ts +10 -0
- package/dist/loggerCore/core/loggerCore.core.js +52 -1
- package/dist/loggerCore/helpers/loggerCore.helper.d.ts +7 -2
- package/dist/loggerCore/helpers/loggerCore.helper.js +10 -9
- package/dist/loggerCore/helpers/loggerCoreMethods/index.d.ts +1 -1
- package/dist/loggerCore/helpers/loggerCoreMethods/index.js +1 -1
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.child.js +3 -1
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.dispatch.d.ts +11 -0
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.dispatch.js +121 -11
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.entry.js +18 -2
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.level.js +9 -2
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.lifecycle.js +33 -0
- package/dist/loggerEntry/loggerEntry.core.js +0 -1
- package/dist/loggerEntry/loggerEntryHelpers/index.d.ts +2 -0
- package/dist/loggerEntry/loggerEntryHelpers/index.js +1 -0
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.sanitize.d.ts +69 -0
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.sanitize.js +113 -0
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.serialize.d.ts +5 -1
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.serialize.js +9 -5
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.valueSerialize.js +9 -1
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.context.js +15 -3
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.js +3 -2
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.json.js +19 -3
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.metadata.js +14 -6
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.text.js +31 -4
- package/dist/loggerOptions/loggerOptions.type.d.ts +12 -1
- package/dist/loggerOptions/loggerOptions.type.js +4 -1
- package/dist/loggerTransport/loggerTransportComposite/loggerTransportComposite.buffered.js +4 -0
- package/dist/loggerTransport/loggerTransportHelpers/loggerTransportHelpers.js +4 -3
- package/package.json +23 -12
- package/dist/.tsbuildinfo +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/loggerContext/index.d.ts.map +0 -1
- package/dist/loggerContext/index.js.map +0 -1
- package/dist/loggerContext/loggerContext.core.d.ts.map +0 -1
- package/dist/loggerContext/loggerContext.core.js.map +0 -1
- package/dist/loggerContext/loggerContext.type.d.ts.map +0 -1
- package/dist/loggerContext/loggerContext.type.js.map +0 -1
- package/dist/loggerContext/loggerContextCreate.d.ts.map +0 -1
- package/dist/loggerContext/loggerContextCreate.js.map +0 -1
- package/dist/loggerContext/loggerContextSerialize.d.ts.map +0 -1
- package/dist/loggerContext/loggerContextSerialize.js.map +0 -1
- package/dist/loggerContext/loggerContextStorage.d.ts.map +0 -1
- package/dist/loggerContext/loggerContextStorage.js.map +0 -1
- package/dist/loggerCore/core/index.d.ts.map +0 -1
- package/dist/loggerCore/core/index.js.map +0 -1
- package/dist/loggerCore/core/loggerCore.context.d.ts.map +0 -1
- package/dist/loggerCore/core/loggerCore.context.js.map +0 -1
- package/dist/loggerCore/core/loggerCore.core.d.ts.map +0 -1
- package/dist/loggerCore/core/loggerCore.core.js.map +0 -1
- package/dist/loggerCore/core/loggerCore.type.d.ts.map +0 -1
- package/dist/loggerCore/core/loggerCore.type.js.map +0 -1
- package/dist/loggerCore/helpers/index.d.ts.map +0 -1
- package/dist/loggerCore/helpers/index.js.map +0 -1
- package/dist/loggerCore/helpers/loggerCore.helper.d.ts.map +0 -1
- package/dist/loggerCore/helpers/loggerCore.helper.js.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreHelpers.d.ts.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreHelpers.js.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreMethods/index.d.ts.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreMethods/index.js.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.child.d.ts.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.child.js.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.dispatch.d.ts.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.dispatch.js.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.entry.d.ts.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.entry.js.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.level.d.ts.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.level.js.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.lifecycle.d.ts.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreMethods/loggerCoreMethods.lifecycle.js.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreMethods.logLevels.d.ts.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreMethods.logLevels.js.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreMethods.loggerProps.d.ts.map +0 -1
- package/dist/loggerCore/helpers/loggerCoreMethods.loggerProps.js.map +0 -1
- package/dist/loggerCore/index.d.ts.map +0 -1
- package/dist/loggerCore/index.js.map +0 -1
- package/dist/loggerEntry/index.d.ts.map +0 -1
- package/dist/loggerEntry/index.js.map +0 -1
- package/dist/loggerEntry/loggerEntry.core.d.ts.map +0 -1
- package/dist/loggerEntry/loggerEntry.core.js.map +0 -1
- package/dist/loggerEntry/loggerEntry.type.d.ts.map +0 -1
- package/dist/loggerEntry/loggerEntry.type.js.map +0 -1
- package/dist/loggerEntry/loggerEntryCreate.d.ts.map +0 -1
- package/dist/loggerEntry/loggerEntryCreate.js.map +0 -1
- package/dist/loggerEntry/loggerEntryHelpers/index.d.ts.map +0 -1
- package/dist/loggerEntry/loggerEntryHelpers/index.js.map +0 -1
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.interfaces.d.ts.map +0 -1
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.interfaces.js.map +0 -1
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.serialize.d.ts.map +0 -1
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.serialize.js.map +0 -1
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.types.d.ts.map +0 -1
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.types.js.map +0 -1
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.valueSerialize.d.ts.map +0 -1
- package/dist/loggerEntry/loggerEntryHelpers/loggerEntryHelpers.valueSerialize.js.map +0 -1
- package/dist/loggerEntry/loggerEntrySerialize.d.ts.map +0 -1
- package/dist/loggerEntry/loggerEntrySerialize.js.map +0 -1
- package/dist/loggerErrors/index.d.ts.map +0 -1
- package/dist/loggerErrors/index.js.map +0 -1
- package/dist/loggerErrors/loggerError.base.d.ts.map +0 -1
- package/dist/loggerErrors/loggerError.base.js.map +0 -1
- package/dist/loggerErrors/loggerError.helpers.d.ts.map +0 -1
- package/dist/loggerErrors/loggerError.helpers.js.map +0 -1
- package/dist/loggerFactory/index.d.ts.map +0 -1
- package/dist/loggerFactory/index.js.map +0 -1
- package/dist/loggerFactory/loggerFactory.core.d.ts.map +0 -1
- package/dist/loggerFactory/loggerFactory.core.js.map +0 -1
- package/dist/loggerFormatter/index.d.ts.map +0 -1
- package/dist/loggerFormatter/index.js.map +0 -1
- package/dist/loggerFormatter/loggerFormatter.core.d.ts.map +0 -1
- package/dist/loggerFormatter/loggerFormatter.core.js.map +0 -1
- package/dist/loggerFormatter/loggerFormatter.type.d.ts.map +0 -1
- package/dist/loggerFormatter/loggerFormatter.type.js.map +0 -1
- package/dist/loggerFormatter/loggerFormatterFormatters/index.d.ts.map +0 -1
- package/dist/loggerFormatter/loggerFormatterFormatters/index.js.map +0 -1
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.context.d.ts.map +0 -1
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.context.js.map +0 -1
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.d.ts.map +0 -1
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.js.map +0 -1
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.json.d.ts.map +0 -1
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.json.js.map +0 -1
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.metadata.d.ts.map +0 -1
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.metadata.js.map +0 -1
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.text.d.ts.map +0 -1
- package/dist/loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.text.js.map +0 -1
- package/dist/loggerFormatter/loggerFormatterGuard.d.ts.map +0 -1
- package/dist/loggerFormatter/loggerFormatterGuard.js.map +0 -1
- package/dist/loggerLevel/index.d.ts.map +0 -1
- package/dist/loggerLevel/index.js.map +0 -1
- package/dist/loggerLevel/loggerLevel.type.d.ts.map +0 -1
- package/dist/loggerLevel/loggerLevel.type.js.map +0 -1
- package/dist/loggerManager/index.d.ts.map +0 -1
- package/dist/loggerManager/index.js.map +0 -1
- package/dist/loggerManager/loggerManager.core.d.ts.map +0 -1
- package/dist/loggerManager/loggerManager.core.js.map +0 -1
- package/dist/loggerOptions/index.d.ts.map +0 -1
- package/dist/loggerOptions/index.js.map +0 -1
- package/dist/loggerOptions/loggerOptions.type.d.ts.map +0 -1
- package/dist/loggerOptions/loggerOptions.type.js.map +0 -1
- package/dist/loggerTransport/index.d.ts.map +0 -1
- package/dist/loggerTransport/index.js.map +0 -1
- package/dist/loggerTransport/loggerTransport.core.d.ts.map +0 -1
- package/dist/loggerTransport/loggerTransport.core.js.map +0 -1
- package/dist/loggerTransport/loggerTransport.registry.d.ts.map +0 -1
- package/dist/loggerTransport/loggerTransport.registry.js.map +0 -1
- package/dist/loggerTransport/loggerTransport.type.d.ts.map +0 -1
- package/dist/loggerTransport/loggerTransport.type.js.map +0 -1
- package/dist/loggerTransport/loggerTransportComposite/index.d.ts.map +0 -1
- package/dist/loggerTransport/loggerTransportComposite/index.js.map +0 -1
- package/dist/loggerTransport/loggerTransportComposite/loggerTransportComposite.buffered.d.ts.map +0 -1
- package/dist/loggerTransport/loggerTransportComposite/loggerTransportComposite.buffered.js.map +0 -1
- package/dist/loggerTransport/loggerTransportComposite/loggerTransportComposite.d.ts.map +0 -1
- package/dist/loggerTransport/loggerTransportComposite/loggerTransportComposite.js.map +0 -1
- package/dist/loggerTransport/loggerTransportConsole/index.d.ts.map +0 -1
- package/dist/loggerTransport/loggerTransportConsole/index.js.map +0 -1
- package/dist/loggerTransport/loggerTransportConsole/loggerTransportConsole.core.d.ts.map +0 -1
- package/dist/loggerTransport/loggerTransportConsole/loggerTransportConsole.core.js.map +0 -1
- package/dist/loggerTransport/loggerTransportGuard.d.ts.map +0 -1
- package/dist/loggerTransport/loggerTransportGuard.js.map +0 -1
- package/dist/loggerTransport/loggerTransportHelpers/index.d.ts.map +0 -1
- package/dist/loggerTransport/loggerTransportHelpers/index.js.map +0 -1
- package/dist/loggerTransport/loggerTransportHelpers/loggerTransportHelpers.d.ts.map +0 -1
- package/dist/loggerTransport/loggerTransportHelpers/loggerTransportHelpers.js.map +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zudojs Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# @zudojs/logger
|
|
2
2
|
|
|
3
|
-
Structured logging with transports, log levels,
|
|
3
|
+
Structured logging with transports, formatters, log levels, secret
|
|
4
|
+
redaction, and context propagation for Zudojs applications.
|
|
4
5
|
|
|
5
6
|
## Installation
|
|
6
7
|
|
|
@@ -17,16 +18,105 @@ const logger = createLogger({ name: "api" });
|
|
|
17
18
|
|
|
18
19
|
logger.info("Server started", { port: 3000, env: "production" });
|
|
19
20
|
logger.error("Connection failed", { error: err.message });
|
|
21
|
+
|
|
22
|
+
await logger.flush(); // drain in-flight writes before exit
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Levels
|
|
26
|
+
|
|
27
|
+
`fatal` (0), `error` (1), `warn` (2), `info` (3), `debug` (4), `trace`
|
|
28
|
+
(5). A logger emits every message at or below its configured level;
|
|
29
|
+
`level` defaults to `info`.
|
|
30
|
+
|
|
31
|
+
## Transports
|
|
32
|
+
|
|
33
|
+
A transport is either a `{ name, enabled, write, flush?, close? }`
|
|
34
|
+
object or a `(entry, context) => void | Promise<void>` function. When no
|
|
35
|
+
transport is configured the logger writes to the console.
|
|
36
|
+
|
|
37
|
+
Built in: `createConsoleLoggerTransport`, and the composites
|
|
38
|
+
`createMultiLoggerTransport`, `createConditionalLoggerTransport` and
|
|
39
|
+
`createBufferedLoggerTransport`. File and HTTP transports are not
|
|
40
|
+
included — implement the `LoggerTransport` interface for those.
|
|
41
|
+
|
|
42
|
+
`transportTimeout` (default 10s) bounds every transport write, so a
|
|
43
|
+
transport that stops responding cannot hang `flush()` or `close()`.
|
|
44
|
+
|
|
45
|
+
`throwTransportErrors` (default `false`) rethrows transport and formatter
|
|
46
|
+
failures instead of dropping them. A synchronous transport throws from the
|
|
47
|
+
log call itself; a failure from an asynchronous transport (or with
|
|
48
|
+
`asynchronous: true`) cannot, so it is rethrown by the next `flush()` or
|
|
49
|
+
`close()`, which still flush and close the transports first.
|
|
50
|
+
|
|
51
|
+
## Flushing
|
|
52
|
+
|
|
53
|
+
Dispatch completes synchronously when every transport is synchronous.
|
|
54
|
+
With an asynchronous transport — or with `asynchronous: true`, which
|
|
55
|
+
always defers so the caller stays off the transport's critical path —
|
|
56
|
+
writes are in flight until drained. `flush()` and `close()` drain them,
|
|
57
|
+
so nothing is lost at exit.
|
|
58
|
+
|
|
59
|
+
## Secret redaction
|
|
60
|
+
|
|
61
|
+
Redaction is **on by default**. Metadata and context fields whose NAME
|
|
62
|
+
looks like a secret — password, secret, token, api key, private key,
|
|
63
|
+
credential, authorization, cookie — are replaced with `"[REDACTED]"`
|
|
64
|
+
before the entry reaches any formatter or transport. Nested objects,
|
|
65
|
+
arrays and getters are all covered.
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
logger.info("login", { user: "alice", password: "hunter2" });
|
|
69
|
+
// metadata: { user: "alice", password: "[REDACTED]" }
|
|
70
|
+
|
|
71
|
+
createLogger({ redact: { keys: ["ssn"], replacement: "***" } });
|
|
72
|
+
createLogger({ redact: { enabled: false } }); // opt out
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Log injection
|
|
76
|
+
|
|
77
|
+
Text-shaped formatters escape control characters in the message, the
|
|
78
|
+
logger name, metadata keys and values, context values and source
|
|
79
|
+
locations. A newline or ANSI escape inside attacker-supplied text
|
|
80
|
+
becomes `\n` / `\u001b` rather than forging an extra log record or
|
|
81
|
+
driving the operator's terminal. The JSON formatter relies on
|
|
82
|
+
`JSON.stringify`, which escapes the same characters.
|
|
83
|
+
|
|
84
|
+
Metadata is normalized before serialization, so circular references
|
|
85
|
+
(`"[Circular]"`), BigInt values and functions never make a formatter
|
|
86
|
+
throw and silently drop the record.
|
|
87
|
+
|
|
88
|
+
## Context
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { createLoggerContext, withLoggerContext } from "@zudojs/logger";
|
|
92
|
+
|
|
93
|
+
const scoped = logger.withContext(
|
|
94
|
+
createLoggerContext({ requestId: "req-1", metadata: { tenant: "acme" } }),
|
|
95
|
+
);
|
|
96
|
+
scoped.info("handled"); // metadata carries requestId and tenant
|
|
97
|
+
|
|
98
|
+
withLoggerContext(logger, createLoggerContext({ traceId }), (scoped) => {
|
|
99
|
+
scoped.info("inside the trace");
|
|
100
|
+
});
|
|
20
101
|
```
|
|
21
102
|
|
|
22
|
-
|
|
103
|
+
Per-call context is merged into the entry's metadata the same way:
|
|
104
|
+
`logger.log(LoggerLevel.INFO, "handled", { context: { tenant: "acme" } })`.
|
|
105
|
+
The entry's `context` also carries the identifiers (`requestId`, `traceId`,
|
|
106
|
+
...) of the active context for transports that read them directly.
|
|
107
|
+
|
|
108
|
+
Child loggers inherit name, level, formatter, transports, metadata and
|
|
109
|
+
redaction settings: `logger.child({ name: "api.db" })`.
|
|
110
|
+
|
|
111
|
+
## Formatters
|
|
112
|
+
|
|
113
|
+
`createTextLoggerFormatter`, `createJsonLoggerFormatter`,
|
|
114
|
+
`createCompactLoggerFormatter`, `createDevelopmentLoggerFormatter`,
|
|
115
|
+
`createProductionLoggerFormatter`, `createStructuredLoggerFormatter`.
|
|
23
116
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
- Child loggers with inherited context
|
|
28
|
-
- Correlation ID propagation
|
|
29
|
-
- Sensitive data redaction
|
|
117
|
+
Pass `{ colors: true }` in the formatter context to colourize the level
|
|
118
|
+
tag of text output. Colour codes are emitted only around the fixed level
|
|
119
|
+
name, never around user-supplied text.
|
|
30
120
|
|
|
31
121
|
## Use Cases
|
|
32
122
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { LoggerLevel } from "../../loggerLevel/loggerLevel.type.js";
|
|
5
5
|
import type { LogMetadata } from "../../loggerEntry/loggerEntry.type.js";
|
|
6
|
-
import type { LoggerContext } from "../../loggerContext/loggerContext.core.js";
|
|
6
|
+
import type { LoggerContext, LoggerContextStorage } from "../../loggerContext/loggerContext.core.js";
|
|
7
7
|
import type { ChildLoggerOptions, LogOptions } from "../../loggerOptions/loggerOptions.type.js";
|
|
8
8
|
import type { Logger } from "./loggerCore.type.js";
|
|
9
9
|
/**
|
|
@@ -12,7 +12,14 @@ import type { Logger } from "./loggerCore.type.js";
|
|
|
12
12
|
export declare class ContextLogger implements Logger {
|
|
13
13
|
private readonly logger;
|
|
14
14
|
private readonly context;
|
|
15
|
-
|
|
15
|
+
private readonly storage;
|
|
16
|
+
/**
|
|
17
|
+
* @param storage The context storage the WRAPPED logger reads from.
|
|
18
|
+
* This used to be created fresh inside `run()`, so the scoped
|
|
19
|
+
* context was written into a throwaway stack and never reached the
|
|
20
|
+
* entry — withContext() silently produced context-free logs.
|
|
21
|
+
*/
|
|
22
|
+
constructor(logger: Logger, context: LoggerContext, storage: LoggerContextStorage);
|
|
16
23
|
get name(): string;
|
|
17
24
|
get level(): LoggerLevel;
|
|
18
25
|
get enabled(): boolean;
|
|
@@ -2,16 +2,23 @@
|
|
|
2
2
|
* ContextLogger wrapper implementation.
|
|
3
3
|
*/
|
|
4
4
|
import { createLoggerContext } from "../../loggerContext/loggerContext.core.js";
|
|
5
|
-
import { createLoggerContextStorage } from "../../loggerContext/loggerContextStorage.js";
|
|
6
5
|
/**
|
|
7
6
|
* Logger wrapper that provides scoped context.
|
|
8
7
|
*/
|
|
9
8
|
export class ContextLogger {
|
|
10
9
|
logger;
|
|
11
10
|
context;
|
|
12
|
-
|
|
11
|
+
storage;
|
|
12
|
+
/**
|
|
13
|
+
* @param storage The context storage the WRAPPED logger reads from.
|
|
14
|
+
* This used to be created fresh inside `run()`, so the scoped
|
|
15
|
+
* context was written into a throwaway stack and never reached the
|
|
16
|
+
* entry — withContext() silently produced context-free logs.
|
|
17
|
+
*/
|
|
18
|
+
constructor(logger, context, storage) {
|
|
13
19
|
this.logger = logger;
|
|
14
20
|
this.context = context;
|
|
21
|
+
this.storage = storage;
|
|
15
22
|
}
|
|
16
23
|
get name() {
|
|
17
24
|
return this.logger.name;
|
|
@@ -44,14 +51,14 @@ export class ContextLogger {
|
|
|
44
51
|
this.run(() => this.logger.log(level, message, options));
|
|
45
52
|
}
|
|
46
53
|
child(options) {
|
|
47
|
-
return new ContextLogger(this.logger.child(options), this.context);
|
|
54
|
+
return new ContextLogger(this.logger.child(options), this.context, this.storage);
|
|
48
55
|
}
|
|
49
56
|
withContext(context) {
|
|
50
57
|
return new ContextLogger(this.logger, createLoggerContext({
|
|
51
58
|
parent: this.context,
|
|
52
59
|
...context.identifiers,
|
|
53
60
|
metadata: context.metadata,
|
|
54
|
-
}));
|
|
61
|
+
}), this.storage);
|
|
55
62
|
}
|
|
56
63
|
setLevel(level) {
|
|
57
64
|
this.logger.setLevel(level);
|
|
@@ -69,8 +76,7 @@ export class ContextLogger {
|
|
|
69
76
|
return this.logger.close();
|
|
70
77
|
}
|
|
71
78
|
run(callback) {
|
|
72
|
-
|
|
73
|
-
storage.run(this.context, callback);
|
|
79
|
+
this.storage.run(this.context, callback);
|
|
74
80
|
}
|
|
75
81
|
}
|
|
76
82
|
//# sourceMappingURL=loggerCore.context.js.map
|
|
@@ -19,6 +19,10 @@ export interface ZudojsLoggerContext {
|
|
|
19
19
|
markDisposed(): void;
|
|
20
20
|
updateConfiguration(config: LoggerConfiguration): void;
|
|
21
21
|
createChildLogger(options: LoggerOptions): Logger;
|
|
22
|
+
/** Registers an in-flight dispatch so it can be awaited later. */
|
|
23
|
+
trackDispatch(dispatch: Promise<void>): void;
|
|
24
|
+
/** Resolves once every registered dispatch has settled. */
|
|
25
|
+
drainDispatches(): Promise<void>;
|
|
22
26
|
}
|
|
23
27
|
/**
|
|
24
28
|
* Logger implementation.
|
|
@@ -27,6 +31,10 @@ export declare class ZudojsLogger implements Logger, ZudojsLoggerContext {
|
|
|
27
31
|
private _configuration;
|
|
28
32
|
private readonly _contextStorage;
|
|
29
33
|
private _disposed;
|
|
34
|
+
private _closing;
|
|
35
|
+
private readonly _pending;
|
|
36
|
+
private readonly _dispatchFailures;
|
|
37
|
+
private _droppedFailures;
|
|
30
38
|
constructor(options?: LoggerOptions, contextStorage?: LoggerContextStorage);
|
|
31
39
|
get configuration(): LoggerConfiguration;
|
|
32
40
|
get contextStorage(): LoggerContextStorage;
|
|
@@ -54,6 +62,8 @@ export declare class ZudojsLogger implements Logger, ZudojsLoggerContext {
|
|
|
54
62
|
markDisposed(): void;
|
|
55
63
|
updateConfiguration(config: LoggerConfiguration): void;
|
|
56
64
|
createChildLogger(options: LoggerOptions): Logger;
|
|
65
|
+
trackDispatch(dispatch: Promise<void>): void;
|
|
66
|
+
drainDispatches(): Promise<void>;
|
|
57
67
|
}
|
|
58
68
|
/**
|
|
59
69
|
* Creates a Zudojs logger.
|
|
@@ -7,6 +7,8 @@ import { resolveLoggerOptions } from "../../loggerOptions/loggerOptions.type.js"
|
|
|
7
7
|
import { normalizeConfiguration, assertActive as assertActiveHelper, assertMutable as assertMutableHelper, handleInfrastructureError as handleInfrastructureErrorHelper, } from "../helpers/loggerCoreHelpers.js";
|
|
8
8
|
import { logAtLevel, childLogger, withContextLogger, setLoggerLevel, enableLogger, disableLogger, flushLogger, closeLogger, } from "../helpers/loggerCoreMethods/index.js";
|
|
9
9
|
import { getLoggerName, getLoggerLevel, getLoggerEnabled, } from "../helpers/loggerCoreMethods.loggerProps.js";
|
|
10
|
+
/** Upper bound on dispatch failures retained between two flushes. */
|
|
11
|
+
const MAX_RETAINED_DISPATCH_FAILURES = 32;
|
|
10
12
|
/**
|
|
11
13
|
* Logger implementation.
|
|
12
14
|
*/
|
|
@@ -14,6 +16,10 @@ export class ZudojsLogger {
|
|
|
14
16
|
_configuration;
|
|
15
17
|
_contextStorage;
|
|
16
18
|
_disposed = false;
|
|
19
|
+
_closing;
|
|
20
|
+
_pending = new Set();
|
|
21
|
+
_dispatchFailures = [];
|
|
22
|
+
_droppedFailures = 0;
|
|
17
23
|
constructor(options = {}, contextStorage) {
|
|
18
24
|
this._configuration = resolveLoggerOptions(options);
|
|
19
25
|
this._contextStorage = contextStorage ?? createLoggerContextStorage();
|
|
@@ -74,7 +80,14 @@ export class ZudojsLogger {
|
|
|
74
80
|
return flushLogger(this);
|
|
75
81
|
}
|
|
76
82
|
close() {
|
|
77
|
-
|
|
83
|
+
// Concurrent callers share one closure: two overlapping close() calls
|
|
84
|
+
// used to drain and close every transport twice.
|
|
85
|
+
if (this._closing)
|
|
86
|
+
return this._closing;
|
|
87
|
+
this._closing = closeLogger(this).finally(() => {
|
|
88
|
+
this._closing = undefined;
|
|
89
|
+
});
|
|
90
|
+
return this._closing;
|
|
78
91
|
}
|
|
79
92
|
assertActive() {
|
|
80
93
|
assertActiveHelper(this._disposed, this._configuration.name);
|
|
@@ -97,6 +110,44 @@ export class ZudojsLogger {
|
|
|
97
110
|
createChildLogger(options) {
|
|
98
111
|
return new ZudojsLogger(options, this._contextStorage);
|
|
99
112
|
}
|
|
113
|
+
trackDispatch(dispatch) {
|
|
114
|
+
// A dispatch rejects only when handleError threw — i.e. when
|
|
115
|
+
// `throwTransportErrors` is on and an asynchronous transport failed.
|
|
116
|
+
// Nothing can throw from the log call that started it, so the failure
|
|
117
|
+
// is kept (bounded) and surfaced by the next flush()/close(); it was
|
|
118
|
+
// previously swallowed outright, which made the option a no-op for
|
|
119
|
+
// every asynchronous transport.
|
|
120
|
+
const tracked = dispatch
|
|
121
|
+
.then(() => { }, (error) => {
|
|
122
|
+
if (!this._configuration.throwTransportErrors)
|
|
123
|
+
return;
|
|
124
|
+
if (this._dispatchFailures.length < MAX_RETAINED_DISPATCH_FAILURES) {
|
|
125
|
+
this._dispatchFailures.push(error);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
this._droppedFailures += 1;
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
.finally(() => {
|
|
132
|
+
this._pending.delete(tracked);
|
|
133
|
+
});
|
|
134
|
+
this._pending.add(tracked);
|
|
135
|
+
}
|
|
136
|
+
async drainDispatches() {
|
|
137
|
+
// A dispatch can start further dispatches (a transport that logs),
|
|
138
|
+
// so drain until the set is genuinely empty.
|
|
139
|
+
while (this._pending.size > 0) {
|
|
140
|
+
await Promise.all([...this._pending]);
|
|
141
|
+
}
|
|
142
|
+
if (this._dispatchFailures.length === 0)
|
|
143
|
+
return;
|
|
144
|
+
const failures = this._dispatchFailures.splice(0);
|
|
145
|
+
const dropped = this._droppedFailures;
|
|
146
|
+
this._droppedFailures = 0;
|
|
147
|
+
if (failures.length === 1 && dropped === 0)
|
|
148
|
+
throw failures[0];
|
|
149
|
+
throw new AggregateError(failures, `${failures.length + dropped} log dispatch(es) failed since the last flush.`);
|
|
150
|
+
}
|
|
100
151
|
}
|
|
101
152
|
/**
|
|
102
153
|
* Creates a Zudojs logger.
|
|
@@ -14,9 +14,14 @@ export declare function createChildLogger(parent: Logger, options?: ChildLoggerO
|
|
|
14
14
|
*/
|
|
15
15
|
export declare function logError(logger: Logger, error: Error, message?: string, metadata?: LogMetadata): void;
|
|
16
16
|
/**
|
|
17
|
-
* Creates a logger
|
|
17
|
+
* Creates a context-scoped logger and runs a callback with it.
|
|
18
|
+
*
|
|
19
|
+
* The scoped logger is passed to the callback. Both branches of the
|
|
20
|
+
* previous implementation were identical and simply discarded the
|
|
21
|
+
* scoped logger, so the context never applied to anything the callback
|
|
22
|
+
* logged.
|
|
18
23
|
*/
|
|
19
|
-
export declare function withLoggerContext<T>(logger: Logger, context: LoggerContext, callback: () => T): T;
|
|
24
|
+
export declare function withLoggerContext<T>(logger: Logger, context: LoggerContext, callback: (scoped: Logger) => T): T;
|
|
20
25
|
/**
|
|
21
26
|
* Creates a default application logger.
|
|
22
27
|
*/
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Logger helper functions.
|
|
3
3
|
*/
|
|
4
|
+
import { LoggerLevel } from "../../loggerLevel/loggerLevel.type.js";
|
|
4
5
|
import { createTextLoggerFormatter } from "../../loggerFormatter/loggerFormatterFormatters/loggerFormatterFormatters.text.js";
|
|
5
6
|
import { createConsoleLoggerTransport } from "../../loggerTransport/loggerTransport.registry.js";
|
|
6
|
-
import { ContextLogger } from "../core/loggerCore.context.js";
|
|
7
7
|
import { createLogger } from "../core/loggerCore.core.js";
|
|
8
8
|
/**
|
|
9
9
|
* Creates a child logger.
|
|
@@ -18,20 +18,21 @@ export function logError(logger, error, message, metadata) {
|
|
|
18
18
|
if (!logger.enabled) {
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
|
-
logger.log(
|
|
21
|
+
logger.log(LoggerLevel.ERROR, message ?? error.message, {
|
|
22
22
|
metadata,
|
|
23
23
|
error,
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
|
-
* Creates a logger
|
|
27
|
+
* Creates a context-scoped logger and runs a callback with it.
|
|
28
|
+
*
|
|
29
|
+
* The scoped logger is passed to the callback. Both branches of the
|
|
30
|
+
* previous implementation were identical and simply discarded the
|
|
31
|
+
* scoped logger, so the context never applied to anything the callback
|
|
32
|
+
* logged.
|
|
28
33
|
*/
|
|
29
34
|
export function withLoggerContext(logger, context, callback) {
|
|
30
|
-
|
|
31
|
-
if (scoped instanceof ContextLogger) {
|
|
32
|
-
return callback();
|
|
33
|
-
}
|
|
34
|
-
return callback();
|
|
35
|
+
return callback(logger.withContext(context));
|
|
35
36
|
}
|
|
36
37
|
/**
|
|
37
38
|
* Creates a default application logger.
|
|
@@ -39,7 +40,7 @@ export function withLoggerContext(logger, context, callback) {
|
|
|
39
40
|
export function createDefaultLogger(name = "zudojs") {
|
|
40
41
|
return createLogger({
|
|
41
42
|
name,
|
|
42
|
-
level:
|
|
43
|
+
level: LoggerLevel.INFO,
|
|
43
44
|
formatter: createTextLoggerFormatter(),
|
|
44
45
|
transports: [createConsoleLoggerTransport()],
|
|
45
46
|
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Logger class helper methods.
|
|
5
5
|
*/
|
|
6
6
|
export { createEntry } from "./loggerCoreMethods.entry.js";
|
|
7
|
-
export { dispatchEntry } from "./loggerCoreMethods.dispatch.js";
|
|
7
|
+
export { dispatchEntry, dispatchEntrySync, } from "./loggerCoreMethods.dispatch.js";
|
|
8
8
|
export { logAtLevel } from "./loggerCoreMethods.level.js";
|
|
9
9
|
export { childLogger, withContextLogger } from "./loggerCoreMethods.child.js";
|
|
10
10
|
export { setLoggerLevel, enableLogger, disableLogger, flushLogger, closeLogger, } from "./loggerCoreMethods.lifecycle.js";
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Logger class helper methods.
|
|
5
5
|
*/
|
|
6
6
|
export { createEntry } from "./loggerCoreMethods.entry.js";
|
|
7
|
-
export { dispatchEntry } from "./loggerCoreMethods.dispatch.js";
|
|
7
|
+
export { dispatchEntry, dispatchEntrySync, } from "./loggerCoreMethods.dispatch.js";
|
|
8
8
|
export { logAtLevel } from "./loggerCoreMethods.level.js";
|
|
9
9
|
export { childLogger, withContextLogger } from "./loggerCoreMethods.child.js";
|
|
10
10
|
export { setLoggerLevel, enableLogger, disableLogger, flushLogger, closeLogger, } from "./loggerCoreMethods.lifecycle.js";
|
|
@@ -17,6 +17,8 @@ export function childLogger(ctx, options = {}) {
|
|
|
17
17
|
export function withContextLogger(ctx, context) {
|
|
18
18
|
ctx.assertActive();
|
|
19
19
|
const child = childLogger(ctx);
|
|
20
|
-
|
|
20
|
+
// The child shares the parent's context storage, so the wrapper must
|
|
21
|
+
// scope THAT storage for the context to reach created entries.
|
|
22
|
+
return new ContextLogger(child, context, ctx.contextStorage);
|
|
21
23
|
}
|
|
22
24
|
//# sourceMappingURL=loggerCoreMethods.child.js.map
|
|
@@ -7,4 +7,15 @@ import type { LoggerConfiguration } from "../../../loggerOptions/loggerOptions.t
|
|
|
7
7
|
* Dispatches an entry to the configured transports.
|
|
8
8
|
*/
|
|
9
9
|
export declare function dispatchEntry(configuration: LoggerConfiguration, entry: LoggerEntry, handleError: (error: Error) => void): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Dispatches an entry, completing synchronously where it can.
|
|
12
|
+
*
|
|
13
|
+
* Returns `undefined` when every configured transport finished
|
|
14
|
+
* synchronously (nothing to await), otherwise a promise covering the
|
|
15
|
+
* asynchronous remainder. `asynchronous: true` skips the fast path
|
|
16
|
+
* entirely and always defers, keeping the caller off the transport's
|
|
17
|
+
* critical path — the option was previously stored and never read, so
|
|
18
|
+
* both settings behaved identically.
|
|
19
|
+
*/
|
|
20
|
+
export declare function dispatchEntrySync(configuration: LoggerConfiguration, entry: LoggerEntry, handleError: (error: Error) => void): void | Promise<void>;
|
|
10
21
|
//# sourceMappingURL=loggerCoreMethods.dispatch.d.ts.map
|
|
@@ -18,6 +18,41 @@ function defaultFormat(configuration, entry) {
|
|
|
18
18
|
environment: configuration.environment,
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Bounds a transport write with the configured transport timeout.
|
|
23
|
+
*
|
|
24
|
+
* `transportTimeout` was accepted, validated and stored but never read,
|
|
25
|
+
* so a transport whose write() never settled blocked flush() and
|
|
26
|
+
* close() forever. A non-positive timeout disables the bound.
|
|
27
|
+
*/
|
|
28
|
+
async function withTransportTimeout(timeoutMs, transportName, operation) {
|
|
29
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
30
|
+
await operation;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
let timer;
|
|
34
|
+
const expiry = new Promise((_resolve, reject) => {
|
|
35
|
+
timer = setTimeout(() => {
|
|
36
|
+
reject(new LoggerTransportError(`Transport "${transportName}" did not complete within ${timeoutMs}ms.`));
|
|
37
|
+
}, timeoutMs);
|
|
38
|
+
// This timer bounds a write; it is not work in its own right. Left
|
|
39
|
+
// referenced it keeps the event loop alive, so a process that has
|
|
40
|
+
// finished but logged through an async transport hangs until the
|
|
41
|
+
// timeout elapses. Every other timer in the framework is unref'd
|
|
42
|
+
// for the same reason.
|
|
43
|
+
timer.unref?.();
|
|
44
|
+
});
|
|
45
|
+
try {
|
|
46
|
+
await Promise.race([operation, expiry]);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
if (timer !== undefined) {
|
|
50
|
+
clearTimeout(timer);
|
|
51
|
+
}
|
|
52
|
+
// The abandoned write must never surface as an unhandled rejection.
|
|
53
|
+
void operation.catch(() => { });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
21
56
|
/**
|
|
22
57
|
* Writes to a transport.
|
|
23
58
|
*/
|
|
@@ -33,22 +68,45 @@ async function writeTransport(configuration, transport, entry, formatted) {
|
|
|
33
68
|
}
|
|
34
69
|
await writeLoggerTransport(transport.transport, entry, transportContext);
|
|
35
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Formats an entry with the configured formatter.
|
|
73
|
+
*/
|
|
74
|
+
function formatEntry(configuration, entry) {
|
|
75
|
+
const formatter = configuration.formatter;
|
|
76
|
+
if (typeof formatter === "string") {
|
|
77
|
+
return defaultFormat(configuration, entry);
|
|
78
|
+
}
|
|
79
|
+
return formatLoggerEntry(formatter, entry, {
|
|
80
|
+
loggerName: configuration.name,
|
|
81
|
+
environment: configuration.environment,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Writes an entry through one transport WITHOUT forcing a microtask.
|
|
86
|
+
*
|
|
87
|
+
* `writeLoggerTransport` is `async`, so awaiting it always defers by at
|
|
88
|
+
* least one microtask even for a fully synchronous transport. Calling
|
|
89
|
+
* the transport directly lets a synchronous console/array transport
|
|
90
|
+
* complete inline, which is what `asynchronous: false` promises.
|
|
91
|
+
*/
|
|
92
|
+
function writeTransportMaybeSync(configuration, transport, entry, formatted) {
|
|
93
|
+
const transportContext = {
|
|
94
|
+
loggerName: configuration.name,
|
|
95
|
+
environment: configuration.environment,
|
|
96
|
+
};
|
|
97
|
+
const payload = typeof formatted === "string" ? { ...entry, message: formatted } : entry;
|
|
98
|
+
const target = transport.transport;
|
|
99
|
+
return typeof target === "function"
|
|
100
|
+
? target(payload, transportContext)
|
|
101
|
+
: target.write(payload, transportContext);
|
|
102
|
+
}
|
|
36
103
|
/**
|
|
37
104
|
* Dispatches an entry to the configured transports.
|
|
38
105
|
*/
|
|
39
106
|
export async function dispatchEntry(configuration, entry, handleError) {
|
|
40
|
-
const formatter = configuration.formatter;
|
|
41
107
|
let formatted;
|
|
42
108
|
try {
|
|
43
|
-
|
|
44
|
-
formatted = defaultFormat(configuration, entry);
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
formatted = formatLoggerEntry(formatter, entry, {
|
|
48
|
-
loggerName: configuration.name,
|
|
49
|
-
environment: configuration.environment,
|
|
50
|
-
});
|
|
51
|
-
}
|
|
109
|
+
formatted = formatEntry(configuration, entry);
|
|
52
110
|
}
|
|
53
111
|
catch (error) {
|
|
54
112
|
const formatterError = new LoggerFormatterError(`Failed to format log entry: ${toLoggerError(error).message}`, { cause: error });
|
|
@@ -64,7 +122,7 @@ export async function dispatchEntry(configuration, entry, handleError) {
|
|
|
64
122
|
if (!registered.enabled) {
|
|
65
123
|
continue;
|
|
66
124
|
}
|
|
67
|
-
await writeTransport(configuration, registered, entry, formatted);
|
|
125
|
+
await withTransportTimeout(configuration.transportTimeout, registered.name, writeTransport(configuration, registered, entry, formatted));
|
|
68
126
|
}
|
|
69
127
|
catch (error) {
|
|
70
128
|
const transportError = new LoggerTransportError(`Failed to write log entry: ${toLoggerError(error).message}`, { cause: error });
|
|
@@ -72,4 +130,56 @@ export async function dispatchEntry(configuration, entry, handleError) {
|
|
|
72
130
|
}
|
|
73
131
|
}
|
|
74
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Dispatches an entry, completing synchronously where it can.
|
|
135
|
+
*
|
|
136
|
+
* Returns `undefined` when every configured transport finished
|
|
137
|
+
* synchronously (nothing to await), otherwise a promise covering the
|
|
138
|
+
* asynchronous remainder. `asynchronous: true` skips the fast path
|
|
139
|
+
* entirely and always defers, keeping the caller off the transport's
|
|
140
|
+
* critical path — the option was previously stored and never read, so
|
|
141
|
+
* both settings behaved identically.
|
|
142
|
+
*/
|
|
143
|
+
export function dispatchEntrySync(configuration, entry, handleError) {
|
|
144
|
+
if (configuration.asynchronous) {
|
|
145
|
+
// `dispatchEntry` runs synchronously up to its first await, so a
|
|
146
|
+
// synchronous transport would still execute inline. Hop a
|
|
147
|
+
// microtask first so `asynchronous: true` genuinely keeps the
|
|
148
|
+
// caller off the transport's critical path.
|
|
149
|
+
return Promise.resolve().then(() => dispatchEntry(configuration, entry, handleError));
|
|
150
|
+
}
|
|
151
|
+
let formatted;
|
|
152
|
+
try {
|
|
153
|
+
formatted = formatEntry(configuration, entry);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
handleError(new LoggerFormatterError(`Failed to format log entry: ${toLoggerError(error).message}`, { cause: error }));
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const pending = [];
|
|
160
|
+
for (const transport of configuration.transports) {
|
|
161
|
+
try {
|
|
162
|
+
if (!isLoggerTransport(transport)) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const registered = createLoggerTransport(transport);
|
|
166
|
+
if (!registered.enabled) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const result = writeTransportMaybeSync(configuration, registered, entry, formatted);
|
|
170
|
+
if (result instanceof Promise) {
|
|
171
|
+
pending.push(withTransportTimeout(configuration.transportTimeout, registered.name, result).catch((error) => {
|
|
172
|
+
handleError(new LoggerTransportError(`Failed to write log entry: ${toLoggerError(error).message}`, { cause: error }));
|
|
173
|
+
}));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
handleError(new LoggerTransportError(`Failed to write log entry: ${toLoggerError(error).message}`, { cause: error }));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (pending.length === 0) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
return Promise.all(pending).then(() => undefined);
|
|
184
|
+
}
|
|
75
185
|
//# sourceMappingURL=loggerCoreMethods.dispatch.js.map
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Logger entry creation for dispatch.
|
|
3
3
|
*/
|
|
4
4
|
import { createLoggerEntry } from "../../../loggerEntry/loggerEntry.core.js";
|
|
5
|
+
import { createSecretMatcher, redactLogValue, LOGGER_REDACTION_TOKEN, } from "../../../loggerEntry/loggerEntryHelpers/loggerEntryHelpers.sanitize.js";
|
|
5
6
|
import { contextToLogMetadata, createLoggerContext, } from "../../../loggerContext/loggerContext.core.js";
|
|
6
7
|
/**
|
|
7
8
|
* Creates a normalized log entry.
|
|
@@ -13,11 +14,22 @@ export function createEntry(configuration, contextStorage, level, message, optio
|
|
|
13
14
|
const contextMetadata = activeContext
|
|
14
15
|
? contextToLogMetadata(activeContext)
|
|
15
16
|
: {};
|
|
16
|
-
|
|
17
|
+
// Per-call `options.context` flows into metadata exactly like the ambient
|
|
18
|
+
// context does. It used to reach only `entry.context.metadata`, which the
|
|
19
|
+
// text formatters never print, so `log(level, msg, { context })` silently
|
|
20
|
+
// dropped the data from every text-shaped line.
|
|
21
|
+
const rawMetadata = {
|
|
17
22
|
...configuration.metadata,
|
|
18
23
|
...contextMetadata,
|
|
24
|
+
...(options.context ?? {}),
|
|
19
25
|
...(options.metadata ?? {}),
|
|
20
26
|
};
|
|
27
|
+
// Redaction is applied HERE, before the entry is frozen, so every
|
|
28
|
+
// formatter and every transport sees the already-masked value and no
|
|
29
|
+
// path can bypass it — including nested objects, arrays and getters.
|
|
30
|
+
const isSecret = createSecretMatcher(configuration.redact);
|
|
31
|
+
const replacement = configuration.redact.replacement ?? LOGGER_REDACTION_TOKEN;
|
|
32
|
+
const metadata = redactLogValue(rawMetadata, isSecret, replacement);
|
|
21
33
|
const context = options.context
|
|
22
34
|
? createLoggerContext({
|
|
23
35
|
parent: activeContext,
|
|
@@ -28,9 +40,13 @@ export function createEntry(configuration, contextStorage, level, message, optio
|
|
|
28
40
|
level,
|
|
29
41
|
message,
|
|
30
42
|
metadata,
|
|
43
|
+
// `LoggerEntryContext` declares the correlation identifiers, but they
|
|
44
|
+
// were never copied here, so a transport reading `entry.context.requestId`
|
|
45
|
+
// always saw `undefined`.
|
|
31
46
|
context: context
|
|
32
47
|
? {
|
|
33
|
-
|
|
48
|
+
...context.identifiers,
|
|
49
|
+
metadata: redactLogValue(context.metadata, isSecret, replacement),
|
|
34
50
|
}
|
|
35
51
|
: undefined,
|
|
36
52
|
source: options.source,
|