@beignet/provider-logger-pino 0.0.1
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 +5 -0
- package/README.md +83 -0
- package/dist/index.d.ts +94 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +166 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -0
- package/src/index.ts +201 -0
package/CHANGELOG.md
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# @beignet/provider-logger-pino
|
|
2
|
+
|
|
3
|
+
Pino-backed `LoggerPort` provider for Beignet.
|
|
4
|
+
|
|
5
|
+
Use this package when you want `ctx.ports.logger` backed by Pino while keeping
|
|
6
|
+
application code dependent on the stable `LoggerPort` interface from
|
|
7
|
+
`@beignet/core/ports`.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bun add @beignet/core @beignet/provider-logger-pino pino
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Pretty development logs are optional:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bun add -d pino-pretty
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Setup
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { createNextServer } from "@beignet/next";
|
|
25
|
+
import { loggerPinoProvider } from "@beignet/provider-logger-pino";
|
|
26
|
+
import { appPorts } from "@/infra/app-ports";
|
|
27
|
+
|
|
28
|
+
export const server = await createNextServer({
|
|
29
|
+
ports: appPorts,
|
|
30
|
+
providers: [loggerPinoProvider],
|
|
31
|
+
createContext: ({ ports }) => ({
|
|
32
|
+
requestId: crypto.randomUUID(),
|
|
33
|
+
ports,
|
|
34
|
+
}),
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The provider installs `ctx.ports.logger`.
|
|
39
|
+
|
|
40
|
+
## Environment
|
|
41
|
+
|
|
42
|
+
The provider reads `LOG_`-prefixed environment variables:
|
|
43
|
+
|
|
44
|
+
| Variable | Default | Description |
|
|
45
|
+
| --- | --- | --- |
|
|
46
|
+
| `LOG_LEVEL` | `info` | `trace`, `debug`, `info`, `warn`, `error`, or `fatal` |
|
|
47
|
+
| `LOG_FORMAT` | `json` | `json` or `pretty` |
|
|
48
|
+
| `LOG_SERVICE` | none | Optional service name added to log bindings |
|
|
49
|
+
| `LOG_TIMESTAMP` | `true` | Include ISO timestamps |
|
|
50
|
+
|
|
51
|
+
`LOG_FORMAT=pretty` requires `pino-pretty`. If it is unavailable, the provider
|
|
52
|
+
falls back to JSON logging and emits a warning.
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
ctx.ports.logger.info("Post published", {
|
|
58
|
+
postId: post.id,
|
|
59
|
+
actorId: ctx.actor.type === "user" ? ctx.actor.id : undefined,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const log = ctx.ports.logger.child({ requestId: ctx.requestId });
|
|
63
|
+
log.error("Failed to publish post", { error });
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Exports
|
|
67
|
+
|
|
68
|
+
| Export | Purpose |
|
|
69
|
+
| --- | --- |
|
|
70
|
+
| `loggerPinoProvider` | Ready-to-install Beignet provider |
|
|
71
|
+
| `LoggerPinoConfig` | Validated provider config type |
|
|
72
|
+
| `LoggerPort` | Re-export from `@beignet/core/ports` |
|
|
73
|
+
| `LogLevel` | Re-export from `@beignet/core/ports` |
|
|
74
|
+
|
|
75
|
+
## Read next
|
|
76
|
+
|
|
77
|
+
- Docs site logging guide: https://beignet.dev/logging
|
|
78
|
+
- Providers: https://beignet.dev/providers
|
|
79
|
+
- Pino documentation: https://getpino.io/
|
|
80
|
+
|
|
81
|
+
## License
|
|
82
|
+
|
|
83
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @beignet/provider-logger-pino
|
|
3
|
+
*
|
|
4
|
+
* Pino logger provider that extends ports with structured logging capabilities using Pino.
|
|
5
|
+
*
|
|
6
|
+
* Configuration:
|
|
7
|
+
* - LOG_LEVEL: Minimum log level (trace/debug/info/warn/error/fatal, default: info)
|
|
8
|
+
* - LOG_FORMAT: Log format (json/pretty, default: json)
|
|
9
|
+
* - LOG_SERVICE: Optional service name for log enrichment
|
|
10
|
+
* - LOG_TIMESTAMP: Whether to include timestamps (true/false, default: true)
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { createNextServer } from "@beignet/next";
|
|
15
|
+
* import { loggerPinoProvider } from "@beignet/provider-logger-pino";
|
|
16
|
+
*
|
|
17
|
+
* const server = await createNextServer({
|
|
18
|
+
* ports: basePorts,
|
|
19
|
+
* providers: [loggerPinoProvider],
|
|
20
|
+
* // ...
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // In your use cases:
|
|
24
|
+
* ctx.ports.logger.info("User logged in", { userId: user.id });
|
|
25
|
+
*
|
|
26
|
+
* // Create child logger with request context:
|
|
27
|
+
* const log = ctx.ports.logger.child({ requestId: ctx.requestId });
|
|
28
|
+
* log.info("Processing request");
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
import type { LoggerPort } from "@beignet/core/ports";
|
|
32
|
+
import { z } from "zod";
|
|
33
|
+
export type { LoggerPort, LogLevel } from "@beignet/core/ports";
|
|
34
|
+
/**
|
|
35
|
+
* Configuration schema for the Pino logger provider.
|
|
36
|
+
* Validates environment variables with LOG_ prefix.
|
|
37
|
+
*/
|
|
38
|
+
declare const LoggerPinoConfigSchema: z.ZodObject<{
|
|
39
|
+
LEVEL: z.ZodDefault<z.ZodEnum<{
|
|
40
|
+
trace: "trace";
|
|
41
|
+
debug: "debug";
|
|
42
|
+
info: "info";
|
|
43
|
+
warn: "warn";
|
|
44
|
+
error: "error";
|
|
45
|
+
fatal: "fatal";
|
|
46
|
+
}>>;
|
|
47
|
+
FORMAT: z.ZodDefault<z.ZodEnum<{
|
|
48
|
+
json: "json";
|
|
49
|
+
pretty: "pretty";
|
|
50
|
+
}>>;
|
|
51
|
+
SERVICE: z.ZodOptional<z.ZodString>;
|
|
52
|
+
TIMESTAMP: z.ZodDefault<z.ZodCoercedBoolean<unknown>>;
|
|
53
|
+
}, z.core.$strip>;
|
|
54
|
+
/**
|
|
55
|
+
* Inferred configuration type for Pino logger provider.
|
|
56
|
+
*/
|
|
57
|
+
export type LoggerPinoConfig = z.infer<typeof LoggerPinoConfigSchema>;
|
|
58
|
+
/**
|
|
59
|
+
* Pino logger provider that extends ports with structured logging capabilities.
|
|
60
|
+
*
|
|
61
|
+
* Configuration via environment variables:
|
|
62
|
+
* - LOG_LEVEL: Minimum log level (trace/debug/info/warn/error/fatal, default: info)
|
|
63
|
+
* - LOG_FORMAT: Log format (json/pretty, default: json)
|
|
64
|
+
* - LOG_SERVICE: Optional service name for log enrichment
|
|
65
|
+
* - LOG_TIMESTAMP: Whether to include timestamps (true/false, default: true)
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* const server = await createNextServer({
|
|
70
|
+
* ports: basePorts,
|
|
71
|
+
* providers: [loggerPinoProvider],
|
|
72
|
+
* // ...
|
|
73
|
+
* });
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare const loggerPinoProvider: import("@beignet/core/providers").ServiceProvider<unknown, z.ZodObject<{
|
|
77
|
+
LEVEL: z.ZodDefault<z.ZodEnum<{
|
|
78
|
+
trace: "trace";
|
|
79
|
+
debug: "debug";
|
|
80
|
+
info: "info";
|
|
81
|
+
warn: "warn";
|
|
82
|
+
error: "error";
|
|
83
|
+
fatal: "fatal";
|
|
84
|
+
}>>;
|
|
85
|
+
FORMAT: z.ZodDefault<z.ZodEnum<{
|
|
86
|
+
json: "json";
|
|
87
|
+
pretty: "pretty";
|
|
88
|
+
}>>;
|
|
89
|
+
SERVICE: z.ZodOptional<z.ZodString>;
|
|
90
|
+
TIMESTAMP: z.ZodDefault<z.ZodCoercedBoolean<unknown>>;
|
|
91
|
+
}, z.core.$strip>, {
|
|
92
|
+
logger: LoggerPort;
|
|
93
|
+
}>;
|
|
94
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGtD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEhE;;;GAGG;AACH,QAAA,MAAM,sBAAsB;;;;;;;;;;;;;;;iBA6B1B,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AA4EtE;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;EA8B7B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @beignet/provider-logger-pino
|
|
3
|
+
*
|
|
4
|
+
* Pino logger provider that extends ports with structured logging capabilities using Pino.
|
|
5
|
+
*
|
|
6
|
+
* Configuration:
|
|
7
|
+
* - LOG_LEVEL: Minimum log level (trace/debug/info/warn/error/fatal, default: info)
|
|
8
|
+
* - LOG_FORMAT: Log format (json/pretty, default: json)
|
|
9
|
+
* - LOG_SERVICE: Optional service name for log enrichment
|
|
10
|
+
* - LOG_TIMESTAMP: Whether to include timestamps (true/false, default: true)
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { createNextServer } from "@beignet/next";
|
|
15
|
+
* import { loggerPinoProvider } from "@beignet/provider-logger-pino";
|
|
16
|
+
*
|
|
17
|
+
* const server = await createNextServer({
|
|
18
|
+
* ports: basePorts,
|
|
19
|
+
* providers: [loggerPinoProvider],
|
|
20
|
+
* // ...
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // In your use cases:
|
|
24
|
+
* ctx.ports.logger.info("User logged in", { userId: user.id });
|
|
25
|
+
*
|
|
26
|
+
* // Create child logger with request context:
|
|
27
|
+
* const log = ctx.ports.logger.child({ requestId: ctx.requestId });
|
|
28
|
+
* log.info("Processing request");
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
import { createProvider } from "@beignet/core/providers";
|
|
32
|
+
import pino from "pino";
|
|
33
|
+
import { z } from "zod";
|
|
34
|
+
/**
|
|
35
|
+
* Configuration schema for the Pino logger provider.
|
|
36
|
+
* Validates environment variables with LOG_ prefix.
|
|
37
|
+
*/
|
|
38
|
+
const LoggerPinoConfigSchema = z.object({
|
|
39
|
+
/**
|
|
40
|
+
* Minimum log level.
|
|
41
|
+
* Messages below this level will be ignored.
|
|
42
|
+
* Default: "info"
|
|
43
|
+
*/
|
|
44
|
+
LEVEL: z
|
|
45
|
+
.enum(["trace", "debug", "info", "warn", "error", "fatal"])
|
|
46
|
+
.default("info"),
|
|
47
|
+
/**
|
|
48
|
+
* Log format.
|
|
49
|
+
* - "json": Newline-delimited JSON (production-friendly)
|
|
50
|
+
* - "pretty": Human-readable format (requires pino-pretty)
|
|
51
|
+
* Default: "json"
|
|
52
|
+
*/
|
|
53
|
+
FORMAT: z.enum(["json", "pretty"]).default("json"),
|
|
54
|
+
/**
|
|
55
|
+
* Optional service name for log enrichment.
|
|
56
|
+
* Will be added to all log entries as { service: "your-service" }
|
|
57
|
+
*/
|
|
58
|
+
SERVICE: z.string().optional(),
|
|
59
|
+
/**
|
|
60
|
+
* Whether to include timestamps in log entries.
|
|
61
|
+
* Default: true
|
|
62
|
+
*/
|
|
63
|
+
TIMESTAMP: z.coerce.boolean().default(true),
|
|
64
|
+
});
|
|
65
|
+
/**
|
|
66
|
+
* Create a Pino logger instance with the given configuration.
|
|
67
|
+
*
|
|
68
|
+
* @param config - Validated logger configuration
|
|
69
|
+
* @param bindings - Optional base bindings to add to all log entries
|
|
70
|
+
* @returns Configured Pino logger instance
|
|
71
|
+
*/
|
|
72
|
+
function createPinoLogger(config, bindings = {}) {
|
|
73
|
+
const pinoOptions = {
|
|
74
|
+
level: config.LEVEL,
|
|
75
|
+
timestamp: config.TIMESTAMP ? pino.stdTimeFunctions.isoTime : false,
|
|
76
|
+
base: bindings,
|
|
77
|
+
};
|
|
78
|
+
// Handle pretty format
|
|
79
|
+
if (config.FORMAT === "pretty") {
|
|
80
|
+
try {
|
|
81
|
+
// Attempt to use pino-pretty transport
|
|
82
|
+
return pino({
|
|
83
|
+
...pinoOptions,
|
|
84
|
+
transport: {
|
|
85
|
+
target: "pino-pretty",
|
|
86
|
+
options: {
|
|
87
|
+
colorize: true,
|
|
88
|
+
translateTime: "HH:MM:ss Z",
|
|
89
|
+
ignore: "pid,hostname",
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
catch (_error) {
|
|
95
|
+
// pino-pretty not available, fall back to JSON with warning
|
|
96
|
+
const jsonLogger = pino(pinoOptions);
|
|
97
|
+
jsonLogger.warn("LOG_FORMAT=pretty requested but pino-pretty is not installed. " +
|
|
98
|
+
"Falling back to JSON format. Install pino-pretty to enable pretty printing.");
|
|
99
|
+
return jsonLogger;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Default to JSON format
|
|
103
|
+
return pino(pinoOptions);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Wrap a Pino logger instance to conform to the LoggerPort interface.
|
|
107
|
+
*
|
|
108
|
+
* Pino's API uses the signature: logger.level(obj, msg) or logger.level(msg)
|
|
109
|
+
* We adapt this to: logger.level(msg, meta?)
|
|
110
|
+
*
|
|
111
|
+
* @param logger - Pino logger instance to wrap
|
|
112
|
+
* @returns LoggerPort implementation
|
|
113
|
+
*/
|
|
114
|
+
function wrapPino(logger) {
|
|
115
|
+
return {
|
|
116
|
+
trace: (message, meta) => meta ? logger.trace(meta, message) : logger.trace(message),
|
|
117
|
+
debug: (message, meta) => meta ? logger.debug(meta, message) : logger.debug(message),
|
|
118
|
+
info: (message, meta) => meta ? logger.info(meta, message) : logger.info(message),
|
|
119
|
+
warn: (message, meta) => meta ? logger.warn(meta, message) : logger.warn(message),
|
|
120
|
+
error: (message, meta) => meta ? logger.error(meta, message) : logger.error(message),
|
|
121
|
+
fatal: (message, meta) => meta ? logger.fatal(meta, message) : logger.fatal(message),
|
|
122
|
+
child: (bindings) => wrapPino(logger.child(bindings)),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Pino logger provider that extends ports with structured logging capabilities.
|
|
127
|
+
*
|
|
128
|
+
* Configuration via environment variables:
|
|
129
|
+
* - LOG_LEVEL: Minimum log level (trace/debug/info/warn/error/fatal, default: info)
|
|
130
|
+
* - LOG_FORMAT: Log format (json/pretty, default: json)
|
|
131
|
+
* - LOG_SERVICE: Optional service name for log enrichment
|
|
132
|
+
* - LOG_TIMESTAMP: Whether to include timestamps (true/false, default: true)
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* const server = await createNextServer({
|
|
137
|
+
* ports: basePorts,
|
|
138
|
+
* providers: [loggerPinoProvider],
|
|
139
|
+
* // ...
|
|
140
|
+
* });
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export const loggerPinoProvider = createProvider({
|
|
144
|
+
name: "logger-pino",
|
|
145
|
+
config: {
|
|
146
|
+
schema: LoggerPinoConfigSchema,
|
|
147
|
+
envPrefix: "LOG_",
|
|
148
|
+
},
|
|
149
|
+
async setup({ config }) {
|
|
150
|
+
if (!config) {
|
|
151
|
+
throw new Error("[loggerPinoProvider] Missing logger config. " +
|
|
152
|
+
"Please set LOG_LEVEL, LOG_FORMAT, etc. environment variables.");
|
|
153
|
+
}
|
|
154
|
+
// Prepare base bindings
|
|
155
|
+
const baseBindings = {};
|
|
156
|
+
if (config.SERVICE) {
|
|
157
|
+
baseBindings.service = config.SERVICE;
|
|
158
|
+
}
|
|
159
|
+
// Create and configure Pino logger
|
|
160
|
+
const logger = createPinoLogger(config, baseBindings);
|
|
161
|
+
// Wrap logger to match LoggerPort interface
|
|
162
|
+
const port = wrapPino(logger);
|
|
163
|
+
return { ports: { logger: port } };
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@beignet/provider-logger-pino",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Pino logger provider for Beignet - adds logger port using pino",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"src",
|
|
17
|
+
"!src/**/*.test.ts",
|
|
18
|
+
"!src/**/*.test.tsx",
|
|
19
|
+
"!src/**/*.test-d.ts",
|
|
20
|
+
"README.md",
|
|
21
|
+
"CHANGELOG.md"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc",
|
|
25
|
+
"dev": "tsc --watch",
|
|
26
|
+
"clean": "rm -rf dist coverage .turbo",
|
|
27
|
+
"test": "bun test",
|
|
28
|
+
"test:coverage": "bun test --coverage",
|
|
29
|
+
"lint": "biome check ."
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"beignet",
|
|
33
|
+
"logger",
|
|
34
|
+
"logging",
|
|
35
|
+
"provider",
|
|
36
|
+
"pino",
|
|
37
|
+
"structured-logging",
|
|
38
|
+
"ports"
|
|
39
|
+
],
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/taylorbryant/beignet.git",
|
|
44
|
+
"directory": "packages/provider-logger-pino"
|
|
45
|
+
},
|
|
46
|
+
"author": "Taylor Bryant",
|
|
47
|
+
"homepage": "https://github.com/taylorbryant/beignet#readme",
|
|
48
|
+
"bugs": "https://github.com/taylorbryant/beignet/issues",
|
|
49
|
+
"sideEffects": false,
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=18.0.0"
|
|
55
|
+
},
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"pino": "^9.0.0 || ^10.0.0"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"zod": "^4.0.0",
|
|
61
|
+
"@beignet/core": "*"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@types/bun": "^1.3.13",
|
|
65
|
+
"@types/node": "^20.10.0",
|
|
66
|
+
"pino": "^10.0.0",
|
|
67
|
+
"typescript": "^5.3.0"
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @beignet/provider-logger-pino
|
|
3
|
+
*
|
|
4
|
+
* Pino logger provider that extends ports with structured logging capabilities using Pino.
|
|
5
|
+
*
|
|
6
|
+
* Configuration:
|
|
7
|
+
* - LOG_LEVEL: Minimum log level (trace/debug/info/warn/error/fatal, default: info)
|
|
8
|
+
* - LOG_FORMAT: Log format (json/pretty, default: json)
|
|
9
|
+
* - LOG_SERVICE: Optional service name for log enrichment
|
|
10
|
+
* - LOG_TIMESTAMP: Whether to include timestamps (true/false, default: true)
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { createNextServer } from "@beignet/next";
|
|
15
|
+
* import { loggerPinoProvider } from "@beignet/provider-logger-pino";
|
|
16
|
+
*
|
|
17
|
+
* const server = await createNextServer({
|
|
18
|
+
* ports: basePorts,
|
|
19
|
+
* providers: [loggerPinoProvider],
|
|
20
|
+
* // ...
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // In your use cases:
|
|
24
|
+
* ctx.ports.logger.info("User logged in", { userId: user.id });
|
|
25
|
+
*
|
|
26
|
+
* // Create child logger with request context:
|
|
27
|
+
* const log = ctx.ports.logger.child({ requestId: ctx.requestId });
|
|
28
|
+
* log.info("Processing request");
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import type { LoggerPort } from "@beignet/core/ports";
|
|
33
|
+
import { createProvider } from "@beignet/core/providers";
|
|
34
|
+
import pino, { type Logger as PinoLogger } from "pino";
|
|
35
|
+
import { z } from "zod";
|
|
36
|
+
|
|
37
|
+
export type { LoggerPort, LogLevel } from "@beignet/core/ports";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Configuration schema for the Pino logger provider.
|
|
41
|
+
* Validates environment variables with LOG_ prefix.
|
|
42
|
+
*/
|
|
43
|
+
const LoggerPinoConfigSchema = z.object({
|
|
44
|
+
/**
|
|
45
|
+
* Minimum log level.
|
|
46
|
+
* Messages below this level will be ignored.
|
|
47
|
+
* Default: "info"
|
|
48
|
+
*/
|
|
49
|
+
LEVEL: z
|
|
50
|
+
.enum(["trace", "debug", "info", "warn", "error", "fatal"])
|
|
51
|
+
.default("info"),
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Log format.
|
|
55
|
+
* - "json": Newline-delimited JSON (production-friendly)
|
|
56
|
+
* - "pretty": Human-readable format (requires pino-pretty)
|
|
57
|
+
* Default: "json"
|
|
58
|
+
*/
|
|
59
|
+
FORMAT: z.enum(["json", "pretty"]).default("json"),
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Optional service name for log enrichment.
|
|
63
|
+
* Will be added to all log entries as { service: "your-service" }
|
|
64
|
+
*/
|
|
65
|
+
SERVICE: z.string().optional(),
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Whether to include timestamps in log entries.
|
|
69
|
+
* Default: true
|
|
70
|
+
*/
|
|
71
|
+
TIMESTAMP: z.coerce.boolean().default(true),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Inferred configuration type for Pino logger provider.
|
|
76
|
+
*/
|
|
77
|
+
export type LoggerPinoConfig = z.infer<typeof LoggerPinoConfigSchema>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Create a Pino logger instance with the given configuration.
|
|
81
|
+
*
|
|
82
|
+
* @param config - Validated logger configuration
|
|
83
|
+
* @param bindings - Optional base bindings to add to all log entries
|
|
84
|
+
* @returns Configured Pino logger instance
|
|
85
|
+
*/
|
|
86
|
+
function createPinoLogger(
|
|
87
|
+
config: LoggerPinoConfig,
|
|
88
|
+
bindings: Record<string, unknown> = {},
|
|
89
|
+
): PinoLogger {
|
|
90
|
+
const pinoOptions: pino.LoggerOptions = {
|
|
91
|
+
level: config.LEVEL,
|
|
92
|
+
timestamp: config.TIMESTAMP ? pino.stdTimeFunctions.isoTime : false,
|
|
93
|
+
base: bindings,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// Handle pretty format
|
|
97
|
+
if (config.FORMAT === "pretty") {
|
|
98
|
+
try {
|
|
99
|
+
// Attempt to use pino-pretty transport
|
|
100
|
+
return pino({
|
|
101
|
+
...pinoOptions,
|
|
102
|
+
transport: {
|
|
103
|
+
target: "pino-pretty",
|
|
104
|
+
options: {
|
|
105
|
+
colorize: true,
|
|
106
|
+
translateTime: "HH:MM:ss Z",
|
|
107
|
+
ignore: "pid,hostname",
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
} catch (_error) {
|
|
112
|
+
// pino-pretty not available, fall back to JSON with warning
|
|
113
|
+
const jsonLogger = pino(pinoOptions);
|
|
114
|
+
jsonLogger.warn(
|
|
115
|
+
"LOG_FORMAT=pretty requested but pino-pretty is not installed. " +
|
|
116
|
+
"Falling back to JSON format. Install pino-pretty to enable pretty printing.",
|
|
117
|
+
);
|
|
118
|
+
return jsonLogger;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Default to JSON format
|
|
123
|
+
return pino(pinoOptions);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Wrap a Pino logger instance to conform to the LoggerPort interface.
|
|
128
|
+
*
|
|
129
|
+
* Pino's API uses the signature: logger.level(obj, msg) or logger.level(msg)
|
|
130
|
+
* We adapt this to: logger.level(msg, meta?)
|
|
131
|
+
*
|
|
132
|
+
* @param logger - Pino logger instance to wrap
|
|
133
|
+
* @returns LoggerPort implementation
|
|
134
|
+
*/
|
|
135
|
+
function wrapPino(logger: PinoLogger): LoggerPort {
|
|
136
|
+
return {
|
|
137
|
+
trace: (message, meta) =>
|
|
138
|
+
meta ? logger.trace(meta, message) : logger.trace(message),
|
|
139
|
+
debug: (message, meta) =>
|
|
140
|
+
meta ? logger.debug(meta, message) : logger.debug(message),
|
|
141
|
+
info: (message, meta) =>
|
|
142
|
+
meta ? logger.info(meta, message) : logger.info(message),
|
|
143
|
+
warn: (message, meta) =>
|
|
144
|
+
meta ? logger.warn(meta, message) : logger.warn(message),
|
|
145
|
+
error: (message, meta) =>
|
|
146
|
+
meta ? logger.error(meta, message) : logger.error(message),
|
|
147
|
+
fatal: (message, meta) =>
|
|
148
|
+
meta ? logger.fatal(meta, message) : logger.fatal(message),
|
|
149
|
+
child: (bindings) => wrapPino(logger.child(bindings)),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Pino logger provider that extends ports with structured logging capabilities.
|
|
155
|
+
*
|
|
156
|
+
* Configuration via environment variables:
|
|
157
|
+
* - LOG_LEVEL: Minimum log level (trace/debug/info/warn/error/fatal, default: info)
|
|
158
|
+
* - LOG_FORMAT: Log format (json/pretty, default: json)
|
|
159
|
+
* - LOG_SERVICE: Optional service name for log enrichment
|
|
160
|
+
* - LOG_TIMESTAMP: Whether to include timestamps (true/false, default: true)
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```ts
|
|
164
|
+
* const server = await createNextServer({
|
|
165
|
+
* ports: basePorts,
|
|
166
|
+
* providers: [loggerPinoProvider],
|
|
167
|
+
* // ...
|
|
168
|
+
* });
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
export const loggerPinoProvider = createProvider({
|
|
172
|
+
name: "logger-pino",
|
|
173
|
+
|
|
174
|
+
config: {
|
|
175
|
+
schema: LoggerPinoConfigSchema,
|
|
176
|
+
envPrefix: "LOG_",
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
async setup({ config }) {
|
|
180
|
+
if (!config) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
"[loggerPinoProvider] Missing logger config. " +
|
|
183
|
+
"Please set LOG_LEVEL, LOG_FORMAT, etc. environment variables.",
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Prepare base bindings
|
|
188
|
+
const baseBindings: Record<string, unknown> = {};
|
|
189
|
+
if (config.SERVICE) {
|
|
190
|
+
baseBindings.service = config.SERVICE;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Create and configure Pino logger
|
|
194
|
+
const logger = createPinoLogger(config, baseBindings);
|
|
195
|
+
|
|
196
|
+
// Wrap logger to match LoggerPort interface
|
|
197
|
+
const port = wrapPino(logger);
|
|
198
|
+
|
|
199
|
+
return { ports: { logger: port } };
|
|
200
|
+
},
|
|
201
|
+
});
|