@shaferllc/keel 0.66.0 → 0.74.0
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/AGENTS.md +169 -0
- package/README.md +44 -6
- package/bin/keel-mcp.mjs +9 -0
- package/dist/api/api.config.stub +9 -0
- package/dist/api/config.d.ts +13 -0
- package/dist/api/config.js +14 -0
- package/dist/api/index.d.ts +16 -0
- package/dist/api/index.js +13 -0
- package/dist/api/provider.d.ts +10 -0
- package/dist/api/provider.js +17 -0
- package/dist/api/query.d.ts +35 -0
- package/dist/api/query.js +42 -0
- package/dist/api/resource.d.ts +91 -0
- package/dist/api/resource.js +188 -0
- package/dist/core/application.d.ts +5 -5
- package/dist/core/application.js +8 -2
- package/dist/core/cache.d.ts +81 -5
- package/dist/core/cache.js +188 -23
- package/dist/core/cli/stubs.d.ts +26 -0
- package/dist/core/cli/stubs.js +225 -0
- package/dist/core/console-prompt.d.ts +79 -0
- package/dist/core/console-prompt.js +239 -0
- package/dist/core/console-ui.d.ts +96 -0
- package/dist/core/console-ui.js +187 -0
- package/dist/core/console.d.ts +188 -0
- package/dist/core/console.js +395 -0
- package/dist/core/database.d.ts +70 -1
- package/dist/core/database.js +174 -15
- package/dist/core/env.d.ts +96 -0
- package/dist/core/env.js +140 -0
- package/dist/core/events.d.ts +129 -5
- package/dist/core/events.js +165 -7
- package/dist/core/health.d.ts +141 -0
- package/dist/core/health.js +226 -0
- package/dist/core/helpers.d.ts +9 -3
- package/dist/core/helpers.js +11 -3
- package/dist/core/http/kernel.d.ts +2 -0
- package/dist/core/http/kernel.js +48 -0
- package/dist/core/http/router.d.ts +5 -5
- package/dist/core/http/router.js +5 -5
- package/dist/core/i18n.d.ts +162 -0
- package/dist/core/i18n.js +472 -0
- package/dist/core/index.d.ts +39 -13
- package/dist/core/index.js +19 -6
- package/dist/core/instrumentation.d.ts +113 -0
- package/dist/core/instrumentation.js +52 -0
- package/dist/core/lock.d.ts +139 -0
- package/dist/core/lock.js +215 -0
- package/dist/core/logger.d.ts +89 -4
- package/dist/core/logger.js +167 -22
- package/dist/core/mail.d.ts +128 -7
- package/dist/core/mail.js +264 -16
- package/dist/core/notification.js +10 -1
- package/dist/core/package.d.ts +120 -0
- package/dist/core/package.js +169 -0
- package/dist/core/pages.d.ts +108 -0
- package/dist/core/pages.js +199 -0
- package/dist/core/queue.d.ts +134 -9
- package/dist/core/queue.js +325 -14
- package/dist/core/repl.d.ts +33 -0
- package/dist/core/repl.js +88 -0
- package/dist/core/scheduler.js +6 -0
- package/dist/core/social.d.ts +4 -4
- package/dist/core/social.js +4 -4
- package/dist/core/storage.d.ts +159 -6
- package/dist/core/storage.js +299 -7
- package/dist/core/telemetry.d.ts +208 -0
- package/dist/core/telemetry.js +383 -0
- package/dist/core/template.d.ts +2 -3
- package/dist/core/template.js +2 -3
- package/dist/core/testing.d.ts +170 -1
- package/dist/core/testing.js +504 -2
- package/dist/db/d1.js +13 -0
- package/dist/db/pg.d.ts +13 -0
- package/dist/db/pg.js +46 -4
- package/dist/mcp/server.d.ts +19 -0
- package/dist/mcp/server.js +355 -0
- package/dist/openapi/config.d.ts +28 -0
- package/dist/openapi/config.js +25 -0
- package/dist/openapi/doc.d.ts +40 -0
- package/dist/openapi/doc.js +20 -0
- package/dist/openapi/export.d.ts +8 -0
- package/dist/openapi/export.js +19 -0
- package/dist/openapi/gate.d.ts +15 -0
- package/dist/openapi/gate.js +27 -0
- package/dist/openapi/index.d.ts +19 -0
- package/dist/openapi/index.js +15 -0
- package/dist/openapi/openapi.config.stub +29 -0
- package/dist/openapi/provider.d.ts +18 -0
- package/dist/openapi/provider.js +35 -0
- package/dist/openapi/routes.d.ts +9 -0
- package/dist/openapi/routes.js +23 -0
- package/dist/openapi/spec.d.ts +23 -0
- package/dist/openapi/spec.js +132 -0
- package/dist/openapi/ui.d.ts +8 -0
- package/dist/openapi/ui.js +31 -0
- package/dist/openapi/zod.d.ts +12 -0
- package/dist/openapi/zod.js +46 -0
- package/dist/watch/config.d.ts +33 -0
- package/dist/watch/config.js +38 -0
- package/dist/watch/entry.d.ts +53 -0
- package/dist/watch/entry.js +105 -0
- package/dist/watch/gate.d.ts +20 -0
- package/dist/watch/gate.js +32 -0
- package/dist/watch/index.d.ts +21 -0
- package/dist/watch/index.js +17 -0
- package/dist/watch/migration.d.ts +7 -0
- package/dist/watch/migration.js +20 -0
- package/dist/watch/provider.d.ts +22 -0
- package/dist/watch/provider.js +58 -0
- package/dist/watch/prune.d.ts +11 -0
- package/dist/watch/prune.js +20 -0
- package/dist/watch/recorder.d.ts +24 -0
- package/dist/watch/recorder.js +39 -0
- package/dist/watch/routes.d.ts +13 -0
- package/dist/watch/routes.js +55 -0
- package/dist/watch/store.d.ts +54 -0
- package/dist/watch/store.js +158 -0
- package/dist/watch/ui/dist/watch.css +1 -0
- package/dist/watch/ui/dist/watch.js +555 -0
- package/dist/watch/ui-shell.d.ts +12 -0
- package/dist/watch/ui-shell.js +24 -0
- package/dist/watch/watch.config.stub +47 -0
- package/dist/watch/watchers.d.ts +12 -0
- package/dist/watch/watchers.js +156 -0
- package/docs/ai-manifest.json +3206 -0
- package/docs/ai.md +128 -0
- package/docs/api-resources.md +118 -0
- package/docs/architecture.md +331 -0
- package/docs/authentication.md +453 -0
- package/docs/authorization.md +167 -0
- package/docs/broadcasting.md +137 -0
- package/docs/broker.md +500 -0
- package/docs/cache.md +558 -0
- package/docs/configuration.md +385 -0
- package/docs/console.md +536 -0
- package/docs/container.md +467 -0
- package/docs/controllers.md +265 -0
- package/docs/cors.md +51 -0
- package/docs/database.md +631 -0
- package/docs/debugging.md +129 -0
- package/docs/decorators.md +127 -0
- package/docs/errors.md +395 -0
- package/docs/events.md +496 -0
- package/docs/examples/architecture-app.ts +27 -0
- package/docs/examples/authentication.ts +61 -0
- package/docs/examples/authorization.ts +79 -0
- package/docs/examples/broadcasting.ts +60 -0
- package/docs/examples/broker-cache-validate.ts +34 -0
- package/docs/examples/broker-fault-tolerance.ts +29 -0
- package/docs/examples/broker-middleware.ts +27 -0
- package/docs/examples/broker.ts +203 -0
- package/docs/examples/cache.ts +222 -0
- package/docs/examples/configuration.ts +121 -0
- package/docs/examples/console.ts +134 -0
- package/docs/examples/container.ts +134 -0
- package/docs/examples/controllers.ts +86 -0
- package/docs/examples/database.ts +208 -0
- package/docs/examples/debugging.ts +41 -0
- package/docs/examples/decorators.ts +40 -0
- package/docs/examples/errors.ts +121 -0
- package/docs/examples/events.ts +204 -0
- package/docs/examples/factories.ts +84 -0
- package/docs/examples/hashing.ts +71 -0
- package/docs/examples/health.ts +94 -0
- package/docs/examples/helpers.ts +171 -0
- package/docs/examples/hooks.ts +54 -0
- package/docs/examples/i18n.ts +117 -0
- package/docs/examples/inertia.ts +81 -0
- package/docs/examples/locks.ts +120 -0
- package/docs/examples/logger.ts +166 -0
- package/docs/examples/mail.ts +263 -0
- package/docs/examples/middleware.ts +119 -0
- package/docs/examples/migrations.ts +126 -0
- package/docs/examples/models.ts +239 -0
- package/docs/examples/notification.ts +124 -0
- package/docs/examples/pages.ts +82 -0
- package/docs/examples/providers.ts +123 -0
- package/docs/examples/queues.ts +254 -0
- package/docs/examples/rate-limiting.ts +42 -0
- package/docs/examples/redis.ts +99 -0
- package/docs/examples/request-response.ts +197 -0
- package/docs/examples/routing.ts +186 -0
- package/docs/examples/scheduling.ts +62 -0
- package/docs/examples/sessions.ts +102 -0
- package/docs/examples/static-files.ts +63 -0
- package/docs/examples/storage.ts +132 -0
- package/docs/examples/telemetry.ts +127 -0
- package/docs/examples/templates.ts +58 -0
- package/docs/examples/testing.ts +215 -0
- package/docs/examples/transformer.ts +141 -0
- package/docs/examples/transformers.ts +49 -0
- package/docs/examples/url-builder.ts +86 -0
- package/docs/examples/validation.ts +102 -0
- package/docs/examples/views.tsx +62 -0
- package/docs/examples/vite.ts +106 -0
- package/docs/factories.md +166 -0
- package/docs/getting-started.md +290 -0
- package/docs/hashing.md +259 -0
- package/docs/health.md +225 -0
- package/docs/helpers.md +347 -0
- package/docs/hono.md +186 -0
- package/docs/hooks.md +118 -0
- package/docs/i18n.md +302 -0
- package/docs/inertia.md +241 -0
- package/docs/locks.md +323 -0
- package/docs/logger.md +436 -0
- package/docs/mail.md +751 -0
- package/docs/middleware.md +425 -0
- package/docs/migrations.md +476 -0
- package/docs/models.md +810 -0
- package/docs/notifications.md +474 -0
- package/docs/openapi.md +111 -0
- package/docs/packages.md +118 -0
- package/docs/pages.md +217 -0
- package/docs/providers.md +363 -0
- package/docs/queues.md +679 -0
- package/docs/rate-limiting.md +155 -0
- package/docs/redis.md +178 -0
- package/docs/request-response.md +953 -0
- package/docs/routing.md +804 -0
- package/docs/scheduling.md +110 -0
- package/docs/security.md +85 -0
- package/docs/sessions.md +354 -0
- package/docs/social-auth.md +174 -0
- package/docs/static-files.md +211 -0
- package/docs/storage.md +452 -0
- package/docs/telemetry.md +263 -0
- package/docs/templates.md +314 -0
- package/docs/testing.md +376 -0
- package/docs/transformers.md +381 -0
- package/docs/url-builder.md +295 -0
- package/docs/validation.md +288 -0
- package/docs/views.md +267 -0
- package/docs/vite.md +434 -0
- package/docs/watch.md +118 -0
- package/llms-full.txt +19816 -0
- package/llms.txt +127 -0
- package/package.json +42 -7
package/docs/logger.md
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
# Logger
|
|
2
|
+
|
|
3
|
+
A small leveled logger. It writes **structured JSON** by default — one line per
|
|
4
|
+
event, ready for log aggregators — and pretty single-line output in debug. Reach
|
|
5
|
+
it with the global `logger()` helper.
|
|
6
|
+
|
|
7
|
+
## Logging
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { logger } from "@shaferllc/keel/core";
|
|
11
|
+
|
|
12
|
+
logger().info("user registered", { userId: user.id });
|
|
13
|
+
logger().warn("cache miss", { key });
|
|
14
|
+
logger().error("payment failed", { orderId, error: String(err) });
|
|
15
|
+
logger().debug("query", { sql, ms });
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The second argument is structured context — it's merged into the log line, not
|
|
19
|
+
string-concatenated, so it stays queryable:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{"level":"info","time":"2026-07-10T…","msg":"user registered","userId":42}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Every line carries three reserved keys — `level`, `time` (an ISO-8601 stamp), and
|
|
26
|
+
`msg` — followed by any bound fields and then the call's `context`. Context is
|
|
27
|
+
spread last, so a context key of `level`, `time`, or `msg` overwrites the reserved
|
|
28
|
+
field; steer clear of those names in your payloads.
|
|
29
|
+
|
|
30
|
+
## Levels
|
|
31
|
+
|
|
32
|
+
`trace` < `debug` < `info` < `warn` < `error` < `fatal`. Only events at or above
|
|
33
|
+
the configured level are emitted. Set the threshold via config:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
// config/logger.ts
|
|
37
|
+
export default { level: env("LOG_LEVEL", "info") };
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Under the hood the levels are ordinal (`trace` 10, `debug` 20, `info` 30, `warn`
|
|
41
|
+
40, `error` 50, `fatal` 60); a line is dropped when its level sits below the
|
|
42
|
+
threshold. The default threshold is `"info"`, so `debug` and `trace` stay silent
|
|
43
|
+
until you lower it.
|
|
44
|
+
|
|
45
|
+
`log(level, message, context?)` takes the level at runtime, when it isn't known
|
|
46
|
+
statically.
|
|
47
|
+
|
|
48
|
+
Pretty output turns on automatically when `app.debug` is true. In pretty mode
|
|
49
|
+
each event is a single human-readable line —
|
|
50
|
+
`[2026-07-10T…] INFO user registered {"userId":42}` — and the writer routes by
|
|
51
|
+
level: `warn` goes to `console.warn`, `error` and `fatal` to `console.error`,
|
|
52
|
+
everything else to `console.log`. In JSON mode every level is written to
|
|
53
|
+
`console.log`.
|
|
54
|
+
|
|
55
|
+
`enabled: false` silences a logger entirely, at every level.
|
|
56
|
+
|
|
57
|
+
### Don't pay for lines you won't emit
|
|
58
|
+
|
|
59
|
+
The threshold drops the *line*, but the **context object is built either way** —
|
|
60
|
+
so an expensive snapshot costs you even when nobody sees it. Gate it:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
if (logger().isLevelEnabled("debug")) {
|
|
64
|
+
logger().debug("state", { snapshot: expensiveSnapshot() });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ...or the callback form
|
|
68
|
+
logger().ifLevelEnabled("debug", (log) => log.debug("state", { snapshot: expensiveSnapshot() }));
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Where the lines go
|
|
72
|
+
|
|
73
|
+
A **sink** is where log records land. The default writes to the console (JSON, or
|
|
74
|
+
pretty), but it's just a function, so logs can go anywhere — a file, an HTTP
|
|
75
|
+
collector, a buffer:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { Logger, type Sink } from "@shaferllc/keel/core";
|
|
79
|
+
|
|
80
|
+
const httpSink: Sink = (record) => {
|
|
81
|
+
void fetch("https://logs.example.com", { method: "POST", body: JSON.stringify(record) });
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
new Logger({ sink: httpSink });
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
A sink receives the structured `LogRecord` — `{ level, time, msg, fields }` — not a
|
|
88
|
+
formatted string, so it can do what it likes with the shape. `fields` is already
|
|
89
|
+
redacted.
|
|
90
|
+
|
|
91
|
+
`MemorySink` collects records in memory, which is what you want in a test:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { Logger, MemorySink } from "@shaferllc/keel/core";
|
|
95
|
+
|
|
96
|
+
const sink = new MemorySink();
|
|
97
|
+
const log = new Logger({ level: "trace", sink: sink.sink });
|
|
98
|
+
|
|
99
|
+
log.info("hello", { userId: 1 });
|
|
100
|
+
|
|
101
|
+
sink.messages(); // ["hello"]
|
|
102
|
+
sink.at("info"); // the records at one level
|
|
103
|
+
sink.records[0].fields; // { userId: 1 }
|
|
104
|
+
sink.clear();
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Named loggers
|
|
108
|
+
|
|
109
|
+
Give a subsystem its own level or destination:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { setLogger, namedLogger, Logger } from "@shaferllc/keel/core";
|
|
113
|
+
|
|
114
|
+
setLogger(new Logger({ level: "trace", sink: auditSink }), "audit");
|
|
115
|
+
|
|
116
|
+
namedLogger("audit").trace("permission granted", { userId });
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The application's own logger stays where it is — reach that with `logger()`.
|
|
120
|
+
|
|
121
|
+
## Child loggers
|
|
122
|
+
|
|
123
|
+
Bind fields once (a request id, a job name) and they appear on every line:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
const log = logger().child({ requestId: request.header("x-request-id") });
|
|
127
|
+
log.info("handling"); // includes requestId
|
|
128
|
+
log.info("done"); // includes requestId
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
A child inherits its parent's `level` and `pretty` settings and *merges* its
|
|
132
|
+
bindings on top of the parent's — so you can nest them, and a child's field wins
|
|
133
|
+
over a parent's field of the same name. The parent is untouched; `child()`
|
|
134
|
+
returns a fresh `Logger`.
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
const base = logger().child({ service: "billing" });
|
|
138
|
+
const job = base.child({ jobId }); // { service, jobId } on every line
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Standing up a logger yourself
|
|
142
|
+
|
|
143
|
+
The framework binds one `Logger` for you, but the class is a plain object you can
|
|
144
|
+
construct directly — handy in a script or a test:
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import { Logger } from "@shaferllc/keel/core";
|
|
148
|
+
|
|
149
|
+
const log = new Logger({ level: "debug", pretty: true, bindings: { env: "dev" } });
|
|
150
|
+
log.debug("boot", { pid: 1 });
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
With no options it defaults to `level: "info"`, `pretty: false`, and no bindings.
|
|
154
|
+
|
|
155
|
+
## Per-request logging
|
|
156
|
+
|
|
157
|
+
`requestLogger()` is a built-in middleware that binds a **child logger with a
|
|
158
|
+
generated `reqId` to each request**, so every log line within a request
|
|
159
|
+
correlates — Fastify's `request.log`. Install it in your HTTP kernel, then reach
|
|
160
|
+
the request's logger anywhere with `requestLog()`:
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
import { requestLogger, requestLog } from "@shaferllc/keel/core";
|
|
164
|
+
|
|
165
|
+
// app/Http/Kernel.ts
|
|
166
|
+
kernel.use(requestLogger());
|
|
167
|
+
|
|
168
|
+
// anywhere in the request — the line carries this request's reqId:
|
|
169
|
+
requestLog().info("charging card", { orderId });
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
By default it also logs the request start and completion:
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{"level":"info","time":"…","msg":"request","reqId":"…","method":"GET","path":"/orders"}
|
|
176
|
+
{"level":"info","time":"…","msg":"request completed","reqId":"…","status":200,"ms":12.4}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Options: `genReqId(c)` to control id generation, `idHeader` to reuse an incoming
|
|
180
|
+
id (e.g. `"x-request-id"` for distributed tracing), and `logRequests: false` to
|
|
181
|
+
skip the automatic start/completion lines. Outside a request (or without the
|
|
182
|
+
middleware), `requestLog()` falls back to the base `logger()`.
|
|
183
|
+
|
|
184
|
+
## Redaction
|
|
185
|
+
|
|
186
|
+
Keep secrets out of your logs with `redact` — top-level keys or dot paths. Matched
|
|
187
|
+
values are replaced with `"[redacted]"`; **the original object is never mutated**,
|
|
188
|
+
so redacting doesn't corrupt the data you're still using:
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
const log = new Logger({
|
|
192
|
+
redact: ["password", "req.headers.authorization"],
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
log.info("login", { user: "ada", password: "s3cret", req: { headers: { authorization: "Bearer x" } } });
|
|
196
|
+
// {"level":"info",…,"user":"ada","password":"[redacted]","req":{"headers":{"authorization":"[redacted]"}}}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
A `*` segment matches every key at that level — which is how you catch a secret
|
|
200
|
+
that appears under a key you don't know in advance:
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
new Logger({ redact: ["*.password", "creds.*.token"] });
|
|
204
|
+
|
|
205
|
+
log.info("audit", {
|
|
206
|
+
alice: { password: "a", name: "Alice" },
|
|
207
|
+
bob: { password: "b", name: "Bob" },
|
|
208
|
+
});
|
|
209
|
+
// both passwords redacted; both names kept
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Pass an object instead of an array to change the placeholder, or drop the key
|
|
213
|
+
outright:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
new Logger({ redact: { paths: ["password"], censor: "***" } });
|
|
217
|
+
new Logger({ redact: { paths: ["password"], remove: true } }); // the key disappears
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Redaction is inherited by child loggers, so a redacting base logger keeps
|
|
221
|
+
per-request loggers safe too, and it runs **before** the sink — a custom sink can
|
|
222
|
+
never see the unredacted values.
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## API reference
|
|
227
|
+
|
|
228
|
+
### `logger()`
|
|
229
|
+
|
|
230
|
+
`logger(): Logger`
|
|
231
|
+
|
|
232
|
+
Resolves the application's shared `Logger` from the container.
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
import { logger } from "@shaferllc/keel/core";
|
|
236
|
+
|
|
237
|
+
logger().info("ready");
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Notes:** a global helper — no need to thread the logger through your call stack.
|
|
241
|
+
Throws `No Keel application has been bootstrapped…` if called before an
|
|
242
|
+
`Application` exists. Returns the same singleton every call, so `child()` off it
|
|
243
|
+
when you want per-request bindings rather than mutating the shared instance.
|
|
244
|
+
|
|
245
|
+
### `Logger`
|
|
246
|
+
|
|
247
|
+
The logger itself. The framework binds one for you (reach it with `logger()`),
|
|
248
|
+
but you can also `new Logger(options)` directly.
|
|
249
|
+
|
|
250
|
+
#### `new Logger(options?)`
|
|
251
|
+
|
|
252
|
+
`new Logger(options?: LoggerOptions): Logger`
|
|
253
|
+
|
|
254
|
+
Creates a logger with the given level, format, and bound fields.
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
const log = new Logger({ level: "warn", pretty: true });
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**Notes:** `options` defaults to `{}`, which resolves to `level: "info"`,
|
|
261
|
+
`pretty: false`, no bindings. The level is captured at construction — there is no
|
|
262
|
+
setter, so change it by creating a new logger.
|
|
263
|
+
|
|
264
|
+
#### `debug(message, context?)`
|
|
265
|
+
|
|
266
|
+
`debug(message: string, context?: Record<string, unknown>): void`
|
|
267
|
+
|
|
268
|
+
Logs at the `debug` level — the noisiest, off by default.
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
log.debug("cache lookup", { key });
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**Notes:** suppressed unless the threshold is `"debug"`. `context` is optional and
|
|
275
|
+
merged into the line after the bound fields.
|
|
276
|
+
|
|
277
|
+
#### `info(message, context?)`
|
|
278
|
+
|
|
279
|
+
`info(message: string, context?: Record<string, unknown>): void`
|
|
280
|
+
|
|
281
|
+
Logs at the `info` level — the default threshold.
|
|
282
|
+
|
|
283
|
+
```ts
|
|
284
|
+
log.info("user registered", { userId: 42 });
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
#### `warn(message, context?)`
|
|
288
|
+
|
|
289
|
+
`warn(message: string, context?: Record<string, unknown>): void`
|
|
290
|
+
|
|
291
|
+
Logs at the `warn` level.
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
log.warn("cache miss", { key });
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Notes:** in pretty mode this routes to `console.warn`; in JSON mode, like every
|
|
298
|
+
level, to `console.log`.
|
|
299
|
+
|
|
300
|
+
#### `error(message, context?)`
|
|
301
|
+
|
|
302
|
+
`error(message: string, context?: Record<string, unknown>): void`
|
|
303
|
+
|
|
304
|
+
Logs at the `error` level — the highest, always emitted.
|
|
305
|
+
|
|
306
|
+
```ts
|
|
307
|
+
log.error("payment failed", { orderId, error: String(err) });
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
**Notes:** in pretty mode this routes to `console.error`. It does not throw or
|
|
311
|
+
capture stack traces for you — serialize an `Error` into `context` yourself
|
|
312
|
+
(e.g. `error: String(err)` or `err.stack`).
|
|
313
|
+
|
|
314
|
+
#### `child(bindings)`
|
|
315
|
+
|
|
316
|
+
`child(bindings: Record<string, unknown>): Logger`
|
|
317
|
+
|
|
318
|
+
Returns a new logger that carries `bindings` on every line, in addition to the
|
|
319
|
+
parent's.
|
|
320
|
+
|
|
321
|
+
```ts
|
|
322
|
+
const reqLog = logger().child({ requestId });
|
|
323
|
+
reqLog.info("handling"); // line includes requestId
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Notes:** inherits the parent's `level` and `pretty`; merges bindings over the
|
|
327
|
+
parent's (child wins on key collisions). Chainable — call `child()` on a child.
|
|
328
|
+
The parent is not modified.
|
|
329
|
+
|
|
330
|
+
### `requestLogger(options?)`
|
|
331
|
+
|
|
332
|
+
`requestLogger(options?: RequestLoggerOptions): MiddlewareHandler`
|
|
333
|
+
|
|
334
|
+
Middleware that binds a `reqId` child logger to each request and (by default)
|
|
335
|
+
logs the request start and completion.
|
|
336
|
+
|
|
337
|
+
```ts
|
|
338
|
+
kernel.use(requestLogger({ idHeader: "x-request-id" }));
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Notes:** options — `genReqId(c)` (default `crypto.randomUUID()`), `idHeader`
|
|
342
|
+
(reuse an incoming id), `logRequests` (default `true`).
|
|
343
|
+
|
|
344
|
+
### `requestLog()`
|
|
345
|
+
|
|
346
|
+
`requestLog(): Logger`
|
|
347
|
+
|
|
348
|
+
The current request's child logger (carrying its `reqId`), or the base `logger()`
|
|
349
|
+
outside a request / without the middleware installed.
|
|
350
|
+
|
|
351
|
+
```ts
|
|
352
|
+
requestLog().info("charging card"); // line carries reqId
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Interfaces & types
|
|
356
|
+
|
|
357
|
+
#### `LoggerOptions`
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
interface LoggerOptions {
|
|
361
|
+
level?: LogLevel; // minimum level to emit; default "info"
|
|
362
|
+
pretty?: boolean; // single-line human output; default false
|
|
363
|
+
bindings?: Record<string, unknown>; // fields merged into every line
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Passed to `new Logger()` (and carried forward by `child()`). Use it to set the
|
|
368
|
+
threshold, switch to pretty output, and attach ambient fields.
|
|
369
|
+
|
|
370
|
+
```ts
|
|
371
|
+
const log = new Logger({ level: "debug", pretty: true, bindings: { app: "api" } });
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
#### `LogLevel`
|
|
375
|
+
|
|
376
|
+
`type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal"`
|
|
377
|
+
|
|
378
|
+
The four severity levels, in ascending order. Used for the `level` option and
|
|
379
|
+
selected implicitly by each method.
|
|
380
|
+
|
|
381
|
+
```ts
|
|
382
|
+
const threshold: LogLevel = "warn";
|
|
383
|
+
new Logger({ level: threshold });
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### `isLevelEnabled(level)` / `ifLevelEnabled(level, fn)`
|
|
387
|
+
|
|
388
|
+
`isLevelEnabled(level: LogLevel): boolean` — whether a level would be emitted.
|
|
389
|
+
Check it before building an expensive context object.
|
|
390
|
+
|
|
391
|
+
`ifLevelEnabled(level: LogLevel, fn: (log: Logger) => void): void` — the callback
|
|
392
|
+
form.
|
|
393
|
+
|
|
394
|
+
### `log(level, message, context?)`
|
|
395
|
+
|
|
396
|
+
`log(level: LogLevel, message: string, context?: Record<string, unknown>): void` —
|
|
397
|
+
log at a level chosen at runtime.
|
|
398
|
+
|
|
399
|
+
### `consoleSink(pretty?)`
|
|
400
|
+
|
|
401
|
+
`consoleSink(pretty = false): Sink` — the default sink. JSON to stdout, or a pretty
|
|
402
|
+
single line.
|
|
403
|
+
|
|
404
|
+
### `MemorySink`
|
|
405
|
+
|
|
406
|
+
Collects records in memory — for tests.
|
|
407
|
+
|
|
408
|
+
| Member | Signature |
|
|
409
|
+
|--------|-----------|
|
|
410
|
+
| `sink` | `Sink` — hand this to `LoggerOptions.sink` |
|
|
411
|
+
| `records` | `LogRecord[]` |
|
|
412
|
+
| `at` | `(level) => LogRecord[]` |
|
|
413
|
+
| `messages` | `() => string[]` |
|
|
414
|
+
| `clear` | `() => void` |
|
|
415
|
+
|
|
416
|
+
### `setLogger(logger, name)` / `namedLogger(name)`
|
|
417
|
+
|
|
418
|
+
Register a logger under a name, and resolve it. `namedLogger` throws for an unknown
|
|
419
|
+
name.
|
|
420
|
+
|
|
421
|
+
### Interfaces & types (added)
|
|
422
|
+
|
|
423
|
+
#### `Sink`
|
|
424
|
+
|
|
425
|
+
`type Sink = (record: LogRecord) => void` — where log lines go.
|
|
426
|
+
|
|
427
|
+
#### `LogRecord`
|
|
428
|
+
|
|
429
|
+
`{ level: LogLevel; time: string; msg: string; fields: Record<string, unknown> }` —
|
|
430
|
+
`fields` is already redacted.
|
|
431
|
+
|
|
432
|
+
#### `RedactOptions`
|
|
433
|
+
|
|
434
|
+
`{ paths: string[]; censor?: string; remove?: boolean }` — a `*` path segment
|
|
435
|
+
matches every key at that level. `LoggerOptions.redact` also accepts a bare
|
|
436
|
+
`string[]`.
|