@prosopo/logger 1.0.3 → 2.0.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.
@@ -1,5 +1,9 @@
1
+ import { stringifyBigInts } from "@prosopo/util";
1
2
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
- import { getLogger, stringifyBigInts } from "../logger.js";
3
+ import { getLogger, parseDirectives, parseLogLevel, resolveLevel, setGlobalDirectives, } from "../logger.js";
4
+ beforeEach(() => {
5
+ setGlobalDirectives("");
6
+ });
3
7
  describe("unpackError", () => {
4
8
  let captured = [];
5
9
  beforeEach(() => {
@@ -56,7 +60,7 @@ describe("unpackError", () => {
56
60
  });
57
61
  });
58
62
  describe("stringifyBigInts", () => {
59
- it("should strigify big int", () => {
63
+ it("should stringify big int", () => {
60
64
  const bigInt = BigInt(12345678901234567890n);
61
65
  const stringified = stringifyBigInts(bigInt);
62
66
  expect(stringified).toEqual(bigInt.toString());
@@ -101,4 +105,211 @@ describe("stringifyBigInts", () => {
101
105
  ]);
102
106
  });
103
107
  });
108
+ describe("parseLogLevel", () => {
109
+ it("parses a bare level", () => {
110
+ expect(parseLogLevel("warn")).toBe("warn");
111
+ });
112
+ it("extracts the global level from a directive string", () => {
113
+ expect(parseLogLevel("warn,database=trace")).toBe("warn");
114
+ });
115
+ it("returns the fallback for a directives-only string", () => {
116
+ expect(parseLogLevel("database=trace", "info")).toBe("info");
117
+ });
118
+ it("returns the fallback for undefined", () => {
119
+ expect(parseLogLevel(undefined, "info")).toBe("info");
120
+ });
121
+ });
122
+ describe("parseDirectives", () => {
123
+ it("parses a bare level as the global default (empty-string key)", () => {
124
+ const directives = parseDirectives("debug");
125
+ expect(directives.get("")).toBe("debug");
126
+ });
127
+ it("parses scope=level pairs", () => {
128
+ const directives = parseDirectives("provider:db=debug,provider:api=trace");
129
+ expect(directives.get("provider:db")).toBe("debug");
130
+ expect(directives.get("provider:api")).toBe("trace");
131
+ });
132
+ it("ignores empty and invalid entries", () => {
133
+ const directives = parseDirectives(" , provider:db=debug , bogus=nope ");
134
+ expect(directives.get("provider:db")).toBe("debug");
135
+ expect(directives.has("bogus")).toBe(false);
136
+ });
137
+ it("ignores an empty-scope entry like '=debug' (only a bare level sets the global default)", () => {
138
+ const directives = parseDirectives("=debug");
139
+ expect(directives.has("")).toBe(false);
140
+ });
141
+ it("does not let an empty-scope entry override a real scope's level", () => {
142
+ const directives = parseDirectives("=debug,provider:db=warn");
143
+ expect(directives.has("")).toBe(false);
144
+ expect(directives.get("provider:db")).toBe("warn");
145
+ });
146
+ it("parses per-scope overrides", () => {
147
+ const d = parseDirectives("warn,database=trace");
148
+ expect(d.get("")).toBe("warn");
149
+ expect(d.get("database")).toBe("trace");
150
+ });
151
+ it("parses directives-only (no global default)", () => {
152
+ const d = parseDirectives("database=trace");
153
+ expect(d.has("")).toBe(false);
154
+ expect(d.get("database")).toBe("trace");
155
+ });
156
+ });
157
+ describe("resolveLevel", () => {
158
+ it("returns an exact scope match", () => {
159
+ const directives = parseDirectives("provider:db=debug");
160
+ expect(resolveLevel("provider:db", directives, "info")).toBe("debug");
161
+ });
162
+ it("returns the most specific matching prefix", () => {
163
+ const directives = parseDirectives("provider=warn,provider:db=debug");
164
+ expect(resolveLevel("provider:db:query", directives, "info")).toBe("debug");
165
+ });
166
+ it("falls back to the global default when no scope matches", () => {
167
+ const directives = parseDirectives("other=trace,error");
168
+ expect(resolveLevel("provider:db", directives, "info")).toBe("error");
169
+ });
170
+ it("falls back to the supplied fallback when nothing matches", () => {
171
+ const directives = parseDirectives("other=trace");
172
+ expect(resolveLevel("provider:db", directives, "info")).toBe("info");
173
+ });
174
+ it("matches the exact scope", () => {
175
+ const d = parseDirectives("warn,database:mongo=trace");
176
+ expect(resolveLevel("database:mongo", d, "info")).toBe("trace");
177
+ });
178
+ it("matches a prefix when exact scope has no entry", () => {
179
+ const d = parseDirectives("warn,database=debug");
180
+ expect(resolveLevel("database:mongo:queries", d, "info")).toBe("debug");
181
+ });
182
+ it("falls back to global default when no scope matches", () => {
183
+ const d = parseDirectives("warn,database=debug");
184
+ expect(resolveLevel("provider:request", d, "info")).toBe("warn");
185
+ });
186
+ it("returns fallback when directives are empty", () => {
187
+ const d = parseDirectives("");
188
+ expect(resolveLevel("any:scope", d, "info")).toBe("info");
189
+ });
190
+ });
191
+ describe("Logger.with subscope + directives", () => {
192
+ let captured = [];
193
+ beforeEach(() => {
194
+ captured = [];
195
+ const capture = (...args) => {
196
+ captured.push(String(args[0]));
197
+ };
198
+ vi.spyOn(console, "debug").mockImplementation(capture);
199
+ vi.spyOn(console, "info").mockImplementation(capture);
200
+ });
201
+ afterEach(() => {
202
+ vi.restoreAllMocks();
203
+ setGlobalDirectives("");
204
+ });
205
+ it("concatenates parent scope with subscope", () => {
206
+ setGlobalDirectives("parent:child=debug");
207
+ const child = getLogger("info", "parent").with({}, "child");
208
+ child.debug(() => ({ msg: "hello" }));
209
+ expect(captured.length).toBe(1);
210
+ });
211
+ it("uses the subscope directly when the parent scope is empty (no leading colon)", () => {
212
+ setGlobalDirectives("admin=debug");
213
+ const child = getLogger("info", "").with({}, "admin");
214
+ child.debug(() => ({ msg: "hello" }));
215
+ expect(captured.length).toBe(1);
216
+ });
217
+ it("re-resolves directives at print time rather than baking them in (no sticky level)", () => {
218
+ setGlobalDirectives("admin=debug");
219
+ const child = getLogger("info", "").with({}, "admin");
220
+ setGlobalDirectives("");
221
+ child.debug(() => ({ msg: "should be suppressed" }));
222
+ expect(captured.length).toBe(0);
223
+ child.info(() => ({ msg: "visible" }));
224
+ expect(captured.length).toBe(1);
225
+ });
226
+ });
227
+ describe("setGlobalDirectives", () => {
228
+ afterEach(() => {
229
+ setGlobalDirectives(process.env.PROSOPO_LOG_LEVEL ?? "");
230
+ });
231
+ it("affects an existing logger's filtering at emit time", () => {
232
+ const logger = getLogger("info", "test-scope");
233
+ const debugSpy = vi.spyOn(console, "debug").mockImplementation(() => { });
234
+ try {
235
+ setGlobalDirectives("warn");
236
+ logger.debug(() => ({ msg: "hidden" }));
237
+ expect(debugSpy).not.toHaveBeenCalled();
238
+ setGlobalDirectives("warn,test-scope=trace");
239
+ logger.debug(() => ({ msg: "shown" }));
240
+ expect(debugSpy).toHaveBeenCalledTimes(1);
241
+ }
242
+ finally {
243
+ debugSpy.mockRestore();
244
+ }
245
+ });
246
+ });
247
+ describe("emitted record", () => {
248
+ afterEach(() => {
249
+ setGlobalDirectives(process.env.PROSOPO_LOG_LEVEL ?? "");
250
+ });
251
+ it("uses the message level rather than the logger's configured level", () => {
252
+ setGlobalDirectives("trace");
253
+ const logger = getLogger("info", "record-scope");
254
+ const errorSpy = vi.spyOn(console, "error").mockImplementation(() => { });
255
+ try {
256
+ logger.error(() => ({ msg: "boom" }));
257
+ expect(errorSpy).toHaveBeenCalledTimes(1);
258
+ const output = errorSpy.mock.calls[0]?.[0];
259
+ expect(typeof output).toBe("string");
260
+ const record = JSON.parse(output);
261
+ expect(record.level).toBe("error");
262
+ expect(record.scope).toBe("record-scope");
263
+ expect(record.msg).toBe("boom");
264
+ }
265
+ finally {
266
+ errorSpy.mockRestore();
267
+ }
268
+ });
269
+ });
270
+ describe("Logger.with subscope", () => {
271
+ it("appends subscope to parent scope", () => {
272
+ const parent = getLogger("info", "provider");
273
+ const child = parent.with({}, "request");
274
+ expect(child.getScope()).toBe("provider:request");
275
+ });
276
+ it("does not produce a leading colon when parent scope is empty", () => {
277
+ const parent = getLogger("info", "");
278
+ const child = parent.with({}, "request");
279
+ expect(child.getScope()).toBe("request");
280
+ });
281
+ it("trims surrounding whitespace from the subscope", () => {
282
+ const parent = getLogger("info", "provider");
283
+ const child = parent.with({}, " request ");
284
+ expect(child.getScope()).toBe("provider:request");
285
+ });
286
+ it("treats a whitespace-only subscope as absent", () => {
287
+ const parent = getLogger("info", "provider");
288
+ const child = parent.with({}, " ");
289
+ expect(child.getScope()).toBe("provider");
290
+ });
291
+ it("merges parent and child default data into emitted records", () => {
292
+ setGlobalDirectives("trace");
293
+ const parent = getLogger("info", "test").with({ requestId: "abc" });
294
+ const child = parent.with({ extra: 1 });
295
+ const infoSpy = vi.spyOn(console, "info").mockImplementation(() => { });
296
+ try {
297
+ child.info(() => ({ data: { perCall: true } }));
298
+ expect(infoSpy).toHaveBeenCalledTimes(1);
299
+ const output = infoSpy.mock.calls[0]?.[0];
300
+ expect(typeof output).toBe("string");
301
+ const record = JSON.parse(output);
302
+ expect(record.scope).toBe("test");
303
+ expect(record.data).toMatchObject({
304
+ requestId: "abc",
305
+ extra: 1,
306
+ perCall: true,
307
+ });
308
+ }
309
+ finally {
310
+ infoSpy.mockRestore();
311
+ setGlobalDirectives(process.env.PROSOPO_LOG_LEVEL ?? "");
312
+ }
313
+ });
314
+ });
104
315
  //# sourceMappingURL=logger.unit.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.unit.test.js","sourceRoot":"","sources":["../../src/tests/logger.unit.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"logger.unit.test.js","sourceRoot":"","sources":["../../src/tests/logger.unit.test.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EACN,SAAS,EACT,eAAe,EACf,aAAa,EACb,YAAY,EACZ,mBAAmB,GACnB,MAAM,cAAc,CAAC;AAMtB,UAAU,CAAC,GAAG,EAAE;IACf,mBAAmB,CAAC,EAAE,CAAC,CAAC;AACzB,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC5B,IAAI,QAAQ,GAAa,EAAE,CAAC;IAE5B,UAAU,CAAC,GAAG,EAAE;QACf,QAAQ,GAAG,EAAE,CAAC;QACd,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,CAAC,GAAG,IAAe,EAAE,EAAE;YACpE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,eAAe,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,SAAS,UAAU;QAClB,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;IACnD,CAAC;IAED,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QACnD,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,IAAI,SAAS,CAAC,WAAW,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QAC9B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,MAAM,CAAC,OAAkC,CAAC;QAC1D,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QACjC,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QAC9B,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC,OAAkC,CAAC;QAChE,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC9D,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;QACpE,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QAC9B,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC,OAAkC,CAAC;QAChE,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACpC,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;QACtC,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;QAC9B,GAA0C,CAAC,KAAK,GAAG,KAAK,CAAC;QAC1D,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QAC9B,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC,OAAkC,CAAC;QAChE,MAAM,SAAS,GAAG,OAAO,CAAC,KAAgC,CAAC;QAC3D,MAAM,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC9D,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QAC9B,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC,OAAkC,CAAC;QAChE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,aAAa,EAAE,CAAC;IACvC,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACnC,MAAM,MAAM,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;QAE7C,MAAM,WAAW,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAE7C,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACxD,MAAM,OAAO,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;QAE9C,MAAM,WAAW,GAAG,gBAAgB,CAAC;YACpC,KAAK,EAAE,OAAO;YACd,MAAM,EAAE;gBACP,KAAK,EAAE;oBACN,KAAK,EAAE,OAAO;iBACd;aACD;YACD,KAAK,EAAE,OAAO;SACd,CAAC,CAAC;QAEH,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC;YAC3B,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE;YACzB,MAAM,EAAE;gBACP,KAAK,EAAE;oBACN,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE;iBACzB;aACD;YACD,KAAK,EAAE,OAAO;SACd,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAChD,MAAM,OAAO,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;QAE9C,MAAM,WAAW,GAAG,gBAAgB,CAAC;YACpC,OAAO;YACP,CAAC,OAAO,EAAE,OAAO,CAAC;YAClB,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE;YAClC,OAAO;SACP,CAAC,CAAC;QAEH,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC;YAC3B,OAAO,CAAC,QAAQ,EAAE;YAClB,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;YAC7B,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE,EAAE;YAC7C,OAAO;SACP,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC9B,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC5D,MAAM,CAAC,aAAa,CAAC,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC5D,MAAM,CAAC,aAAa,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,aAAa,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACvE,MAAM,UAAU,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACnC,MAAM,UAAU,GAAG,eAAe,CAAC,sCAAsC,CAAC,CAAC;QAC3E,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC5C,MAAM,UAAU,GAAG,eAAe,CAAC,oCAAoC,CAAC,CAAC;QACzE,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wFAAwF,EAAE,GAAG,EAAE;QACjG,MAAM,UAAU,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QAC1E,MAAM,UAAU,GAAG,eAAe,CAAC,yBAAyB,CAAC,CAAC;QAC9D,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,GAAG,eAAe,CAAC,qBAAqB,CAAC,CAAC;QACjD,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/B,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAC;QAC5C,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACvC,MAAM,UAAU,GAAG,eAAe,CAAC,mBAAmB,CAAC,CAAC;QACxD,MAAM,CAAC,YAAY,CAAC,aAAa,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACpD,MAAM,UAAU,GAAG,eAAe,CAAC,iCAAiC,CAAC,CAAC;QACtE,MAAM,CAAC,YAAY,CAAC,mBAAmB,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QACjE,MAAM,UAAU,GAAG,eAAe,CAAC,mBAAmB,CAAC,CAAC;QACxD,MAAM,CAAC,YAAY,CAAC,aAAa,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QACnE,MAAM,UAAU,GAAG,eAAe,CAAC,aAAa,CAAC,CAAC;QAClD,MAAM,CAAC,YAAY,CAAC,aAAa,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QAClC,MAAM,CAAC,GAAG,eAAe,CAAC,2BAA2B,CAAC,CAAC;QACvD,MAAM,CAAC,YAAY,CAAC,gBAAgB,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,GAAG,eAAe,CAAC,qBAAqB,CAAC,CAAC;QACjD,MAAM,CAAC,YAAY,CAAC,wBAAwB,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC7D,MAAM,CAAC,GAAG,eAAe,CAAC,qBAAqB,CAAC,CAAC;QACjD,MAAM,CAAC,YAAY,CAAC,kBAAkB,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,CAAC,CAAC;QAC9B,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,mCAAmC,EAAE,GAAG,EAAE;IAClD,IAAI,QAAQ,GAAa,EAAE,CAAC;IAE5B,UAAU,CAAC,GAAG,EAAE;QACf,QAAQ,GAAG,EAAE,CAAC;QACd,MAAM,OAAO,GAAG,CAAC,GAAG,IAAe,EAAQ,EAAE;YAC5C,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,CAAC,CAAC;QACF,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACvD,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,eAAe,EAAE,CAAC;QACrB,mBAAmB,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QAClD,mBAAmB,CAAC,oBAAoB,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAC5D,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QACtC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8EAA8E,EAAE,GAAG,EAAE;QACvF,mBAAmB,CAAC,aAAa,CAAC,CAAC;QACnC,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACtD,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QAGtC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mFAAmF,EAAE,GAAG,EAAE;QAC5F,mBAAmB,CAAC,aAAa,CAAC,CAAC;QACnC,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAEtD,mBAAmB,CAAC,EAAE,CAAC,CAAC;QACxB,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,sBAAsB,EAAE,CAAC,CAAC,CAAC;QACrD,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAEhC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QACvC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IAGpC,SAAS,CAAC,GAAG,EAAE;QACd,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC9D,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAC/C,MAAM,QAAQ,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACzE,IAAI,CAAC;YAEJ,mBAAmB,CAAC,MAAM,CAAC,CAAC;YAC5B,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;YACxC,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;YAGxC,mBAAmB,CAAC,uBAAuB,CAAC,CAAC;YAC7C,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;YACvC,MAAM,CAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC3C,CAAC;gBAAS,CAAC;YACV,QAAQ,CAAC,WAAW,EAAE,CAAC;QACxB,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC/B,SAAS,CAAC,GAAG,EAAE;QACd,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC3E,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC7B,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;QACjD,MAAM,QAAQ,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACzE,IAAI,CAAC;YACJ,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;YACtC,MAAM,CAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;YAC1C,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC3C,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACrC,MAAM,MAAM,GAAkD,IAAI,CAAC,KAAK,CACvE,MAAgB,CAChB,CAAC;YACF,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAC1C,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC;gBAAS,CAAC;YACV,QAAQ,CAAC,WAAW,EAAE,CAAC;QACxB,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC3C,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACtE,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACzD,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACtD,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACpE,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC7B,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACxC,MAAM,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACvE,IAAI,CAAC;YACJ,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;YAChD,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;YACzC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC1C,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACrC,MAAM,MAAM,GACX,IAAI,CAAC,KAAK,CAAC,MAAgB,CAAC,CAAC;YAC9B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC;gBACjC,SAAS,EAAE,KAAK;gBAChB,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,IAAI;aACb,CAAC,CAAC;QACJ,CAAC;gBAAS,CAAC;YACV,OAAO,CAAC,WAAW,EAAE,CAAC;YACtB,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;QAC1D,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prosopo/logger",
3
- "version": "1.0.3",
3
+ "version": "2.0.0",
4
4
  "description": "Prosopo logger library",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -28,11 +28,11 @@
28
28
  "author": "Prosopo Limited",
29
29
  "license": "Apache-2.0",
30
30
  "dependencies": {
31
- "@prosopo/util": "3.3.0",
31
+ "@prosopo/util": "3.3.2",
32
32
  "zod": "3.23.8"
33
33
  },
34
34
  "devDependencies": {
35
- "@prosopo/config": "3.3.1",
35
+ "@prosopo/config": "3.3.4",
36
36
  "@types/node": "22.10.2",
37
37
  "@vitest/coverage-v8": "3.2.4",
38
38
  "concurrently": "9.0.1",
@@ -42,7 +42,7 @@
42
42
  "tslib": "2.7.0",
43
43
  "tsx": "4.20.3",
44
44
  "typescript": "5.6.2",
45
- "vite": "6.4.1",
45
+ "vite": "6.4.3",
46
46
  "vitest": "3.2.4"
47
47
  },
48
48
  "repository": {
package/src/logger.ts CHANGED
@@ -15,8 +15,6 @@
15
15
  import { stringifyBigInts } from "@prosopo/util";
16
16
  import { z } from "zod";
17
17
 
18
- export { stringifyBigInts };
19
-
20
18
  export type LogObject = object;
21
19
  export type LogRecord = {
22
20
  err?: unknown;
@@ -37,11 +35,15 @@ export type Logger = {
37
35
  fatal(fn: LogRecordFn): void;
38
36
  log(level: LogLevel, fn: LogRecordFn): void;
39
37
  /**
40
- * Creates a new logger instance which includes the given object in every log message. Akin to a child logger.
41
- * This is useful for adding context to log messages, such as user IDs, request IDs, etc.
42
- * @param obj An object to log, which will be added to every log message.
38
+ * Creates a child logger with merged default data and an optional subscope appended to the
39
+ * current scope (e.g. parent "database" + subscope "queries" → "database:queries").
40
+ * The child's configured level is snapshotted at creation by resolving PROSOPO_LOG_LEVEL
41
+ * directives for the new scope, falling back to the parent's level when no directive matches.
42
+ * At emit time `print()` re-resolves directives for the scope, using the child's own configured
43
+ * level (this snapshot) as the fallback — so later directive changes are honoured, but a direct
44
+ * `setLogLevel()` on the child overrides the snapshot.
43
45
  */
44
- with(obj: LogObject): Logger;
46
+ with(obj: LogObject, subscope?: string): Logger;
45
47
  getPretty(): boolean;
46
48
  setPretty(pretty: boolean): void;
47
49
  getPrintStack(): boolean;
@@ -50,12 +52,14 @@ export type Logger = {
50
52
  setFormat(format: Format): void;
51
53
  };
52
54
 
53
- export const InfoLevel = "info";
54
- export const DebugLevel = "debug";
55
- export const TraceLevel = "trace";
56
- export const WarnLevel = "warn";
57
- export const ErrorLevel = "error";
58
- export const FatalLevel = "fatal";
55
+ // Level string constants are internal to this module; consumers use the
56
+ // `LogLevel` enum/type or the string literals it parses.
57
+ const InfoLevel = "info";
58
+ const DebugLevel = "debug";
59
+ const TraceLevel = "trace";
60
+ const WarnLevel = "warn";
61
+ const ErrorLevel = "error";
62
+ const FatalLevel = "fatal";
59
63
 
60
64
  export const LogLevel = z.enum([
61
65
  InfoLevel,
@@ -67,11 +71,7 @@ export const LogLevel = z.enum([
67
71
  ]);
68
72
  export type LogLevel = z.infer<typeof LogLevel>;
69
73
 
70
- export type LevelMap = {
71
- [K in LogLevel]: number;
72
- };
73
-
74
- const logLevelMap: LevelMap = {
74
+ const logLevelMap: Record<LogLevel, number> = {
75
75
  [TraceLevel]: 0,
76
76
  [DebugLevel]: 1,
77
77
  [InfoLevel]: 2,
@@ -84,8 +84,97 @@ export function parseLogLevel(
84
84
  level: string | undefined,
85
85
  or: LogLevel = InfoLevel,
86
86
  ): LogLevel {
87
- const result = LogLevel.safeParse(level);
88
- return result.success ? result.data : or;
87
+ if (!level) return or;
88
+ // Handle directive strings like "warn,database=trace" — extract the bare global level.
89
+ for (const part of level.split(",")) {
90
+ const trimmed = part.trim();
91
+ if (trimmed && !trimmed.includes("=")) {
92
+ const parsed = LogLevel.safeParse(trimmed);
93
+ if (parsed.success) return parsed.data;
94
+ }
95
+ }
96
+ return or;
97
+ }
98
+
99
+ // ---------------------------------------------------------------------------
100
+ // Directive-based filtering
101
+ //
102
+ // PROSOPO_LOG_LEVEL supports both a bare level and a comma-separated directive
103
+ // string with optional per-scope overrides, e.g.:
104
+ // "warn" – global floor
105
+ // "warn,database=trace" – global warn, database:* gets trace
106
+ // "database=trace,http=debug" – per-scope only, no global default
107
+ //
108
+ // Scope segments are separated by ":" mirroring module paths, e.g.
109
+ // "provider:db=debug" matches any logger whose scope starts with "provider:db".
110
+ // ---------------------------------------------------------------------------
111
+
112
+ export type Directives = Map<string, LogLevel>;
113
+
114
+ /**
115
+ * Parse a PROSOPO_LOG_LEVEL directive string into a scope→level map.
116
+ * An entry with an empty-string key represents the global default.
117
+ */
118
+ export function parseDirectives(raw: string): Directives {
119
+ const map: Directives = new Map();
120
+ for (const part of raw.split(",")) {
121
+ const trimmed = part.trim();
122
+ if (!trimmed) continue;
123
+ const eqIdx = trimmed.indexOf("=");
124
+ if (eqIdx === -1) {
125
+ const parsed = LogLevel.safeParse(trimmed);
126
+ if (parsed.success) map.set("", parsed.data);
127
+ } else {
128
+ const scope = trimmed.slice(0, eqIdx).trim();
129
+ // Ignore entries with an empty scope (e.g. "=debug") — only a bare
130
+ // level (no "=") may set the global default.
131
+ if (!scope) continue;
132
+ const parsed = LogLevel.safeParse(trimmed.slice(eqIdx + 1).trim());
133
+ if (parsed.success) map.set(scope, parsed.data);
134
+ }
135
+ }
136
+ return map;
137
+ }
138
+
139
+ /**
140
+ * Find the most specific directive that matches the given scope.
141
+ * Walks from the full scope up to the empty-string global default.
142
+ * Returns the fallback if nothing matches.
143
+ */
144
+ export function resolveLevel(
145
+ scope: string,
146
+ directives: Directives,
147
+ fallback: LogLevel,
148
+ ): LogLevel {
149
+ if (directives.has(scope)) return directives.get(scope) as LogLevel;
150
+ const parts = scope.split(":");
151
+ for (let i = parts.length - 1; i > 0; i--) {
152
+ const prefix = parts.slice(0, i).join(":");
153
+ if (directives.has(prefix)) return directives.get(prefix) as LogLevel;
154
+ }
155
+ if (directives.has("")) return directives.get("") as LogLevel;
156
+ return fallback;
157
+ }
158
+
159
+ let _globalDirectives: Directives | undefined;
160
+
161
+ function getGlobalDirectives(): Directives {
162
+ if (!_globalDirectives) {
163
+ const raw =
164
+ typeof process !== "undefined"
165
+ ? (process.env?.PROSOPO_LOG_LEVEL ?? "")
166
+ : "";
167
+ _globalDirectives = parseDirectives(raw);
168
+ }
169
+ return _globalDirectives;
170
+ }
171
+
172
+ /**
173
+ * Override the global directives at runtime (useful in tests or at app startup).
174
+ * Accepts the same directive string as PROSOPO_LOG_LEVEL.
175
+ */
176
+ export function setGlobalDirectives(raw: string): void {
177
+ _globalDirectives = parseDirectives(raw);
89
178
  }
90
179
 
91
180
  // Create a new logger with the given level and scope
@@ -98,8 +187,8 @@ export function getLogger(logLevel: LogLevel, scope: string): Logger {
98
187
  const inBrowser =
99
188
  typeof window !== "undefined" && typeof window.document !== "undefined";
100
189
 
101
- export const FormatJson = "json";
102
- export const FormatPlain = "plain";
190
+ const FormatJson = "json";
191
+ const FormatPlain = "plain";
103
192
  export const Format = z.enum([FormatJson, FormatPlain]);
104
193
  export type Format = z.infer<typeof Format>;
105
194
 
@@ -111,17 +200,15 @@ export class NativeLogger implements Logger {
111
200
  // this provides utility for adding properties to every log message
112
201
  private defaultData: LogObject | undefined;
113
202
  private level: LogLevel;
114
- private levelNum: number;
115
203
  private pretty = 0; // pretty print indentation level - 0 = no pretty print, 2 = 2 spaces, etc
116
204
  private printStack = false; // whether to print the stack trace in the log
117
205
  private format: Format = FormatJson;
118
206
 
119
207
  constructor(
120
208
  private scope: string,
121
- private levelMap: LevelMap = logLevelMap,
209
+ private levelMap: Record<LogLevel, number> = logLevelMap,
122
210
  ) {
123
211
  this.level = InfoLevel; // default log level
124
- this.levelNum = this.levelMap[this.level];
125
212
  }
126
213
 
127
214
  setFormat(format: Format): void {
@@ -135,9 +222,31 @@ export class NativeLogger implements Logger {
135
222
  return this.format;
136
223
  }
137
224
 
138
- with(obj: LogObject): Logger {
139
- const newLogger = new NativeLogger(this.scope);
140
- newLogger.defaultData = { ...this.defaultData, ...obj };
225
+ with(obj: LogObject, subscope?: string): Logger {
226
+ // Trim so a whitespace-only subscope is treated as absent and the
227
+ // resulting scope matches the trimmed keys produced by parseDirectives().
228
+ const trimmedSubscope = subscope?.trim();
229
+ const newScope = trimmedSubscope
230
+ ? this.scope
231
+ ? `${this.scope}:${trimmedSubscope}`
232
+ : trimmedSubscope
233
+ : this.scope;
234
+ const newLogger = new NativeLogger(newScope, this.levelMap);
235
+ // Avoid allocating an empty defaultData object when scoping only
236
+ // (e.g. with({}, "subscope")). Reuse the parent reference when obj has no
237
+ // keys, and skip merging entirely when neither side has data.
238
+ const hasObjData = Object.keys(obj).length > 0;
239
+ if (hasObjData) {
240
+ newLogger.defaultData = { ...this.defaultData, ...obj };
241
+ } else if (this.defaultData) {
242
+ newLogger.defaultData = this.defaultData;
243
+ }
244
+ newLogger.setPretty(this.getPretty());
245
+ newLogger.setPrintStack(this.getPrintStack());
246
+ // Inherit the parent's configured level as the fallback only. Directives
247
+ // are applied dynamically at print time (see print()), so we must NOT bake
248
+ // the currently-resolved level in here — otherwise a directive active now
249
+ // would stick to this child even after setGlobalDirectives() clears it.
141
250
  newLogger.setLogLevel(this.getLogLevel());
142
251
  return newLogger;
143
252
  }
@@ -164,7 +273,6 @@ export class NativeLogger implements Logger {
164
273
 
165
274
  setLogLevel(level: LogLevel): void {
166
275
  this.level = level;
167
- this.levelNum = this.levelMap[level];
168
276
  }
169
277
 
170
278
  getLogLevel(): LogLevel {
@@ -212,8 +320,13 @@ export class NativeLogger implements Logger {
212
320
  fn: LogRecordFn,
213
321
  level: LogLevel,
214
322
  ): void {
215
- if (this.levelMap[level] < this.levelNum) {
216
- return; // skip logging if the level is below the current log level threshold
323
+ // Re-resolve at print time so directives set after construction are respected.
324
+ const effectiveLevelNum =
325
+ this.levelMap[
326
+ resolveLevel(this.scope, getGlobalDirectives(), this.level)
327
+ ];
328
+ if (this.levelMap[level] < effectiveLevelNum) {
329
+ return; // skip logging if the level is below the effective threshold
217
330
  }
218
331
  const ts = new Date().toISOString();
219
332
  // populate the log fields using the fn