@shaferllc/keel 0.66.0 → 0.68.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 +167 -0
- package/README.md +30 -1
- package/bin/keel-mcp.mjs +9 -0
- package/dist/core/application.d.ts +5 -5
- package/dist/core/application.js +2 -2
- package/dist/core/cache.d.ts +82 -5
- package/dist/core/cache.js +181 -23
- package/dist/core/cli/stubs.d.ts +12 -0
- package/dist/core/cli/stubs.js +120 -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/index.d.ts +15 -10
- package/dist/core/index.js +7 -4
- package/dist/core/lock.d.ts +139 -0
- package/dist/core/lock.js +215 -0
- package/dist/core/logger.d.ts +82 -4
- package/dist/core/logger.js +141 -23
- package/dist/core/mail.d.ts +128 -7
- package/dist/core/mail.js +264 -16
- package/dist/core/queue.d.ts +134 -9
- package/dist/core/queue.js +304 -14
- package/dist/core/storage.d.ts +159 -6
- package/dist/core/storage.js +287 -7
- package/dist/mcp/server.d.ts +19 -0
- package/dist/mcp/server.js +355 -0
- package/docs/ai-manifest.json +2472 -0
- package/docs/ai.md +128 -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 +311 -0
- package/docs/console.md +356 -0
- package/docs/container.md +467 -0
- package/docs/controllers.md +265 -0
- package/docs/cors.md +51 -0
- package/docs/database.md +530 -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 +81 -0
- package/docs/examples/container.ts +134 -0
- package/docs/examples/controllers.ts +86 -0
- package/docs/examples/database.ts +118 -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/inertia.ts +81 -0
- package/docs/examples/locks.ts +120 -0
- package/docs/examples/logger.ts +92 -0
- package/docs/examples/mail.ts +160 -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/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/templates.ts +58 -0
- package/docs/examples/testing.ts +66 -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/inertia.md +241 -0
- package/docs/locks.md +323 -0
- package/docs/logger.md +290 -0
- package/docs/mail.md +678 -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/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 +450 -0
- package/docs/templates.md +315 -0
- package/docs/testing.md +125 -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/llms-full.txt +17694 -0
- package/llms.txt +116 -0
- package/package.json +26 -7
package/docs/logger.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
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
|
+
`debug` < `info` < `warn` < `error`. Only events at or above the configured
|
|
33
|
+
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 (`debug` 10, `info` 20, `warn` 30,
|
|
41
|
+
`error` 40); a line is dropped when its level sits below the threshold. The
|
|
42
|
+
default threshold is `"info"`, so `debug` lines stay silent until you lower it.
|
|
43
|
+
|
|
44
|
+
Pretty output turns on automatically when `app.debug` is true. In pretty mode
|
|
45
|
+
each event is a single human-readable line —
|
|
46
|
+
`[2026-07-10T…] INFO user registered {"userId":42}` — and the writer routes by
|
|
47
|
+
level: `warn` goes to `console.warn`, `error` to `console.error`, everything else
|
|
48
|
+
to `console.log`. In JSON mode every level is written to `console.log`.
|
|
49
|
+
|
|
50
|
+
## Child loggers
|
|
51
|
+
|
|
52
|
+
Bind fields once (a request id, a job name) and they appear on every line:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
const log = logger().child({ requestId: request.header("x-request-id") });
|
|
56
|
+
log.info("handling"); // includes requestId
|
|
57
|
+
log.info("done"); // includes requestId
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
A child inherits its parent's `level` and `pretty` settings and *merges* its
|
|
61
|
+
bindings on top of the parent's — so you can nest them, and a child's field wins
|
|
62
|
+
over a parent's field of the same name. The parent is untouched; `child()`
|
|
63
|
+
returns a fresh `Logger`.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
const base = logger().child({ service: "billing" });
|
|
67
|
+
const job = base.child({ jobId }); // { service, jobId } on every line
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Standing up a logger yourself
|
|
71
|
+
|
|
72
|
+
The framework binds one `Logger` for you, but the class is a plain object you can
|
|
73
|
+
construct directly — handy in a script or a test:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { Logger } from "@shaferllc/keel/core";
|
|
77
|
+
|
|
78
|
+
const log = new Logger({ level: "debug", pretty: true, bindings: { env: "dev" } });
|
|
79
|
+
log.debug("boot", { pid: 1 });
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
With no options it defaults to `level: "info"`, `pretty: false`, and no bindings.
|
|
83
|
+
|
|
84
|
+
## Per-request logging
|
|
85
|
+
|
|
86
|
+
`requestLogger()` is a built-in middleware that binds a **child logger with a
|
|
87
|
+
generated `reqId` to each request**, so every log line within a request
|
|
88
|
+
correlates — Fastify's `request.log`. Install it in your HTTP kernel, then reach
|
|
89
|
+
the request's logger anywhere with `requestLog()`:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { requestLogger, requestLog } from "@shaferllc/keel/core";
|
|
93
|
+
|
|
94
|
+
// app/Http/Kernel.ts
|
|
95
|
+
kernel.use(requestLogger());
|
|
96
|
+
|
|
97
|
+
// anywhere in the request — the line carries this request's reqId:
|
|
98
|
+
requestLog().info("charging card", { orderId });
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
By default it also logs the request start and completion:
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{"level":"info","time":"…","msg":"request","reqId":"…","method":"GET","path":"/orders"}
|
|
105
|
+
{"level":"info","time":"…","msg":"request completed","reqId":"…","status":200,"ms":12.4}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Options: `genReqId(c)` to control id generation, `idHeader` to reuse an incoming
|
|
109
|
+
id (e.g. `"x-request-id"` for distributed tracing), and `logRequests: false` to
|
|
110
|
+
skip the automatic start/completion lines. Outside a request (or without the
|
|
111
|
+
middleware), `requestLog()` falls back to the base `logger()`.
|
|
112
|
+
|
|
113
|
+
## Redaction
|
|
114
|
+
|
|
115
|
+
Keep secrets out of your logs with `redact` — top-level keys or dot paths. Matched
|
|
116
|
+
values are replaced with `"[redacted]"`; the original object is never mutated:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
const log = new Logger({
|
|
120
|
+
redact: ["password", "req.headers.authorization"],
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
log.info("login", { user: "ada", password: "s3cret", req: { headers: { authorization: "Bearer x" } } });
|
|
124
|
+
// {"level":"info",…,"user":"ada","password":"[redacted]","req":{"headers":{"authorization":"[redacted]"}}}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Redaction is inherited by child loggers, so a redacting base logger keeps
|
|
128
|
+
per-request loggers safe too.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## API reference
|
|
133
|
+
|
|
134
|
+
### `logger()`
|
|
135
|
+
|
|
136
|
+
`logger(): Logger`
|
|
137
|
+
|
|
138
|
+
Resolves the application's shared `Logger` from the container.
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
import { logger } from "@shaferllc/keel/core";
|
|
142
|
+
|
|
143
|
+
logger().info("ready");
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Notes:** a global helper — no need to thread the logger through your call stack.
|
|
147
|
+
Throws `No Keel application has been bootstrapped…` if called before an
|
|
148
|
+
`Application` exists. Returns the same singleton every call, so `child()` off it
|
|
149
|
+
when you want per-request bindings rather than mutating the shared instance.
|
|
150
|
+
|
|
151
|
+
### `Logger`
|
|
152
|
+
|
|
153
|
+
The logger itself. The framework binds one for you (reach it with `logger()`),
|
|
154
|
+
but you can also `new Logger(options)` directly.
|
|
155
|
+
|
|
156
|
+
#### `new Logger(options?)`
|
|
157
|
+
|
|
158
|
+
`new Logger(options?: LoggerOptions): Logger`
|
|
159
|
+
|
|
160
|
+
Creates a logger with the given level, format, and bound fields.
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
const log = new Logger({ level: "warn", pretty: true });
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Notes:** `options` defaults to `{}`, which resolves to `level: "info"`,
|
|
167
|
+
`pretty: false`, no bindings. The level is captured at construction — there is no
|
|
168
|
+
setter, so change it by creating a new logger.
|
|
169
|
+
|
|
170
|
+
#### `debug(message, context?)`
|
|
171
|
+
|
|
172
|
+
`debug(message: string, context?: Record<string, unknown>): void`
|
|
173
|
+
|
|
174
|
+
Logs at the `debug` level — the noisiest, off by default.
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
log.debug("cache lookup", { key });
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Notes:** suppressed unless the threshold is `"debug"`. `context` is optional and
|
|
181
|
+
merged into the line after the bound fields.
|
|
182
|
+
|
|
183
|
+
#### `info(message, context?)`
|
|
184
|
+
|
|
185
|
+
`info(message: string, context?: Record<string, unknown>): void`
|
|
186
|
+
|
|
187
|
+
Logs at the `info` level — the default threshold.
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
log.info("user registered", { userId: 42 });
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### `warn(message, context?)`
|
|
194
|
+
|
|
195
|
+
`warn(message: string, context?: Record<string, unknown>): void`
|
|
196
|
+
|
|
197
|
+
Logs at the `warn` level.
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
log.warn("cache miss", { key });
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Notes:** in pretty mode this routes to `console.warn`; in JSON mode, like every
|
|
204
|
+
level, to `console.log`.
|
|
205
|
+
|
|
206
|
+
#### `error(message, context?)`
|
|
207
|
+
|
|
208
|
+
`error(message: string, context?: Record<string, unknown>): void`
|
|
209
|
+
|
|
210
|
+
Logs at the `error` level — the highest, always emitted.
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
log.error("payment failed", { orderId, error: String(err) });
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Notes:** in pretty mode this routes to `console.error`. It does not throw or
|
|
217
|
+
capture stack traces for you — serialize an `Error` into `context` yourself
|
|
218
|
+
(e.g. `error: String(err)` or `err.stack`).
|
|
219
|
+
|
|
220
|
+
#### `child(bindings)`
|
|
221
|
+
|
|
222
|
+
`child(bindings: Record<string, unknown>): Logger`
|
|
223
|
+
|
|
224
|
+
Returns a new logger that carries `bindings` on every line, in addition to the
|
|
225
|
+
parent's.
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
const reqLog = logger().child({ requestId });
|
|
229
|
+
reqLog.info("handling"); // line includes requestId
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**Notes:** inherits the parent's `level` and `pretty`; merges bindings over the
|
|
233
|
+
parent's (child wins on key collisions). Chainable — call `child()` on a child.
|
|
234
|
+
The parent is not modified.
|
|
235
|
+
|
|
236
|
+
### `requestLogger(options?)`
|
|
237
|
+
|
|
238
|
+
`requestLogger(options?: RequestLoggerOptions): MiddlewareHandler`
|
|
239
|
+
|
|
240
|
+
Middleware that binds a `reqId` child logger to each request and (by default)
|
|
241
|
+
logs the request start and completion.
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
kernel.use(requestLogger({ idHeader: "x-request-id" }));
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Notes:** options — `genReqId(c)` (default `crypto.randomUUID()`), `idHeader`
|
|
248
|
+
(reuse an incoming id), `logRequests` (default `true`).
|
|
249
|
+
|
|
250
|
+
### `requestLog()`
|
|
251
|
+
|
|
252
|
+
`requestLog(): Logger`
|
|
253
|
+
|
|
254
|
+
The current request's child logger (carrying its `reqId`), or the base `logger()`
|
|
255
|
+
outside a request / without the middleware installed.
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
requestLog().info("charging card"); // line carries reqId
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Interfaces & types
|
|
262
|
+
|
|
263
|
+
#### `LoggerOptions`
|
|
264
|
+
|
|
265
|
+
```ts
|
|
266
|
+
interface LoggerOptions {
|
|
267
|
+
level?: LogLevel; // minimum level to emit; default "info"
|
|
268
|
+
pretty?: boolean; // single-line human output; default false
|
|
269
|
+
bindings?: Record<string, unknown>; // fields merged into every line
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Passed to `new Logger()` (and carried forward by `child()`). Use it to set the
|
|
274
|
+
threshold, switch to pretty output, and attach ambient fields.
|
|
275
|
+
|
|
276
|
+
```ts
|
|
277
|
+
const log = new Logger({ level: "debug", pretty: true, bindings: { app: "api" } });
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
#### `LogLevel`
|
|
281
|
+
|
|
282
|
+
`type LogLevel = "debug" | "info" | "warn" | "error"`
|
|
283
|
+
|
|
284
|
+
The four severity levels, in ascending order. Used for the `level` option and
|
|
285
|
+
selected implicitly by each method.
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
const threshold: LogLevel = "warn";
|
|
289
|
+
new Logger({ level: threshold });
|
|
290
|
+
```
|