@monochromatic-dev/module-logger 0.1.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 +27 -0
- package/README.md +58 -12
- package/dist/final/neutral/browser.d.mts +60 -0
- package/dist/final/neutral/browser.mjs +1 -0
- package/dist/final/neutral/index.d.mts +366 -572
- 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 +366 -572
- 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 +19 -5
- package/src/artifact-platform-split.unit.test.ts +140 -0
- package/src/browser.ts +14 -0
- package/src/create-logger.ts +249 -151
- package/src/create-logger.unit.test.ts +527 -75
- 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/index.ts +2 -0
- 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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @monochromatic-dev/module-logger
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- The root entry is now platform-neutral and neither built artifact contains a dynamic `import()`.
|
|
8
|
+
`createFileSink` moved to `@monochromatic-dev/module-logger/node`;
|
|
9
|
+
`createIndexedDbSink` and `createOpfsSink` moved to `@monochromatic-dev/module-logger/browser`.
|
|
10
|
+
The default logger keeps file logging under the `node` export condition and IndexedDB under `default`;
|
|
11
|
+
a Node consumer whose bundler resolves `default` no longer gets file logging.
|
|
12
|
+
Commit `ce38d07`.
|
|
13
|
+
|
|
14
|
+
## 0.2.0
|
|
15
|
+
|
|
16
|
+
### Minor Changes
|
|
17
|
+
|
|
18
|
+
- Records logged before every sink has answered its verify now buffer under `STARTUP_BUFFER_CAP` (10000,
|
|
19
|
+
exported).
|
|
20
|
+
On overflow the oldest buffered record is dropped,
|
|
21
|
+
and once initialization completes one `warn` record naming the dropped count is written to every available sink.
|
|
22
|
+
Commit `fee2427`.
|
|
23
|
+
- Sinks now verify concurrently,
|
|
24
|
+
each under a time limit.
|
|
25
|
+
A backend probe that never answers no longer starves the sinks after it or keeps the logger from initializing.
|
|
26
|
+
The limit is the new `createLogger` option `verifyTimeoutMs` (default `DEFAULT_VERIFY_TIMEOUT_MS`,
|
|
27
|
+
5000 ms).
|
|
28
|
+
Commit `ee58222`.
|
|
29
|
+
|
|
3
30
|
## 0.1.0
|
|
4
31
|
|
|
5
32
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -60,6 +60,17 @@ The published package exposes the built artifact only.
|
|
|
60
60
|
The `/ts` source subpath used inside this workspace is stripped at publish time,
|
|
61
61
|
because Node refuses `.ts` files under `node_modules`.
|
|
62
62
|
|
|
63
|
+
The root entry is platform-neutral and is built twice.
|
|
64
|
+
The `node` export condition serves a build whose default logger includes the file sink,
|
|
65
|
+
with static `node:fs/promises` and `node:path` imports,
|
|
66
|
+
and which carries no browser storage code.
|
|
67
|
+
Every other resolution (`default`) serves a build whose default logger includes the IndexedDB sink
|
|
68
|
+
and which references no Node module.
|
|
69
|
+
Neither build contains a dynamic `import()`,
|
|
70
|
+
and a unit test reads every chunk of both builds to keep it that way.
|
|
71
|
+
The root types are identical on both conditions.
|
|
72
|
+
A Node consumer whose bundler resolves the `default` condition gets the neutral build and no file logging.
|
|
73
|
+
|
|
63
74
|
## Log levels
|
|
64
75
|
|
|
65
76
|
Six levels,
|
|
@@ -89,9 +100,19 @@ already provides its own log-level filtering.
|
|
|
89
100
|
## Sinks
|
|
90
101
|
|
|
91
102
|
The default logger writes to **all** available sinks simultaneously.
|
|
92
|
-
Availability is verified at module load
|
|
93
|
-
|
|
103
|
+
Availability is verified at module load,
|
|
104
|
+
every sink concurrently,
|
|
105
|
+
each under its own time limit (`verifyTimeoutMs`,
|
|
106
|
+
default `DEFAULT_VERIFY_TIMEOUT_MS`,
|
|
107
|
+
5000 ms);
|
|
108
|
+
a sink that does not answer in time counts as unavailable,
|
|
109
|
+
so one hung backend probe cannot starve the others.
|
|
110
|
+
Records emitted while an async sink is
|
|
94
111
|
still being verified are replayed to that sink when it becomes available.
|
|
112
|
+
That startup buffer holds at most `STARTUP_BUFFER_CAP` records (10000,
|
|
113
|
+
exported);
|
|
114
|
+
past the cap the oldest buffered record is dropped,
|
|
115
|
+
and once every sink has answered one `warn` record naming the dropped count is written to every available sink.
|
|
95
116
|
|
|
96
117
|
- **console**:
|
|
97
118
|
formats as `[level] [ISO timestamp] message`;
|
|
@@ -178,15 +199,16 @@ still being verified are replayed to that sink when it becomes available.
|
|
|
178
199
|
discards all records;
|
|
179
200
|
a stand-in that disables logging without removing log calls
|
|
180
201
|
|
|
181
|
-
Each sink is a factory
|
|
202
|
+
Each sink is a factory.
|
|
203
|
+
The cross-platform ones,
|
|
182
204
|
`createConsoleSink()`,
|
|
183
|
-
`
|
|
184
|
-
`createIndexedDbSink()`,
|
|
185
|
-
`createOpfsSink()`,
|
|
186
|
-
`createSessionStorageSink()`,
|
|
205
|
+
`createSessionStorageSink()`,
|
|
187
206
|
`createLocalStorageSink()`,
|
|
188
207
|
and `createNoopSink()`,
|
|
189
|
-
exported under the `sinks` namespace.
|
|
208
|
+
are exported under the `sinks` namespace of the root entry.
|
|
209
|
+
`createFileSink()` is exported from `@monochromatic-dev/module-logger/node`,
|
|
210
|
+
and `createIndexedDbSink()` and `createOpfsSink()` from `@monochromatic-dev/module-logger/browser`,
|
|
211
|
+
so importing a platform-only sink is the consumer's own assertion of the platform.
|
|
190
212
|
A sink instance keeps its own buffers,
|
|
191
213
|
streams,
|
|
192
214
|
and counters,
|
|
@@ -256,9 +278,15 @@ File,
|
|
|
256
278
|
- Throws at log time once initialization has completed with no available backend.
|
|
257
279
|
The console sink verifies wherever `console` and `queueMicrotask` exist,
|
|
258
280
|
so this is reachable only through `createLogger` with sinks that all fail verification
|
|
259
|
-
- A sink is dropped when its `verify` reports unavailable
|
|
260
|
-
|
|
281
|
+
- A sink is dropped when its `verify` reports unavailable,
|
|
282
|
+
runs past `verifyTimeoutMs`,
|
|
283
|
+
or its flush hook rejects;
|
|
284
|
+
remaining sinks continue,
|
|
285
|
+
and a late verify answer after the limit is ignored
|
|
261
286
|
- Individual `write` failures are handled per sink and do not disable the backend
|
|
287
|
+
- Records logged before every sink has answered buffer under `STARTUP_BUFFER_CAP`;
|
|
288
|
+
on overflow the oldest is dropped and the count is reported as one `warn` record after initialization,
|
|
289
|
+
never silently
|
|
262
290
|
|
|
263
291
|
## Custom loggers
|
|
264
292
|
|
|
@@ -281,11 +309,13 @@ Raise the flush deadline for a slow but working backend,
|
|
|
281
309
|
such as a network filesystem:
|
|
282
310
|
|
|
283
311
|
```ts
|
|
284
|
-
import { createLogger,
|
|
312
|
+
import { createLogger, } from '@monochromatic-dev/module-logger';
|
|
313
|
+
import { createFileSink, } from '@monochromatic-dev/module-logger/node';
|
|
285
314
|
|
|
286
315
|
const { logger, } = createLogger({
|
|
287
|
-
sinks: [
|
|
316
|
+
sinks: [createFileSink(),],
|
|
288
317
|
flushDeadlineMs: 30_000,
|
|
318
|
+
verifyTimeoutMs: 30_000,
|
|
289
319
|
},);
|
|
290
320
|
```
|
|
291
321
|
|
|
@@ -332,6 +362,11 @@ See [DECISIONS.md](DECISIONS.md) for rationale on:
|
|
|
332
362
|
- localStorage and IndexedDB sink designs and measurements
|
|
333
363
|
- Console output neutralizes control characters
|
|
334
364
|
- `flush()` has a deadline
|
|
365
|
+
- Sinks verify concurrently under a time limit
|
|
366
|
+
- The startup buffer is bounded and overflow is reported
|
|
367
|
+
- Platform-specific sinks live behind `./node` and `./browser`
|
|
368
|
+
- Zero-config at import,
|
|
369
|
+
no configure step (the logtape migration observations)
|
|
335
370
|
|
|
336
371
|
## Source files
|
|
337
372
|
|
|
@@ -348,6 +383,17 @@ See [DECISIONS.md](DECISIONS.md) for rationale on:
|
|
|
348
383
|
flush)
|
|
349
384
|
- `src/logger.ts`:
|
|
350
385
|
default singleton built by applying `createLogger` to the default sinks
|
|
386
|
+
- `src/node.ts`:
|
|
387
|
+
the `./node` subpath entry,
|
|
388
|
+
`createFileSink()`
|
|
389
|
+
- `src/browser.ts`:
|
|
390
|
+
the `./browser` subpath entry,
|
|
391
|
+
`createIndexedDbSink()` and `createOpfsSink()`
|
|
392
|
+
- `src/default-sinks.node.ts` and `src/default-sinks.neutral.ts`:
|
|
393
|
+
the two default sink lists,
|
|
394
|
+
selected at build time through the `#default-sinks` entry of `package.json` `imports`
|
|
395
|
+
- `src/artifact-platform-split.unit.test.ts`:
|
|
396
|
+
guard that reads every chunk of both builds and rejects dynamic imports and cross-platform leaks
|
|
351
397
|
- `src/tagged.ts`:
|
|
352
398
|
`tagged()` wrapper for composable prefixes
|
|
353
399
|
- `src/sink/console.ts`:
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { i as Sink } from "./types-BkkBXgY3.mjs";
|
|
2
|
+
//#region src/sink/indexed-db.d.ts
|
|
3
|
+
/**
|
|
4
|
+
Builds an IndexedDB sink that buffers serialized records through the shared
|
|
5
|
+
{@link createRecordBuffer} policy and persists each newline-joined JSONL
|
|
6
|
+
batch as one string value per transaction, measured at 0.15 µs of
|
|
7
|
+
main-thread enqueue per record on headless Chromium 149 (one `add` per
|
|
8
|
+
32 KiB batch). The connection lives in this instance's closure (no
|
|
9
|
+
module-global state), so independent loggers and tests never share a
|
|
10
|
+
handle or need a reset hook.
|
|
11
|
+
|
|
12
|
+
Records are readable the moment their transaction settles (DevTools
|
|
13
|
+
Application tab included), survive tab close and browser restart, and
|
|
14
|
+
auto-incremented keys serialize across tabs, so no run-scoped naming is
|
|
15
|
+
needed. Retention trims oldest-first past {@link MAX_STORED_BATCHES}.
|
|
16
|
+
Transactions use the default relaxed durability: relaxed commits reach the
|
|
17
|
+
browser's storage backend promptly and survive renderer crashes, and the
|
|
18
|
+
OS-crash window `durability: 'strict'` would close is the rarest failure
|
|
19
|
+
class, not worth an fsync per batch.
|
|
20
|
+
|
|
21
|
+
Flush triggers (32 KiB in-write cap, `warn`-or-worse severity, 250 ms
|
|
22
|
+
quiet-period deadline, page lifecycle, and the `flush` hook) are the
|
|
23
|
+
buffer's; see {@link createRecordBuffer}. The sink's `flush` hook awaits
|
|
24
|
+
every issued batch transaction before resolving.
|
|
25
|
+
|
|
26
|
+
@returns Sink backed by IndexedDB.
|
|
27
|
+
|
|
28
|
+
@example
|
|
29
|
+
```ts
|
|
30
|
+
const { logger } = createLogger({ sinks: [createIndexedDbSink()] });
|
|
31
|
+
logger.warn('quota nearing limit');
|
|
32
|
+
```
|
|
33
|
+
*/
|
|
34
|
+
export declare function createIndexedDbSink(): Sink;
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/sink/opfs.d.ts
|
|
37
|
+
/**
|
|
38
|
+
Builds an OPFS sink that buffers serialized records through the shared
|
|
39
|
+
{@link createRecordBuffer} policy and appends each newline-joined JSONL
|
|
40
|
+
batch to a per-session file in the Origin Private File System with one
|
|
41
|
+
stream write per batch. The kept-open writable stream lives in this
|
|
42
|
+
instance's closure (no module-global state), so independent loggers and
|
|
43
|
+
tests never share a handle or need a reset hook.
|
|
44
|
+
|
|
45
|
+
Flush triggers (32 KiB in-write cap, `warn`-or-worse severity, 250 ms
|
|
46
|
+
quiet-period deadline, page lifecycle, and the `flush` hook) are the
|
|
47
|
+
buffer's; see {@link createRecordBuffer}. Batch writes queue on the stream
|
|
48
|
+
in issue order, so ordering holds at the batch boundary, and the sink's
|
|
49
|
+
`flush` hook awaits every issued batch before resolving.
|
|
50
|
+
|
|
51
|
+
@returns Sink backed by OPFS.
|
|
52
|
+
|
|
53
|
+
@example
|
|
54
|
+
```ts
|
|
55
|
+
const { logger } = createLogger({ sinks: [createOpfsSink()] });
|
|
56
|
+
logger.warn('quota nearing limit');
|
|
57
|
+
```
|
|
58
|
+
*/
|
|
59
|
+
export declare function createOpfsSink(): Sink;
|
|
60
|
+
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as createRecordBuffer,r as reportLoggerInternalError,t as createIndexedDbSink}from"./indexed-db-hsIfv7Cv.mjs";function createOpfsSink(){let state={},pendingBatchWrites=new Set;async function verify(){try{let opfsRoot=await navigator.storage.getDirectory(),timestamp=new Date().toISOString().replaceAll(`:`,`-`),fileHandle=await opfsRoot.getFileHandle(`monochromatic-${timestamp}.log.jsonl`,{create:!0}),probeWritable=await fileHandle.createWritable({keepExistingData:!0}),testData=`{"test":true,"timestamp":${Date.now()}}\n`;await probeWritable.write(testData),await probeWritable.close();let available=(await(await fileHandle.getFile()).text()).includes(`"test":true`);return available&&(state.writable=await fileHandle.createWritable({keepExistingData:!0})),available}catch(error){let opfsStorage=globalThis.navigator?.storage;return opfsStorage!==void 0&&`getDirectory`in opfsStorage&&reportLoggerInternalError({context:`OPFS sink verification failed`,error}),!1}}async function writeBatch(batch){if(state.writable)try{await state.writable.write(`${batch}\n`)}catch(error){reportLoggerInternalError({context:`OPFS sink record write failed`,error})}}async function removePendingWhenSettled(pending){await pending,pendingBatchWrites.delete(pending)}function handOffBatch(batch){let pending=writeBatch(batch);pendingBatchWrites.add(pending),removePendingWhenSettled(pending)}let buffer=createRecordBuffer({onFlush:handOffBatch});function write(record){return buffer.add({level:record.level,serialized:JSON.stringify(record)}),Promise.resolve()}async function flush(){buffer.drain();let writes=[...pendingBatchWrites];await Promise.all(writes)}return{flush,verify,write}}export{createIndexedDbSink,createOpfsSink};
|