@ontrails/pino 1.0.0-beta.18

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/CHANGELOG.md ADDED
@@ -0,0 +1,22 @@
1
+ # @ontrails/pino
2
+
3
+ ## 1.0.0-beta.18
4
+
5
+ ### Minor Changes
6
+
7
+ - e504e67: Add the publishable `@ontrails/pino` package scaffold.
8
+ - 1a65022: Implement the structural Pino log sink.
9
+
10
+ ### Patch Changes
11
+
12
+ - bf44972: Document Pino sink usage and publish-readiness checks.
13
+ - Updated dependencies [bf44972]
14
+ - Updated dependencies [e0ae995]
15
+ - @ontrails/observe@1.0.0-beta.18
16
+
17
+ ## 1.0.0-beta.17
18
+
19
+ ### Minor Changes
20
+
21
+ - Initial publishable package scaffold.
22
+ - Add structural Pino log sink.
package/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # @ontrails/pino
2
+
3
+ Pino adapter package for `@ontrails/observe`.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @ontrails/observe @ontrails/pino pino
9
+ ```
10
+
11
+ `pino` is supplied by your application. `@ontrails/pino` has no hard runtime
12
+ dependency on it.
13
+
14
+ ## Usage
15
+
16
+ Use `createPinoSink(...)` when you already have a Pino-shaped logger and want
17
+ Trails log records to flow into it:
18
+
19
+ ```typescript
20
+ import pino from 'pino';
21
+ import { createPinoSink } from '@ontrails/pino';
22
+
23
+ const sink = createPinoSink(pino());
24
+ ```
25
+
26
+ The package does not depend on `pino`; it accepts any object shaped like a Pino
27
+ logger through `PinoLoggerLike`. Records are forwarded in Pino's object-first
28
+ style as `logger.info(payload, message)`, preserving the metadata already
29
+ redacted by Trails.
30
+
31
+ ```typescript
32
+ import pino from 'pino';
33
+ import { topo } from '@ontrails/core';
34
+ import { createPinoSink } from '@ontrails/pino';
35
+
36
+ const logger = pino();
37
+ // trails is your application's array of Trail definitions.
38
+ const graph = topo('app', trails, {
39
+ observe: {
40
+ log: createPinoSink(logger),
41
+ },
42
+ });
43
+ ```
44
+
45
+ ## Structural Logger Shape
46
+
47
+ `PinoLoggerLike` requires `trace`, `debug`, `info`, `warn`, `error`, and
48
+ `fatal` methods that accept `(payload, message)`. The sink forwards:
49
+
50
+ - `record.message` as the Pino message argument.
51
+ - `record.category`, `record.timestamp`, and `record.metadata` in the payload.
52
+ - `silent` records as no-ops.
53
+
54
+ If a required method is missing at runtime, the sink throws instead of silently
55
+ dropping the record.
56
+
57
+ ## Publishing
58
+
59
+ `@ontrails/pino` participates in the standard Trails package publish checks:
60
+
61
+ ```bash
62
+ bun run publish:check
63
+ bun run publish:registry-check
64
+ ```
65
+
66
+ `publish:registry-check` is read-only. Before the first registry publication it
67
+ may report `@ontrails/pino` as a first-time package candidate. Actual package
68
+ publication still goes through the repo script:
69
+
70
+ ```bash
71
+ bun run publish:packages
72
+ ```
73
+
74
+ Do not publish this package with `npm publish` or `changeset publish`.
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@ontrails/pino",
3
+ "version": "1.0.0-beta.18",
4
+ "files": [
5
+ "src/**/*.ts",
6
+ "!src/**/__tests__/**",
7
+ "!src/**/*.test.ts",
8
+ "!src/**/*.test-d.ts",
9
+ "README.md",
10
+ "CHANGELOG.md"
11
+ ],
12
+ "type": "module",
13
+ "exports": {
14
+ ".": "./src/index.ts",
15
+ "./package.json": "./package.json"
16
+ },
17
+ "scripts": {
18
+ "build": "tsc -b",
19
+ "test": "bun test",
20
+ "typecheck": "tsc --noEmit",
21
+ "lint": "oxlint ./src",
22
+ "clean": "rm -rf dist *.tsbuildinfo"
23
+ },
24
+ "peerDependencies": {
25
+ "@ontrails/observe": "^1.0.0-beta.17"
26
+ }
27
+ }
package/src/index.ts ADDED
@@ -0,0 +1,90 @@
1
+ import type { LogLevel, LogRecord, LogSink } from '@ontrails/observe';
2
+
3
+ /**
4
+ * Package identifier for the publishable Pino adapter package.
5
+ */
6
+ export const pinoPackageName = '@ontrails/pino';
7
+
8
+ export type PinoLogMethod = (
9
+ payload: Record<string, unknown>,
10
+ message?: string
11
+ ) => void;
12
+
13
+ /**
14
+ * Structural subset of a Pino logger.
15
+ */
16
+ export interface PinoLoggerLike {
17
+ debug: PinoLogMethod;
18
+ error: PinoLogMethod;
19
+ fatal: PinoLogMethod;
20
+ info: PinoLogMethod;
21
+ trace: PinoLogMethod;
22
+ warn: PinoLogMethod;
23
+ }
24
+
25
+ export interface PinoSinkOptions {
26
+ /** Sink name exposed to Trails observe configuration. Defaults to `pino`. */
27
+ readonly name?: string | undefined;
28
+ }
29
+
30
+ type ForwardMethod = Exclude<LogLevel, 'silent'>;
31
+
32
+ const LEVEL_MAP: Record<LogLevel, ForwardMethod | undefined> = {
33
+ debug: 'debug',
34
+ error: 'error',
35
+ fatal: 'fatal',
36
+ info: 'info',
37
+ silent: undefined,
38
+ trace: 'trace',
39
+ warn: 'warn',
40
+ };
41
+
42
+ const buildPayload = (record: LogRecord): Record<string, unknown> => ({
43
+ ...record.metadata,
44
+ category: record.category,
45
+ timestamp: record.timestamp.toISOString(),
46
+ });
47
+
48
+ const resolveLoggerMethod = (
49
+ logger: PinoLoggerLike,
50
+ method: ForwardMethod
51
+ ): PinoLogMethod => {
52
+ const loggerMethod = logger[method];
53
+ if (typeof loggerMethod !== 'function') {
54
+ throw new TypeError(`Pino logger is missing "${method}" method`);
55
+ }
56
+ return loggerMethod.bind(logger);
57
+ };
58
+
59
+ const resolveLoggerMethods = (
60
+ logger: PinoLoggerLike
61
+ ): Record<ForwardMethod, PinoLogMethod> => ({
62
+ debug: resolveLoggerMethod(logger, 'debug'),
63
+ error: resolveLoggerMethod(logger, 'error'),
64
+ fatal: resolveLoggerMethod(logger, 'fatal'),
65
+ info: resolveLoggerMethod(logger, 'info'),
66
+ trace: resolveLoggerMethod(logger, 'trace'),
67
+ warn: resolveLoggerMethod(logger, 'warn'),
68
+ });
69
+
70
+ /**
71
+ * Create a Trails log sink that forwards records to a structural Pino logger.
72
+ */
73
+ export const createPinoSink = (
74
+ logger: PinoLoggerLike,
75
+ options: PinoSinkOptions = {}
76
+ ): LogSink => {
77
+ const methods = resolveLoggerMethods(logger);
78
+
79
+ return {
80
+ name: options.name ?? 'pino',
81
+ write(record: LogRecord): void {
82
+ const method = LEVEL_MAP[record.level];
83
+ if (method === undefined) {
84
+ return;
85
+ }
86
+
87
+ methods[method](buildPayload(record), record.message);
88
+ },
89
+ };
90
+ };