@emdzej/bimmerz-logger 0.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.
Files changed (47) hide show
  1. package/LICENSE +131 -0
  2. package/README.md +235 -0
  3. package/dist/categories.d.ts +21 -0
  4. package/dist/categories.d.ts.map +1 -0
  5. package/dist/categories.js +36 -0
  6. package/dist/categories.js.map +1 -0
  7. package/dist/index.d.ts +20 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +25 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/levels.d.ts +21 -0
  12. package/dist/levels.d.ts.map +1 -0
  13. package/dist/levels.js +30 -0
  14. package/dist/levels.js.map +1 -0
  15. package/dist/logger.d.ts +49 -0
  16. package/dist/logger.d.ts.map +1 -0
  17. package/dist/logger.js +136 -0
  18. package/dist/logger.js.map +1 -0
  19. package/dist/logger.test.d.ts +2 -0
  20. package/dist/logger.test.d.ts.map +1 -0
  21. package/dist/logger.test.js +199 -0
  22. package/dist/logger.test.js.map +1 -0
  23. package/dist/sinks/buffer.d.ts +23 -0
  24. package/dist/sinks/buffer.d.ts.map +1 -0
  25. package/dist/sinks/buffer.js +44 -0
  26. package/dist/sinks/buffer.js.map +1 -0
  27. package/dist/sinks/console.d.ts +34 -0
  28. package/dist/sinks/console.d.ts.map +1 -0
  29. package/dist/sinks/console.js +106 -0
  30. package/dist/sinks/console.js.map +1 -0
  31. package/dist/sinks/multi.d.ts +13 -0
  32. package/dist/sinks/multi.d.ts.map +1 -0
  33. package/dist/sinks/multi.js +26 -0
  34. package/dist/sinks/multi.js.map +1 -0
  35. package/dist/sinks/null.d.ts +10 -0
  36. package/dist/sinks/null.d.ts.map +1 -0
  37. package/dist/sinks/null.js +15 -0
  38. package/dist/sinks/null.js.map +1 -0
  39. package/dist/sinks/pino.d.ts +37 -0
  40. package/dist/sinks/pino.d.ts.map +1 -0
  41. package/dist/sinks/pino.js +39 -0
  42. package/dist/sinks/pino.js.map +1 -0
  43. package/dist/types.d.ts +121 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +7 -0
  46. package/dist/types.js.map +1 -0
  47. package/package.json +47 -0
