@monochromatic-dev/module-logger 0.2.0 → 0.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/CHANGELOG.md +11 -0
- package/README.md +33 -8
- package/dist/final/neutral/browser.d.mts +60 -0
- package/dist/final/neutral/browser.mjs +1 -0
- package/dist/final/neutral/index.d.mts +360 -594
- package/dist/final/neutral/index.mjs +2 -3
- package/dist/final/neutral/indexed-db-hsIfv7Cv.mjs +2 -0
- package/dist/final/neutral/types-BkkBXgY3.d.mts +76 -0
- package/dist/final/node/file-CRGb1hDK.mjs +1 -0
- package/dist/final/node/index.d.mts +360 -594
- package/dist/final/node/index.mjs +3 -3
- package/dist/final/node/node.d.mts +103 -0
- package/dist/final/node/node.mjs +1 -0
- package/dist/final/node/types-BkkBXgY3.d.mts +76 -0
- package/package.json +18 -4
- package/src/artifact-platform-split.unit.test.ts +140 -0
- package/src/browser.ts +14 -0
- package/src/create-logger.ts +183 -183
- package/src/create-logger.unit.test.ts +112 -112
- package/src/default-sinks.neutral.ts +34 -0
- package/src/default-sinks.node.ts +32 -0
- package/src/error-format.ts +23 -23
- package/src/logger.ts +23 -50
- package/src/node.ts +23 -0
- package/src/sink/console-control-chars.ts +64 -64
- package/src/sink/console-control-chars.unit.test.ts +14 -14
- package/src/sink/console.ts +194 -194
- package/src/sink/console.unit.test.ts +18 -18
- package/src/sink/file.ts +136 -140
- package/src/sink/file.unit.test.ts +19 -26
- package/src/sink/index.ts +4 -7
- package/src/sink/indexed-db-util.ts +42 -42
- package/src/sink/indexed-db.browser.test.ts +7 -7
- package/src/sink/indexed-db.ts +109 -109
- package/src/sink/indexed-db.unit.test.ts +5 -13
- package/src/sink/local-storage-key.ts +73 -73
- package/src/sink/local-storage-key.unit.test.ts +8 -8
- package/src/sink/local-storage-quota.ts +37 -37
- package/src/sink/local-storage-quota.unit.test.ts +8 -8
- package/src/sink/local-storage-store.ts +113 -113
- package/src/sink/local-storage-store.unit.test.ts +35 -35
- package/src/sink/local-storage.ts +72 -72
- package/src/sink/local-storage.unit.test.ts +27 -27
- package/src/sink/noop.ts +20 -20
- package/src/sink/noop.unit.test.ts +1 -1
- package/src/sink/opfs.browser.test.ts +7 -7
- package/src/sink/opfs.ts +62 -62
- package/src/sink/opfs.unit.test.ts +5 -13
- package/src/sink/record-buffer.ts +84 -84
- package/src/sink/record-buffer.unit.test.ts +20 -20
- package/src/sink/session-storage-quota.ts +34 -34
- package/src/sink/session-storage-quota.unit.test.ts +8 -8
- package/src/sink/session-storage-store.ts +72 -72
- package/src/sink/session-storage.ts +48 -48
- package/src/sink/session-storage.unit.test.ts +39 -39
- package/src/sink/web-storage-quota-error.ts +22 -22
- package/src/sink/web-storage-quota-error.unit.test.ts +2 -2
- package/src/sink/web-storage-runtime.ts +24 -24
- package/src/startup.unit.test.ts +18 -18
- package/src/tagged.ts +35 -35
- package/src/tagged.unit.test.ts +8 -8
- package/src/types.ts +39 -39
package/src/create-logger.ts
CHANGED
|
@@ -9,41 +9,41 @@ import type {
|
|
|
9
9
|
} from './types.ts';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
Default `flush()` deadline in milliseconds. Measured on 2026-09-06: a
|
|
13
|
+
default logger flushing 100 records through the console and file sinks
|
|
14
|
+
settles in about 2 ms locally, so this leaves three orders of magnitude for
|
|
15
|
+
a slow but working backend while still bounding shutdown on a wedged one.
|
|
16
|
+
Override per logger through the `flushDeadlineMs` option of
|
|
17
|
+
{@link createLogger}.
|
|
18
18
|
*/
|
|
19
19
|
export const DEFAULT_FLUSH_DEADLINE_MS = 5_000;
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
Default per-sink `verify()` time limit in milliseconds. Measured on
|
|
23
|
+
2026-09-06: the default logger's five shipped verifies complete together in
|
|
24
|
+
about 2.4 ms locally, so this leaves three orders of magnitude for a slow
|
|
25
|
+
but working backend probe (a network filesystem, a busy IndexedDB) while a
|
|
26
|
+
verify that never answers (a hung mount, an IndexedDB open blocked by
|
|
27
|
+
another tab) can no longer stall startup. Override per logger through the
|
|
28
|
+
`verifyTimeoutMs` option of {@link createLogger}.
|
|
29
29
|
*/
|
|
30
30
|
export const DEFAULT_VERIFY_TIMEOUT_MS = 5_000;
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
Most records the logger buffers before its sinks have verified. Startup
|
|
34
|
+
lasts at most {@link DEFAULT_VERIFY_TIMEOUT_MS}, so this bounds the memory a
|
|
35
|
+
burst during that window can claim; on overflow the oldest buffered record
|
|
36
|
+
is dropped so the newest (usually most diagnostic) context survives, and
|
|
37
|
+
one synthetic `warn` record naming the dropped count is written to every
|
|
38
|
+
available sink once initialization completes.
|
|
39
39
|
*/
|
|
40
40
|
export const STARTUP_BUFFER_CAP = 10_000;
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
Sink paired with its current availability inside one logger instance.
|
|
44
|
+
Availability starts `false` (not-yet-verified reads as unavailable,
|
|
45
|
+
identical to a verified failure) and flips `true` once the sink's own
|
|
46
|
+
`verify` confirms its backend.
|
|
47
47
|
*/
|
|
48
48
|
type SinkEntry = {
|
|
49
49
|
available: boolean;
|
|
@@ -51,12 +51,12 @@ type SinkEntry = {
|
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
Awaits one sink write so `flush()` can observe its settling. A rejected
|
|
55
|
+
write is swallowed here via {@link reportLoggerInternalError} (the sink
|
|
56
|
+
owns its own write-error handling) and does not disable the sink: one
|
|
57
|
+
transient failure must not retire a backend for the rest of the run.
|
|
58
|
+
|
|
59
|
+
@param writePromise - Promise returned by the sink write call.
|
|
60
60
|
*/
|
|
61
61
|
async function trackWrite(
|
|
62
62
|
{ writePromise, }: { readonly writePromise: Promise<void>; },
|
|
@@ -73,56 +73,56 @@ async function trackWrite(
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
76
|
+
Builds a multi-sink logger over the supplied sink adapters. All
|
|
77
|
+
orchestration (per-sink availability, startup buffering and replay,
|
|
78
|
+
in-flight write tracking, and flush) lives here; the exported default
|
|
79
|
+
`logger` is just this factory applied to the default sink set, and tests
|
|
80
|
+
apply it to fake sinks to exercise the orchestration directly.
|
|
81
|
+
|
|
82
|
+
Verification runs eagerly at construction and never blocks callers:
|
|
83
|
+
records emitted while an async sink is still verifying buffer internally
|
|
84
|
+
and replay to that sink the moment it verifies. Every sink verifies
|
|
85
|
+
concurrently under its own time limit (`verifyTimeoutMs`, default
|
|
86
|
+
{@link DEFAULT_VERIFY_TIMEOUT_MS}), so one backend that never answers
|
|
87
|
+
cannot starve the others or keep the logger from initializing. A sink
|
|
88
|
+
whose `verify` resolves `false`, throws, or runs past the limit is dropped
|
|
89
|
+
and receives no records; an answer that arrives after the limit is
|
|
90
|
+
ignored. A rejected `write` is the sink's own concern and does not disable
|
|
91
|
+
the backend.
|
|
92
|
+
|
|
93
|
+
`flush()` always resolves: one deadline (`flushDeadlineMs`, default
|
|
94
|
+
{@link DEFAULT_FLUSH_DEADLINE_MS}) wraps startup verification, the
|
|
95
|
+
in-flight write drain, and every sink flush hook together. When it elapses
|
|
96
|
+
the logger reports one breadcrumb, abandons the tracked writes from its
|
|
97
|
+
view (the sinks expose no cancellation, so the underlying work continues),
|
|
98
|
+
and resolves, so a wedged backend cannot hang a shutdown.
|
|
99
|
+
|
|
100
|
+
@param sinks - Sink adapters to fan each record out to, in priority order.
|
|
101
|
+
|
|
102
|
+
@param flushDeadlineMs - Milliseconds one `flush()` may take before it
|
|
103
|
+
resolves anyway; raise it for slow but working backends such as network
|
|
104
|
+
filesystems.
|
|
105
|
+
|
|
106
|
+
@param verifyTimeoutMs - Milliseconds one sink's `verify()` may take before
|
|
107
|
+
the sink counts as unavailable; raise it for a slow but working probe.
|
|
108
|
+
|
|
109
|
+
@returns Logger plus its eager `initPromise`; callers need not await
|
|
110
|
+
`initPromise` before logging, since startup records replay on verify.
|
|
111
|
+
|
|
112
|
+
@example
|
|
113
|
+
```ts
|
|
114
|
+
const { logger } = createLogger({ sinks: [createConsoleSink()] });
|
|
115
|
+
logger.info('ready');
|
|
116
|
+
await logger.flush();
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
@example
|
|
120
|
+
```ts
|
|
121
|
+
const { logger } = createLogger({
|
|
122
|
+
sinks: [createFileSink()],
|
|
123
|
+
flushDeadlineMs: 30_000,
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
126
|
*/
|
|
127
127
|
export function createLogger(
|
|
128
128
|
{
|
|
@@ -139,7 +139,7 @@ export function createLogger(
|
|
|
139
139
|
readonly logger: Logger;
|
|
140
140
|
} {
|
|
141
141
|
/**
|
|
142
|
-
|
|
142
|
+
Per-sink availability for this logger instance, indexed by startup order.
|
|
143
143
|
*/
|
|
144
144
|
const entries: SinkEntry[] = sinks.map(function toEntry(sink,): SinkEntry {
|
|
145
145
|
return {
|
|
@@ -149,26 +149,26 @@ export function createLogger(
|
|
|
149
149
|
},);
|
|
150
150
|
|
|
151
151
|
/**
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
152
|
+
Log records emitted before every sink has completed startup verification.
|
|
153
|
+
Records stay here only during initialization; each sink that later
|
|
154
|
+
verifies as available receives a replay, while already-available sinks
|
|
155
|
+
still receive writes immediately.
|
|
156
156
|
*/
|
|
157
157
|
const startupRecords: LogRecord[] = [];
|
|
158
158
|
|
|
159
159
|
/**
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
Sink writes currently in flight. Logger-level `flush()` drains these so
|
|
161
|
+
sinks without their own flush hook, such as file writes, still settle
|
|
162
|
+
before the flush resolves.
|
|
163
163
|
*/
|
|
164
164
|
const pendingWrites = new Set<Promise<void>>();
|
|
165
165
|
|
|
166
166
|
/**
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
167
|
+
Instance-local aggregate flags. `initialized` flips true once the eager
|
|
168
|
+
`initialize()` settles; `hasAvailableSink` reflects whether any sink
|
|
169
|
+
survives verification and is recomputed by `recomputeAvailability`;
|
|
170
|
+
`droppedStartupRecords` counts startup-buffer overflow drops for the
|
|
171
|
+
post-initialization marker record.
|
|
172
172
|
*/
|
|
173
173
|
const state: {
|
|
174
174
|
droppedStartupRecords: number;
|
|
@@ -181,11 +181,11 @@ export function createLogger(
|
|
|
181
181
|
};
|
|
182
182
|
|
|
183
183
|
/**
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
184
|
+
Buffers a pre-initialization record under {@link STARTUP_BUFFER_CAP},
|
|
185
|
+
dropping the oldest buffered record (and counting the drop) when the cap
|
|
186
|
+
is reached.
|
|
187
|
+
|
|
188
|
+
@param record - Record logged before every sink has verified.
|
|
189
189
|
*/
|
|
190
190
|
function bufferStartupRecord({ record, }: { readonly record: LogRecord; },): void {
|
|
191
191
|
if (startupRecords.length >= STARTUP_BUFFER_CAP) {
|
|
@@ -196,7 +196,7 @@ export function createLogger(
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
/**
|
|
199
|
-
|
|
199
|
+
Recomputes aggregate availability after a sink entry's flag flips.
|
|
200
200
|
*/
|
|
201
201
|
function recomputeAvailability(): void {
|
|
202
202
|
state.hasAvailableSink = entries.some(function isAvailable(entry,) {
|
|
@@ -205,17 +205,17 @@ export function createLogger(
|
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
/**
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
208
|
+
Reads a sink entry by startup-order index.
|
|
209
|
+
|
|
210
|
+
@param entryIndex - Sink entry index from the construction-time order.
|
|
211
|
+
|
|
212
|
+
@returns Sink entry at that index.
|
|
213
|
+
|
|
214
|
+
@throws Error when index no longer maps to a sink entry.
|
|
215
215
|
*/
|
|
216
216
|
function getSinkEntry({ entryIndex, }: { readonly entryIndex: number; },): SinkEntry {
|
|
217
217
|
/**
|
|
218
|
-
|
|
218
|
+
Sink entry read from startup-order storage; undefined means the caller supplied an invalid index.
|
|
219
219
|
*/
|
|
220
220
|
const entry = entries[entryIndex];
|
|
221
221
|
if (entry === undefined)
|
|
@@ -225,15 +225,15 @@ export function createLogger(
|
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
/**
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
228
|
+
Marks a sink unavailable after its verification fails or throws. Write
|
|
229
|
+
failures never reach here: a rejected write is the sink's own concern and
|
|
230
|
+
leaves the backend available, so one transient hiccup does not retire it.
|
|
231
|
+
|
|
232
|
+
@param entryIndex - Sink entry index whose backend failed verification.
|
|
233
233
|
*/
|
|
234
234
|
function markEntryUnavailable({ entryIndex, }: { readonly entryIndex: number; },): void {
|
|
235
235
|
/**
|
|
236
|
-
|
|
236
|
+
Mutable sink entry being disabled.
|
|
237
237
|
*/
|
|
238
238
|
const entry = getSinkEntry({ entryIndex, },);
|
|
239
239
|
entry.available = false;
|
|
@@ -241,9 +241,9 @@ export function createLogger(
|
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
/**
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
244
|
+
Removes a tracked sink write from {@link pendingWrites} once it settles.
|
|
245
|
+
|
|
246
|
+
@param trackedWrite - Promise returned by {@link trackWrite}.
|
|
247
247
|
*/
|
|
248
248
|
async function removePendingWriteWhenSettled(
|
|
249
249
|
{ trackedWrite, }: { readonly trackedWrite: Promise<void>; },
|
|
@@ -253,11 +253,11 @@ export function createLogger(
|
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
/**
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
256
|
+
Sends a record to one sink, recording the write so `flush()` can await it.
|
|
257
|
+
|
|
258
|
+
@param entryIndex - Available sink entry index to receive the record.
|
|
259
|
+
|
|
260
|
+
@param record - Log record to deliver.
|
|
261
261
|
*/
|
|
262
262
|
function writeRecordToEntry(
|
|
263
263
|
{
|
|
@@ -270,11 +270,11 @@ export function createLogger(
|
|
|
270
270
|
): void {
|
|
271
271
|
try {
|
|
272
272
|
/**
|
|
273
|
-
|
|
273
|
+
Sink entry receiving the record.
|
|
274
274
|
*/
|
|
275
275
|
const entry = getSinkEntry({ entryIndex, },);
|
|
276
276
|
/**
|
|
277
|
-
|
|
277
|
+
Monitored sink write; resolves even when the underlying write rejects, because {@link trackWrite} swallows rejection.
|
|
278
278
|
*/
|
|
279
279
|
const trackedWrite = trackWrite({
|
|
280
280
|
writePromise: entry.sink
|
|
@@ -292,9 +292,9 @@ export function createLogger(
|
|
|
292
292
|
}
|
|
293
293
|
|
|
294
294
|
/**
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
295
|
+
Replays buffered startup records to a sink that just became available.
|
|
296
|
+
|
|
297
|
+
@param entryIndex - Newly available sink entry index.
|
|
298
298
|
*/
|
|
299
299
|
function replayStartupRecordsToEntry({ entryIndex, }: { readonly entryIndex: number; },): void {
|
|
300
300
|
startupRecords.forEach(function replayStartupRecord(record,) {
|
|
@@ -306,12 +306,12 @@ export function createLogger(
|
|
|
306
306
|
}
|
|
307
307
|
|
|
308
308
|
/**
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
309
|
+
Applies a verification result to a sink and replays startup records on
|
|
310
|
+
success.
|
|
311
|
+
|
|
312
|
+
@param entryIndex - Sink entry index whose verification completed.
|
|
313
|
+
|
|
314
|
+
@param available - Whether the backend verified successfully.
|
|
315
315
|
*/
|
|
316
316
|
function setEntryAvailability(
|
|
317
317
|
{
|
|
@@ -323,7 +323,7 @@ export function createLogger(
|
|
|
323
323
|
},
|
|
324
324
|
): void {
|
|
325
325
|
/**
|
|
326
|
-
|
|
326
|
+
Sink entry whose availability is changing.
|
|
327
327
|
*/
|
|
328
328
|
const entry = getSinkEntry({ entryIndex, },);
|
|
329
329
|
entry.available = available;
|
|
@@ -333,18 +333,18 @@ export function createLogger(
|
|
|
333
333
|
}
|
|
334
334
|
|
|
335
335
|
/**
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
336
|
+
Runs one sink's verification under the verify time limit and records the
|
|
337
|
+
result, replaying buffered startup records to it on success. A rejected
|
|
338
|
+
verification, a synchronous throw from the verifier, or a verify that
|
|
339
|
+
runs past `verifyTimeoutMs` drops the sink; a late answer after the limit
|
|
340
|
+
is never observed, so it cannot flip availability afterwards.
|
|
341
|
+
|
|
342
|
+
@param entryIndex - Sink entry index to verify.
|
|
343
343
|
*/
|
|
344
344
|
async function verifyAndApply({ entryIndex, }: { readonly entryIndex: number; },): Promise<void> {
|
|
345
345
|
try {
|
|
346
346
|
/**
|
|
347
|
-
|
|
347
|
+
Sink entry whose verifier is about to run.
|
|
348
348
|
*/
|
|
349
349
|
const entry = getSinkEntry({ entryIndex, },);
|
|
350
350
|
setEntryAvailability({
|
|
@@ -367,19 +367,19 @@ export function createLogger(
|
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
/**
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
370
|
+
Writes one synthetic `warn` record to every available sink when the
|
|
371
|
+
startup buffer overflowed, so the loss is never silent. Runs once, after
|
|
372
|
+
initialization, when the dropped count is known and final.
|
|
373
373
|
*/
|
|
374
374
|
function emitDroppedStartupMarker(): void {
|
|
375
375
|
/**
|
|
376
|
-
|
|
376
|
+
Records dropped from the startup buffer; zero means nothing to report.
|
|
377
377
|
*/
|
|
378
378
|
const dropped = state.droppedStartupRecords;
|
|
379
379
|
if (dropped === 0)
|
|
380
380
|
return;
|
|
381
381
|
/**
|
|
382
|
-
|
|
382
|
+
Marker record naming the loss; the noun agrees with the count.
|
|
383
383
|
*/
|
|
384
384
|
const marker: LogRecord = {
|
|
385
385
|
level: 'warn',
|
|
@@ -400,14 +400,14 @@ export function createLogger(
|
|
|
400
400
|
}
|
|
401
401
|
|
|
402
402
|
/**
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
403
|
+
Initializes all sink backends by verifying their availability. Runs once
|
|
404
|
+
at construction. Every verifier runs concurrently, each under the verify
|
|
405
|
+
time limit, so one backend that never answers cannot starve the rest or
|
|
406
|
+
keep `initialized` from flipping. Completion order does not affect
|
|
407
|
+
correctness: a record's immediate-write set (sinks already available when
|
|
408
|
+
it was logged) and its replay set (sinks that become available later) are
|
|
409
|
+
disjoint, so each available sink receives each record exactly once
|
|
410
|
+
whichever verify settles first.
|
|
411
411
|
*/
|
|
412
412
|
async function initialize(): Promise<void> {
|
|
413
413
|
if (state.initialized)
|
|
@@ -428,29 +428,29 @@ export function createLogger(
|
|
|
428
428
|
}
|
|
429
429
|
|
|
430
430
|
/**
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
431
|
+
Eager readiness promise. Consumers do not need to await this before
|
|
432
|
+
logging; `flush()` awaits it internally, and startup records replay to
|
|
433
|
+
async sinks as they become available.
|
|
434
434
|
*/
|
|
435
435
|
const initPromise: Promise<void> = initialize();
|
|
436
436
|
|
|
437
437
|
/**
|
|
438
|
-
|
|
438
|
+
Drains every currently tracked sink write.
|
|
439
439
|
*/
|
|
440
440
|
async function drainPendingWrites(): Promise<void> {
|
|
441
441
|
/**
|
|
442
|
-
|
|
442
|
+
Snapshot of tracked writes at flush time.
|
|
443
443
|
*/
|
|
444
444
|
const writes = [...pendingWrites,];
|
|
445
445
|
await Promise.all(writes,);
|
|
446
446
|
}
|
|
447
447
|
|
|
448
448
|
/**
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
449
|
+
Creates a logging method for the specified severity level.
|
|
450
|
+
|
|
451
|
+
@param level - Log severity level for messages from this method.
|
|
452
|
+
|
|
453
|
+
@returns Logging function for the given level.
|
|
454
454
|
*/
|
|
455
455
|
function createMethod(level: Level,): (message: string,) => void {
|
|
456
456
|
return function logAtLevel(message: string,): void {
|
|
@@ -459,7 +459,7 @@ export function createLogger(
|
|
|
459
459
|
throw new Error('No logging backends available',);
|
|
460
460
|
|
|
461
461
|
/**
|
|
462
|
-
|
|
462
|
+
Shared LogRecord forwarded to every available sink; built once per call so all sinks see the same timestamp.
|
|
463
463
|
*/
|
|
464
464
|
const record: LogRecord = {
|
|
465
465
|
level,
|
|
@@ -471,7 +471,7 @@ export function createLogger(
|
|
|
471
471
|
bufferStartupRecord({ record, },);
|
|
472
472
|
|
|
473
473
|
/**
|
|
474
|
-
|
|
474
|
+
Indices of sinks that survived verification; recomputed per call so a sink dropped at verify time is excluded next time.
|
|
475
475
|
*/
|
|
476
476
|
const availableIndices = entries
|
|
477
477
|
.map(function indexEntry(
|
|
@@ -495,8 +495,8 @@ export function createLogger(
|
|
|
495
495
|
}
|
|
496
496
|
|
|
497
497
|
/**
|
|
498
|
-
|
|
499
|
-
|
|
498
|
+
Runs every available sink's own `flush` hook. A rejecting hook marks that
|
|
499
|
+
sink unavailable and does not fail the aggregate.
|
|
500
500
|
*/
|
|
501
501
|
async function runSinkFlushHooks(): Promise<void> {
|
|
502
502
|
await Promise.all(
|
|
@@ -505,7 +505,7 @@ export function createLogger(
|
|
|
505
505
|
entryIndex,
|
|
506
506
|
) {
|
|
507
507
|
/**
|
|
508
|
-
|
|
508
|
+
Optional sink-supplied flush hook; absent when the sink writes synchronously and needs no draining.
|
|
509
509
|
*/
|
|
510
510
|
const sinkFlush = entry.sink
|
|
511
511
|
.flush;
|
|
@@ -526,11 +526,11 @@ export function createLogger(
|
|
|
526
526
|
}
|
|
527
527
|
|
|
528
528
|
/**
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
529
|
+
Drains startup verification, in-flight sink writes (via
|
|
530
|
+
{@link drainPendingWrites}), and every sink flush hook (via
|
|
531
|
+
{@link runSinkFlushHooks}) in order. Never rejects on its own: every
|
|
532
|
+
inner failure is already reported and swallowed, so the only way this
|
|
533
|
+
stays pending is a verify, write, or hook that never settles.
|
|
534
534
|
*/
|
|
535
535
|
async function drainEverything(): Promise<void> {
|
|
536
536
|
await initPromise;
|
|
@@ -539,21 +539,21 @@ export function createLogger(
|
|
|
539
539
|
}
|
|
540
540
|
|
|
541
541
|
/**
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
542
|
+
Drops every tracked write from the logger's view after the flush deadline
|
|
543
|
+
elapsed. The tracked wrappers keep their own rejection handling, so a
|
|
544
|
+
late settlement can neither surface as an unhandled rejection nor stall
|
|
545
|
+
the next `flush()`. The underlying sink work is not cancelled: sinks
|
|
546
|
+
expose no cancellation signal.
|
|
547
547
|
*/
|
|
548
548
|
function abandonPendingWrites(): void {
|
|
549
549
|
pendingWrites.clear();
|
|
550
550
|
}
|
|
551
551
|
|
|
552
552
|
/**
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
553
|
+
Runs {@link drainEverything} under the flush deadline. Resolves once all
|
|
554
|
+
tracked writes and hooks have settled, or once `flushDeadlineMs` elapses,
|
|
555
|
+
whichever comes first; a deadline hit reports one breadcrumb and abandons
|
|
556
|
+
the tracked writes so shutdown proceeds.
|
|
557
557
|
*/
|
|
558
558
|
async function flushAll(): Promise<void> {
|
|
559
559
|
try {
|
|
@@ -573,7 +573,7 @@ export function createLogger(
|
|
|
573
573
|
}
|
|
574
574
|
|
|
575
575
|
/**
|
|
576
|
-
|
|
576
|
+
Multi-sink logger that writes to all available backends.
|
|
577
577
|
*/
|
|
578
578
|
const logger: Logger = {
|
|
579
579
|
debug: createMethod('debug',),
|