@logtape/fastify 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 +5 -2
- package/deno.json +0 -34
- package/src/mod.test.ts +0 -779
- package/src/mod.ts +0 -252
- package/tsdown.config.ts +0 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logtape/fastify",
|
|
3
|
-
"version": "1.4.0-dev.
|
|
3
|
+
"version": "1.4.0-dev.413+1097da33",
|
|
4
4
|
"description": "Fastify adapter for LogTape logging library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"logging",
|
|
@@ -45,8 +45,11 @@
|
|
|
45
45
|
"./package.json": "./package.json"
|
|
46
46
|
},
|
|
47
47
|
"sideEffects": false,
|
|
48
|
+
"files": [
|
|
49
|
+
"dist/"
|
|
50
|
+
],
|
|
48
51
|
"peerDependencies": {
|
|
49
|
-
"@logtape/logtape": "^1.4.0-dev.
|
|
52
|
+
"@logtape/logtape": "^1.4.0-dev.413+1097da33"
|
|
50
53
|
},
|
|
51
54
|
"devDependencies": {
|
|
52
55
|
"@alinea/suite": "^0.6.3",
|
package/deno.json
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@logtape/fastify",
|
|
3
|
-
"version": "1.4.0-dev.409+63c2cd45",
|
|
4
|
-
"license": "MIT",
|
|
5
|
-
"exports": "./src/mod.ts",
|
|
6
|
-
"exclude": [
|
|
7
|
-
"coverage/",
|
|
8
|
-
"npm/",
|
|
9
|
-
"dist/"
|
|
10
|
-
],
|
|
11
|
-
"tasks": {
|
|
12
|
-
"build": "pnpm build",
|
|
13
|
-
"test": "deno test --allow-env --allow-sys --allow-net",
|
|
14
|
-
"test:node": {
|
|
15
|
-
"dependencies": [
|
|
16
|
-
"build"
|
|
17
|
-
],
|
|
18
|
-
"command": "node --experimental-transform-types --test"
|
|
19
|
-
},
|
|
20
|
-
"test:bun": {
|
|
21
|
-
"dependencies": [
|
|
22
|
-
"build"
|
|
23
|
-
],
|
|
24
|
-
"command": "bun test"
|
|
25
|
-
},
|
|
26
|
-
"test-all": {
|
|
27
|
-
"dependencies": [
|
|
28
|
-
"test",
|
|
29
|
-
"test:node",
|
|
30
|
-
"test:bun"
|
|
31
|
-
]
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
package/src/mod.test.ts
DELETED
|
@@ -1,779 +0,0 @@
|
|
|
1
|
-
import { suite } from "@alinea/suite";
|
|
2
|
-
import { assertEquals } from "@std/assert/equals";
|
|
3
|
-
import { configure, type LogRecord, reset } from "@logtape/logtape";
|
|
4
|
-
import { getLogTapeFastifyLogger } from "./mod.ts";
|
|
5
|
-
|
|
6
|
-
const test = suite(import.meta);
|
|
7
|
-
|
|
8
|
-
// Test fixture: Collect log records, filtering out internal LogTape meta logs
|
|
9
|
-
function createTestSink(): {
|
|
10
|
-
sink: (record: LogRecord) => void;
|
|
11
|
-
logs: LogRecord[];
|
|
12
|
-
} {
|
|
13
|
-
const logs: LogRecord[] = [];
|
|
14
|
-
return {
|
|
15
|
-
// Filter out logtape meta logs automatically
|
|
16
|
-
sink: (record: LogRecord) => {
|
|
17
|
-
if (record.category[0] !== "logtape") {
|
|
18
|
-
logs.push(record);
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
logs,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Setup helper
|
|
26
|
-
async function setupLogtape(): Promise<{
|
|
27
|
-
logs: LogRecord[];
|
|
28
|
-
cleanup: () => Promise<void>;
|
|
29
|
-
}> {
|
|
30
|
-
const { sink, logs } = createTestSink();
|
|
31
|
-
await configure({
|
|
32
|
-
sinks: { test: sink },
|
|
33
|
-
loggers: [{ category: [], sinks: ["test"] }],
|
|
34
|
-
});
|
|
35
|
-
return { logs, cleanup: () => reset() };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// ============================================
|
|
39
|
-
// Basic Logger Creation Tests
|
|
40
|
-
// ============================================
|
|
41
|
-
|
|
42
|
-
test("getLogTapeFastifyLogger(): creates a Pino-like logger", async () => {
|
|
43
|
-
const { cleanup } = await setupLogtape();
|
|
44
|
-
try {
|
|
45
|
-
const logger = getLogTapeFastifyLogger();
|
|
46
|
-
|
|
47
|
-
assertEquals(typeof logger.info, "function");
|
|
48
|
-
assertEquals(typeof logger.error, "function");
|
|
49
|
-
assertEquals(typeof logger.debug, "function");
|
|
50
|
-
assertEquals(typeof logger.warn, "function");
|
|
51
|
-
assertEquals(typeof logger.trace, "function");
|
|
52
|
-
assertEquals(typeof logger.fatal, "function");
|
|
53
|
-
assertEquals(typeof logger.silent, "function");
|
|
54
|
-
assertEquals(typeof logger.child, "function");
|
|
55
|
-
assertEquals(typeof logger.level, "string");
|
|
56
|
-
} finally {
|
|
57
|
-
await cleanup();
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test("getLogTapeFastifyLogger(): uses default category ['fastify']", async () => {
|
|
62
|
-
const { logs, cleanup } = await setupLogtape();
|
|
63
|
-
try {
|
|
64
|
-
const logger = getLogTapeFastifyLogger();
|
|
65
|
-
logger.info("test message");
|
|
66
|
-
|
|
67
|
-
assertEquals(logs.length, 1);
|
|
68
|
-
assertEquals(logs[0].category, ["fastify"]);
|
|
69
|
-
} finally {
|
|
70
|
-
await cleanup();
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test("getLogTapeFastifyLogger(): uses custom category array", async () => {
|
|
75
|
-
const { logs, cleanup } = await setupLogtape();
|
|
76
|
-
try {
|
|
77
|
-
const logger = getLogTapeFastifyLogger({ category: ["myapp", "http"] });
|
|
78
|
-
logger.info("test message");
|
|
79
|
-
|
|
80
|
-
assertEquals(logs.length, 1);
|
|
81
|
-
assertEquals(logs[0].category, ["myapp", "http"]);
|
|
82
|
-
} finally {
|
|
83
|
-
await cleanup();
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("getLogTapeFastifyLogger(): accepts string category", async () => {
|
|
88
|
-
const { logs, cleanup } = await setupLogtape();
|
|
89
|
-
try {
|
|
90
|
-
const logger = getLogTapeFastifyLogger({ category: "myapp" });
|
|
91
|
-
logger.info("test message");
|
|
92
|
-
|
|
93
|
-
assertEquals(logs.length, 1);
|
|
94
|
-
assertEquals(logs[0].category, ["myapp"]);
|
|
95
|
-
} finally {
|
|
96
|
-
await cleanup();
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
// ============================================
|
|
101
|
-
// Log Level Method Tests - String Messages
|
|
102
|
-
// ============================================
|
|
103
|
-
|
|
104
|
-
test("logger.info(): logs at info level with string message", async () => {
|
|
105
|
-
const { logs, cleanup } = await setupLogtape();
|
|
106
|
-
try {
|
|
107
|
-
const logger = getLogTapeFastifyLogger();
|
|
108
|
-
logger.info("Hello world");
|
|
109
|
-
|
|
110
|
-
assertEquals(logs.length, 1);
|
|
111
|
-
assertEquals(logs[0].level, "info");
|
|
112
|
-
assertEquals(logs[0].rawMessage, "Hello world");
|
|
113
|
-
} finally {
|
|
114
|
-
await cleanup();
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test("logger.error(): logs at error level", async () => {
|
|
119
|
-
const { logs, cleanup } = await setupLogtape();
|
|
120
|
-
try {
|
|
121
|
-
const logger = getLogTapeFastifyLogger();
|
|
122
|
-
logger.error("An error occurred");
|
|
123
|
-
|
|
124
|
-
assertEquals(logs.length, 1);
|
|
125
|
-
assertEquals(logs[0].level, "error");
|
|
126
|
-
assertEquals(logs[0].rawMessage, "An error occurred");
|
|
127
|
-
} finally {
|
|
128
|
-
await cleanup();
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
test("logger.debug(): logs at debug level", async () => {
|
|
133
|
-
const { logs, cleanup } = await setupLogtape();
|
|
134
|
-
try {
|
|
135
|
-
const logger = getLogTapeFastifyLogger();
|
|
136
|
-
logger.debug("Debug message");
|
|
137
|
-
|
|
138
|
-
assertEquals(logs.length, 1);
|
|
139
|
-
assertEquals(logs[0].level, "debug");
|
|
140
|
-
assertEquals(logs[0].rawMessage, "Debug message");
|
|
141
|
-
} finally {
|
|
142
|
-
await cleanup();
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
test("logger.warn(): logs at warning level", async () => {
|
|
147
|
-
const { logs, cleanup } = await setupLogtape();
|
|
148
|
-
try {
|
|
149
|
-
const logger = getLogTapeFastifyLogger();
|
|
150
|
-
logger.warn("Warning message");
|
|
151
|
-
|
|
152
|
-
assertEquals(logs.length, 1);
|
|
153
|
-
assertEquals(logs[0].level, "warning");
|
|
154
|
-
assertEquals(logs[0].rawMessage, "Warning message");
|
|
155
|
-
} finally {
|
|
156
|
-
await cleanup();
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
test("logger.trace(): logs at trace level", async () => {
|
|
161
|
-
const { logs, cleanup } = await setupLogtape();
|
|
162
|
-
try {
|
|
163
|
-
const logger = getLogTapeFastifyLogger();
|
|
164
|
-
logger.trace("Trace message");
|
|
165
|
-
|
|
166
|
-
assertEquals(logs.length, 1);
|
|
167
|
-
assertEquals(logs[0].level, "trace");
|
|
168
|
-
assertEquals(logs[0].rawMessage, "Trace message");
|
|
169
|
-
} finally {
|
|
170
|
-
await cleanup();
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
test("logger.fatal(): logs at fatal level", async () => {
|
|
175
|
-
const { logs, cleanup } = await setupLogtape();
|
|
176
|
-
try {
|
|
177
|
-
const logger = getLogTapeFastifyLogger();
|
|
178
|
-
logger.fatal("Fatal error");
|
|
179
|
-
|
|
180
|
-
assertEquals(logs.length, 1);
|
|
181
|
-
assertEquals(logs[0].level, "fatal");
|
|
182
|
-
assertEquals(logs[0].rawMessage, "Fatal error");
|
|
183
|
-
} finally {
|
|
184
|
-
await cleanup();
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
test("logger.silent(): is a no-op", async () => {
|
|
189
|
-
const { logs, cleanup } = await setupLogtape();
|
|
190
|
-
try {
|
|
191
|
-
const logger = getLogTapeFastifyLogger();
|
|
192
|
-
logger.silent();
|
|
193
|
-
|
|
194
|
-
assertEquals(logs.length, 0);
|
|
195
|
-
} finally {
|
|
196
|
-
await cleanup();
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
// ============================================
|
|
201
|
-
// Object + Message Tests
|
|
202
|
-
// ============================================
|
|
203
|
-
|
|
204
|
-
test("logger.info(): logs with object and message", async () => {
|
|
205
|
-
const { logs, cleanup } = await setupLogtape();
|
|
206
|
-
try {
|
|
207
|
-
const logger = getLogTapeFastifyLogger();
|
|
208
|
-
logger.info({ userId: 123, action: "login" }, "User logged in");
|
|
209
|
-
|
|
210
|
-
assertEquals(logs.length, 1);
|
|
211
|
-
assertEquals(logs[0].level, "info");
|
|
212
|
-
assertEquals(logs[0].rawMessage, "User logged in");
|
|
213
|
-
assertEquals(logs[0].properties.userId, 123);
|
|
214
|
-
assertEquals(logs[0].properties.action, "login");
|
|
215
|
-
} finally {
|
|
216
|
-
await cleanup();
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
test("logger.info(): logs object-only with msg property", async () => {
|
|
221
|
-
const { logs, cleanup } = await setupLogtape();
|
|
222
|
-
try {
|
|
223
|
-
const logger = getLogTapeFastifyLogger();
|
|
224
|
-
logger.info({ msg: "Hello", data: 456 });
|
|
225
|
-
|
|
226
|
-
assertEquals(logs.length, 1);
|
|
227
|
-
assertEquals(logs[0].rawMessage, "Hello");
|
|
228
|
-
assertEquals(logs[0].properties.data, 456);
|
|
229
|
-
assertEquals("msg" in logs[0].properties, false);
|
|
230
|
-
} finally {
|
|
231
|
-
await cleanup();
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
test("logger.info(): logs object-only without msg property", async () => {
|
|
236
|
-
const { logs, cleanup } = await setupLogtape();
|
|
237
|
-
try {
|
|
238
|
-
const logger = getLogTapeFastifyLogger();
|
|
239
|
-
logger.info({ key: "value", num: 789 });
|
|
240
|
-
|
|
241
|
-
assertEquals(logs.length, 1);
|
|
242
|
-
assertEquals(logs[0].rawMessage, "{*}");
|
|
243
|
-
assertEquals(logs[0].properties.key, "value");
|
|
244
|
-
assertEquals(logs[0].properties.num, 789);
|
|
245
|
-
} finally {
|
|
246
|
-
await cleanup();
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
// ============================================
|
|
251
|
-
// Printf-style Interpolation Tests
|
|
252
|
-
// ============================================
|
|
253
|
-
|
|
254
|
-
test("logger.info(): supports printf-style %s interpolation", async () => {
|
|
255
|
-
const { logs, cleanup } = await setupLogtape();
|
|
256
|
-
try {
|
|
257
|
-
const logger = getLogTapeFastifyLogger();
|
|
258
|
-
logger.info("Hello %s", "world");
|
|
259
|
-
|
|
260
|
-
assertEquals(logs.length, 1);
|
|
261
|
-
assertEquals(logs[0].rawMessage, "Hello world");
|
|
262
|
-
} finally {
|
|
263
|
-
await cleanup();
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
test("logger.info(): supports printf-style %d interpolation", async () => {
|
|
268
|
-
const { logs, cleanup } = await setupLogtape();
|
|
269
|
-
try {
|
|
270
|
-
const logger = getLogTapeFastifyLogger();
|
|
271
|
-
logger.info("Count: %d", 42);
|
|
272
|
-
|
|
273
|
-
assertEquals(logs.length, 1);
|
|
274
|
-
assertEquals(logs[0].rawMessage, "Count: 42");
|
|
275
|
-
} finally {
|
|
276
|
-
await cleanup();
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
test("logger.info(): supports printf-style %j interpolation", async () => {
|
|
281
|
-
const { logs, cleanup } = await setupLogtape();
|
|
282
|
-
try {
|
|
283
|
-
const logger = getLogTapeFastifyLogger();
|
|
284
|
-
logger.info("Data: %j", { key: "value" });
|
|
285
|
-
|
|
286
|
-
assertEquals(logs.length, 1);
|
|
287
|
-
assertEquals(logs[0].rawMessage, 'Data: {"key":"value"}');
|
|
288
|
-
} finally {
|
|
289
|
-
await cleanup();
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
test("logger.info(): supports printf-style %o interpolation", async () => {
|
|
294
|
-
const { logs, cleanup } = await setupLogtape();
|
|
295
|
-
try {
|
|
296
|
-
const logger = getLogTapeFastifyLogger();
|
|
297
|
-
logger.info("Object: %o", { a: 1 });
|
|
298
|
-
|
|
299
|
-
assertEquals(logs.length, 1);
|
|
300
|
-
assertEquals(logs[0].rawMessage, 'Object: {"a":1}');
|
|
301
|
-
} finally {
|
|
302
|
-
await cleanup();
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
test("logger.info(): supports multiple interpolations", async () => {
|
|
307
|
-
const { logs, cleanup } = await setupLogtape();
|
|
308
|
-
try {
|
|
309
|
-
const logger = getLogTapeFastifyLogger();
|
|
310
|
-
logger.info("User %s performed %s %d times", "alice", "login", 3);
|
|
311
|
-
|
|
312
|
-
assertEquals(logs.length, 1);
|
|
313
|
-
assertEquals(logs[0].rawMessage, "User alice performed login 3 times");
|
|
314
|
-
} finally {
|
|
315
|
-
await cleanup();
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
test("logger.info(): handles escaped %%", async () => {
|
|
320
|
-
const { logs, cleanup } = await setupLogtape();
|
|
321
|
-
try {
|
|
322
|
-
const logger = getLogTapeFastifyLogger();
|
|
323
|
-
logger.info("100%% complete");
|
|
324
|
-
|
|
325
|
-
assertEquals(logs.length, 1);
|
|
326
|
-
assertEquals(logs[0].rawMessage, "100% complete");
|
|
327
|
-
} finally {
|
|
328
|
-
await cleanup();
|
|
329
|
-
}
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
test("logger.info(): handles missing interpolation args", async () => {
|
|
333
|
-
const { logs, cleanup } = await setupLogtape();
|
|
334
|
-
try {
|
|
335
|
-
const logger = getLogTapeFastifyLogger();
|
|
336
|
-
logger.info("Hello %s %s", "world");
|
|
337
|
-
|
|
338
|
-
assertEquals(logs.length, 1);
|
|
339
|
-
assertEquals(logs[0].rawMessage, "Hello world %s");
|
|
340
|
-
} finally {
|
|
341
|
-
await cleanup();
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
// ============================================
|
|
346
|
-
// Child Logger Tests
|
|
347
|
-
// ============================================
|
|
348
|
-
|
|
349
|
-
test("logger.child(): creates a child logger with bindings", async () => {
|
|
350
|
-
const { logs, cleanup } = await setupLogtape();
|
|
351
|
-
try {
|
|
352
|
-
const logger = getLogTapeFastifyLogger();
|
|
353
|
-
const child = logger.child({ reqId: "abc123" });
|
|
354
|
-
|
|
355
|
-
child.info("Request received");
|
|
356
|
-
|
|
357
|
-
assertEquals(logs.length, 1);
|
|
358
|
-
assertEquals(logs[0].properties.reqId, "abc123");
|
|
359
|
-
} finally {
|
|
360
|
-
await cleanup();
|
|
361
|
-
}
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
test("logger.child(): returns a PinoLikeLogger", async () => {
|
|
365
|
-
const { cleanup } = await setupLogtape();
|
|
366
|
-
try {
|
|
367
|
-
const logger = getLogTapeFastifyLogger();
|
|
368
|
-
const child = logger.child({ reqId: "abc123" });
|
|
369
|
-
|
|
370
|
-
assertEquals(typeof child.info, "function");
|
|
371
|
-
assertEquals(typeof child.error, "function");
|
|
372
|
-
assertEquals(typeof child.child, "function");
|
|
373
|
-
assertEquals(typeof child.level, "string");
|
|
374
|
-
} finally {
|
|
375
|
-
await cleanup();
|
|
376
|
-
}
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
test("logger.child(): merges bindings with additional properties", async () => {
|
|
380
|
-
const { logs, cleanup } = await setupLogtape();
|
|
381
|
-
try {
|
|
382
|
-
const logger = getLogTapeFastifyLogger();
|
|
383
|
-
const child = logger.child({ reqId: "abc123" });
|
|
384
|
-
|
|
385
|
-
child.info({ action: "test" }, "Message");
|
|
386
|
-
|
|
387
|
-
assertEquals(logs.length, 1);
|
|
388
|
-
assertEquals(logs[0].properties.reqId, "abc123");
|
|
389
|
-
assertEquals(logs[0].properties.action, "test");
|
|
390
|
-
} finally {
|
|
391
|
-
await cleanup();
|
|
392
|
-
}
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
test("logger.child(): creates nested child loggers", async () => {
|
|
396
|
-
const { logs, cleanup } = await setupLogtape();
|
|
397
|
-
try {
|
|
398
|
-
const logger = getLogTapeFastifyLogger();
|
|
399
|
-
const child1 = logger.child({ reqId: "abc123" });
|
|
400
|
-
const child2 = child1.child({ userId: 456 });
|
|
401
|
-
|
|
402
|
-
child2.info("Nested log");
|
|
403
|
-
|
|
404
|
-
assertEquals(logs.length, 1);
|
|
405
|
-
assertEquals(logs[0].properties.reqId, "abc123");
|
|
406
|
-
assertEquals(logs[0].properties.userId, 456);
|
|
407
|
-
} finally {
|
|
408
|
-
await cleanup();
|
|
409
|
-
}
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
test("logger.child(): child can override parent bindings", async () => {
|
|
413
|
-
const { logs, cleanup } = await setupLogtape();
|
|
414
|
-
try {
|
|
415
|
-
const logger = getLogTapeFastifyLogger();
|
|
416
|
-
const parent = logger.child({ key: "parent" });
|
|
417
|
-
const child = parent.child({ key: "child" });
|
|
418
|
-
|
|
419
|
-
child.info("Test");
|
|
420
|
-
|
|
421
|
-
assertEquals(logs.length, 1);
|
|
422
|
-
assertEquals(logs[0].properties.key, "child");
|
|
423
|
-
} finally {
|
|
424
|
-
await cleanup();
|
|
425
|
-
}
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
test("logger.child(): preserves parent category", async () => {
|
|
429
|
-
const { logs, cleanup } = await setupLogtape();
|
|
430
|
-
try {
|
|
431
|
-
const logger = getLogTapeFastifyLogger({ category: ["myapp"] });
|
|
432
|
-
const child = logger.child({ reqId: "123" });
|
|
433
|
-
|
|
434
|
-
child.info("Test");
|
|
435
|
-
|
|
436
|
-
assertEquals(logs.length, 1);
|
|
437
|
-
assertEquals(logs[0].category, ["myapp"]);
|
|
438
|
-
} finally {
|
|
439
|
-
await cleanup();
|
|
440
|
-
}
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
// ============================================
|
|
444
|
-
// Level Property Tests
|
|
445
|
-
// ============================================
|
|
446
|
-
|
|
447
|
-
test("logger.level: has default level 'info'", async () => {
|
|
448
|
-
const { cleanup } = await setupLogtape();
|
|
449
|
-
try {
|
|
450
|
-
const logger = getLogTapeFastifyLogger();
|
|
451
|
-
assertEquals(logger.level, "info");
|
|
452
|
-
} finally {
|
|
453
|
-
await cleanup();
|
|
454
|
-
}
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
test("logger.level: accepts custom initial level", async () => {
|
|
458
|
-
const { cleanup } = await setupLogtape();
|
|
459
|
-
try {
|
|
460
|
-
const logger = getLogTapeFastifyLogger({ level: "debug" });
|
|
461
|
-
assertEquals(logger.level, "debug");
|
|
462
|
-
} finally {
|
|
463
|
-
await cleanup();
|
|
464
|
-
}
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
test("logger.level: is writable", async () => {
|
|
468
|
-
const { cleanup } = await setupLogtape();
|
|
469
|
-
try {
|
|
470
|
-
const logger = getLogTapeFastifyLogger();
|
|
471
|
-
logger.level = "error";
|
|
472
|
-
assertEquals(logger.level, "error");
|
|
473
|
-
} finally {
|
|
474
|
-
await cleanup();
|
|
475
|
-
}
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
test("logger.level: child inherits parent level", async () => {
|
|
479
|
-
const { cleanup } = await setupLogtape();
|
|
480
|
-
try {
|
|
481
|
-
const logger = getLogTapeFastifyLogger({ level: "debug" });
|
|
482
|
-
const child = logger.child({ reqId: "123" });
|
|
483
|
-
assertEquals(child.level, "debug");
|
|
484
|
-
} finally {
|
|
485
|
-
await cleanup();
|
|
486
|
-
}
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
// ============================================
|
|
490
|
-
// Fastify-specific Request/Response Object Tests
|
|
491
|
-
// ============================================
|
|
492
|
-
|
|
493
|
-
test("logger handles serialized request object from Fastify", async () => {
|
|
494
|
-
const { logs, cleanup } = await setupLogtape();
|
|
495
|
-
try {
|
|
496
|
-
const logger = getLogTapeFastifyLogger();
|
|
497
|
-
const serializedReq = {
|
|
498
|
-
method: "GET",
|
|
499
|
-
url: "/api/users",
|
|
500
|
-
hostname: "localhost",
|
|
501
|
-
remoteAddress: "127.0.0.1",
|
|
502
|
-
};
|
|
503
|
-
|
|
504
|
-
logger.info({ req: serializedReq }, "incoming request");
|
|
505
|
-
|
|
506
|
-
assertEquals(logs.length, 1);
|
|
507
|
-
assertEquals(logs[0].properties.req, serializedReq);
|
|
508
|
-
assertEquals(logs[0].rawMessage, "incoming request");
|
|
509
|
-
} finally {
|
|
510
|
-
await cleanup();
|
|
511
|
-
}
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
test("logger handles serialized response object from Fastify", async () => {
|
|
515
|
-
const { logs, cleanup } = await setupLogtape();
|
|
516
|
-
try {
|
|
517
|
-
const logger = getLogTapeFastifyLogger();
|
|
518
|
-
const serializedRes = {
|
|
519
|
-
statusCode: 200,
|
|
520
|
-
};
|
|
521
|
-
|
|
522
|
-
logger.info({ res: serializedRes, responseTime: 42 }, "request completed");
|
|
523
|
-
|
|
524
|
-
assertEquals(logs.length, 1);
|
|
525
|
-
assertEquals(logs[0].properties.res, serializedRes);
|
|
526
|
-
assertEquals(logs[0].properties.responseTime, 42);
|
|
527
|
-
assertEquals(logs[0].rawMessage, "request completed");
|
|
528
|
-
} finally {
|
|
529
|
-
await cleanup();
|
|
530
|
-
}
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
// ============================================
|
|
534
|
-
// Error Object Tests
|
|
535
|
-
// ============================================
|
|
536
|
-
|
|
537
|
-
test("logger.error(): handles error objects in properties", async () => {
|
|
538
|
-
const { logs, cleanup } = await setupLogtape();
|
|
539
|
-
try {
|
|
540
|
-
const logger = getLogTapeFastifyLogger();
|
|
541
|
-
const error = new Error("Something went wrong");
|
|
542
|
-
|
|
543
|
-
logger.error({ err: error }, "An error occurred");
|
|
544
|
-
|
|
545
|
-
assertEquals(logs.length, 1);
|
|
546
|
-
assertEquals(logs[0].level, "error");
|
|
547
|
-
assertEquals(logs[0].properties.err, error);
|
|
548
|
-
assertEquals(logs[0].rawMessage, "An error occurred");
|
|
549
|
-
} finally {
|
|
550
|
-
await cleanup();
|
|
551
|
-
}
|
|
552
|
-
});
|
|
553
|
-
|
|
554
|
-
// ============================================
|
|
555
|
-
// Edge Cases
|
|
556
|
-
// ============================================
|
|
557
|
-
|
|
558
|
-
test("logger handles empty message", async () => {
|
|
559
|
-
const { logs, cleanup } = await setupLogtape();
|
|
560
|
-
try {
|
|
561
|
-
const logger = getLogTapeFastifyLogger();
|
|
562
|
-
logger.info("");
|
|
563
|
-
|
|
564
|
-
assertEquals(logs.length, 1);
|
|
565
|
-
assertEquals(logs[0].rawMessage, "");
|
|
566
|
-
} finally {
|
|
567
|
-
await cleanup();
|
|
568
|
-
}
|
|
569
|
-
});
|
|
570
|
-
|
|
571
|
-
test("logger handles empty object", async () => {
|
|
572
|
-
const { logs, cleanup } = await setupLogtape();
|
|
573
|
-
try {
|
|
574
|
-
const logger = getLogTapeFastifyLogger();
|
|
575
|
-
logger.info({});
|
|
576
|
-
|
|
577
|
-
assertEquals(logs.length, 1);
|
|
578
|
-
assertEquals(logs[0].rawMessage, "{*}");
|
|
579
|
-
} finally {
|
|
580
|
-
await cleanup();
|
|
581
|
-
}
|
|
582
|
-
});
|
|
583
|
-
|
|
584
|
-
test("logger handles null-ish values in interpolation", async () => {
|
|
585
|
-
const { logs, cleanup } = await setupLogtape();
|
|
586
|
-
try {
|
|
587
|
-
const logger = getLogTapeFastifyLogger();
|
|
588
|
-
logger.info("Value: %s", null);
|
|
589
|
-
|
|
590
|
-
assertEquals(logs.length, 1);
|
|
591
|
-
assertEquals(logs[0].rawMessage, "Value: null");
|
|
592
|
-
} finally {
|
|
593
|
-
await cleanup();
|
|
594
|
-
}
|
|
595
|
-
});
|
|
596
|
-
|
|
597
|
-
test("logger handles undefined in interpolation", async () => {
|
|
598
|
-
const { logs, cleanup } = await setupLogtape();
|
|
599
|
-
try {
|
|
600
|
-
const logger = getLogTapeFastifyLogger();
|
|
601
|
-
logger.info("Value: %s", undefined);
|
|
602
|
-
|
|
603
|
-
assertEquals(logs.length, 1);
|
|
604
|
-
assertEquals(logs[0].rawMessage, "Value: undefined");
|
|
605
|
-
} finally {
|
|
606
|
-
await cleanup();
|
|
607
|
-
}
|
|
608
|
-
});
|
|
609
|
-
|
|
610
|
-
// ============================================
|
|
611
|
-
// Integration Tests with Fastify
|
|
612
|
-
// ============================================
|
|
613
|
-
|
|
614
|
-
// Note: Fastify uses internal timers, so we need to disable sanitizers for integration tests
|
|
615
|
-
const sanitizerOptions = {
|
|
616
|
-
sanitizeOps: false,
|
|
617
|
-
sanitizeResources: false,
|
|
618
|
-
};
|
|
619
|
-
|
|
620
|
-
test(
|
|
621
|
-
"integration: Fastify server logs requests",
|
|
622
|
-
sanitizerOptions,
|
|
623
|
-
async () => {
|
|
624
|
-
const { default: Fastify } = await import("fastify");
|
|
625
|
-
const { logs, cleanup } = await setupLogtape();
|
|
626
|
-
try {
|
|
627
|
-
const fastify = Fastify({
|
|
628
|
-
loggerInstance: getLogTapeFastifyLogger({
|
|
629
|
-
category: ["fastify", "test"],
|
|
630
|
-
}),
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
fastify.get("/", (_request, _reply) => {
|
|
634
|
-
return { hello: "world" };
|
|
635
|
-
});
|
|
636
|
-
|
|
637
|
-
await fastify.ready();
|
|
638
|
-
|
|
639
|
-
// Make a request using inject (doesn't require actual server)
|
|
640
|
-
const response = await fastify.inject({
|
|
641
|
-
method: "GET",
|
|
642
|
-
url: "/",
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
assertEquals(response.statusCode, 200);
|
|
646
|
-
assertEquals(JSON.parse(response.body), { hello: "world" });
|
|
647
|
-
|
|
648
|
-
// Fastify should have logged server ready message
|
|
649
|
-
const serverLogs = logs.filter((log) =>
|
|
650
|
-
log.category[0] === "fastify" && log.category[1] === "test"
|
|
651
|
-
);
|
|
652
|
-
assertEquals(serverLogs.length > 0, true);
|
|
653
|
-
|
|
654
|
-
await fastify.close();
|
|
655
|
-
} finally {
|
|
656
|
-
await cleanup();
|
|
657
|
-
}
|
|
658
|
-
},
|
|
659
|
-
);
|
|
660
|
-
|
|
661
|
-
test(
|
|
662
|
-
"integration: Fastify request.log creates child logger",
|
|
663
|
-
sanitizerOptions,
|
|
664
|
-
async () => {
|
|
665
|
-
const { default: Fastify } = await import("fastify");
|
|
666
|
-
const { logs, cleanup } = await setupLogtape();
|
|
667
|
-
try {
|
|
668
|
-
const fastify = Fastify({
|
|
669
|
-
loggerInstance: getLogTapeFastifyLogger({ category: ["fastify"] }),
|
|
670
|
-
});
|
|
671
|
-
|
|
672
|
-
fastify.get("/test", (request, _reply) => {
|
|
673
|
-
request.log.info({ customProp: "test-value" }, "Request handler log");
|
|
674
|
-
return { ok: true };
|
|
675
|
-
});
|
|
676
|
-
|
|
677
|
-
await fastify.ready();
|
|
678
|
-
|
|
679
|
-
const response = await fastify.inject({
|
|
680
|
-
method: "GET",
|
|
681
|
-
url: "/test",
|
|
682
|
-
});
|
|
683
|
-
|
|
684
|
-
assertEquals(response.statusCode, 200);
|
|
685
|
-
|
|
686
|
-
// Find the log from our handler
|
|
687
|
-
const handlerLog = logs.find((log) =>
|
|
688
|
-
log.rawMessage === "Request handler log"
|
|
689
|
-
);
|
|
690
|
-
assertEquals(handlerLog !== undefined, true);
|
|
691
|
-
assertEquals(handlerLog?.properties.customProp, "test-value");
|
|
692
|
-
// Fastify adds reqId to child logger bindings
|
|
693
|
-
assertEquals("reqId" in (handlerLog?.properties ?? {}), true);
|
|
694
|
-
|
|
695
|
-
await fastify.close();
|
|
696
|
-
} finally {
|
|
697
|
-
await cleanup();
|
|
698
|
-
}
|
|
699
|
-
},
|
|
700
|
-
);
|
|
701
|
-
|
|
702
|
-
test(
|
|
703
|
-
"integration: Fastify logs at different levels",
|
|
704
|
-
sanitizerOptions,
|
|
705
|
-
async () => {
|
|
706
|
-
const { default: Fastify } = await import("fastify");
|
|
707
|
-
const { logs, cleanup } = await setupLogtape();
|
|
708
|
-
try {
|
|
709
|
-
const fastify = Fastify({
|
|
710
|
-
loggerInstance: getLogTapeFastifyLogger(),
|
|
711
|
-
});
|
|
712
|
-
|
|
713
|
-
fastify.get("/levels", (request, _reply) => {
|
|
714
|
-
request.log.debug("Debug message");
|
|
715
|
-
request.log.info("Info message");
|
|
716
|
-
request.log.warn("Warn message");
|
|
717
|
-
request.log.error("Error message");
|
|
718
|
-
return { ok: true };
|
|
719
|
-
});
|
|
720
|
-
|
|
721
|
-
await fastify.ready();
|
|
722
|
-
|
|
723
|
-
await fastify.inject({
|
|
724
|
-
method: "GET",
|
|
725
|
-
url: "/levels",
|
|
726
|
-
});
|
|
727
|
-
|
|
728
|
-
const debugLog = logs.find((log) => log.rawMessage === "Debug message");
|
|
729
|
-
const infoLog = logs.find((log) => log.rawMessage === "Info message");
|
|
730
|
-
const warnLog = logs.find((log) => log.rawMessage === "Warn message");
|
|
731
|
-
const errorLog = logs.find((log) => log.rawMessage === "Error message");
|
|
732
|
-
|
|
733
|
-
assertEquals(debugLog?.level, "debug");
|
|
734
|
-
assertEquals(infoLog?.level, "info");
|
|
735
|
-
assertEquals(warnLog?.level, "warning");
|
|
736
|
-
assertEquals(errorLog?.level, "error");
|
|
737
|
-
|
|
738
|
-
await fastify.close();
|
|
739
|
-
} finally {
|
|
740
|
-
await cleanup();
|
|
741
|
-
}
|
|
742
|
-
},
|
|
743
|
-
);
|
|
744
|
-
|
|
745
|
-
test(
|
|
746
|
-
"integration: Fastify logs with object properties",
|
|
747
|
-
sanitizerOptions,
|
|
748
|
-
async () => {
|
|
749
|
-
const { default: Fastify } = await import("fastify");
|
|
750
|
-
const { logs, cleanup } = await setupLogtape();
|
|
751
|
-
try {
|
|
752
|
-
const fastify = Fastify({
|
|
753
|
-
loggerInstance: getLogTapeFastifyLogger(),
|
|
754
|
-
});
|
|
755
|
-
|
|
756
|
-
fastify.get("/props", (request, _reply) => {
|
|
757
|
-
request.log.info({ userId: 123, action: "test" }, "Action performed");
|
|
758
|
-
return { ok: true };
|
|
759
|
-
});
|
|
760
|
-
|
|
761
|
-
await fastify.ready();
|
|
762
|
-
|
|
763
|
-
await fastify.inject({
|
|
764
|
-
method: "GET",
|
|
765
|
-
url: "/props",
|
|
766
|
-
});
|
|
767
|
-
|
|
768
|
-
const actionLog = logs.find((log) =>
|
|
769
|
-
log.rawMessage === "Action performed"
|
|
770
|
-
);
|
|
771
|
-
assertEquals(actionLog?.properties.userId, 123);
|
|
772
|
-
assertEquals(actionLog?.properties.action, "test");
|
|
773
|
-
|
|
774
|
-
await fastify.close();
|
|
775
|
-
} finally {
|
|
776
|
-
await cleanup();
|
|
777
|
-
}
|
|
778
|
-
},
|
|
779
|
-
);
|
package/src/mod.ts
DELETED
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import { getLogger, type Logger } from "@logtape/logtape";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Pino log levels as strings.
|
|
5
|
-
* @since 1.3.0
|
|
6
|
-
*/
|
|
7
|
-
export type PinoLevel =
|
|
8
|
-
| "trace"
|
|
9
|
-
| "debug"
|
|
10
|
-
| "info"
|
|
11
|
-
| "warn"
|
|
12
|
-
| "error"
|
|
13
|
-
| "fatal"
|
|
14
|
-
| "silent";
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Options for configuring the Fastify LogTape logger.
|
|
18
|
-
* @since 1.3.0
|
|
19
|
-
*/
|
|
20
|
-
export interface FastifyLogTapeOptions {
|
|
21
|
-
/**
|
|
22
|
-
* The LogTape category to use for logging.
|
|
23
|
-
* @default ["fastify"]
|
|
24
|
-
*/
|
|
25
|
-
readonly category?: string | readonly string[];
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* The initial log level. This is tracked internally to satisfy
|
|
29
|
-
* Pino's level property requirement.
|
|
30
|
-
* Note: Actual filtering is controlled by LogTape configuration.
|
|
31
|
-
* @default "info"
|
|
32
|
-
*/
|
|
33
|
-
readonly level?: PinoLevel;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Pino-style log method supporting multiple signatures.
|
|
38
|
-
*
|
|
39
|
-
* Supports the following calling conventions:
|
|
40
|
-
* - `logger.info("message")` - Simple message
|
|
41
|
-
* - `logger.info("message %s", arg)` - Printf-style interpolation
|
|
42
|
-
* - `logger.info({ key: "value" }, "message")` - Object with message
|
|
43
|
-
* - `logger.info({ key: "value" })` - Object only
|
|
44
|
-
* - `logger.info({ msg: "message", key: "value" })` - Object with msg property
|
|
45
|
-
*
|
|
46
|
-
* @since 1.3.0
|
|
47
|
-
*/
|
|
48
|
-
export interface PinoLogMethod {
|
|
49
|
-
/** Log with object and optional message */
|
|
50
|
-
(obj: Record<string, unknown>, msg?: string, ...args: unknown[]): void;
|
|
51
|
-
/** Log with just a message and optional interpolation args */
|
|
52
|
-
(msg: string, ...args: unknown[]): void;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* A Pino-compatible logger interface that wraps LogTape.
|
|
57
|
-
* This interface satisfies Fastify's `loggerInstance` requirements.
|
|
58
|
-
*
|
|
59
|
-
* @example
|
|
60
|
-
* ```typescript
|
|
61
|
-
* import Fastify from "fastify";
|
|
62
|
-
* import { getLogTapeFastifyLogger } from "@logtape/fastify";
|
|
63
|
-
*
|
|
64
|
-
* const fastify = Fastify({
|
|
65
|
-
* loggerInstance: getLogTapeFastifyLogger(),
|
|
66
|
-
* });
|
|
67
|
-
* ```
|
|
68
|
-
*
|
|
69
|
-
* @since 1.3.0
|
|
70
|
-
*/
|
|
71
|
-
export interface PinoLikeLogger {
|
|
72
|
-
/** Log at trace level */
|
|
73
|
-
trace: PinoLogMethod;
|
|
74
|
-
/** Log at debug level */
|
|
75
|
-
debug: PinoLogMethod;
|
|
76
|
-
/** Log at info level */
|
|
77
|
-
info: PinoLogMethod;
|
|
78
|
-
/** Log at warn level */
|
|
79
|
-
warn: PinoLogMethod;
|
|
80
|
-
/** Log at error level */
|
|
81
|
-
error: PinoLogMethod;
|
|
82
|
-
/** Log at fatal level */
|
|
83
|
-
fatal: PinoLogMethod;
|
|
84
|
-
/** No-op silent method for Pino compatibility */
|
|
85
|
-
silent: () => void;
|
|
86
|
-
/** Create a child logger with additional bindings */
|
|
87
|
-
child: (bindings: Record<string, unknown>) => PinoLikeLogger;
|
|
88
|
-
/** Current log level (readable/writable for Pino compatibility) */
|
|
89
|
-
level: string;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Creates a Pino-compatible logger that wraps LogTape.
|
|
94
|
-
* This logger can be used as Fastify's `loggerInstance`.
|
|
95
|
-
*
|
|
96
|
-
* @example Basic usage
|
|
97
|
-
* ```typescript
|
|
98
|
-
* import Fastify from "fastify";
|
|
99
|
-
* import { configure } from "@logtape/logtape";
|
|
100
|
-
* import { getLogTapeFastifyLogger } from "@logtape/fastify";
|
|
101
|
-
*
|
|
102
|
-
* await configure({
|
|
103
|
-
* // ... LogTape configuration
|
|
104
|
-
* });
|
|
105
|
-
*
|
|
106
|
-
* const fastify = Fastify({
|
|
107
|
-
* loggerInstance: getLogTapeFastifyLogger(),
|
|
108
|
-
* });
|
|
109
|
-
* ```
|
|
110
|
-
*
|
|
111
|
-
* @example With custom category
|
|
112
|
-
* ```typescript
|
|
113
|
-
* const fastify = Fastify({
|
|
114
|
-
* loggerInstance: getLogTapeFastifyLogger({
|
|
115
|
-
* category: ["myapp", "http"],
|
|
116
|
-
* }),
|
|
117
|
-
* });
|
|
118
|
-
* ```
|
|
119
|
-
*
|
|
120
|
-
* @param options Configuration options for the logger.
|
|
121
|
-
* @returns A Pino-compatible logger wrapping LogTape.
|
|
122
|
-
* @since 1.3.0
|
|
123
|
-
*/
|
|
124
|
-
export function getLogTapeFastifyLogger(
|
|
125
|
-
options: FastifyLogTapeOptions = {},
|
|
126
|
-
): PinoLikeLogger {
|
|
127
|
-
const category = normalizeCategory(options.category ?? ["fastify"]);
|
|
128
|
-
const logger = getLogger(category);
|
|
129
|
-
const initialLevel = options.level ?? "info";
|
|
130
|
-
|
|
131
|
-
return createPinoLikeLogger(logger, initialLevel);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Normalize category to array format.
|
|
136
|
-
*/
|
|
137
|
-
function normalizeCategory(
|
|
138
|
-
category: string | readonly string[],
|
|
139
|
-
): readonly string[] {
|
|
140
|
-
return typeof category === "string" ? [category] : category;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Format message with printf-style interpolation.
|
|
145
|
-
* Supports: %s (string), %d (number), %j (JSON), %o/%O (object), %% (escaped %)
|
|
146
|
-
*/
|
|
147
|
-
function formatMessage(template: string, ...args: unknown[]): string {
|
|
148
|
-
let argIndex = 0;
|
|
149
|
-
return template.replace(/%[sdjoO%]/g, (match) => {
|
|
150
|
-
if (match === "%%") return "%";
|
|
151
|
-
if (argIndex >= args.length) return match;
|
|
152
|
-
|
|
153
|
-
const arg = args[argIndex++];
|
|
154
|
-
switch (match) {
|
|
155
|
-
case "%s":
|
|
156
|
-
return String(arg);
|
|
157
|
-
case "%d":
|
|
158
|
-
return Number(arg).toString();
|
|
159
|
-
case "%j":
|
|
160
|
-
case "%o":
|
|
161
|
-
case "%O":
|
|
162
|
-
return JSON.stringify(arg);
|
|
163
|
-
default:
|
|
164
|
-
return match;
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Creates a Pino-like logger wrapper around a LogTape logger.
|
|
171
|
-
*/
|
|
172
|
-
function createPinoLikeLogger(
|
|
173
|
-
logger: Logger,
|
|
174
|
-
initialLevel: PinoLevel,
|
|
175
|
-
bindings: Record<string, unknown> = {},
|
|
176
|
-
): PinoLikeLogger {
|
|
177
|
-
// Track level internally for Pino compatibility
|
|
178
|
-
let _level: string = initialLevel;
|
|
179
|
-
|
|
180
|
-
// If there are bindings, create a contextual logger
|
|
181
|
-
const contextLogger = Object.keys(bindings).length > 0
|
|
182
|
-
? logger.with(bindings)
|
|
183
|
-
: logger;
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Create a log method for a specific level.
|
|
187
|
-
*/
|
|
188
|
-
function createLogMethod(
|
|
189
|
-
logFn: (msg: string, props?: Record<string, unknown>) => void,
|
|
190
|
-
): PinoLogMethod {
|
|
191
|
-
return function pinoLogMethod(
|
|
192
|
-
objOrMsg: Record<string, unknown> | string,
|
|
193
|
-
...restArgs: unknown[]
|
|
194
|
-
): void {
|
|
195
|
-
// Detect calling convention
|
|
196
|
-
if (typeof objOrMsg === "string") {
|
|
197
|
-
// Called as: logger.info("message") or logger.info("message %s", arg)
|
|
198
|
-
const message = formatMessage(objOrMsg, ...restArgs);
|
|
199
|
-
logFn(message);
|
|
200
|
-
} else if (typeof objOrMsg === "object" && objOrMsg !== null) {
|
|
201
|
-
// Called as: logger.info({ key: value }, "message") or logger.info({ key: value })
|
|
202
|
-
const properties = { ...objOrMsg };
|
|
203
|
-
const [msgOrArg, ...args] = restArgs;
|
|
204
|
-
|
|
205
|
-
if (typeof msgOrArg === "string") {
|
|
206
|
-
// Has message string: logger.info({ foo: 1 }, "message %s", arg)
|
|
207
|
-
const message = formatMessage(msgOrArg, ...args);
|
|
208
|
-
logFn(message, properties);
|
|
209
|
-
} else if ("msg" in properties && typeof properties.msg === "string") {
|
|
210
|
-
// Extract message from object: logger.info({ msg: "hello", foo: 1 })
|
|
211
|
-
const message = properties.msg as string;
|
|
212
|
-
delete properties.msg;
|
|
213
|
-
logFn(message, properties);
|
|
214
|
-
} else {
|
|
215
|
-
// Object-only logging: logger.info({ foo: 1 })
|
|
216
|
-
logFn("{*}", properties);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
} as PinoLogMethod;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const pinoLogger: PinoLikeLogger = {
|
|
223
|
-
trace: createLogMethod((msg, props) => contextLogger.trace(msg, props)),
|
|
224
|
-
debug: createLogMethod((msg, props) => contextLogger.debug(msg, props)),
|
|
225
|
-
info: createLogMethod((msg, props) => contextLogger.info(msg, props)),
|
|
226
|
-
warn: createLogMethod((msg, props) => contextLogger.warn(msg, props)),
|
|
227
|
-
error: createLogMethod((msg, props) => contextLogger.error(msg, props)),
|
|
228
|
-
fatal: createLogMethod((msg, props) => contextLogger.fatal(msg, props)),
|
|
229
|
-
|
|
230
|
-
silent: () => {
|
|
231
|
-
// No-op for silent level
|
|
232
|
-
},
|
|
233
|
-
|
|
234
|
-
child: (childBindings: Record<string, unknown>) => {
|
|
235
|
-
// Merge parent bindings with child bindings
|
|
236
|
-
const mergedBindings = { ...bindings, ...childBindings };
|
|
237
|
-
return createPinoLikeLogger(logger, _level as PinoLevel, mergedBindings);
|
|
238
|
-
},
|
|
239
|
-
|
|
240
|
-
get level() {
|
|
241
|
-
return _level;
|
|
242
|
-
},
|
|
243
|
-
|
|
244
|
-
set level(newLevel: string) {
|
|
245
|
-
// Store level for Pino compatibility
|
|
246
|
-
// Note: Actual filtering is handled by LogTape configuration
|
|
247
|
-
_level = newLevel;
|
|
248
|
-
},
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
return pinoLogger;
|
|
252
|
-
}
|