@logtape/adaptor-pino 1.3.1 → 1.3.2
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 -37
- package/src/mod.test.ts +0 -405
- package/src/mod.ts +0 -250
- package/tsdown.config.ts +0 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logtape/adaptor-pino",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "Pino adapter for LogTape logging library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"logging",
|
|
@@ -45,9 +45,12 @@
|
|
|
45
45
|
"./package.json": "./package.json"
|
|
46
46
|
},
|
|
47
47
|
"sideEffects": false,
|
|
48
|
+
"files": [
|
|
49
|
+
"dist/"
|
|
50
|
+
],
|
|
48
51
|
"peerDependencies": {
|
|
49
52
|
"pino": "^9.7.0",
|
|
50
|
-
"@logtape/logtape": "^1.3.
|
|
53
|
+
"@logtape/logtape": "^1.3.2"
|
|
51
54
|
},
|
|
52
55
|
"devDependencies": {
|
|
53
56
|
"@alinea/suite": "^0.6.3",
|
package/deno.json
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@logtape/adaptor-pino",
|
|
3
|
-
"version": "1.3.1",
|
|
4
|
-
"license": "MIT",
|
|
5
|
-
"exports": "./src/mod.ts",
|
|
6
|
-
"imports": {
|
|
7
|
-
"pino-abstract-transport": "npm:pino-abstract-transport@^2.0.0"
|
|
8
|
-
},
|
|
9
|
-
"exclude": [
|
|
10
|
-
"coverage/",
|
|
11
|
-
"npm/",
|
|
12
|
-
".dnt-import-map.json"
|
|
13
|
-
],
|
|
14
|
-
"tasks": {
|
|
15
|
-
"build": "pnpm build",
|
|
16
|
-
"test": "deno test --allow-env --allow-sys",
|
|
17
|
-
"test:node": {
|
|
18
|
-
"dependencies": [
|
|
19
|
-
"build"
|
|
20
|
-
],
|
|
21
|
-
"command": "node --experimental-transform-types --test"
|
|
22
|
-
},
|
|
23
|
-
"test:bun": {
|
|
24
|
-
"dependencies": [
|
|
25
|
-
"build"
|
|
26
|
-
],
|
|
27
|
-
"command": "bun test"
|
|
28
|
-
},
|
|
29
|
-
"test-all": {
|
|
30
|
-
"dependencies": [
|
|
31
|
-
"test",
|
|
32
|
-
"test:node",
|
|
33
|
-
"test:bun"
|
|
34
|
-
]
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
package/src/mod.test.ts
DELETED
|
@@ -1,405 +0,0 @@
|
|
|
1
|
-
import { suite } from "@alinea/suite";
|
|
2
|
-
import { assertEquals } from "@std/assert/equals";
|
|
3
|
-
import { assertGreaterOrEqual } from "@std/assert/greater-or-equal";
|
|
4
|
-
import { assertLessOrEqual } from "@std/assert/less-or-equal";
|
|
5
|
-
import { delay } from "@std/async/delay";
|
|
6
|
-
import os from "node:os";
|
|
7
|
-
import process from "node:process";
|
|
8
|
-
import { pino } from "pino";
|
|
9
|
-
import build from "pino-abstract-transport";
|
|
10
|
-
import { getPinoSink } from "./mod.ts";
|
|
11
|
-
|
|
12
|
-
const test = suite(import.meta);
|
|
13
|
-
|
|
14
|
-
interface PinoLog {
|
|
15
|
-
level: number;
|
|
16
|
-
time: number;
|
|
17
|
-
pid: number;
|
|
18
|
-
hostname: string;
|
|
19
|
-
value: Record<string, unknown>;
|
|
20
|
-
msg: string;
|
|
21
|
-
[key: string]: unknown; // Allow additional properties from LogTape properties
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
test("getPinoSink(): basic scenario", async () => {
|
|
25
|
-
const buffer: PinoLog[] = [];
|
|
26
|
-
const dest = build(async (source) => {
|
|
27
|
-
for await (const obj of source) {
|
|
28
|
-
buffer.push(obj);
|
|
29
|
-
}
|
|
30
|
-
}, {});
|
|
31
|
-
const logger = pino({
|
|
32
|
-
useOnlyCustomLevels: false,
|
|
33
|
-
}, dest);
|
|
34
|
-
const sink = getPinoSink(logger);
|
|
35
|
-
const before = Date.now();
|
|
36
|
-
sink({
|
|
37
|
-
category: ["test", "category"],
|
|
38
|
-
level: "info",
|
|
39
|
-
message: ["Test log: ", { foo: 123 }, ""],
|
|
40
|
-
properties: { value: { foo: 123 } },
|
|
41
|
-
rawMessage: "Test log: {value}",
|
|
42
|
-
timestamp: Date.now(),
|
|
43
|
-
});
|
|
44
|
-
const after = Date.now();
|
|
45
|
-
logger.flush();
|
|
46
|
-
await delay(500);
|
|
47
|
-
assertEquals(buffer, [
|
|
48
|
-
{
|
|
49
|
-
level: 30,
|
|
50
|
-
time: buffer[0]?.time,
|
|
51
|
-
pid: process.pid,
|
|
52
|
-
hostname: os.hostname(),
|
|
53
|
-
value: { foo: 123 },
|
|
54
|
-
msg: 'Test log: [{"foo":123}]',
|
|
55
|
-
},
|
|
56
|
-
]);
|
|
57
|
-
assertGreaterOrEqual(buffer[0].time, before);
|
|
58
|
-
assertLessOrEqual(buffer[0].time, after);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test("getPinoSink(): log level mappings", async () => {
|
|
62
|
-
const buffer: PinoLog[] = [];
|
|
63
|
-
const dest = build(async (source) => {
|
|
64
|
-
for await (const obj of source) {
|
|
65
|
-
buffer.push(obj);
|
|
66
|
-
}
|
|
67
|
-
}, {});
|
|
68
|
-
const logger = pino({
|
|
69
|
-
useOnlyCustomLevels: false,
|
|
70
|
-
}, dest);
|
|
71
|
-
const sink = getPinoSink(logger);
|
|
72
|
-
|
|
73
|
-
const logLevels = [
|
|
74
|
-
{ logTapeLevel: "info", expectedPinoLevel: 30 },
|
|
75
|
-
{ logTapeLevel: "warning", expectedPinoLevel: 40 },
|
|
76
|
-
{ logTapeLevel: "error", expectedPinoLevel: 50 },
|
|
77
|
-
{ logTapeLevel: "fatal", expectedPinoLevel: 60 },
|
|
78
|
-
] as const;
|
|
79
|
-
|
|
80
|
-
for (const { logTapeLevel } of logLevels) {
|
|
81
|
-
sink({
|
|
82
|
-
category: ["test"],
|
|
83
|
-
level: logTapeLevel,
|
|
84
|
-
message: [`${logTapeLevel} message`],
|
|
85
|
-
properties: {},
|
|
86
|
-
rawMessage: `${logTapeLevel} message`,
|
|
87
|
-
timestamp: Date.now(),
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
logger.flush();
|
|
92
|
-
await delay(100);
|
|
93
|
-
|
|
94
|
-
assertEquals(buffer.length, 4);
|
|
95
|
-
for (let i = 0; i < logLevels.length; i++) {
|
|
96
|
-
assertEquals(
|
|
97
|
-
buffer[i].level,
|
|
98
|
-
logLevels[i].expectedPinoLevel,
|
|
99
|
-
`Level mapping failed for ${logLevels[i].logTapeLevel}`,
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test("getPinoSink(): category option - false/undefined", async () => {
|
|
105
|
-
const buffer: PinoLog[] = [];
|
|
106
|
-
const dest = build(async (source) => {
|
|
107
|
-
for await (const obj of source) {
|
|
108
|
-
buffer.push(obj);
|
|
109
|
-
}
|
|
110
|
-
}, {});
|
|
111
|
-
const logger = pino({
|
|
112
|
-
useOnlyCustomLevels: false,
|
|
113
|
-
}, dest);
|
|
114
|
-
|
|
115
|
-
// Test with category: false
|
|
116
|
-
const sinkFalse = getPinoSink(logger, { category: false });
|
|
117
|
-
sinkFalse({
|
|
118
|
-
category: ["test", "category"],
|
|
119
|
-
level: "info",
|
|
120
|
-
message: ["Test message"],
|
|
121
|
-
properties: {},
|
|
122
|
-
rawMessage: "Test message",
|
|
123
|
-
timestamp: Date.now(),
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// Test with no options (undefined)
|
|
127
|
-
const sinkUndefined = getPinoSink(logger);
|
|
128
|
-
sinkUndefined({
|
|
129
|
-
category: ["test", "category"],
|
|
130
|
-
level: "info",
|
|
131
|
-
message: ["Test message 2"],
|
|
132
|
-
properties: {},
|
|
133
|
-
rawMessage: "Test message 2",
|
|
134
|
-
timestamp: Date.now(),
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
logger.flush();
|
|
138
|
-
await delay(100);
|
|
139
|
-
|
|
140
|
-
assertEquals(buffer.length, 2);
|
|
141
|
-
assertEquals(buffer[0].msg, "Test message");
|
|
142
|
-
assertEquals(buffer[1].msg, "Test message 2");
|
|
143
|
-
// Both should NOT include category in the message
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
test("getPinoSink(): category option - true (default formatting)", async () => {
|
|
147
|
-
const buffer: PinoLog[] = [];
|
|
148
|
-
const dest = build(async (source) => {
|
|
149
|
-
for await (const obj of source) {
|
|
150
|
-
buffer.push(obj);
|
|
151
|
-
}
|
|
152
|
-
}, {});
|
|
153
|
-
const logger = pino({
|
|
154
|
-
useOnlyCustomLevels: false,
|
|
155
|
-
}, dest);
|
|
156
|
-
|
|
157
|
-
const sink = getPinoSink(logger, { category: true });
|
|
158
|
-
sink({
|
|
159
|
-
category: ["test", "category"],
|
|
160
|
-
level: "info",
|
|
161
|
-
message: ["Test message"],
|
|
162
|
-
properties: {},
|
|
163
|
-
rawMessage: "Test message",
|
|
164
|
-
timestamp: Date.now(),
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
logger.flush();
|
|
168
|
-
await delay(100);
|
|
169
|
-
|
|
170
|
-
assertEquals(buffer.length, 1);
|
|
171
|
-
assertEquals(buffer[0].msg, "test·category: Test message");
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
test("getPinoSink(): category decorators", async () => {
|
|
175
|
-
const buffer: PinoLog[] = [];
|
|
176
|
-
const dest = build(async (source) => {
|
|
177
|
-
for await (const obj of source) {
|
|
178
|
-
buffer.push(obj);
|
|
179
|
-
}
|
|
180
|
-
}, {});
|
|
181
|
-
const logger = pino({
|
|
182
|
-
useOnlyCustomLevels: false,
|
|
183
|
-
}, dest);
|
|
184
|
-
|
|
185
|
-
const decorators = [
|
|
186
|
-
{ decorator: "[]", expected: "[test] Message" },
|
|
187
|
-
{ decorator: "()", expected: "(test) Message" },
|
|
188
|
-
{ decorator: "<>", expected: "<test> Message" },
|
|
189
|
-
{ decorator: "{}", expected: "{test} Message" },
|
|
190
|
-
{ decorator: ":", expected: "test: Message" },
|
|
191
|
-
{ decorator: "-", expected: "test - Message" },
|
|
192
|
-
{ decorator: "|", expected: "test | Message" },
|
|
193
|
-
{ decorator: "/", expected: "test / Message" },
|
|
194
|
-
{ decorator: "", expected: "test Message" },
|
|
195
|
-
] as const;
|
|
196
|
-
|
|
197
|
-
for (const { decorator } of decorators) {
|
|
198
|
-
const sink = getPinoSink(logger, {
|
|
199
|
-
category: { decorator, position: "start" },
|
|
200
|
-
});
|
|
201
|
-
sink({
|
|
202
|
-
category: ["test"],
|
|
203
|
-
level: "info",
|
|
204
|
-
message: ["Message"],
|
|
205
|
-
properties: {},
|
|
206
|
-
rawMessage: "Message",
|
|
207
|
-
timestamp: Date.now(),
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
logger.flush();
|
|
212
|
-
await delay(100);
|
|
213
|
-
|
|
214
|
-
assertEquals(buffer.length, decorators.length);
|
|
215
|
-
for (let i = 0; i < decorators.length; i++) {
|
|
216
|
-
assertEquals(
|
|
217
|
-
buffer[i].msg,
|
|
218
|
-
decorators[i].expected,
|
|
219
|
-
`Decorator '${decorators[i].decorator}' failed`,
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
test("getPinoSink(): category position (start vs end)", async () => {
|
|
225
|
-
const buffer: PinoLog[] = [];
|
|
226
|
-
const dest = build(async (source) => {
|
|
227
|
-
for await (const obj of source) {
|
|
228
|
-
buffer.push(obj);
|
|
229
|
-
}
|
|
230
|
-
}, {});
|
|
231
|
-
const logger = pino({
|
|
232
|
-
useOnlyCustomLevels: false,
|
|
233
|
-
}, dest);
|
|
234
|
-
|
|
235
|
-
// Test position: "start"
|
|
236
|
-
const sinkStart = getPinoSink(logger, {
|
|
237
|
-
category: { position: "start", decorator: "[]" },
|
|
238
|
-
});
|
|
239
|
-
sinkStart({
|
|
240
|
-
category: ["test"],
|
|
241
|
-
level: "info",
|
|
242
|
-
message: ["Message"],
|
|
243
|
-
properties: {},
|
|
244
|
-
rawMessage: "Message",
|
|
245
|
-
timestamp: Date.now(),
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
// Test position: "end"
|
|
249
|
-
const sinkEnd = getPinoSink(logger, {
|
|
250
|
-
category: { position: "end", decorator: "[]" },
|
|
251
|
-
});
|
|
252
|
-
sinkEnd({
|
|
253
|
-
category: ["test"],
|
|
254
|
-
level: "info",
|
|
255
|
-
message: ["Message"],
|
|
256
|
-
properties: {},
|
|
257
|
-
rawMessage: "Message",
|
|
258
|
-
timestamp: Date.now(),
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
logger.flush();
|
|
262
|
-
await delay(100);
|
|
263
|
-
|
|
264
|
-
assertEquals(buffer.length, 2);
|
|
265
|
-
assertEquals(buffer[0].msg, "[test] Message");
|
|
266
|
-
assertEquals(buffer[1].msg, "Message [test]");
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
test("getPinoSink(): category separator", async () => {
|
|
270
|
-
const buffer: PinoLog[] = [];
|
|
271
|
-
const dest = build(async (source) => {
|
|
272
|
-
for await (const obj of source) {
|
|
273
|
-
buffer.push(obj);
|
|
274
|
-
}
|
|
275
|
-
}, {});
|
|
276
|
-
const logger = pino({
|
|
277
|
-
useOnlyCustomLevels: false,
|
|
278
|
-
}, dest);
|
|
279
|
-
|
|
280
|
-
const separators = [
|
|
281
|
-
{ separator: ".", expected: "[app.service.logger] Message" },
|
|
282
|
-
{ separator: "/", expected: "[app/service/logger] Message" },
|
|
283
|
-
{ separator: "::", expected: "[app::service::logger] Message" },
|
|
284
|
-
{ separator: " > ", expected: "[app > service > logger] Message" },
|
|
285
|
-
];
|
|
286
|
-
|
|
287
|
-
for (const { separator } of separators) {
|
|
288
|
-
const sink = getPinoSink(logger, {
|
|
289
|
-
category: { separator, decorator: "[]", position: "start" },
|
|
290
|
-
});
|
|
291
|
-
sink({
|
|
292
|
-
category: ["app", "service", "logger"],
|
|
293
|
-
level: "info",
|
|
294
|
-
message: ["Message"],
|
|
295
|
-
properties: {},
|
|
296
|
-
rawMessage: "Message",
|
|
297
|
-
timestamp: Date.now(),
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
logger.flush();
|
|
302
|
-
await delay(100);
|
|
303
|
-
|
|
304
|
-
assertEquals(buffer.length, separators.length);
|
|
305
|
-
for (let i = 0; i < separators.length; i++) {
|
|
306
|
-
assertEquals(
|
|
307
|
-
buffer[i].msg,
|
|
308
|
-
separators[i].expected,
|
|
309
|
-
`Separator '${separators[i].separator}' failed`,
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
test("getPinoSink(): empty category handling", async () => {
|
|
315
|
-
const buffer: PinoLog[] = [];
|
|
316
|
-
const dest = build(async (source) => {
|
|
317
|
-
for await (const obj of source) {
|
|
318
|
-
buffer.push(obj);
|
|
319
|
-
}
|
|
320
|
-
}, {});
|
|
321
|
-
const logger = pino({
|
|
322
|
-
useOnlyCustomLevels: false,
|
|
323
|
-
}, dest);
|
|
324
|
-
|
|
325
|
-
const sink = getPinoSink(logger, {
|
|
326
|
-
category: { decorator: "[]", position: "start" },
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
// Test with empty category array
|
|
330
|
-
sink({
|
|
331
|
-
category: [],
|
|
332
|
-
level: "info",
|
|
333
|
-
message: ["Message with empty category"],
|
|
334
|
-
properties: {},
|
|
335
|
-
rawMessage: "Message with empty category",
|
|
336
|
-
timestamp: Date.now(),
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
// Test with single empty string category
|
|
340
|
-
sink({
|
|
341
|
-
category: [""],
|
|
342
|
-
level: "info",
|
|
343
|
-
message: ["Message with empty string category"],
|
|
344
|
-
properties: {},
|
|
345
|
-
rawMessage: "Message with empty string category",
|
|
346
|
-
timestamp: Date.now(),
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
logger.flush();
|
|
350
|
-
await delay(100);
|
|
351
|
-
|
|
352
|
-
assertEquals(buffer.length, 2);
|
|
353
|
-
// Empty category array should not include category in message
|
|
354
|
-
assertEquals(buffer[0].msg, "Message with empty category");
|
|
355
|
-
// Single empty string should still show the decorator
|
|
356
|
-
assertEquals(buffer[1].msg, "[] Message with empty string category");
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
test("getPinoSink(): message interpolation", async () => {
|
|
360
|
-
const buffer: PinoLog[] = [];
|
|
361
|
-
const dest = build(async (source) => {
|
|
362
|
-
for await (const obj of source) {
|
|
363
|
-
buffer.push(obj);
|
|
364
|
-
}
|
|
365
|
-
}, {});
|
|
366
|
-
const logger = pino({
|
|
367
|
-
useOnlyCustomLevels: false,
|
|
368
|
-
}, dest);
|
|
369
|
-
|
|
370
|
-
const sink = getPinoSink(logger, {
|
|
371
|
-
category: { decorator: ":", position: "start" },
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
// Test message with interpolated values
|
|
375
|
-
sink({
|
|
376
|
-
category: ["app", "auth"],
|
|
377
|
-
level: "info",
|
|
378
|
-
message: [
|
|
379
|
-
"User ",
|
|
380
|
-
{ userId: 123, username: "johndoe" },
|
|
381
|
-
" logged in",
|
|
382
|
-
],
|
|
383
|
-
properties: { sessionId: "sess_abc123", source: "web" },
|
|
384
|
-
rawMessage: "User {user} logged in",
|
|
385
|
-
timestamp: Date.now(),
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
logger.flush();
|
|
389
|
-
await delay(100);
|
|
390
|
-
|
|
391
|
-
assertEquals(buffer.length, 1);
|
|
392
|
-
|
|
393
|
-
// Check that the message contains expected parts
|
|
394
|
-
const actualMsg = buffer[0].msg;
|
|
395
|
-
assertEquals(actualMsg.includes("app·auth"), true, "Should contain category");
|
|
396
|
-
assertEquals(actualMsg.includes("User"), true, "Should contain 'User'");
|
|
397
|
-
assertEquals(
|
|
398
|
-
actualMsg.includes("logged in"),
|
|
399
|
-
true,
|
|
400
|
-
"Should contain 'logged in'",
|
|
401
|
-
);
|
|
402
|
-
|
|
403
|
-
assertEquals(buffer[0].sessionId, "sess_abc123");
|
|
404
|
-
assertEquals(buffer[0].source, "web");
|
|
405
|
-
});
|
package/src/mod.ts
DELETED
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
import type { Logger } from "pino";
|
|
2
|
-
import { configureSync, type LogRecord, type Sink } from "@logtape/logtape";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Options for configuring the Pino sink adapter.
|
|
6
|
-
* @since 1.0.0
|
|
7
|
-
*/
|
|
8
|
-
export interface PinoSinkOptions {
|
|
9
|
-
/**
|
|
10
|
-
* Configuration for how LogTape categories are handled in Pino logs.
|
|
11
|
-
* - `false` or `undefined`: Categories are not included in the log message
|
|
12
|
-
* - `true`: Categories are included with default formatting
|
|
13
|
-
* - `CategoryOptions`: Custom category formatting configuration
|
|
14
|
-
*/
|
|
15
|
-
readonly category?: boolean | CategoryOptions;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Configuration options for formatting LogTape categories in Pino log messages.
|
|
20
|
-
* @since 1.0.0
|
|
21
|
-
*/
|
|
22
|
-
export interface CategoryOptions {
|
|
23
|
-
/**
|
|
24
|
-
* The separator used to join category parts when multiple categories exist.
|
|
25
|
-
* @default "·"
|
|
26
|
-
*/
|
|
27
|
-
readonly separator?: string;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Where to position the category in the log message.
|
|
31
|
-
* - `"start"`: Category appears at the beginning of the message
|
|
32
|
-
* - `"end"`: Category appears at the end of the message
|
|
33
|
-
* @default "start"
|
|
34
|
-
*/
|
|
35
|
-
readonly position?: "start" | "end";
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* The decorator used to format the category in the log message.
|
|
39
|
-
* - `"[]"`: [category] format
|
|
40
|
-
* - `"()"`: (category) format
|
|
41
|
-
* - `"<>"`: <category> format
|
|
42
|
-
* - `"{}"`: {category} format
|
|
43
|
-
* - `":"`: category: format
|
|
44
|
-
* - `"-"`: category - format
|
|
45
|
-
* - `"|"`: category | format
|
|
46
|
-
* - `"/"`: category / format
|
|
47
|
-
* - `""`: category format (no decoration)
|
|
48
|
-
* @default ":"
|
|
49
|
-
*/
|
|
50
|
-
readonly decorator?: "[]" | "()" | "<>" | "{}" | ":" | "-" | "|" | "/" | "";
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Creates a LogTape sink that forwards log records to a Pino logger.
|
|
55
|
-
*
|
|
56
|
-
* This adapter allows LogTape-enabled libraries to integrate seamlessly with
|
|
57
|
-
* applications that use Pino for logging.
|
|
58
|
-
*
|
|
59
|
-
* @example
|
|
60
|
-
* ```typescript
|
|
61
|
-
* import { configure } from "@logtape/logtape";
|
|
62
|
-
* import { getPinoSink } from "@logtape/adaptor-pino";
|
|
63
|
-
* import pino from "pino";
|
|
64
|
-
*
|
|
65
|
-
* const pinoLogger = pino();
|
|
66
|
-
*
|
|
67
|
-
* await configure({
|
|
68
|
-
* sinks: {
|
|
69
|
-
* pino: getPinoSink(pinoLogger, {
|
|
70
|
-
* category: {
|
|
71
|
-
* position: "start",
|
|
72
|
-
* decorator: "[]",
|
|
73
|
-
* separator: "."
|
|
74
|
-
* }
|
|
75
|
-
* })
|
|
76
|
-
* },
|
|
77
|
-
* loggers: [
|
|
78
|
-
* { category: "my-library", sinks: ["pino"] }
|
|
79
|
-
* ]
|
|
80
|
-
* });
|
|
81
|
-
* ```
|
|
82
|
-
*
|
|
83
|
-
* @template CustomLevels The custom log levels supported by the Pino logger.
|
|
84
|
-
* @template UseOnlyCustomLevels Whether to use only custom levels defined
|
|
85
|
-
* in the Pino logger.
|
|
86
|
-
* @param logger The Pino logger instance to forward logs to.
|
|
87
|
-
* @param options Configuration options for the sink adapter.
|
|
88
|
-
* @returns A LogTape sink function that can be used in LogTape configuration.
|
|
89
|
-
* @since 1.0.0
|
|
90
|
-
*/
|
|
91
|
-
export function getPinoSink<
|
|
92
|
-
CustomLevels extends string,
|
|
93
|
-
UseOnlyCustomLevels extends boolean,
|
|
94
|
-
>(
|
|
95
|
-
logger: Logger<CustomLevels, UseOnlyCustomLevels>,
|
|
96
|
-
options: PinoSinkOptions = {},
|
|
97
|
-
): Sink {
|
|
98
|
-
const categoryOptions = !options.category
|
|
99
|
-
? undefined
|
|
100
|
-
: typeof options.category === "object"
|
|
101
|
-
? options.category
|
|
102
|
-
: {};
|
|
103
|
-
const category: Required<CategoryOptions> | undefined =
|
|
104
|
-
categoryOptions == null ? undefined : {
|
|
105
|
-
separator: categoryOptions.separator ?? "·",
|
|
106
|
-
position: categoryOptions.position ?? "start",
|
|
107
|
-
decorator: categoryOptions.decorator ?? ":",
|
|
108
|
-
};
|
|
109
|
-
return (record: LogRecord) => {
|
|
110
|
-
let message = "";
|
|
111
|
-
const interpolationValues: unknown[] = [];
|
|
112
|
-
if (category?.position === "start" && record.category.length > 0) {
|
|
113
|
-
message += category.decorator === "[]"
|
|
114
|
-
? "[%s] "
|
|
115
|
-
: category.decorator === "()"
|
|
116
|
-
? "(%s) "
|
|
117
|
-
: category.decorator === "<>"
|
|
118
|
-
? "<%s> "
|
|
119
|
-
: category.decorator === "{}"
|
|
120
|
-
? "{%s} "
|
|
121
|
-
: category.decorator === ":"
|
|
122
|
-
? "%s: "
|
|
123
|
-
: category.decorator === "-"
|
|
124
|
-
? "%s - "
|
|
125
|
-
: category.decorator === "|"
|
|
126
|
-
? "%s | "
|
|
127
|
-
: category.decorator === "/"
|
|
128
|
-
? "%s / "
|
|
129
|
-
: "%s ";
|
|
130
|
-
interpolationValues.push(
|
|
131
|
-
record.category.join(category.separator),
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
for (let i = 0; i < record.message.length; i += 2) {
|
|
135
|
-
message += record.message[i];
|
|
136
|
-
if (i + 1 < record.message.length) {
|
|
137
|
-
message += "%o";
|
|
138
|
-
interpolationValues.push(record.message[i + 1]);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
if (category?.position === "end" && record.category.length > 0) {
|
|
142
|
-
message += category.decorator === "[]"
|
|
143
|
-
? " [%s]"
|
|
144
|
-
: category.decorator === "()"
|
|
145
|
-
? " (%s)"
|
|
146
|
-
: category.decorator === "<>"
|
|
147
|
-
? " <%s>"
|
|
148
|
-
: category.decorator === "{}"
|
|
149
|
-
? " {%s}"
|
|
150
|
-
: category.decorator === ":"
|
|
151
|
-
? ": %s"
|
|
152
|
-
: category.decorator === "-"
|
|
153
|
-
? " - %s"
|
|
154
|
-
: category.decorator === "|"
|
|
155
|
-
? " | %s"
|
|
156
|
-
: category.decorator === "/"
|
|
157
|
-
? " / %s"
|
|
158
|
-
: " %s";
|
|
159
|
-
interpolationValues.push(
|
|
160
|
-
record.category.join(category.separator),
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
switch (record.level) {
|
|
164
|
-
case "trace":
|
|
165
|
-
return logger.trace(record.properties, message, interpolationValues);
|
|
166
|
-
case "debug":
|
|
167
|
-
return logger.debug(record.properties, message, interpolationValues);
|
|
168
|
-
case "info":
|
|
169
|
-
return logger.info(record.properties, message, interpolationValues);
|
|
170
|
-
case "warning":
|
|
171
|
-
return logger.warn(record.properties, message, interpolationValues);
|
|
172
|
-
case "error":
|
|
173
|
-
return logger.error(record.properties, message, interpolationValues);
|
|
174
|
-
case "fatal":
|
|
175
|
-
return logger.fatal(record.properties, message, interpolationValues);
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Automatically configures LogTape to route all logs to a Pino logger.
|
|
182
|
-
*
|
|
183
|
-
* This is a convenience function that automatically sets up LogTape to forward
|
|
184
|
-
* all log records to a Pino logger instance.
|
|
185
|
-
*
|
|
186
|
-
* @example Basic auto-configuration
|
|
187
|
-
* ```typescript
|
|
188
|
-
* import pino from "pino";
|
|
189
|
-
* import { install } from "@logtape/adaptor-pino";
|
|
190
|
-
*
|
|
191
|
-
* const pinoLogger = pino();
|
|
192
|
-
*
|
|
193
|
-
* // Automatically route all LogTape logs to the Pino logger
|
|
194
|
-
* install(pinoLogger);
|
|
195
|
-
*
|
|
196
|
-
* // Now any LogTape-enabled library will log through Pino
|
|
197
|
-
* import { getLogger } from "@logtape/logtape";
|
|
198
|
-
* const logger = getLogger("my-app");
|
|
199
|
-
* logger.info("This will be logged through Pino");
|
|
200
|
-
* ```
|
|
201
|
-
*
|
|
202
|
-
* @example Auto-configuration with custom options
|
|
203
|
-
* ```typescript
|
|
204
|
-
* import pino from "pino";
|
|
205
|
-
* import { install } from "@logtape/adaptor-pino";
|
|
206
|
-
*
|
|
207
|
-
* const pinoLogger = pino({
|
|
208
|
-
* level: "info",
|
|
209
|
-
* transport: {
|
|
210
|
-
* target: "pino-pretty"
|
|
211
|
-
* }
|
|
212
|
-
* });
|
|
213
|
-
*
|
|
214
|
-
* install(pinoLogger, {
|
|
215
|
-
* category: {
|
|
216
|
-
* position: "start",
|
|
217
|
-
* decorator: "[]",
|
|
218
|
-
* separator: "."
|
|
219
|
-
* }
|
|
220
|
-
* });
|
|
221
|
-
* ```
|
|
222
|
-
*
|
|
223
|
-
* @template CustomLevels The custom log levels supported by the Pino logger.
|
|
224
|
-
* @template UseOnlyCustomLevels Whether to use only custom levels defined
|
|
225
|
-
* in the Pino logger.
|
|
226
|
-
* @param logger The Pino logger instance to forward logs to.
|
|
227
|
-
* @param options Configuration options for the sink adapter.
|
|
228
|
-
* @since 1.0.0
|
|
229
|
-
*/
|
|
230
|
-
export function install<
|
|
231
|
-
CustomLevels extends string,
|
|
232
|
-
UseOnlyCustomLevels extends boolean,
|
|
233
|
-
>(
|
|
234
|
-
logger: Logger<CustomLevels, UseOnlyCustomLevels>,
|
|
235
|
-
options: PinoSinkOptions = {},
|
|
236
|
-
): void {
|
|
237
|
-
configureSync({
|
|
238
|
-
sinks: {
|
|
239
|
-
pino: getPinoSink(logger, options),
|
|
240
|
-
},
|
|
241
|
-
loggers: [
|
|
242
|
-
{
|
|
243
|
-
category: ["logtape", "meta"],
|
|
244
|
-
sinks: ["pino"],
|
|
245
|
-
lowestLevel: "warning",
|
|
246
|
-
},
|
|
247
|
-
{ category: [], sinks: ["pino"] },
|
|
248
|
-
],
|
|
249
|
-
});
|
|
250
|
-
}
|