package/dist/logger.js ADDED
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Logger implementation + central config.
3
+ *
4
+ * Logger handles are thin proxies that consult the live module-
5
+ * scoped config on every call. That's what makes
6
+ * `configureLogger({ categories: { EDIABASX: 'debug' } })` flip
7
+ * existing handles' behaviour instantly — no need to refetch
8
+ * loggers from callers.
9
+ *
10
+ * Performance: level resolution is a Map lookup + at most ~3 dot-
11
+ * segment slices for typical category paths. Both happen *before*
12
+ * any binding merge or sink dispatch, so a `log.debug({…}, 'msg')`
13
+ * call that's below threshold spends about as much time as a
14
+ * branch + Map.get. Same shape as pino's "no-op below level"
15
+ * pattern.
16
+ */
17
+ import { resolveLevel } from './categories.js';
18
+ import { levelPasses } from './levels.js';
19
+ /**
20
+ * Default sink — lazily installed on the active config when no
21
+ * `sink` is explicitly supplied. Set via `setDefaultSink()` from
22
+ * the sink-side init so we don't have a circular dep.
23
+ */
24
+ let DEFAULT_SINK = null;
25
+ /** @internal — called from index.ts after sink modules load. */
26
+ export function setDefaultSink(sink) {
27
+ DEFAULT_SINK = sink;
28
+ }
29
+ /** Module-scoped live config. Initialized to a sensible "info, console" default. */
30
+ const active = {
31
+ level: 'info',
32
+ };
33
+ /**
34
+ * Replace or merge into the active configuration. The merge is
35
+ * shallow — `categories` and `sink` are replaced wholesale when
36
+ * present in the partial. Pass `categories: {}` to clear all
37
+ * per-category overrides.
38
+ *
39
+ * Effective immediately for every logger handle, including ones
40
+ * already returned by `getLogger()`.
41
+ */
42
+ export function configureLogger(config) {
43
+ if (config.level !== undefined)
44
+ active.level = config.level;
45
+ if (config.categories !== undefined)
46
+ active.categories = config.categories;
47
+ if (config.sink !== undefined)
48
+ active.sink = config.sink;
49
+ }
50
+ /**
51
+ * Snapshot of the active configuration. Returned object is a copy
52
+ * — mutating it doesn't affect the live config (use
53
+ * `configureLogger()` for that).
54
+ */
55
+ export function getLoggerConfig() {
56
+ return {
57
+ level: active.level,
58
+ categories: active.categories ? { ...active.categories } : undefined,
59
+ sink: active.sink,
60
+ };
61
+ }
62
+ class LoggerImpl {
63
+ category;
64
+ bindings;
65
+ constructor(category, bindings = {}) {
66
+ this.category = category;
67
+ this.bindings = bindings;
68
+ }
69
+ get level() {
70
+ return resolveLevel(this.category, active.categories, active.level);
71
+ }
72
+ trace(a, b) {
73
+ this.emit('trace', a, b);
74
+ }
75
+ debug(a, b) {
76
+ this.emit('debug', a, b);
77
+ }
78
+ info(a, b) {
79
+ this.emit('info', a, b);
80
+ }
81
+ warn(a, b) {
82
+ this.emit('warn', a, b);
83
+ }
84
+ error(a, b) {
85
+ this.emit('error', a, b);
86
+ }
87
+ fatal(a, b) {
88
+ this.emit('fatal', a, b);
89
+ }
90
+ child(bindings) {
91
+ return new LoggerImpl(this.category, { ...this.bindings, ...bindings });
92
+ }
93
+ emit(level, a, b) {
94
+ const threshold = resolveLevel(this.category, active.categories, active.level);
95
+ if (!levelPasses(level, threshold))
96
+ return;
97
+ let msg;
98
+ let callBindings;
99
+ if (typeof a === 'string') {
100
+ msg = a;
101
+ }
102
+ else {
103
+ callBindings = a;
104
+ msg = b ?? '';
105
+ }
106
+ const mergedBindings = callBindings === undefined
107
+ ? this.bindings
108
+ : { ...this.bindings, ...callBindings };
109
+ const record = {
110
+ level,
111
+ category: this.category,
112
+ bindings: mergedBindings,
113
+ msg,
114
+ time: Date.now(),
115
+ };
116
+ const sink = active.sink ?? DEFAULT_SINK;
117
+ if (sink)
118
+ sink.write(record);
119
+ }
120
+ }
121
+ /**
122
+ * Get a logger handle. Call with no arguments for the root logger
123
+ * (category = `null`); pass a dot-separated string to scope under
124
+ * a category. Re-calling with the same category does NOT return
125
+ * the same instance — handles are cheap, and shared identity would
126
+ * make `child()` bindings spill across callers.
127
+ *
128
+ * Examples:
129
+ * const log = getLogger();
130
+ * const parser = getLogger("EDIABASX.parser");
131
+ * const job = parser.child({ jobId: "C_S_LESEN" });
132
+ */
133
+ export function getLogger(category) {
134
+ return new LoggerImpl(category ?? null);
135
+ }
136
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=logger.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.test.d.ts","sourceRoot":"","sources":["../src/logger.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,199 @@
1
+ import { afterEach, describe, expect, it } from 'vitest';
2
+ import { configureLogger, getLogger, getLoggerConfig } from './logger.js';
3
+ import { resolveLevel } from './categories.js';
4
+ import { levelPasses } from './levels.js';
5
+ import { bufferSink } from './sinks/buffer.js';
6
+ import { multiSink } from './sinks/multi.js';
7
+ import { nullSink } from './sinks/null.js';
8
+ /** Reset to a known state after each test so tests don't leak config. */
9
+ afterEach(() => {
10
+ configureLogger({ level: 'info', categories: {}, sink: nullSink() });
11
+ });
12
+ describe('levelPasses', () => {
13
+ it('matches pino ordering', () => {
14
+ expect(levelPasses('debug', 'info')).toBe(false);
15
+ expect(levelPasses('info', 'info')).toBe(true);
16
+ expect(levelPasses('error', 'info')).toBe(true);
17
+ });
18
+ it('silent blocks everything', () => {
19
+ expect(levelPasses('fatal', 'silent')).toBe(false);
20
+ expect(levelPasses('trace', 'silent')).toBe(false);
21
+ });
22
+ });
23
+ describe('resolveLevel hierarchy', () => {
24
+ const rules = { EDIABASX: 'debug', 'EDIABASX.parser': 'trace', INPAX: 'info' };
25
+ it('returns fallback for null category', () => {
26
+ expect(resolveLevel(null, rules, 'warn')).toBe('warn');
27
+ });
28
+ it('uses exact match when present', () => {
29
+ expect(resolveLevel('EDIABASX', rules, 'warn')).toBe('debug');
30
+ });
31
+ it('walks up dot segments', () => {
32
+ expect(resolveLevel('EDIABASX.parser.lexer', rules, 'warn')).toBe('trace');
33
+ expect(resolveLevel('EDIABASX.runner', rules, 'warn')).toBe('debug');
34
+ });
35
+ it('falls back when no segment matches', () => {
36
+ expect(resolveLevel('OTHER.subsystem', rules, 'warn')).toBe('warn');
37
+ });
38
+ it('ignores rules with bad level values', () => {
39
+ expect(resolveLevel('FOO', { FOO: 'bogus' }, 'warn')).toBe('warn');
40
+ });
41
+ });
42
+ describe('Logger basic dispatch', () => {
43
+ it('emits records at or above the threshold', () => {
44
+ const buf = bufferSink();
45
+ configureLogger({ level: 'info', sink: buf });
46
+ const log = getLogger();
47
+ log.debug('skipped');
48
+ log.info('kept');
49
+ log.warn('kept too');
50
+ const recs = buf.snapshot();
51
+ expect(recs.map((r) => r.msg)).toEqual(['kept', 'kept too']);
52
+ expect(recs[0]?.level).toBe('info');
53
+ });
54
+ it('attaches bindings when called with (obj, msg)', () => {
55
+ const buf = bufferSink();
56
+ configureLogger({ level: 'trace', sink: buf });
57
+ getLogger('A').info({ id: 7, tag: 'x' }, 'with bindings');
58
+ expect(buf.snapshot()[0]).toMatchObject({
59
+ level: 'info',
60
+ category: 'A',
61
+ bindings: { id: 7, tag: 'x' },
62
+ msg: 'with bindings',
63
+ });
64
+ });
65
+ it('emits empty bindings when called with (msg) only', () => {
66
+ const buf = bufferSink();
67
+ configureLogger({ level: 'trace', sink: buf });
68
+ getLogger().info('bare');
69
+ expect(buf.snapshot()[0]?.bindings).toEqual({});
70
+ });
71
+ it('attaches a `time` epoch ms', () => {
72
+ const buf = bufferSink();
73
+ configureLogger({ level: 'trace', sink: buf });
74
+ const before = Date.now();
75
+ getLogger().info('t');
76
+ const after = Date.now();
77
+ const rec = buf.snapshot()[0];
78
+ expect(rec.time).toBeGreaterThanOrEqual(before);
79
+ expect(rec.time).toBeLessThanOrEqual(after);
80
+ });
81
+ });
82
+ describe('Logger category resolution', () => {
83
+ it('applies per-category levels', () => {
84
+ const buf = bufferSink();
85
+ configureLogger({
86
+ level: 'warn',
87
+ categories: { EDIABASX: 'debug' },
88
+ sink: buf,
89
+ });
90
+ getLogger('EDIABASX').debug('kept');
91
+ getLogger('INPAX').debug('dropped');
92
+ getLogger().debug('dropped too');
93
+ expect(buf.snapshot().map((r) => r.msg)).toEqual(['kept']);
94
+ });
95
+ it('hierarchical override beats parent', () => {
96
+ const buf = bufferSink();
97
+ configureLogger({
98
+ level: 'warn',
99
+ categories: { EDIABASX: 'debug', 'EDIABASX.parser': 'trace' },
100
+ sink: buf,
101
+ });
102
+ getLogger('EDIABASX.parser').trace('kept');
103
+ getLogger('EDIABASX.runner').trace('dropped');
104
+ expect(buf.snapshot().map((r) => r.msg)).toEqual(['kept']);
105
+ });
106
+ it('logger.level reflects the live effective threshold', () => {
107
+ configureLogger({ level: 'warn', categories: { FOO: 'trace' } });
108
+ expect(getLogger('FOO').level).toBe('trace');
109
+ expect(getLogger('BAR').level).toBe('warn');
110
+ });
111
+ });
112
+ describe('runtime reconfigure', () => {
113
+ it('affects existing logger handles', () => {
114
+ const buf = bufferSink();
115
+ configureLogger({ level: 'info', sink: buf });
116
+ const log = getLogger('X');
117
+ log.debug('dropped #1');
118
+ configureLogger({ level: 'debug' });
119
+ log.debug('kept after reconfigure');
120
+ expect(buf.snapshot().map((r) => r.msg)).toEqual(['kept after reconfigure']);
121
+ });
122
+ it('partial reconfigure merges with existing config', () => {
123
+ configureLogger({ level: 'error', categories: { A: 'debug' } });
124
+ configureLogger({ level: 'info' }); // categories should remain
125
+ const cfg = getLoggerConfig();
126
+ expect(cfg.level).toBe('info');
127
+ expect(cfg.categories).toEqual({ A: 'debug' });
128
+ });
129
+ it('explicit empty categories clears overrides', () => {
130
+ configureLogger({ level: 'warn', categories: { A: 'debug' } });
131
+ configureLogger({ categories: {} });
132
+ expect(getLoggerConfig().categories).toEqual({});
133
+ });
134
+ });
135
+ describe('child loggers', () => {
136
+ it('carry permanent bindings on every call', () => {
137
+ const buf = bufferSink();
138
+ configureLogger({ level: 'trace', sink: buf });
139
+ const log = getLogger('X').child({ tenant: 'acme' });
140
+ log.info('one');
141
+ log.info({ extra: 1 }, 'two');
142
+ expect(buf.snapshot().map((r) => r.bindings)).toEqual([
143
+ { tenant: 'acme' },
144
+ { tenant: 'acme', extra: 1 },
145
+ ]);
146
+ });
147
+ it('call-binding overrides child binding on key conflict', () => {
148
+ const buf = bufferSink();
149
+ configureLogger({ level: 'trace', sink: buf });
150
+ const log = getLogger().child({ tenant: 'acme' });
151
+ log.info({ tenant: 'override' }, 'x');
152
+ expect(buf.snapshot()[0]?.bindings).toEqual({ tenant: 'override' });
153
+ });
154
+ it('child inherits category + level resolution', () => {
155
+ configureLogger({ level: 'warn', categories: { Y: 'trace' } });
156
+ const log = getLogger('Y').child({ a: 1 });
157
+ expect(log.level).toBe('trace');
158
+ });
159
+ });
160
+ describe('bufferSink', () => {
161
+ it('keeps records up to capacity', () => {
162
+ const buf = bufferSink({ capacity: 3 });
163
+ configureLogger({ level: 'trace', sink: buf });
164
+ for (let i = 0; i < 5; i++)
165
+ getLogger().info(`m${i}`);
166
+ expect(buf.snapshot().map((r) => r.msg)).toEqual(['m2', 'm3', 'm4']);
167
+ expect(buf.size()).toBe(3);
168
+ });
169
+ it('clear() empties the buffer', () => {
170
+ const buf = bufferSink();
171
+ configureLogger({ level: 'trace', sink: buf });
172
+ getLogger().info('a');
173
+ buf.clear();
174
+ expect(buf.size()).toBe(0);
175
+ expect(buf.snapshot()).toEqual([]);
176
+ });
177
+ });
178
+ describe('multiSink', () => {
179
+ it('fans out to every wrapped sink', () => {
180
+ const a = bufferSink();
181
+ const b = bufferSink();
182
+ configureLogger({ level: 'trace', sink: multiSink(a, b) });
183
+ getLogger().info('hi');
184
+ expect(a.snapshot().map((r) => r.msg)).toEqual(['hi']);
185
+ expect(b.snapshot().map((r) => r.msg)).toEqual(['hi']);
186
+ });
187
+ it('isolates failing sinks from healthy ones', () => {
188
+ const angry = {
189
+ write() {
190
+ throw new Error('boom');
191
+ },
192
+ };
193
+ const calm = bufferSink();
194
+ configureLogger({ level: 'trace', sink: multiSink(angry, calm) });
195
+ expect(() => getLogger().info('hi')).not.toThrow();
196
+ expect(calm.snapshot().map((r) => r.msg)).toEqual(['hi']);
197
+ });
198
+ });
199
+ //# sourceMappingURL=logger.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.test.js","sourceRoot":"","sources":["../src/logger.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * In-memory ring-buffer sink. Holds the last `capacity` records and
3
+ * lets UI code snapshot them on demand — drives "Download log"
4
+ * buttons in web apps without having to also intercept the console.
5
+ *
6
+ * Capacity defaults to 5000 (~1MB if records average 200 bytes).
7
+ * Older records are dropped silently as new ones arrive.
8
+ */
9
+ import type { LogRecord, Sink } from '../types.js';
10
+ export interface BufferSink extends Sink {
11
+ /** Records currently held, oldest → newest. */
12
+ snapshot(): LogRecord[];
13
+ /** Drop every held record. */
14
+ clear(): void;
15
+ /** Number of records currently held (≤ capacity). */
16
+ size(): number;
17
+ }
18
+ export interface BufferSinkOptions {
19
+ /** Max records held; older ones drop. Default 5000. */
20
+ capacity?: number;
21
+ }
22
+ export declare function bufferSink(opts?: BufferSinkOptions): BufferSink;
23
+ //# sourceMappingURL=buffer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buffer.d.ts","sourceRoot":"","sources":["../../src/sinks/buffer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnD,MAAM,WAAW,UAAW,SAAQ,IAAI;IACtC,+CAA+C;IAC/C,QAAQ,IAAI,SAAS,EAAE,CAAC;IACxB,8BAA8B;IAC9B,KAAK,IAAI,IAAI,CAAC;IACd,qDAAqD;IACrD,IAAI,IAAI,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,UAAU,CAAC,IAAI,GAAE,iBAAsB,GAAG,UAAU,CAgCnE"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * In-memory ring-buffer sink. Holds the last `capacity` records and
3
+ * lets UI code snapshot them on demand — drives "Download log"
4
+ * buttons in web apps without having to also intercept the console.
5
+ *
6
+ * Capacity defaults to 5000 (~1MB if records average 200 bytes).
7
+ * Older records are dropped silently as new ones arrive.
8
+ */
9
+ export function bufferSink(opts = {}) {
10
+ const capacity = Math.max(1, opts.capacity ?? 5000);
11
+ // Plain array is fine — pop+shift would be O(N); we use an
12
+ // index-based ring so push is O(1) and snapshot is O(N) only on
13
+ // demand.
14
+ const ring = new Array(capacity);
15
+ let head = 0; // next write position
16
+ let count = 0;
17
+ return {
18
+ write(record) {
19
+ ring[head] = record;
20
+ head = (head + 1) % capacity;
21
+ if (count < capacity)
22
+ count++;
23
+ },
24
+ snapshot() {
25
+ const out = [];
26
+ const start = count < capacity ? 0 : head;
27
+ for (let i = 0; i < count; i++) {
28
+ const r = ring[(start + i) % capacity];
29
+ if (r)
30
+ out.push(r);
31
+ }
32
+ return out;
33
+ },
34
+ clear() {
35
+ ring.fill(undefined);
36
+ head = 0;
37
+ count = 0;
38
+ },
39
+ size() {
40
+ return count;
41
+ },
42
+ };
43
+ }
44
+ //# sourceMappingURL=buffer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buffer.js","sourceRoot":"","sources":["../../src/sinks/buffer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAkBH,MAAM,UAAU,UAAU,CAAC,OAA0B,EAAE;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC;IACpD,2DAA2D;IAC3D,gEAAgE;IAChE,UAAU;IACV,MAAM,IAAI,GAA8B,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC5D,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,sBAAsB;IACpC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO;QACL,KAAK,CAAC,MAAiB;YACrB,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC;YACpB,IAAI,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;YAC7B,IAAI,KAAK,GAAG,QAAQ;gBAAE,KAAK,EAAE,CAAC;QAChC,CAAC;QACD,QAAQ;YACN,MAAM,GAAG,GAAgB,EAAE,CAAC;YAC5B,MAAM,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC/B,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC;gBACvC,IAAI,CAAC;oBAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACrB,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;QACD,KAAK;YACH,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACrB,IAAI,GAAG,CAAC,CAAC;YACT,KAAK,GAAG,CAAC,CAAC;QACZ,CAAC;QACD,IAAI;YACF,OAAO,KAAK,CAAC;QACf,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Default sink — works in Node and the browser, no dependencies.
3
+ *
4
+ * Routes through `console.{trace|debug|info|warn|error}` per level
5
+ * so browser devtools' level-filter sidebar works correctly. In
6
+ * Node, console.* writes to stderr (warn/error) or stdout (rest)
7
+ * — same as bare `console.log`. For Node CLIs that want
8
+ * pino-pretty / file destinations, switch to
9
+ * `@emdzej/bimmerz-logger/sinks/pino`.
10
+ *
11
+ * Line format:
12
+ * 2026-05-23T19:14:32.123Z DEBUG [EDIABASX.parser] msg {jobId:"C_S_LESEN"}
13
+ *
14
+ * Bindings render as compact JSON. ANSI colours are added when the
15
+ * environment looks like a TTY (best-effort: `process.stdout.isTTY`
16
+ * in Node, never in the browser).
17
+ */
18
+ import type { Sink } from '../types.js';
19
+ export interface ConsoleSinkOptions {
20
+ /**
21
+ * Force colour on/off. Defaults to "on if Node TTY, off otherwise".
22
+ * Browser devtools renders ANSI literally, so we leave it off there
23
+ * unless explicitly requested.
24
+ */
25
+ colors?: boolean;
26
+ /**
27
+ * Render `time` as a relative `+12ms` offset from the sink's
28
+ * creation moment instead of an absolute ISO timestamp. Handy for
29
+ * spotting time gaps between adjacent calls.
30
+ */
31
+ relativeTime?: boolean;
32
+ }
33
+ export declare function consoleSink(opts?: ConsoleSinkOptions): Sink;
34
+ //# sourceMappingURL=console.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../src/sinks/console.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAuB,IAAI,EAAE,MAAM,aAAa,CAAC;AAE7D,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAkDD,wBAAgB,WAAW,CAAC,IAAI,GAAE,kBAAuB,GAAG,IAAI,CAmB/D"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Default sink — works in Node and the browser, no dependencies.
3
+ *
4
+ * Routes through `console.{trace|debug|info|warn|error}` per level
5
+ * so browser devtools' level-filter sidebar works correctly. In
6
+ * Node, console.* writes to stderr (warn/error) or stdout (rest)
7
+ * — same as bare `console.log`. For Node CLIs that want
8
+ * pino-pretty / file destinations, switch to
9
+ * `@emdzej/bimmerz-logger/sinks/pino`.
10
+ *
11
+ * Line format:
12
+ * 2026-05-23T19:14:32.123Z DEBUG [EDIABASX.parser] msg {jobId:"C_S_LESEN"}
13
+ *
14
+ * Bindings render as compact JSON. ANSI colours are added when the
15
+ * environment looks like a TTY (best-effort: `process.stdout.isTTY`
16
+ * in Node, never in the browser).
17
+ */
18
+ const ANSI = {
19
+ reset: '\x1b[0m',
20
+ gray: '\x1b[90m',
21
+ cyan: '\x1b[36m',
22
+ green: '\x1b[32m',
23
+ yellow: '\x1b[33m',
24
+ red: '\x1b[31m',
25
+ bgRed: '\x1b[41m',
26
+ };
27
+ const LEVEL_COLOR = {
28
+ trace: ANSI.gray,
29
+ debug: ANSI.cyan,
30
+ info: ANSI.green,
31
+ warn: ANSI.yellow,
32
+ error: ANSI.red,
33
+ fatal: ANSI.bgRed,
34
+ silent: '',
35
+ };
36
+ const LEVEL_TAG = {
37
+ trace: 'TRACE',
38
+ debug: 'DEBUG',
39
+ info: 'INFO ',
40
+ warn: 'WARN ',
41
+ error: 'ERROR',
42
+ fatal: 'FATAL',
43
+ silent: '',
44
+ };
45
+ const LEVEL_METHOD = {
46
+ trace: 'debug',
47
+ debug: 'debug',
48
+ info: 'info',
49
+ warn: 'warn',
50
+ error: 'error',
51
+ fatal: 'error',
52
+ silent: 'log',
53
+ };
54
+ function detectTty() {
55
+ // Node: process.stdout.isTTY → typically true under terminals.
56
+ // Avoid `process` in a way that bundlers can't tree-shake out
57
+ // gracefully — `globalThis` access is cheap.
58
+ const g = globalThis;
59
+ return !!g.process?.stdout?.isTTY;
60
+ }
61
+ export function consoleSink(opts = {}) {
62
+ const colors = opts.colors ?? detectTty();
63
+ const t0 = opts.relativeTime ? Date.now() : 0;
64
+ return {
65
+ write(record) {
66
+ const tag = LEVEL_TAG[record.level];
67
+ const tagColored = colors ? `${LEVEL_COLOR[record.level]}${tag}${ANSI.reset}` : tag;
68
+ const time = opts.relativeTime
69
+ ? `+${(record.time - t0).toString().padStart(4)}ms`
70
+ : new Date(record.time).toISOString();
71
+ const cat = record.category ? ` [${record.category}]` : '';
72
+ const bindings = Object.keys(record.bindings).length > 0
73
+ ? ` ${safeStringify(record.bindings)}`
74
+ : '';
75
+ const line = `${time} ${tagColored}${cat} ${record.msg}${bindings}`;
76
+ const method = LEVEL_METHOD[record.level];
77
+ console[method](line);
78
+ },
79
+ };
80
+ }
81
+ /**
82
+ * JSON.stringify with circular-ref guard and Error → {name, message,
83
+ * stack} expansion. Bindings often carry Error instances or shared
84
+ * state objects; we don't want a circular ref or a "[object Object]"
85
+ * to drop information.
86
+ */
87
+ function safeStringify(value) {
88
+ const seen = new WeakSet();
89
+ try {
90
+ return JSON.stringify(value, (_, v) => {
91
+ if (v instanceof Error) {
92
+ return { name: v.name, message: v.message, stack: v.stack };
93
+ }
94
+ if (typeof v === 'object' && v !== null) {
95
+ if (seen.has(v))
96
+ return '[Circular]';
97
+ seen.add(v);
98
+ }
99
+ return v;
100
+ });
101
+ }
102
+ catch {
103
+ return '[unserializable]';
104
+ }
105
+ }
106
+ //# sourceMappingURL=console.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.js","sourceRoot":"","sources":["../../src/sinks/console.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Fan out one record to every wrapped sink. Common pairing:
3
+ * `multiSink(consoleSink(), bufferSink({ capacity: 10_000 }))` —
4
+ * see lines in devtools while also accumulating them for an
5
+ * end-user "Download log" button.
6
+ *
7
+ * Failures in one sink don't stop later sinks — each `write` is
8
+ * wrapped in try/catch and errors are swallowed (logging
9
+ * recursively from a logging error would loop).
10
+ */
11
+ import type { Sink } from '../types.js';
12
+ export declare function multiSink(...sinks: Sink[]): Sink;
13
+ //# sourceMappingURL=multi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi.d.ts","sourceRoot":"","sources":["../../src/sinks/multi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAa,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnD,wBAAgB,SAAS,CAAC,GAAG,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAahD"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Fan out one record to every wrapped sink. Common pairing:
3
+ * `multiSink(consoleSink(), bufferSink({ capacity: 10_000 }))` —
4
+ * see lines in devtools while also accumulating them for an
5
+ * end-user "Download log" button.
6
+ *
7
+ * Failures in one sink don't stop later sinks — each `write` is
8
+ * wrapped in try/catch and errors are swallowed (logging
9
+ * recursively from a logging error would loop).
10
+ */
11
+ export function multiSink(...sinks) {
12
+ return {
13
+ write(record) {
14
+ for (const s of sinks) {
15
+ try {
16
+ s.write(record);
17
+ }
18
+ catch {
19
+ // Intentional: don't let one bad sink kill the others, and
20
+ // don't log the failure (that would re-enter `write`).
21
+ }
22
+ }
23
+ },
24
+ };
25
+ }
26
+ //# sourceMappingURL=multi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi.js","sourceRoot":"","sources":["../../src/sinks/multi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,UAAU,SAAS,CAAC,GAAG,KAAa;IACxC,OAAO;QACL,KAAK,CAAC,MAAiB;YACrB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;gBACtB,IAAI,CAAC;oBACH,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBAClB,CAAC;gBAAC,MAAM,CAAC;oBACP,2DAA2D;oBAC3D,uDAAuD;gBACzD,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Null sink — drops every record. Use in tests so logging code
3
+ * doesn't pollute the test runner output, or in production code
4
+ * paths where you want to disable logging entirely without going
5
+ * through `silent` level (e.g. a "verbose" CLI flag that controls
6
+ * level but a separate "quiet" flag that controls the sink).
7
+ */
8
+ import type { Sink } from '../types.js';
9
+ export declare function nullSink(): Sink;
10
+ //# sourceMappingURL=null.d.ts.map