@logtape/logtape 1.4.0-dev.409 → 1.4.0-dev.413
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/package.json +4 -1
- package/deno.json +0 -36
- package/src/config.test.ts +0 -589
- package/src/config.ts +0 -409
- package/src/context.test.ts +0 -341
- package/src/context.ts +0 -150
- package/src/filter.test.ts +0 -84
- package/src/filter.ts +0 -64
- package/src/fixtures.ts +0 -35
- package/src/formatter.test.ts +0 -569
- package/src/formatter.ts +0 -961
- package/src/level.test.ts +0 -71
- package/src/level.ts +0 -86
- package/src/logger.test.ts +0 -1508
- package/src/logger.ts +0 -1805
- package/src/mod.ts +0 -63
- package/src/record.ts +0 -49
- package/src/sink.test.ts +0 -2590
- package/src/sink.ts +0 -976
- package/src/util.deno.ts +0 -19
- package/src/util.node.ts +0 -12
- package/src/util.ts +0 -11
- package/tsdown.config.ts +0 -24
package/src/context.test.ts
DELETED
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
import { suite } from "@alinea/suite";
|
|
2
|
-
import { assertEquals } from "@std/assert/equals";
|
|
3
|
-
import { delay } from "@std/async/delay";
|
|
4
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
|
-
import { configure, reset } from "./config.ts";
|
|
6
|
-
import { withCategoryPrefix, withContext } from "./context.ts";
|
|
7
|
-
import { getLogger } from "./logger.ts";
|
|
8
|
-
import type { LogRecord } from "./record.ts";
|
|
9
|
-
|
|
10
|
-
const test = suite(import.meta);
|
|
11
|
-
|
|
12
|
-
test("withContext()", async () => {
|
|
13
|
-
const buffer: LogRecord[] = [];
|
|
14
|
-
|
|
15
|
-
{ // set up
|
|
16
|
-
await configure({
|
|
17
|
-
sinks: {
|
|
18
|
-
buffer: buffer.push.bind(buffer),
|
|
19
|
-
},
|
|
20
|
-
loggers: [
|
|
21
|
-
{ category: "my-app", sinks: ["buffer"], lowestLevel: "debug" },
|
|
22
|
-
{ category: ["logtape", "meta"], sinks: [], lowestLevel: "warning" },
|
|
23
|
-
],
|
|
24
|
-
contextLocalStorage: new AsyncLocalStorage(),
|
|
25
|
-
reset: true,
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
try {
|
|
30
|
-
// test
|
|
31
|
-
getLogger("my-app").debug("hello", { foo: 1, bar: 2 });
|
|
32
|
-
assertEquals(buffer, [
|
|
33
|
-
{
|
|
34
|
-
category: ["my-app"],
|
|
35
|
-
level: "debug",
|
|
36
|
-
message: ["hello"],
|
|
37
|
-
rawMessage: "hello",
|
|
38
|
-
properties: { foo: 1, bar: 2 },
|
|
39
|
-
timestamp: buffer[0].timestamp,
|
|
40
|
-
},
|
|
41
|
-
]);
|
|
42
|
-
buffer.pop();
|
|
43
|
-
const rv = withContext({ foo: 3, baz: 4 }, () => {
|
|
44
|
-
getLogger("my-app").debug("world", { foo: 1, bar: 2 });
|
|
45
|
-
return 123;
|
|
46
|
-
});
|
|
47
|
-
assertEquals(rv, 123);
|
|
48
|
-
assertEquals(buffer, [
|
|
49
|
-
{
|
|
50
|
-
category: ["my-app"],
|
|
51
|
-
level: "debug",
|
|
52
|
-
message: ["world"],
|
|
53
|
-
rawMessage: "world",
|
|
54
|
-
properties: { foo: 1, bar: 2, baz: 4 },
|
|
55
|
-
timestamp: buffer[0].timestamp,
|
|
56
|
-
},
|
|
57
|
-
]);
|
|
58
|
-
buffer.pop();
|
|
59
|
-
getLogger("my-app").debug("hello", { foo: 1, bar: 2 });
|
|
60
|
-
assertEquals(buffer, [
|
|
61
|
-
{
|
|
62
|
-
category: ["my-app"],
|
|
63
|
-
level: "debug",
|
|
64
|
-
message: ["hello"],
|
|
65
|
-
rawMessage: "hello",
|
|
66
|
-
properties: { foo: 1, bar: 2 },
|
|
67
|
-
timestamp: buffer[0].timestamp,
|
|
68
|
-
},
|
|
69
|
-
]);
|
|
70
|
-
|
|
71
|
-
// nesting
|
|
72
|
-
while (buffer.length > 0) buffer.pop();
|
|
73
|
-
withContext({ foo: 1, bar: 2 }, () => {
|
|
74
|
-
withContext({ foo: 3, baz: 4 }, () => {
|
|
75
|
-
getLogger("my-app").debug("hello");
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
assertEquals(buffer, [
|
|
79
|
-
{
|
|
80
|
-
category: ["my-app"],
|
|
81
|
-
level: "debug",
|
|
82
|
-
message: ["hello"],
|
|
83
|
-
rawMessage: "hello",
|
|
84
|
-
properties: { foo: 3, bar: 2, baz: 4 },
|
|
85
|
-
timestamp: buffer[0].timestamp,
|
|
86
|
-
},
|
|
87
|
-
]);
|
|
88
|
-
|
|
89
|
-
// concurrent runs
|
|
90
|
-
while (buffer.length > 0) buffer.pop();
|
|
91
|
-
await Promise.all([
|
|
92
|
-
(async () => {
|
|
93
|
-
await delay(Math.random() * 100);
|
|
94
|
-
withContext({ foo: 1 }, () => {
|
|
95
|
-
getLogger("my-app").debug("foo");
|
|
96
|
-
});
|
|
97
|
-
})(),
|
|
98
|
-
(async () => {
|
|
99
|
-
await delay(Math.random() * 100);
|
|
100
|
-
withContext({ bar: 2 }, () => {
|
|
101
|
-
getLogger("my-app").debug("bar");
|
|
102
|
-
});
|
|
103
|
-
})(),
|
|
104
|
-
(async () => {
|
|
105
|
-
await delay(Math.random() * 100);
|
|
106
|
-
withContext({ baz: 3 }, () => {
|
|
107
|
-
getLogger("my-app").debug("baz");
|
|
108
|
-
});
|
|
109
|
-
})(),
|
|
110
|
-
(async () => {
|
|
111
|
-
await delay(Math.random() * 100);
|
|
112
|
-
withContext({ qux: 4 }, () => {
|
|
113
|
-
getLogger("my-app").debug("qux");
|
|
114
|
-
});
|
|
115
|
-
})(),
|
|
116
|
-
]);
|
|
117
|
-
assertEquals(buffer.length, 4);
|
|
118
|
-
for (const log of buffer) {
|
|
119
|
-
if (log.message[0] === "foo") {
|
|
120
|
-
assertEquals(log.properties, { foo: 1 });
|
|
121
|
-
} else if (log.message[0] === "bar") {
|
|
122
|
-
assertEquals(log.properties, { bar: 2 });
|
|
123
|
-
} else if (log.message[0] === "baz") {
|
|
124
|
-
assertEquals(log.properties, { baz: 3 });
|
|
125
|
-
} else {
|
|
126
|
-
assertEquals(log.properties, { qux: 4 });
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
} finally {
|
|
130
|
-
await reset();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const metaBuffer: LogRecord[] = [];
|
|
134
|
-
|
|
135
|
-
{ // set up
|
|
136
|
-
await configure({
|
|
137
|
-
sinks: {
|
|
138
|
-
buffer: buffer.push.bind(buffer),
|
|
139
|
-
metaBuffer: metaBuffer.push.bind(metaBuffer),
|
|
140
|
-
},
|
|
141
|
-
loggers: [
|
|
142
|
-
{ category: "my-app", sinks: ["buffer"], lowestLevel: "debug" },
|
|
143
|
-
{
|
|
144
|
-
category: ["logtape", "meta"],
|
|
145
|
-
sinks: ["metaBuffer"],
|
|
146
|
-
lowestLevel: "warning",
|
|
147
|
-
},
|
|
148
|
-
],
|
|
149
|
-
reset: true,
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
try { // without settings
|
|
154
|
-
while (buffer.length > 0) buffer.pop();
|
|
155
|
-
const rv = withContext({ foo: 1 }, () => {
|
|
156
|
-
getLogger("my-app").debug("hello", { bar: 2 });
|
|
157
|
-
return 123;
|
|
158
|
-
});
|
|
159
|
-
assertEquals(rv, 123);
|
|
160
|
-
assertEquals(buffer, [
|
|
161
|
-
{
|
|
162
|
-
category: ["my-app"],
|
|
163
|
-
level: "debug",
|
|
164
|
-
message: ["hello"],
|
|
165
|
-
rawMessage: "hello",
|
|
166
|
-
properties: { bar: 2 },
|
|
167
|
-
timestamp: buffer[0].timestamp,
|
|
168
|
-
},
|
|
169
|
-
]);
|
|
170
|
-
assertEquals(metaBuffer, [
|
|
171
|
-
{
|
|
172
|
-
category: ["logtape", "meta"],
|
|
173
|
-
level: "warning",
|
|
174
|
-
message: [
|
|
175
|
-
"Context-local storage is not configured. " +
|
|
176
|
-
"Specify contextLocalStorage option in the configure() function.",
|
|
177
|
-
],
|
|
178
|
-
properties: {},
|
|
179
|
-
rawMessage: "Context-local storage is not configured. " +
|
|
180
|
-
"Specify contextLocalStorage option in the configure() function.",
|
|
181
|
-
timestamp: metaBuffer[0].timestamp,
|
|
182
|
-
},
|
|
183
|
-
]);
|
|
184
|
-
} finally {
|
|
185
|
-
await reset();
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
test("withCategoryPrefix()", async () => {
|
|
190
|
-
const buffer: LogRecord[] = [];
|
|
191
|
-
|
|
192
|
-
{ // set up
|
|
193
|
-
await configure({
|
|
194
|
-
sinks: {
|
|
195
|
-
buffer: buffer.push.bind(buffer),
|
|
196
|
-
},
|
|
197
|
-
loggers: [
|
|
198
|
-
{ category: [], sinks: ["buffer"], lowestLevel: "debug" },
|
|
199
|
-
{ category: ["logtape", "meta"], sinks: [], lowestLevel: "warning" },
|
|
200
|
-
],
|
|
201
|
-
contextLocalStorage: new AsyncLocalStorage(),
|
|
202
|
-
reset: true,
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
try {
|
|
207
|
-
// basic prefix with array
|
|
208
|
-
getLogger(["core-lib"]).debug("without prefix");
|
|
209
|
-
assertEquals(buffer[0].category, ["core-lib"]);
|
|
210
|
-
buffer.pop();
|
|
211
|
-
|
|
212
|
-
const rv = withCategoryPrefix(["sdk-1"], () => {
|
|
213
|
-
getLogger(["core-lib"]).debug("with prefix");
|
|
214
|
-
return 123;
|
|
215
|
-
});
|
|
216
|
-
assertEquals(rv, 123);
|
|
217
|
-
assertEquals(buffer[0].category, ["sdk-1", "core-lib"]);
|
|
218
|
-
buffer.pop();
|
|
219
|
-
|
|
220
|
-
// prefix should not persist after callback
|
|
221
|
-
getLogger(["core-lib"]).debug("after prefix");
|
|
222
|
-
assertEquals(buffer[0].category, ["core-lib"]);
|
|
223
|
-
buffer.pop();
|
|
224
|
-
|
|
225
|
-
// basic prefix with string
|
|
226
|
-
withCategoryPrefix("sdk-2", () => {
|
|
227
|
-
getLogger(["core-lib"]).debug("string prefix");
|
|
228
|
-
});
|
|
229
|
-
assertEquals(buffer[0].category, ["sdk-2", "core-lib"]);
|
|
230
|
-
buffer.pop();
|
|
231
|
-
|
|
232
|
-
// nesting prefixes
|
|
233
|
-
withCategoryPrefix(["app"], () => {
|
|
234
|
-
withCategoryPrefix(["sdk-1"], () => {
|
|
235
|
-
getLogger(["core-lib"]).debug("nested");
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
|
-
assertEquals(buffer[0].category, ["app", "sdk-1", "core-lib"]);
|
|
239
|
-
buffer.pop();
|
|
240
|
-
|
|
241
|
-
// combining with withContext
|
|
242
|
-
withCategoryPrefix(["my-sdk"], () => {
|
|
243
|
-
withContext({ requestId: "abc-123" }, () => {
|
|
244
|
-
getLogger(["internal"]).debug("combined");
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
assertEquals(buffer[0].category, ["my-sdk", "internal"]);
|
|
248
|
-
assertEquals(buffer[0].properties, { requestId: "abc-123" });
|
|
249
|
-
buffer.pop();
|
|
250
|
-
|
|
251
|
-
// withContext inside withCategoryPrefix
|
|
252
|
-
withContext({ userId: 42 }, () => {
|
|
253
|
-
withCategoryPrefix(["sdk"], () => {
|
|
254
|
-
getLogger(["lib"]).debug("context then prefix");
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
assertEquals(buffer[0].category, ["sdk", "lib"]);
|
|
258
|
-
assertEquals(buffer[0].properties, { userId: 42 });
|
|
259
|
-
buffer.pop();
|
|
260
|
-
|
|
261
|
-
// concurrent runs - each should have isolated prefix
|
|
262
|
-
await Promise.all([
|
|
263
|
-
(async () => {
|
|
264
|
-
await delay(Math.random() * 100);
|
|
265
|
-
withCategoryPrefix(["sdk-a"], () => {
|
|
266
|
-
getLogger(["lib"]).debug("a");
|
|
267
|
-
});
|
|
268
|
-
})(),
|
|
269
|
-
(async () => {
|
|
270
|
-
await delay(Math.random() * 100);
|
|
271
|
-
withCategoryPrefix(["sdk-b"], () => {
|
|
272
|
-
getLogger(["lib"]).debug("b");
|
|
273
|
-
});
|
|
274
|
-
})(),
|
|
275
|
-
(async () => {
|
|
276
|
-
await delay(Math.random() * 100);
|
|
277
|
-
withCategoryPrefix(["sdk-c"], () => {
|
|
278
|
-
getLogger(["lib"]).debug("c");
|
|
279
|
-
});
|
|
280
|
-
})(),
|
|
281
|
-
(async () => {
|
|
282
|
-
await delay(Math.random() * 100);
|
|
283
|
-
withCategoryPrefix(["sdk-d"], () => {
|
|
284
|
-
getLogger(["lib"]).debug("d");
|
|
285
|
-
});
|
|
286
|
-
})(),
|
|
287
|
-
]);
|
|
288
|
-
assertEquals(buffer.length, 4);
|
|
289
|
-
for (const log of buffer) {
|
|
290
|
-
if (log.message[0] === "a") {
|
|
291
|
-
assertEquals(log.category, ["sdk-a", "lib"]);
|
|
292
|
-
} else if (log.message[0] === "b") {
|
|
293
|
-
assertEquals(log.category, ["sdk-b", "lib"]);
|
|
294
|
-
} else if (log.message[0] === "c") {
|
|
295
|
-
assertEquals(log.category, ["sdk-c", "lib"]);
|
|
296
|
-
} else {
|
|
297
|
-
assertEquals(log.category, ["sdk-d", "lib"]);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
} finally {
|
|
301
|
-
await reset();
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// without contextLocalStorage configured
|
|
305
|
-
const metaBuffer: LogRecord[] = [];
|
|
306
|
-
|
|
307
|
-
{ // set up
|
|
308
|
-
await configure({
|
|
309
|
-
sinks: {
|
|
310
|
-
buffer: buffer.push.bind(buffer),
|
|
311
|
-
metaBuffer: metaBuffer.push.bind(metaBuffer),
|
|
312
|
-
},
|
|
313
|
-
loggers: [
|
|
314
|
-
{ category: "my-app", sinks: ["buffer"], lowestLevel: "debug" },
|
|
315
|
-
{
|
|
316
|
-
category: ["logtape", "meta"],
|
|
317
|
-
sinks: ["metaBuffer"],
|
|
318
|
-
lowestLevel: "warning",
|
|
319
|
-
},
|
|
320
|
-
],
|
|
321
|
-
reset: true,
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
try {
|
|
326
|
-
while (buffer.length > 0) buffer.pop();
|
|
327
|
-
const rv = withCategoryPrefix(["sdk-1"], () => {
|
|
328
|
-
getLogger(["my-app"]).debug("no storage");
|
|
329
|
-
return 456;
|
|
330
|
-
});
|
|
331
|
-
assertEquals(rv, 456);
|
|
332
|
-
// Without storage, category should not have prefix
|
|
333
|
-
assertEquals(buffer[0].category, ["my-app"]);
|
|
334
|
-
// Warning should be logged to meta logger
|
|
335
|
-
assertEquals(metaBuffer.length, 1);
|
|
336
|
-
assertEquals(metaBuffer[0].category, ["logtape", "meta"]);
|
|
337
|
-
assertEquals(metaBuffer[0].level, "warning");
|
|
338
|
-
} finally {
|
|
339
|
-
await reset();
|
|
340
|
-
}
|
|
341
|
-
});
|
package/src/context.ts
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import { LoggerImpl } from "./logger.ts";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Internal symbol for storing category prefix in context.
|
|
5
|
-
*/
|
|
6
|
-
const categoryPrefixSymbol: unique symbol = Symbol.for(
|
|
7
|
-
"logtape.categoryPrefix",
|
|
8
|
-
) as typeof categoryPrefixSymbol;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* A generic interface for a context-local storage. It resembles
|
|
12
|
-
* the {@link AsyncLocalStorage} API from Node.js.
|
|
13
|
-
* @template T The type of the context-local store.
|
|
14
|
-
* @since 0.7.0
|
|
15
|
-
*/
|
|
16
|
-
export interface ContextLocalStorage<T> {
|
|
17
|
-
/**
|
|
18
|
-
* Runs a callback with the given store as the context-local store.
|
|
19
|
-
* @param store The store to use as the context-local store.
|
|
20
|
-
* @param callback The callback to run.
|
|
21
|
-
* @returns The return value of the callback.
|
|
22
|
-
*/
|
|
23
|
-
run<R>(store: T, callback: () => R): R;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Returns the current context-local store.
|
|
27
|
-
* @returns The current context-local store, or `undefined` if there is no
|
|
28
|
-
* store.
|
|
29
|
-
*/
|
|
30
|
-
getStore(): T | undefined;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Runs a callback with the given implicit context. Every single log record
|
|
35
|
-
* in the callback will have the given context.
|
|
36
|
-
*
|
|
37
|
-
* If no `contextLocalStorage` is configured, this function does nothing and
|
|
38
|
-
* just returns the return value of the callback. It also logs a warning to
|
|
39
|
-
* the `["logtape", "meta"]` logger in this case.
|
|
40
|
-
* @param context The context to inject.
|
|
41
|
-
* @param callback The callback to run.
|
|
42
|
-
* @returns The return value of the callback.
|
|
43
|
-
* @since 0.7.0
|
|
44
|
-
*/
|
|
45
|
-
export function withContext<T>(
|
|
46
|
-
context: Record<string, unknown>,
|
|
47
|
-
callback: () => T,
|
|
48
|
-
): T {
|
|
49
|
-
const rootLogger = LoggerImpl.getLogger();
|
|
50
|
-
if (rootLogger.contextLocalStorage == null) {
|
|
51
|
-
LoggerImpl.getLogger(["logtape", "meta"]).warn(
|
|
52
|
-
"Context-local storage is not configured. " +
|
|
53
|
-
"Specify contextLocalStorage option in the configure() function.",
|
|
54
|
-
);
|
|
55
|
-
return callback();
|
|
56
|
-
}
|
|
57
|
-
const parentContext = rootLogger.contextLocalStorage.getStore() ?? {};
|
|
58
|
-
return rootLogger.contextLocalStorage.run(
|
|
59
|
-
{ ...parentContext, ...context },
|
|
60
|
-
callback,
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Gets the current category prefix from context local storage.
|
|
66
|
-
* @returns The current category prefix, or an empty array if not set.
|
|
67
|
-
* @since 1.3.0
|
|
68
|
-
*/
|
|
69
|
-
export function getCategoryPrefix(): readonly string[] {
|
|
70
|
-
const rootLogger = LoggerImpl.getLogger();
|
|
71
|
-
const store = rootLogger.contextLocalStorage?.getStore();
|
|
72
|
-
if (store == null) return [];
|
|
73
|
-
const prefix = store[categoryPrefixSymbol as unknown as string];
|
|
74
|
-
return Array.isArray(prefix) ? prefix : [];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Gets the current implicit context from context local storage, excluding
|
|
79
|
-
* internal symbol keys (like category prefix).
|
|
80
|
-
* @returns The current implicit context without internal symbol keys.
|
|
81
|
-
* @since 1.3.0
|
|
82
|
-
*/
|
|
83
|
-
export function getImplicitContext(): Record<string, unknown> {
|
|
84
|
-
const rootLogger = LoggerImpl.getLogger();
|
|
85
|
-
const store = rootLogger.contextLocalStorage?.getStore();
|
|
86
|
-
if (store == null) return {};
|
|
87
|
-
// Filter out symbol keys (like categoryPrefixSymbol)
|
|
88
|
-
const result: Record<string, unknown> = {};
|
|
89
|
-
for (const key of Object.keys(store)) {
|
|
90
|
-
result[key] = store[key];
|
|
91
|
-
}
|
|
92
|
-
return result;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Runs a callback with the given category prefix prepended to all log
|
|
97
|
-
* categories within the callback context.
|
|
98
|
-
*
|
|
99
|
-
* This is useful for SDKs or libraries that want to add their own category
|
|
100
|
-
* as a prefix to logs from their internal dependencies.
|
|
101
|
-
*
|
|
102
|
-
* If no `contextLocalStorage` is configured, this function does nothing and
|
|
103
|
-
* just returns the return value of the callback. It also logs a warning to
|
|
104
|
-
* the `["logtape", "meta"]` logger in this case.
|
|
105
|
-
*
|
|
106
|
-
* @example Basic usage
|
|
107
|
-
* ```typescript
|
|
108
|
-
* import { getLogger, withCategoryPrefix } from "@logtape/logtape";
|
|
109
|
-
*
|
|
110
|
-
* export function sdkFunction() {
|
|
111
|
-
* return withCategoryPrefix(["my-sdk"], () => {
|
|
112
|
-
* // Any logs from internal libraries within this context
|
|
113
|
-
* // will have ["my-sdk"] prepended to their category
|
|
114
|
-
* return internalLibraryFunction();
|
|
115
|
-
* });
|
|
116
|
-
* }
|
|
117
|
-
* ```
|
|
118
|
-
*
|
|
119
|
-
* @param prefix The category prefix to prepend. Can be a string or an array
|
|
120
|
-
* of strings.
|
|
121
|
-
* @param callback The callback to run.
|
|
122
|
-
* @returns The return value of the callback.
|
|
123
|
-
* @since 1.3.0
|
|
124
|
-
*/
|
|
125
|
-
export function withCategoryPrefix<T>(
|
|
126
|
-
prefix: string | readonly string[],
|
|
127
|
-
callback: () => T,
|
|
128
|
-
): T {
|
|
129
|
-
const rootLogger = LoggerImpl.getLogger();
|
|
130
|
-
if (rootLogger.contextLocalStorage == null) {
|
|
131
|
-
LoggerImpl.getLogger(["logtape", "meta"]).warn(
|
|
132
|
-
"Context-local storage is not configured. " +
|
|
133
|
-
"Specify contextLocalStorage option in the configure() function.",
|
|
134
|
-
);
|
|
135
|
-
return callback();
|
|
136
|
-
}
|
|
137
|
-
const parentContext = rootLogger.contextLocalStorage.getStore() ?? {};
|
|
138
|
-
const parentPrefix = getCategoryPrefix();
|
|
139
|
-
const newPrefix = typeof prefix === "string" ? [prefix] : [...prefix];
|
|
140
|
-
return rootLogger.contextLocalStorage.run(
|
|
141
|
-
{
|
|
142
|
-
...parentContext,
|
|
143
|
-
[categoryPrefixSymbol as unknown as string]: [
|
|
144
|
-
...parentPrefix,
|
|
145
|
-
...newPrefix,
|
|
146
|
-
],
|
|
147
|
-
},
|
|
148
|
-
callback,
|
|
149
|
-
);
|
|
150
|
-
}
|
package/src/filter.test.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { suite } from "@alinea/suite";
|
|
2
|
-
import { assert } from "@std/assert/assert";
|
|
3
|
-
import { assertFalse } from "@std/assert/false";
|
|
4
|
-
import { assertStrictEquals } from "@std/assert/strict-equals";
|
|
5
|
-
import { assertThrows } from "@std/assert/throws";
|
|
6
|
-
import { type Filter, getLevelFilter, toFilter } from "./filter.ts";
|
|
7
|
-
import { debug, error, fatal, info, trace, warning } from "./fixtures.ts";
|
|
8
|
-
import type { LogLevel } from "./level.ts";
|
|
9
|
-
|
|
10
|
-
const test = suite(import.meta);
|
|
11
|
-
|
|
12
|
-
test("getLevelFilter()", () => {
|
|
13
|
-
const noneFilter = getLevelFilter(null);
|
|
14
|
-
assertFalse(noneFilter(fatal));
|
|
15
|
-
assertFalse(noneFilter(error));
|
|
16
|
-
assertFalse(noneFilter(warning));
|
|
17
|
-
assertFalse(noneFilter(info));
|
|
18
|
-
assertFalse(noneFilter(debug));
|
|
19
|
-
assertFalse(noneFilter(trace));
|
|
20
|
-
|
|
21
|
-
const fatalFilter = getLevelFilter("fatal");
|
|
22
|
-
assert(fatalFilter(fatal));
|
|
23
|
-
assertFalse(fatalFilter(error));
|
|
24
|
-
assertFalse(fatalFilter(warning));
|
|
25
|
-
assertFalse(fatalFilter(info));
|
|
26
|
-
assertFalse(fatalFilter(debug));
|
|
27
|
-
assertFalse(fatalFilter(trace));
|
|
28
|
-
|
|
29
|
-
const errorFilter = getLevelFilter("error");
|
|
30
|
-
assert(errorFilter(fatal));
|
|
31
|
-
assert(errorFilter(error));
|
|
32
|
-
assertFalse(errorFilter(warning));
|
|
33
|
-
assertFalse(errorFilter(info));
|
|
34
|
-
assertFalse(errorFilter(debug));
|
|
35
|
-
assertFalse(errorFilter(trace));
|
|
36
|
-
|
|
37
|
-
const warningFilter = getLevelFilter("warning");
|
|
38
|
-
assert(warningFilter(fatal));
|
|
39
|
-
assert(warningFilter(error));
|
|
40
|
-
assert(warningFilter(warning));
|
|
41
|
-
assertFalse(warningFilter(info));
|
|
42
|
-
assertFalse(warningFilter(debug));
|
|
43
|
-
assertFalse(warningFilter(trace));
|
|
44
|
-
|
|
45
|
-
const infoFilter = getLevelFilter("info");
|
|
46
|
-
assert(infoFilter(fatal));
|
|
47
|
-
assert(infoFilter(error));
|
|
48
|
-
assert(infoFilter(warning));
|
|
49
|
-
assert(infoFilter(info));
|
|
50
|
-
assertFalse(infoFilter(debug));
|
|
51
|
-
assertFalse(infoFilter(trace));
|
|
52
|
-
|
|
53
|
-
const debugFilter = getLevelFilter("debug");
|
|
54
|
-
assert(debugFilter(fatal));
|
|
55
|
-
assert(debugFilter(error));
|
|
56
|
-
assert(debugFilter(warning));
|
|
57
|
-
assert(debugFilter(info));
|
|
58
|
-
assert(debugFilter(debug));
|
|
59
|
-
assertFalse(debugFilter(trace));
|
|
60
|
-
|
|
61
|
-
const traceFilter = getLevelFilter("trace");
|
|
62
|
-
assert(traceFilter(fatal));
|
|
63
|
-
assert(traceFilter(error));
|
|
64
|
-
assert(traceFilter(warning));
|
|
65
|
-
assert(traceFilter(info));
|
|
66
|
-
assert(traceFilter(debug));
|
|
67
|
-
assert(traceFilter(trace));
|
|
68
|
-
|
|
69
|
-
assertThrows(
|
|
70
|
-
() => getLevelFilter("invalid" as LogLevel),
|
|
71
|
-
TypeError,
|
|
72
|
-
"Invalid log level: invalid.",
|
|
73
|
-
);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test("toFilter()", () => {
|
|
77
|
-
const hasJunk: Filter = (record) => record.category.includes("junk");
|
|
78
|
-
assertStrictEquals(toFilter(hasJunk), hasJunk);
|
|
79
|
-
|
|
80
|
-
const infoFilter = toFilter("info");
|
|
81
|
-
assertFalse(infoFilter(debug));
|
|
82
|
-
assert(infoFilter(info));
|
|
83
|
-
assert(infoFilter(warning));
|
|
84
|
-
});
|
package/src/filter.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import type { LogLevel } from "./level.ts";
|
|
2
|
-
import type { LogRecord } from "./record.ts";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* A filter is a function that accepts a log record and returns `true` if the
|
|
6
|
-
* record should be passed to the sink.
|
|
7
|
-
*
|
|
8
|
-
* @param record The log record to filter.
|
|
9
|
-
* @returns `true` if the record should be passed to the sink.
|
|
10
|
-
*/
|
|
11
|
-
export type Filter = (record: LogRecord) => boolean;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* A filter-like value is either a {@link Filter} or a {@link LogLevel}.
|
|
15
|
-
* `null` is also allowed to represent a filter that rejects all records.
|
|
16
|
-
*/
|
|
17
|
-
export type FilterLike = Filter | LogLevel | null;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Converts a {@link FilterLike} value to an actual {@link Filter}.
|
|
21
|
-
*
|
|
22
|
-
* @param filter The filter-like value to convert.
|
|
23
|
-
* @returns The actual filter.
|
|
24
|
-
*/
|
|
25
|
-
export function toFilter(filter: FilterLike): Filter {
|
|
26
|
-
if (typeof filter === "function") return filter;
|
|
27
|
-
return getLevelFilter(filter);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Returns a filter that accepts log records with the specified level.
|
|
32
|
-
*
|
|
33
|
-
* @param level The level to filter by. If `null`, the filter will reject all
|
|
34
|
-
* records.
|
|
35
|
-
* @returns The filter.
|
|
36
|
-
*/
|
|
37
|
-
export function getLevelFilter(level: LogLevel | null): Filter {
|
|
38
|
-
if (level == null) return () => false;
|
|
39
|
-
if (level === "fatal") {
|
|
40
|
-
return (record: LogRecord) => record.level === "fatal";
|
|
41
|
-
} else if (level === "error") {
|
|
42
|
-
return (record: LogRecord) =>
|
|
43
|
-
record.level === "fatal" || record.level === "error";
|
|
44
|
-
} else if (level === "warning") {
|
|
45
|
-
return (record: LogRecord) =>
|
|
46
|
-
record.level === "fatal" ||
|
|
47
|
-
record.level === "error" ||
|
|
48
|
-
record.level === "warning";
|
|
49
|
-
} else if (level === "info") {
|
|
50
|
-
return (record: LogRecord) =>
|
|
51
|
-
record.level === "fatal" ||
|
|
52
|
-
record.level === "error" ||
|
|
53
|
-
record.level === "warning" ||
|
|
54
|
-
record.level === "info";
|
|
55
|
-
} else if (level === "debug") {
|
|
56
|
-
return (record: LogRecord) =>
|
|
57
|
-
record.level === "fatal" ||
|
|
58
|
-
record.level === "error" ||
|
|
59
|
-
record.level === "warning" ||
|
|
60
|
-
record.level === "info" ||
|
|
61
|
-
record.level === "debug";
|
|
62
|
-
} else if (level === "trace") return () => true;
|
|
63
|
-
throw new TypeError(`Invalid log level: ${level}.`);
|
|
64
|
-
}
|
package/src/fixtures.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { LogRecord } from "./record.ts";
|
|
2
|
-
|
|
3
|
-
export const info: LogRecord = {
|
|
4
|
-
level: "info",
|
|
5
|
-
category: ["my-app", "junk"],
|
|
6
|
-
message: ["Hello, ", 123, " & ", 456, "!"],
|
|
7
|
-
rawMessage: "Hello, {a} & {b}!",
|
|
8
|
-
timestamp: 1700000000000,
|
|
9
|
-
properties: {},
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const trace: LogRecord = {
|
|
13
|
-
...info,
|
|
14
|
-
level: "trace",
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export const debug: LogRecord = {
|
|
18
|
-
...info,
|
|
19
|
-
level: "debug",
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export const warning: LogRecord = {
|
|
23
|
-
...info,
|
|
24
|
-
level: "warning",
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export const error: LogRecord = {
|
|
28
|
-
...info,
|
|
29
|
-
level: "error",
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export const fatal: LogRecord = {
|
|
33
|
-
...info,
|
|
34
|
-
level: "fatal",
|
|
35
|
-
};
|