@logtape/adaptor-pino 1.0.0-dev.253

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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright 2024 Hong Minhee
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,92 @@
1
+ <!-- deno-fmt-ignore-file -->
2
+
3
+ @logtape/adaptor-pino
4
+ =====================
5
+
6
+ [![JSR][JSR badge]][JSR]
7
+ [![npm][npm badge]][npm]
8
+
9
+ *@logtape/adaptor-pino* is a [LogTape] adapter that forwards log records to
10
+ [Pino] loggers, enabling seamless integration between LogTape-enabled libraries
11
+ and applications using Pino for logging infrastructure.
12
+
13
+ [JSR]: https://jsr.io/@logtape/adaptor-pino
14
+ [JSR badge]: https://jsr.io/badges/@logtape/adaptor-pino
15
+ [npm]: https://www.npmjs.com/package/@logtape/adaptor-pino
16
+ [npm badge]: https://img.shields.io/npm/v/@logtape/adaptor-pino?logo=npm
17
+ [LogTape]: https://logtape.org/
18
+ [Pino]: https://getpino.io/
19
+
20
+
21
+ Installation
22
+ ------------
23
+
24
+ ~~~~ sh
25
+ deno add jsr:@logtape/adaptor-pino # for Deno
26
+ npm add @logtape/adaptor-pino # for npm
27
+ pnpm add @logtape/adaptor-pino # for pnpm
28
+ yarn add @logtape/adaptor-pino # for Yarn
29
+ bun add @logtape/adaptor-pino # for Bun
30
+ ~~~~
31
+
32
+
33
+ Usage
34
+ -----
35
+
36
+ Configure LogTape to use the Pino adapter in your application:
37
+
38
+ ~~~~ typescript
39
+ import { configure } from "@logtape/logtape";
40
+ import { getPinoSink } from "@logtape/adaptor-pino";
41
+ import pino from "pino";
42
+
43
+ const pinoLogger = pino();
44
+
45
+ await configure({
46
+ sinks: {
47
+ pino: getPinoSink(pinoLogger, {
48
+ category: {
49
+ position: "start",
50
+ decorator: "[]",
51
+ separator: "."
52
+ }
53
+ })
54
+ },
55
+ loggers: [
56
+ { category: "my-library", sinks: ["pino"] }
57
+ ]
58
+ });
59
+ ~~~~
60
+
61
+
62
+ Category formatting
63
+ -------------------
64
+
65
+ The adapter supports flexible category formatting options:
66
+
67
+ ~~~~typescript
68
+ import { getPinoSink } from "@logtape/adaptor-pino";
69
+
70
+ // Hide categories completely
71
+ const sink1 = getPinoSink(logger, { category: false });
72
+
73
+ // Use default formatting (category with ":" separator)
74
+ const sink2 = getPinoSink(logger, { category: true });
75
+
76
+ // Custom formatting
77
+ const sink3 = getPinoSink(logger, {
78
+ category: {
79
+ position: "end", // "start" or "end"
80
+ decorator: "[]", // "[]", "()", "<>", "{}", ":", "-", "|", "/", ""
81
+ separator: "::" // custom separator for multi-part categories
82
+ }
83
+ });
84
+ ~~~~
85
+
86
+
87
+ Docs
88
+ ----
89
+
90
+ See the [API reference] on JSR for further details.
91
+
92
+ [API reference]: https://jsr.io/@logtape/adaptor-pino/doc
package/deno.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@logtape/adaptor-pino",
3
+ "version": "1.0.0-dev.253+3fd9a841",
4
+ "license": "MIT",
5
+ "exports": "./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/dist/mod.cjs ADDED
@@ -0,0 +1,78 @@
1
+
2
+ //#region mod.ts
3
+ /**
4
+ * Creates a LogTape sink that forwards log records to a Pino logger.
5
+ *
6
+ * This adapter allows LogTape-enabled libraries to integrate seamlessly with
7
+ * applications that use Pino for logging.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { configure } from "@logtape/logtape";
12
+ * import { getPinoSink } from "@logtape/adaptor-pino";
13
+ * import pino from "pino";
14
+ *
15
+ * const pinoLogger = pino();
16
+ *
17
+ * await configure({
18
+ * sinks: {
19
+ * pino: getPinoSink(pinoLogger, {
20
+ * category: {
21
+ * position: "start",
22
+ * decorator: "[]",
23
+ * separator: "."
24
+ * }
25
+ * })
26
+ * },
27
+ * loggers: [
28
+ * { category: "my-library", sinks: ["pino"] }
29
+ * ]
30
+ * });
31
+ * ```
32
+ *
33
+ * @typeParam CustomLevels The custom log levels supported by the Pino logger.
34
+ * @typeParam UseOnlyCustomLevels Whether to use only custom levels defined
35
+ * in the Pino logger.
36
+ * @param logger The Pino logger instance to forward logs to.
37
+ * @param options Configuration options for the sink adapter.
38
+ * @returns A LogTape sink function that can be used in LogTape configuration.
39
+ * @since 1.0.0
40
+ */
41
+ function getPinoSink(logger, options) {
42
+ const categoryOptions = !options?.category ? void 0 : typeof options.category === "object" ? options.category : {};
43
+ const category = categoryOptions == null ? void 0 : {
44
+ separator: categoryOptions.separator ?? "·",
45
+ position: categoryOptions.position ?? "start",
46
+ decorator: categoryOptions.decorator ?? ":"
47
+ };
48
+ return (record) => {
49
+ let message = "";
50
+ const interpolationValues = [];
51
+ if (category?.position === "start" && record.category.length > 0) {
52
+ message += category.decorator === "[]" ? "[%s] " : category.decorator === "()" ? "(%s) " : category.decorator === "<>" ? "<%s> " : category.decorator === "{}" ? "{%s} " : category.decorator === ":" ? "%s: " : category.decorator === "-" ? "%s - " : category.decorator === "|" ? "%s | " : category.decorator === "/" ? "%s / " : "%s ";
53
+ interpolationValues.push(record.category.join(category.separator));
54
+ }
55
+ for (let i = 0; i < record.message.length; i += 2) {
56
+ message += record.message[i];
57
+ if (i + 1 < record.message.length) {
58
+ message += "%o";
59
+ interpolationValues.push(record.message[i + 1]);
60
+ }
61
+ }
62
+ if (category?.position === "end" && record.category.length > 0) {
63
+ message += category.decorator === "[]" ? " [%s]" : category.decorator === "()" ? " (%s)" : category.decorator === "<>" ? " <%s>" : category.decorator === "{}" ? " {%s}" : category.decorator === ":" ? ": %s" : category.decorator === "-" ? " - %s" : category.decorator === "|" ? " | %s" : category.decorator === "/" ? " / %s" : " %s";
64
+ interpolationValues.push(record.category.join(category.separator));
65
+ }
66
+ switch (record.level) {
67
+ case "trace": return logger.trace(record.properties, message, interpolationValues);
68
+ case "debug": return logger.debug(record.properties, message, interpolationValues);
69
+ case "info": return logger.info(record.properties, message, interpolationValues);
70
+ case "warning": return logger.warn(record.properties, message, interpolationValues);
71
+ case "error": return logger.error(record.properties, message, interpolationValues);
72
+ case "fatal": return logger.fatal(record.properties, message, interpolationValues);
73
+ }
74
+ };
75
+ }
76
+
77
+ //#endregion
78
+ exports.getPinoSink = getPinoSink;
package/dist/mod.d.cts ADDED
@@ -0,0 +1,93 @@
1
+ import { Logger } from "pino";
2
+ import { Sink } from "@logtape/logtape";
3
+
4
+ //#region mod.d.ts
5
+
6
+ /**
7
+ * Options for configuring the Pino sink adapter.
8
+ * @since 1.0.0
9
+ */
10
+ interface PinoSinkOptions {
11
+ /**
12
+ * Configuration for how LogTape categories are handled in Pino logs.
13
+ * - `false` or `undefined`: Categories are not included in the log message
14
+ * - `true`: Categories are included with default formatting
15
+ * - `CategoryOptions`: Custom category formatting configuration
16
+ */
17
+ readonly category?: boolean | CategoryOptions;
18
+ }
19
+ /**
20
+ * Configuration options for formatting LogTape categories in Pino log messages.
21
+ * @since 1.0.0
22
+ */
23
+ interface CategoryOptions {
24
+ /**
25
+ * The separator used to join category parts when multiple categories exist.
26
+ * @default "·"
27
+ */
28
+ readonly separator?: string;
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
+ * The decorator used to format the category in the log message.
38
+ * - `"[]"`: [category] format
39
+ * - `"()"`: (category) format
40
+ * - `"<>"`: <category> format
41
+ * - `"{}"`: {category} format
42
+ * - `":"`: category: format
43
+ * - `"-"`: category - format
44
+ * - `"|"`: category | format
45
+ * - `"/"`: category / format
46
+ * - `""`: category format (no decoration)
47
+ * @default ":"
48
+ */
49
+ readonly decorator?: "[]" | "()" | "<>" | "{}" | ":" | "-" | "|" | "/" | "";
50
+ }
51
+ /**
52
+ * Creates a LogTape sink that forwards log records to a Pino logger.
53
+ *
54
+ * This adapter allows LogTape-enabled libraries to integrate seamlessly with
55
+ * applications that use Pino for logging.
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * import { configure } from "@logtape/logtape";
60
+ * import { getPinoSink } from "@logtape/adaptor-pino";
61
+ * import pino from "pino";
62
+ *
63
+ * const pinoLogger = pino();
64
+ *
65
+ * await configure({
66
+ * sinks: {
67
+ * pino: getPinoSink(pinoLogger, {
68
+ * category: {
69
+ * position: "start",
70
+ * decorator: "[]",
71
+ * separator: "."
72
+ * }
73
+ * })
74
+ * },
75
+ * loggers: [
76
+ * { category: "my-library", sinks: ["pino"] }
77
+ * ]
78
+ * });
79
+ * ```
80
+ *
81
+ * @typeParam CustomLevels The custom log levels supported by the Pino logger.
82
+ * @typeParam UseOnlyCustomLevels Whether to use only custom levels defined
83
+ * in the Pino logger.
84
+ * @param logger The Pino logger instance to forward logs to.
85
+ * @param options Configuration options for the sink adapter.
86
+ * @returns A LogTape sink function that can be used in LogTape configuration.
87
+ * @since 1.0.0
88
+ */
89
+ declare function getPinoSink<CustomLevels extends string, UseOnlyCustomLevels extends boolean>(logger: Logger<CustomLevels, UseOnlyCustomLevels>, options?: PinoSinkOptions): Sink;
90
+ //# sourceMappingURL=mod.d.ts.map
91
+ //#endregion
92
+ export { CategoryOptions, PinoSinkOptions, getPinoSink };
93
+ //# sourceMappingURL=mod.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.cts","names":[],"sources":["../mod.ts"],"sourcesContent":[],"mappings":";;;;;;;AAOA;AAcA;AAqEgB,UAnFC,eAAA,CAmFU;EAAA;;;;;;EAMpB,SAAA,QAAA,CAAA,EAAA,OAAA,GAlFyB,eAkFzB;;;;;;UA3EU,eAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqED,sFAIN,OAAO,cAAc,gCACnB,kBACT"}
package/dist/mod.d.ts ADDED
@@ -0,0 +1,93 @@
1
+ import { Logger } from "pino";
2
+ import { Sink } from "@logtape/logtape";
3
+
4
+ //#region mod.d.ts
5
+
6
+ /**
7
+ * Options for configuring the Pino sink adapter.
8
+ * @since 1.0.0
9
+ */
10
+ interface PinoSinkOptions {
11
+ /**
12
+ * Configuration for how LogTape categories are handled in Pino logs.
13
+ * - `false` or `undefined`: Categories are not included in the log message
14
+ * - `true`: Categories are included with default formatting
15
+ * - `CategoryOptions`: Custom category formatting configuration
16
+ */
17
+ readonly category?: boolean | CategoryOptions;
18
+ }
19
+ /**
20
+ * Configuration options for formatting LogTape categories in Pino log messages.
21
+ * @since 1.0.0
22
+ */
23
+ interface CategoryOptions {
24
+ /**
25
+ * The separator used to join category parts when multiple categories exist.
26
+ * @default "·"
27
+ */
28
+ readonly separator?: string;
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
+ * The decorator used to format the category in the log message.
38
+ * - `"[]"`: [category] format
39
+ * - `"()"`: (category) format
40
+ * - `"<>"`: <category> format
41
+ * - `"{}"`: {category} format
42
+ * - `":"`: category: format
43
+ * - `"-"`: category - format
44
+ * - `"|"`: category | format
45
+ * - `"/"`: category / format
46
+ * - `""`: category format (no decoration)
47
+ * @default ":"
48
+ */
49
+ readonly decorator?: "[]" | "()" | "<>" | "{}" | ":" | "-" | "|" | "/" | "";
50
+ }
51
+ /**
52
+ * Creates a LogTape sink that forwards log records to a Pino logger.
53
+ *
54
+ * This adapter allows LogTape-enabled libraries to integrate seamlessly with
55
+ * applications that use Pino for logging.
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * import { configure } from "@logtape/logtape";
60
+ * import { getPinoSink } from "@logtape/adaptor-pino";
61
+ * import pino from "pino";
62
+ *
63
+ * const pinoLogger = pino();
64
+ *
65
+ * await configure({
66
+ * sinks: {
67
+ * pino: getPinoSink(pinoLogger, {
68
+ * category: {
69
+ * position: "start",
70
+ * decorator: "[]",
71
+ * separator: "."
72
+ * }
73
+ * })
74
+ * },
75
+ * loggers: [
76
+ * { category: "my-library", sinks: ["pino"] }
77
+ * ]
78
+ * });
79
+ * ```
80
+ *
81
+ * @typeParam CustomLevels The custom log levels supported by the Pino logger.
82
+ * @typeParam UseOnlyCustomLevels Whether to use only custom levels defined
83
+ * in the Pino logger.
84
+ * @param logger The Pino logger instance to forward logs to.
85
+ * @param options Configuration options for the sink adapter.
86
+ * @returns A LogTape sink function that can be used in LogTape configuration.
87
+ * @since 1.0.0
88
+ */
89
+ declare function getPinoSink<CustomLevels extends string, UseOnlyCustomLevels extends boolean>(logger: Logger<CustomLevels, UseOnlyCustomLevels>, options?: PinoSinkOptions): Sink;
90
+ //# sourceMappingURL=mod.d.ts.map
91
+ //#endregion
92
+ export { CategoryOptions, PinoSinkOptions, getPinoSink };
93
+ //# sourceMappingURL=mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.ts","names":[],"sources":["../mod.ts"],"sourcesContent":[],"mappings":";;;;;;;AAOA;AAcA;AAqEgB,UAnFC,eAAA,CAmFU;EAAA;;;;;;EAMpB,SAAA,QAAA,CAAA,EAAA,OAAA,GAlFyB,eAkFzB;;;;;;UA3EU,eAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqED,sFAIN,OAAO,cAAc,gCACnB,kBACT"}
package/dist/mod.js ADDED
@@ -0,0 +1,78 @@
1
+ //#region mod.ts
2
+ /**
3
+ * Creates a LogTape sink that forwards log records to a Pino logger.
4
+ *
5
+ * This adapter allows LogTape-enabled libraries to integrate seamlessly with
6
+ * applications that use Pino for logging.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { configure } from "@logtape/logtape";
11
+ * import { getPinoSink } from "@logtape/adaptor-pino";
12
+ * import pino from "pino";
13
+ *
14
+ * const pinoLogger = pino();
15
+ *
16
+ * await configure({
17
+ * sinks: {
18
+ * pino: getPinoSink(pinoLogger, {
19
+ * category: {
20
+ * position: "start",
21
+ * decorator: "[]",
22
+ * separator: "."
23
+ * }
24
+ * })
25
+ * },
26
+ * loggers: [
27
+ * { category: "my-library", sinks: ["pino"] }
28
+ * ]
29
+ * });
30
+ * ```
31
+ *
32
+ * @typeParam CustomLevels The custom log levels supported by the Pino logger.
33
+ * @typeParam UseOnlyCustomLevels Whether to use only custom levels defined
34
+ * in the Pino logger.
35
+ * @param logger The Pino logger instance to forward logs to.
36
+ * @param options Configuration options for the sink adapter.
37
+ * @returns A LogTape sink function that can be used in LogTape configuration.
38
+ * @since 1.0.0
39
+ */
40
+ function getPinoSink(logger, options) {
41
+ const categoryOptions = !options?.category ? void 0 : typeof options.category === "object" ? options.category : {};
42
+ const category = categoryOptions == null ? void 0 : {
43
+ separator: categoryOptions.separator ?? "·",
44
+ position: categoryOptions.position ?? "start",
45
+ decorator: categoryOptions.decorator ?? ":"
46
+ };
47
+ return (record) => {
48
+ let message = "";
49
+ const interpolationValues = [];
50
+ if (category?.position === "start" && record.category.length > 0) {
51
+ message += category.decorator === "[]" ? "[%s] " : category.decorator === "()" ? "(%s) " : category.decorator === "<>" ? "<%s> " : category.decorator === "{}" ? "{%s} " : category.decorator === ":" ? "%s: " : category.decorator === "-" ? "%s - " : category.decorator === "|" ? "%s | " : category.decorator === "/" ? "%s / " : "%s ";
52
+ interpolationValues.push(record.category.join(category.separator));
53
+ }
54
+ for (let i = 0; i < record.message.length; i += 2) {
55
+ message += record.message[i];
56
+ if (i + 1 < record.message.length) {
57
+ message += "%o";
58
+ interpolationValues.push(record.message[i + 1]);
59
+ }
60
+ }
61
+ if (category?.position === "end" && record.category.length > 0) {
62
+ message += category.decorator === "[]" ? " [%s]" : category.decorator === "()" ? " (%s)" : category.decorator === "<>" ? " <%s>" : category.decorator === "{}" ? " {%s}" : category.decorator === ":" ? ": %s" : category.decorator === "-" ? " - %s" : category.decorator === "|" ? " | %s" : category.decorator === "/" ? " / %s" : " %s";
63
+ interpolationValues.push(record.category.join(category.separator));
64
+ }
65
+ switch (record.level) {
66
+ case "trace": return logger.trace(record.properties, message, interpolationValues);
67
+ case "debug": return logger.debug(record.properties, message, interpolationValues);
68
+ case "info": return logger.info(record.properties, message, interpolationValues);
69
+ case "warning": return logger.warn(record.properties, message, interpolationValues);
70
+ case "error": return logger.error(record.properties, message, interpolationValues);
71
+ case "fatal": return logger.fatal(record.properties, message, interpolationValues);
72
+ }
73
+ };
74
+ }
75
+
76
+ //#endregion
77
+ export { getPinoSink };
78
+ //# sourceMappingURL=mod.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.js","names":["logger: Logger<CustomLevels, UseOnlyCustomLevels>","options?: PinoSinkOptions","category: Required<CategoryOptions> | undefined","record: LogRecord","interpolationValues: unknown[]"],"sources":["../mod.ts"],"sourcesContent":["import type { Logger } from \"pino\";\nimport type { LogRecord, Sink } from \"@logtape/logtape\";\n\n/**\n * Options for configuring the Pino sink adapter.\n * @since 1.0.0\n */\nexport interface PinoSinkOptions {\n /**\n * Configuration for how LogTape categories are handled in Pino logs.\n * - `false` or `undefined`: Categories are not included in the log message\n * - `true`: Categories are included with default formatting\n * - `CategoryOptions`: Custom category formatting configuration\n */\n readonly category?: boolean | CategoryOptions;\n}\n\n/**\n * Configuration options for formatting LogTape categories in Pino log messages.\n * @since 1.0.0\n */\nexport interface CategoryOptions {\n /**\n * The separator used to join category parts when multiple categories exist.\n * @default \"·\"\n */\n readonly separator?: string;\n\n /**\n * Where to position the category in the log message.\n * - `\"start\"`: Category appears at the beginning of the message\n * - `\"end\"`: Category appears at the end of the message\n * @default \"start\"\n */\n readonly position?: \"start\" | \"end\";\n\n /**\n * The decorator used to format the category in the log message.\n * - `\"[]\"`: [category] format\n * - `\"()\"`: (category) format\n * - `\"<>\"`: <category> format\n * - `\"{}\"`: {category} format\n * - `\":\"`: category: format\n * - `\"-\"`: category - format\n * - `\"|\"`: category | format\n * - `\"/\"`: category / format\n * - `\"\"`: category format (no decoration)\n * @default \":\"\n */\n readonly decorator?: \"[]\" | \"()\" | \"<>\" | \"{}\" | \":\" | \"-\" | \"|\" | \"/\" | \"\";\n}\n\n/**\n * Creates a LogTape sink that forwards log records to a Pino logger.\n *\n * This adapter allows LogTape-enabled libraries to integrate seamlessly with\n * applications that use Pino for logging.\n *\n * @example\n * ```typescript\n * import { configure } from \"@logtape/logtape\";\n * import { getPinoSink } from \"@logtape/adaptor-pino\";\n * import pino from \"pino\";\n *\n * const pinoLogger = pino();\n *\n * await configure({\n * sinks: {\n * pino: getPinoSink(pinoLogger, {\n * category: {\n * position: \"start\",\n * decorator: \"[]\",\n * separator: \".\"\n * }\n * })\n * },\n * loggers: [\n * { category: \"my-library\", sinks: [\"pino\"] }\n * ]\n * });\n * ```\n *\n * @typeParam CustomLevels The custom log levels supported by the Pino logger.\n * @typeParam UseOnlyCustomLevels Whether to use only custom levels defined\n * in the Pino logger.\n * @param logger The Pino logger instance to forward logs to.\n * @param options Configuration options for the sink adapter.\n * @returns A LogTape sink function that can be used in LogTape configuration.\n * @since 1.0.0\n */\nexport function getPinoSink<\n CustomLevels extends string,\n UseOnlyCustomLevels extends boolean,\n>(\n logger: Logger<CustomLevels, UseOnlyCustomLevels>,\n options?: PinoSinkOptions,\n): Sink {\n const categoryOptions = !options?.category\n ? undefined\n : typeof options.category === \"object\"\n ? options.category\n : {};\n const category: Required<CategoryOptions> | undefined =\n categoryOptions == null ? undefined : {\n separator: categoryOptions.separator ?? \"·\",\n position: categoryOptions.position ?? \"start\",\n decorator: categoryOptions.decorator ?? \":\",\n };\n return (record: LogRecord) => {\n let message = \"\";\n const interpolationValues: unknown[] = [];\n if (category?.position === \"start\" && record.category.length > 0) {\n message += category.decorator === \"[]\"\n ? \"[%s] \"\n : category.decorator === \"()\"\n ? \"(%s) \"\n : category.decorator === \"<>\"\n ? \"<%s> \"\n : category.decorator === \"{}\"\n ? \"{%s} \"\n : category.decorator === \":\"\n ? \"%s: \"\n : category.decorator === \"-\"\n ? \"%s - \"\n : category.decorator === \"|\"\n ? \"%s | \"\n : category.decorator === \"/\"\n ? \"%s / \"\n : \"%s \";\n interpolationValues.push(\n record.category.join(category.separator),\n );\n }\n for (let i = 0; i < record.message.length; i += 2) {\n message += record.message[i];\n if (i + 1 < record.message.length) {\n message += \"%o\";\n interpolationValues.push(record.message[i + 1]);\n }\n }\n if (category?.position === \"end\" && record.category.length > 0) {\n message += category.decorator === \"[]\"\n ? \" [%s]\"\n : category.decorator === \"()\"\n ? \" (%s)\"\n : category.decorator === \"<>\"\n ? \" <%s>\"\n : category.decorator === \"{}\"\n ? \" {%s}\"\n : category.decorator === \":\"\n ? \": %s\"\n : category.decorator === \"-\"\n ? \" - %s\"\n : category.decorator === \"|\"\n ? \" | %s\"\n : category.decorator === \"/\"\n ? \" / %s\"\n : \" %s\";\n interpolationValues.push(\n record.category.join(category.separator),\n );\n }\n switch (record.level) {\n case \"trace\":\n return logger.trace(record.properties, message, interpolationValues);\n case \"debug\":\n return logger.debug(record.properties, message, interpolationValues);\n case \"info\":\n return logger.info(record.properties, message, interpolationValues);\n case \"warning\":\n return logger.warn(record.properties, message, interpolationValues);\n case \"error\":\n return logger.error(record.properties, message, interpolationValues);\n case \"fatal\":\n return logger.fatal(record.properties, message, interpolationValues);\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0FA,SAAgB,YAIdA,QACAC,SACM;CACN,MAAM,mBAAmB,SAAS,2BAEvB,QAAQ,aAAa,WAC5B,QAAQ,WACR,CAAE;CACN,MAAMC,WACJ,mBAAmB,gBAAmB;EACpC,WAAW,gBAAgB,aAAa;EACxC,UAAU,gBAAgB,YAAY;EACtC,WAAW,gBAAgB,aAAa;CACzC;AACH,QAAO,CAACC,WAAsB;EAC5B,IAAI,UAAU;EACd,MAAMC,sBAAiC,CAAE;AACzC,MAAI,UAAU,aAAa,WAAW,OAAO,SAAS,SAAS,GAAG;AAChE,cAAW,SAAS,cAAc,OAC9B,UACA,SAAS,cAAc,OACvB,UACA,SAAS,cAAc,OACvB,UACA,SAAS,cAAc,OACvB,UACA,SAAS,cAAc,MACvB,SACA,SAAS,cAAc,MACvB,UACA,SAAS,cAAc,MACvB,UACA,SAAS,cAAc,MACvB,UACA;AACJ,uBAAoB,KAClB,OAAO,SAAS,KAAK,SAAS,UAAU,CACzC;EACF;AACD,OAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,QAAQ,KAAK,GAAG;AACjD,cAAW,OAAO,QAAQ;AAC1B,OAAI,IAAI,IAAI,OAAO,QAAQ,QAAQ;AACjC,eAAW;AACX,wBAAoB,KAAK,OAAO,QAAQ,IAAI,GAAG;GAChD;EACF;AACD,MAAI,UAAU,aAAa,SAAS,OAAO,SAAS,SAAS,GAAG;AAC9D,cAAW,SAAS,cAAc,OAC9B,UACA,SAAS,cAAc,OACvB,UACA,SAAS,cAAc,OACvB,UACA,SAAS,cAAc,OACvB,UACA,SAAS,cAAc,MACvB,SACA,SAAS,cAAc,MACvB,UACA,SAAS,cAAc,MACvB,UACA,SAAS,cAAc,MACvB,UACA;AACJ,uBAAoB,KAClB,OAAO,SAAS,KAAK,SAAS,UAAU,CACzC;EACF;AACD,UAAQ,OAAO,OAAf;GACE,KAAK,QACH,QAAO,OAAO,MAAM,OAAO,YAAY,SAAS,oBAAoB;GACtE,KAAK,QACH,QAAO,OAAO,MAAM,OAAO,YAAY,SAAS,oBAAoB;GACtE,KAAK,OACH,QAAO,OAAO,KAAK,OAAO,YAAY,SAAS,oBAAoB;GACrE,KAAK,UACH,QAAO,OAAO,KAAK,OAAO,YAAY,SAAS,oBAAoB;GACrE,KAAK,QACH,QAAO,OAAO,MAAM,OAAO,YAAY,SAAS,oBAAoB;GACtE,KAAK,QACH,QAAO,OAAO,MAAM,OAAO,YAAY,SAAS,oBAAoB;EACvE;CACF;AACF"}
package/mod.test.ts ADDED
@@ -0,0 +1,405 @@
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/mod.ts ADDED
@@ -0,0 +1,178 @@
1
+ import type { Logger } from "pino";
2
+ import type { LogRecord, 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
+ * @typeParam CustomLevels The custom log levels supported by the Pino logger.
84
+ * @typeParam 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
+ }
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@logtape/adaptor-pino",
3
+ "version": "1.0.0-dev.253+3fd9a841",
4
+ "description": "Pino adapter for LogTape logging library",
5
+ "keywords": [
6
+ "logging",
7
+ "log",
8
+ "logger",
9
+ "pino",
10
+ "adapter",
11
+ "logtape",
12
+ "sink"
13
+ ],
14
+ "license": "MIT",
15
+ "author": {
16
+ "name": "Hong Minhee",
17
+ "email": "hong@minhee.org",
18
+ "url": "https://hongminhee.org/"
19
+ },
20
+ "homepage": "https://logtape.org/",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/dahlia/logtape.git",
24
+ "directory": "adaptor-pino/"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/dahlia/logtape/issues"
28
+ },
29
+ "funding": [
30
+ "https://github.com/sponsors/dahlia"
31
+ ],
32
+ "type": "module",
33
+ "module": "./dist/mod.js",
34
+ "main": "./dist/mod.cjs",
35
+ "types": "./dist/mod.d.ts",
36
+ "exports": {
37
+ ".": {
38
+ "types": {
39
+ "import": "./dist/mod.d.ts",
40
+ "require": "./dist/mod.d.cts"
41
+ },
42
+ "import": "./dist/mod.js",
43
+ "require": "./dist/mod.cjs"
44
+ },
45
+ "./package.json": "./package.json"
46
+ },
47
+ "sideEffects": false,
48
+ "peerDependencies": {
49
+ "pino": "^9.7.0",
50
+ "@logtape/logtape": "1.0.0-dev.253+3fd9a841"
51
+ },
52
+ "devDependencies": {
53
+ "@alinea/suite": "^0.6.3",
54
+ "@std/assert": "npm:@jsr/std__assert@^1.0.13",
55
+ "@std/async": "npm:@jsr/std__async@^1.0.13",
56
+ "pino": "^9.7.0",
57
+ "pino-abstract-transport": "^2.0.0",
58
+ "tsdown": "^0.12.7",
59
+ "typescript": "^5.8.3"
60
+ },
61
+ "scripts": {
62
+ "build": "tsdown",
63
+ "prepublish": "tsdown",
64
+ "test": "tsdown && node --experimental-transform-types --test",
65
+ "test:bun": "tsdown && bun test",
66
+ "test:deno": "deno test --allow-env --allow-sys",
67
+ "test-all": "tsdown && node --experimental-transform-types --test && bun test && deno test"
68
+ }
69
+ }
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from "tsdown";
2
+
3
+ export default defineConfig({
4
+ entry: "mod.ts",
5
+ dts: {
6
+ sourcemap: true,
7
+ },
8
+ format: ["esm", "cjs"],
9
+ platform: "node",
10
+ unbundle: true,
11
+ });