@visulima/pail 4.0.0-alpha.5 → 4.0.0-alpha.7
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 +37 -0
- package/LICENSE.md +6 -6
- package/README.md +323 -0
- package/dist/error.d.ts +104 -0
- package/dist/error.js +76 -0
- package/dist/index.browser.d.ts +2 -0
- package/dist/index.browser.js +2 -1
- package/dist/index.server.d.ts +2 -0
- package/dist/index.server.js +6 -4
- package/dist/interactive/index.js +1 -1
- package/dist/middleware/elysia.d.ts +71 -0
- package/dist/middleware/elysia.js +70 -0
- package/dist/middleware/express.d.ts +86 -0
- package/dist/middleware/express.js +29 -0
- package/dist/middleware/fastify.d.ts +81 -0
- package/dist/middleware/fastify.js +46 -0
- package/dist/middleware/hono.d.ts +85 -0
- package/dist/middleware/hono.js +33 -0
- package/dist/middleware/next/handler.d.ts +36 -0
- package/dist/middleware/next/handler.js +53 -0
- package/dist/middleware/next/middleware.d.ts +59 -0
- package/dist/middleware/next/storage.d.ts +14 -0
- package/dist/middleware/shared/create-middleware-logger.d.ts +82 -0
- package/dist/middleware/shared/headers.d.ts +14 -0
- package/dist/middleware/shared/routes.d.ts +30 -0
- package/dist/middleware/shared/storage.d.ts +29 -0
- package/dist/middleware/sveltekit.d.ts +123 -0
- package/dist/middleware/sveltekit.js +43 -0
- package/dist/packem_shared/{AbstractJsonReporter-DWRpTtGw.js → AbstractJsonReporter-CGKHS8_M.js} +103 -21
- package/dist/packem_shared/{AbstractJsonReporter-BaZ33PlE.js → AbstractJsonReporter-DDjDkciI.js} +103 -21
- package/dist/packem_shared/{InteractiveManager-CbE7d1kY.js → InteractiveManager-Cd6A14ZK.js} +1 -1
- package/dist/packem_shared/{JsonReporter-BV5lMnJX.js → JsonReporter-B3XX8GHN.js} +1 -1
- package/dist/packem_shared/{JsonReporter-BRw4skd5.js → JsonReporter-p_BXg6Sj.js} +1 -1
- package/dist/packem_shared/{PrettyReporter-DLQtmATi.js → PrettyReporter-CvBn-hxP.js} +5 -4
- package/dist/packem_shared/{Spinner-B9JUdsbY.js → Spinner-DIdVcfWq.js} +34 -1
- package/dist/packem_shared/{abstract-pretty-reporter-DckLMlGF.js → abstract-pretty-reporter-jU8WL_6c.js} +121 -15
- package/dist/packem_shared/createPailError-B11aRfrT.js +76 -0
- package/dist/packem_shared/headers-Cp4uLtr4.js +123 -0
- package/dist/packem_shared/{index-EZ_WSQZS.js → index-frijFf5m.js} +120 -14
- package/dist/packem_shared/pailMiddleware-Ci88geIF.js +24 -0
- package/dist/packem_shared/storage-D0vqz8OX.js +36 -0
- package/dist/packem_shared/useLogger-D0rU3lcX.js +33 -0
- package/dist/processor/environment-processor.d.ts +124 -0
- package/dist/processor/environment-processor.js +78 -0
- package/dist/processor/message-formatter-processor.d.ts +1 -2
- package/dist/processor/sampling-processor.d.ts +111 -0
- package/dist/processor/sampling-processor.js +59 -0
- package/dist/reporter/file/json-file-reporter.js +1 -1
- package/dist/reporter/http/abstract-http-reporter.js +1 -1
- package/dist/reporter/http/http-reporter.edge-light.js +103 -21
- package/dist/reporter/json/index.browser.js +2 -2
- package/dist/reporter/json/index.js +2 -2
- package/dist/reporter/pretty/index.js +1 -1
- package/dist/reporter/simple/simple-reporter.server.js +4 -3
- package/dist/spinner.js +34 -1
- package/dist/wide-event.d.ts +300 -0
- package/dist/wide-event.js +281 -0
- package/package.json +68 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,40 @@
|
|
|
1
|
+
## @visulima/pail [4.0.0-alpha.7](https://github.com/visulima/visulima/compare/@visulima/pail@4.0.0-alpha.6...@visulima/pail@4.0.0-alpha.7) (2026-03-16)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **pail:** add self-documenting errors, sampling processor, and environment processor ([3ec79de](https://github.com/visulima/visulima/commit/3ec79de3034cee4d1514dc93e78da2d05df4f9a7))
|
|
6
|
+
* **pail:** add wide events, framework middleware, tests, and docs ([6d1a4f3](https://github.com/visulima/visulima/commit/6d1a4f3ef4455da78371b8754cdbca9c939bc8b4))
|
|
7
|
+
|
|
8
|
+
### Miscellaneous Chores
|
|
9
|
+
|
|
10
|
+
* **pail:** update dependencies ([72b9561](https://github.com/visulima/visulima/commit/72b9561a3416a121198ea47cdcbe0202c0d8cbef))
|
|
11
|
+
* remove exit 0 ([b6d2408](https://github.com/visulima/visulima/commit/b6d2408fab8b5299a5ae902021b229909c84f184))
|
|
12
|
+
* visulima website ([#591](https://github.com/visulima/visulima/issues/591)) ([59ab2e2](https://github.com/visulima/visulima/commit/59ab2e2befb03e51cd2088956f83d9b87de6d033))
|
|
13
|
+
|
|
14
|
+
## @visulima/pail [4.0.0-alpha.6](https://github.com/visulima/visulima/compare/@visulima/pail@4.0.0-alpha.5...@visulima/pail@4.0.0-alpha.6) (2026-03-06)
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* **pail:** update packem to 2.0.0-alpha.54 ([100f545](https://github.com/visulima/visulima/commit/100f54587dca2c89aa70867d9a398d56ffb86ada))
|
|
19
|
+
|
|
20
|
+
### Miscellaneous Chores
|
|
21
|
+
|
|
22
|
+
* **error-debugging:** update dependencies ([6002ece](https://github.com/visulima/visulima/commit/6002ece1803b2ba8261cff42a362dd6e8ddcc3ee))
|
|
23
|
+
* **pail:** update dependencies ([5e4bf79](https://github.com/visulima/visulima/commit/5e4bf79575b61d2a299fcd45957fa3ea782f8194))
|
|
24
|
+
* **pail:** update dependencies ([4cc1e2a](https://github.com/visulima/visulima/commit/4cc1e2a48881684296d88a7f2107bd29f8457a25))
|
|
25
|
+
* update lock file maintenance ([d83e716](https://github.com/visulima/visulima/commit/d83e71697b75d24704185b66bb521a934d2db02d))
|
|
26
|
+
* year update ([47f4105](https://github.com/visulima/visulima/commit/47f410596ce7190cfea36a073db32e0cec50bbcd))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Dependencies
|
|
30
|
+
|
|
31
|
+
* **@visulima/colorize:** upgraded to 2.0.0-alpha.5
|
|
32
|
+
* **@visulima/error:** upgraded to 6.0.0-alpha.5
|
|
33
|
+
* **@visulima/fmt:** upgraded to 2.0.0-alpha.5
|
|
34
|
+
* **@visulima/inspector:** upgraded to 2.0.0-alpha.4
|
|
35
|
+
* **@visulima/redact:** upgraded to 3.0.0-alpha.5
|
|
36
|
+
* **@visulima/string:** upgraded to 3.0.0-alpha.6
|
|
37
|
+
|
|
1
38
|
## @visulima/pail [4.0.0-alpha.5](https://github.com/visulima/visulima/compare/@visulima/pail@4.0.0-alpha.4...@visulima/pail@4.0.0-alpha.5) (2025-12-27)
|
|
2
39
|
|
|
3
40
|
### Bug Fixes
|
package/LICENSE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2026 visulima
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -34,7 +34,7 @@ Repository: git+https://github.com/visulima/visulima.git
|
|
|
34
34
|
|
|
35
35
|
> MIT License
|
|
36
36
|
>
|
|
37
|
-
> Copyright (c)
|
|
37
|
+
> Copyright (c) 2026 visulima
|
|
38
38
|
>
|
|
39
39
|
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
40
40
|
> of this software and associated documentation files (the "Software"), to deal
|
|
@@ -68,7 +68,7 @@ Repository: git+https://github.com/visulima/visulima.git
|
|
|
68
68
|
>
|
|
69
69
|
> > MIT License
|
|
70
70
|
> >
|
|
71
|
-
> > Copyright (c)
|
|
71
|
+
> > Copyright (c) 2026 visulima
|
|
72
72
|
> >
|
|
73
73
|
> > Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
74
74
|
> > of this software and associated documentation files (the "Software"), to deal
|
|
@@ -210,7 +210,7 @@ Repository: git+https://github.com/visulima/visulima.git
|
|
|
210
210
|
|
|
211
211
|
> MIT License
|
|
212
212
|
>
|
|
213
|
-
> Copyright (c)
|
|
213
|
+
> Copyright (c) 2026 visulima
|
|
214
214
|
>
|
|
215
215
|
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
216
216
|
> of this software and associated documentation files (the "Software"), to deal
|
|
@@ -239,7 +239,7 @@ Repository: git+https://github.com/visulima/visulima.git
|
|
|
239
239
|
|
|
240
240
|
> MIT License
|
|
241
241
|
>
|
|
242
|
-
> Copyright (c)
|
|
242
|
+
> Copyright (c) 2026 visulima
|
|
243
243
|
>
|
|
244
244
|
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
245
245
|
> of this software and associated documentation files (the "Software"), to deal
|
|
@@ -419,7 +419,7 @@ Repository: git+https://github.com/visulima/visulima.git
|
|
|
419
419
|
|
|
420
420
|
> MIT License
|
|
421
421
|
>
|
|
422
|
-
> Copyright (c)
|
|
422
|
+
> Copyright (c) 2026 visulima
|
|
423
423
|
>
|
|
424
424
|
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
425
425
|
> of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -63,6 +63,8 @@ If you're upgrading from an earlier version of pail, check out our [Migration Gu
|
|
|
63
63
|
- ESM‑only with tree‑shaking support (Node.js ≥ 20.19)
|
|
64
64
|
- Supports circular structures
|
|
65
65
|
- Fast and powerful, see the [benchmarks](__bench__/README.md)
|
|
66
|
+
- [Wide events](#wide-events) — accumulate context across an operation and emit once
|
|
67
|
+
- [Framework middleware](#framework-middleware) for Express, Fastify, Hono, Elysia, SvelteKit, and Next.js
|
|
66
68
|
|
|
67
69
|
## Install
|
|
68
70
|
|
|
@@ -1064,6 +1066,327 @@ When a log entry exceeds `maxLogSize`, a `LogSizeError` is thrown. When a batch
|
|
|
1064
1066
|
| `onDebugRequestResponse` | `(reqRes: {...}) => void` | `undefined` | Debug callback for HTTP requests/responses |
|
|
1065
1067
|
| `payloadTemplate` | `(data: {...}) => string` | `undefined` | Custom payload formatter |
|
|
1066
1068
|
|
|
1069
|
+
## Wide Events
|
|
1070
|
+
|
|
1071
|
+
Wide events let you accumulate structured context throughout an operation and emit a single, comprehensive log event at the end — instead of scattering multiple log calls.
|
|
1072
|
+
|
|
1073
|
+
Inspired by [Charity Majors' wide events](https://charity.wtf/2019/02/05/logs-vs-structured-events/) pattern.
|
|
1074
|
+
|
|
1075
|
+
### Basic Usage
|
|
1076
|
+
|
|
1077
|
+
```typescript
|
|
1078
|
+
import { createPail } from "@visulima/pail";
|
|
1079
|
+
import { createWideEvent } from "@visulima/pail/wide-event";
|
|
1080
|
+
|
|
1081
|
+
const logger = createPail();
|
|
1082
|
+
|
|
1083
|
+
const ev = createWideEvent({ pail: logger, name: "api.checkout" });
|
|
1084
|
+
|
|
1085
|
+
ev.set({ user: { id: 1, plan: "pro" } });
|
|
1086
|
+
ev.info("Validated cart", { itemCount: 3 });
|
|
1087
|
+
ev.set({ cart: { id: 42, items: 3, total: 9999 } });
|
|
1088
|
+
ev.info("Payment processed");
|
|
1089
|
+
ev.finish({ status: 200 });
|
|
1090
|
+
|
|
1091
|
+
// Emits a single structured log:
|
|
1092
|
+
// {
|
|
1093
|
+
// event: "api.checkout",
|
|
1094
|
+
// timestamp: "2026-03-14T10:23:45.612Z",
|
|
1095
|
+
// duration: "127ms",
|
|
1096
|
+
// duration_ms: 127,
|
|
1097
|
+
// status: 200,
|
|
1098
|
+
// user: { id: 1, plan: "pro" },
|
|
1099
|
+
// cart: { id: 42, items: 3, total: 9999 },
|
|
1100
|
+
// requestLogs: [
|
|
1101
|
+
// { level: "info", message: "Validated cart", context: { itemCount: 3 }, timestamp: "..." },
|
|
1102
|
+
// { level: "info", message: "Payment processed", timestamp: "..." }
|
|
1103
|
+
// ]
|
|
1104
|
+
// }
|
|
1105
|
+
```
|
|
1106
|
+
|
|
1107
|
+
### Auto-Emit with Explicit Resource Management
|
|
1108
|
+
|
|
1109
|
+
Wide events implement `Disposable` for use with TC39 Explicit Resource Management (`using`):
|
|
1110
|
+
|
|
1111
|
+
```typescript
|
|
1112
|
+
{
|
|
1113
|
+
using ev = createWideEvent({ pail: logger, name: "api.checkout" });
|
|
1114
|
+
ev.set({ user: { id: 1 } });
|
|
1115
|
+
// auto-emits when scope exits
|
|
1116
|
+
}
|
|
1117
|
+
```
|
|
1118
|
+
|
|
1119
|
+
### Typed Data Shape
|
|
1120
|
+
|
|
1121
|
+
```typescript
|
|
1122
|
+
interface CheckoutData {
|
|
1123
|
+
user: { id: number; plan: string };
|
|
1124
|
+
cart: { items: number; total: number };
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
const ev = createWideEvent<CheckoutData>({ pail: logger, name: "api.checkout" });
|
|
1128
|
+
ev.set({ user: { id: 1, plan: "pro" } }); // fully typed
|
|
1129
|
+
```
|
|
1130
|
+
|
|
1131
|
+
### Level Escalation
|
|
1132
|
+
|
|
1133
|
+
The event level auto-escalates based on lifecycle log entries. If you call `warn()` or `error()`, the final emission uses the highest severity:
|
|
1134
|
+
|
|
1135
|
+
```typescript
|
|
1136
|
+
const ev = createWideEvent({ pail: logger, name: "api.checkout" });
|
|
1137
|
+
|
|
1138
|
+
ev.info("Cart validated"); // level stays "info"
|
|
1139
|
+
ev.warn("Rate limit approaching"); // level escalates to "warn"
|
|
1140
|
+
ev.info("Payment processed"); // level stays "warn" (no de-escalation)
|
|
1141
|
+
ev.finish({ status: 200 }); // emits at "warn" level
|
|
1142
|
+
```
|
|
1143
|
+
|
|
1144
|
+
### Error Handling
|
|
1145
|
+
|
|
1146
|
+
```typescript
|
|
1147
|
+
const ev = createWideEvent({ pail: logger, name: "api.checkout" });
|
|
1148
|
+
|
|
1149
|
+
try {
|
|
1150
|
+
await processPayment();
|
|
1151
|
+
} catch (error) {
|
|
1152
|
+
ev.error("Payment failed", error);
|
|
1153
|
+
ev.finish({ status: 500, error });
|
|
1154
|
+
// Emits with serialized error (name, message, stack, status, cause chain)
|
|
1155
|
+
}
|
|
1156
|
+
```
|
|
1157
|
+
|
|
1158
|
+
### Options
|
|
1159
|
+
|
|
1160
|
+
| Option | Type | Default | Description |
|
|
1161
|
+
| ---------- | ------------- | -------- | --------------------------------- |
|
|
1162
|
+
| `name` | `string` | required | Event name, e.g. `"api.checkout"` |
|
|
1163
|
+
| `pail` | Pail instance | required | The pail logger to emit through |
|
|
1164
|
+
| `service` | `string` | — | Service name override |
|
|
1165
|
+
| `type` | `string` | `"info"` | Base log type (may be escalated) |
|
|
1166
|
+
| `autoEmit` | `boolean` | `true` | Auto-emit on `Symbol.dispose` |
|
|
1167
|
+
|
|
1168
|
+
### Methods
|
|
1169
|
+
|
|
1170
|
+
| Method | Description |
|
|
1171
|
+
| ------------------------ | --------------------------------------------- |
|
|
1172
|
+
| `set(data)` | Deep-merge partial data into the event |
|
|
1173
|
+
| `info(msg, ctx?)` | Record an info-level lifecycle entry |
|
|
1174
|
+
| `warn(msg, ctx?)` | Record a warn-level entry (escalates level) |
|
|
1175
|
+
| `error(msg, err?, ctx?)` | Record an error-level entry (escalates level) |
|
|
1176
|
+
| `debug(msg, ctx?)` | Record a debug-level entry |
|
|
1177
|
+
| `setError(error)` | Attach an error (escalates to "error") |
|
|
1178
|
+
| `setStatus(code)` | Set HTTP status code |
|
|
1179
|
+
| `finish(opts?)` | Set status/error and emit |
|
|
1180
|
+
| `emit(typeOverride?)` | Emit the event (only fires once) |
|
|
1181
|
+
| `getData()` | Get read-only snapshot of accumulated data |
|
|
1182
|
+
| `getLevel()` | Get current severity level |
|
|
1183
|
+
| `getRequestLogs()` | Get read-only lifecycle log entries |
|
|
1184
|
+
|
|
1185
|
+
## Framework Middleware
|
|
1186
|
+
|
|
1187
|
+
Pail provides first-class middleware/plugin adapters for popular web frameworks. Each adapter creates a request-scoped [Wide Event](#wide-events) that:
|
|
1188
|
+
|
|
1189
|
+
- Accumulates context throughout the request lifecycle
|
|
1190
|
+
- Auto-emits a single structured log when the response completes or an error occurs
|
|
1191
|
+
- Is accessible via `req.log` / `request.log` / `context.log` and `useLogger()`
|
|
1192
|
+
- Supports route inclusion/exclusion via glob patterns
|
|
1193
|
+
- Supports per-route service name overrides
|
|
1194
|
+
|
|
1195
|
+
### Shared Options
|
|
1196
|
+
|
|
1197
|
+
All middleware adapters accept these options:
|
|
1198
|
+
|
|
1199
|
+
| Option | Type | Description |
|
|
1200
|
+
| --------- | ------------------------------ | ----------------------------------- |
|
|
1201
|
+
| `pail` | Pail instance | The pail logger (required) |
|
|
1202
|
+
| `service` | `string` | Default service name |
|
|
1203
|
+
| `include` | `string[]` | Glob patterns for paths to include |
|
|
1204
|
+
| `exclude` | `string[]` | Glob patterns for paths to exclude |
|
|
1205
|
+
| `routes` | `Record<string, { service? }>` | Per-route config (first match wins) |
|
|
1206
|
+
|
|
1207
|
+
### Express
|
|
1208
|
+
|
|
1209
|
+
```typescript
|
|
1210
|
+
import express from "express";
|
|
1211
|
+
import { createPail } from "@visulima/pail";
|
|
1212
|
+
import { pailMiddleware, useLogger } from "@visulima/pail/middleware/express";
|
|
1213
|
+
|
|
1214
|
+
const app = express();
|
|
1215
|
+
const logger = createPail();
|
|
1216
|
+
|
|
1217
|
+
app.use(pailMiddleware({ pail: logger }));
|
|
1218
|
+
|
|
1219
|
+
app.get("/api/users", (req, res) => {
|
|
1220
|
+
req.log.set({ user: { id: 1 } });
|
|
1221
|
+
req.log.info("Fetched user list");
|
|
1222
|
+
// or from anywhere in the async call stack:
|
|
1223
|
+
// useLogger().set({ user: { id: 1 } });
|
|
1224
|
+
res.json({ ok: true });
|
|
1225
|
+
});
|
|
1226
|
+
```
|
|
1227
|
+
|
|
1228
|
+
**Exports:** `pailMiddleware`, `useLogger`, `PailRequest`, `PailResponse`, `PailNextFunction`, `PailExpressMiddleware`, `WideEvent`
|
|
1229
|
+
|
|
1230
|
+
### Fastify
|
|
1231
|
+
|
|
1232
|
+
```typescript
|
|
1233
|
+
import Fastify from "fastify";
|
|
1234
|
+
import { createPail } from "@visulima/pail";
|
|
1235
|
+
import { pailPlugin, useLogger } from "@visulima/pail/middleware/fastify";
|
|
1236
|
+
|
|
1237
|
+
const app = Fastify();
|
|
1238
|
+
const logger = createPail();
|
|
1239
|
+
|
|
1240
|
+
pailPlugin(app, { pail: logger });
|
|
1241
|
+
|
|
1242
|
+
app.get("/api/users", async (request, reply) => {
|
|
1243
|
+
request.log.set({ user: { id: 1 } });
|
|
1244
|
+
return { ok: true };
|
|
1245
|
+
});
|
|
1246
|
+
```
|
|
1247
|
+
|
|
1248
|
+
**Exports:** `pailPlugin`, `useLogger`, `PailFastifyRequest`, `PailFastifyReply`, `PailFastifyInstance`, `WideEvent`
|
|
1249
|
+
|
|
1250
|
+
### Hono
|
|
1251
|
+
|
|
1252
|
+
```typescript
|
|
1253
|
+
import { Hono } from "hono";
|
|
1254
|
+
import { createPail } from "@visulima/pail";
|
|
1255
|
+
import { pailMiddleware, useLogger } from "@visulima/pail/middleware/hono";
|
|
1256
|
+
|
|
1257
|
+
const app = new Hono();
|
|
1258
|
+
const logger = createPail();
|
|
1259
|
+
|
|
1260
|
+
app.use("*", pailMiddleware({ pail: logger }));
|
|
1261
|
+
|
|
1262
|
+
app.get("/api/users", (c) => {
|
|
1263
|
+
const log = useLogger(c);
|
|
1264
|
+
log.set({ user: { id: 1 } });
|
|
1265
|
+
return c.json({ ok: true });
|
|
1266
|
+
});
|
|
1267
|
+
```
|
|
1268
|
+
|
|
1269
|
+
> **Note:** Hono's `useLogger()` takes the context `c` as an argument (the logger is stored on the context, not in AsyncLocalStorage).
|
|
1270
|
+
|
|
1271
|
+
**Exports:** `pailMiddleware`, `useLogger`, `PailHonoContext`, `PailHonoNext`, `PailHonoMiddleware`, `WideEvent`
|
|
1272
|
+
|
|
1273
|
+
### Elysia
|
|
1274
|
+
|
|
1275
|
+
```typescript
|
|
1276
|
+
import { Elysia } from "elysia";
|
|
1277
|
+
import { createPail } from "@visulima/pail";
|
|
1278
|
+
import { pailPlugin, useLogger } from "@visulima/pail/middleware/elysia";
|
|
1279
|
+
|
|
1280
|
+
const logger = createPail();
|
|
1281
|
+
const app = new Elysia();
|
|
1282
|
+
|
|
1283
|
+
pailPlugin(app, { pail: logger });
|
|
1284
|
+
|
|
1285
|
+
app.get("/api/users", ({ log }) => {
|
|
1286
|
+
log.set({ user: { id: 1 } });
|
|
1287
|
+
return { ok: true };
|
|
1288
|
+
});
|
|
1289
|
+
```
|
|
1290
|
+
|
|
1291
|
+
**Exports:** `pailPlugin`, `useLogger`, `PailElysiaInstance`, `WideEvent`
|
|
1292
|
+
|
|
1293
|
+
### SvelteKit
|
|
1294
|
+
|
|
1295
|
+
```typescript
|
|
1296
|
+
// src/hooks.server.ts
|
|
1297
|
+
import { createPail } from "@visulima/pail";
|
|
1298
|
+
import { createPailHooks, useLogger } from "@visulima/pail/middleware/sveltekit";
|
|
1299
|
+
|
|
1300
|
+
const logger = createPail();
|
|
1301
|
+
const { handle, handleError } = createPailHooks({ pail: logger });
|
|
1302
|
+
|
|
1303
|
+
export { handle, handleError };
|
|
1304
|
+
```
|
|
1305
|
+
|
|
1306
|
+
```typescript
|
|
1307
|
+
// In a load function or action:
|
|
1308
|
+
import { useLogger } from "@visulima/pail/middleware/sveltekit";
|
|
1309
|
+
|
|
1310
|
+
export const load = async (event) => {
|
|
1311
|
+
event.locals.log.set({ user: { id: 1 } });
|
|
1312
|
+
// or from anywhere in the async call stack:
|
|
1313
|
+
// useLogger().set({ user: { id: 1 } });
|
|
1314
|
+
};
|
|
1315
|
+
```
|
|
1316
|
+
|
|
1317
|
+
**Exports:** `pailHandle`, `pailHandleError`, `createPailHooks`, `useLogger`, `PailSvelteKitEvent`, `PailSvelteKitHandle`, `PailSvelteKitHandleError`, `WideEvent`
|
|
1318
|
+
|
|
1319
|
+
### Next.js
|
|
1320
|
+
|
|
1321
|
+
Next.js integration uses two parts: an edge middleware for request ID injection and a handler wrapper for wide event logging.
|
|
1322
|
+
|
|
1323
|
+
```typescript
|
|
1324
|
+
// middleware.ts
|
|
1325
|
+
import { NextResponse } from "next/server";
|
|
1326
|
+
import { pailMiddleware } from "@visulima/pail/middleware/next";
|
|
1327
|
+
|
|
1328
|
+
export default pailMiddleware(NextResponse, {
|
|
1329
|
+
exclude: ["/_next/**", "/favicon.ico"],
|
|
1330
|
+
});
|
|
1331
|
+
```
|
|
1332
|
+
|
|
1333
|
+
```typescript
|
|
1334
|
+
// lib/pail.ts
|
|
1335
|
+
import { createPail } from "@visulima/pail";
|
|
1336
|
+
import { createWithPail } from "@visulima/pail/middleware/next";
|
|
1337
|
+
|
|
1338
|
+
const logger = createPail();
|
|
1339
|
+
export const withPail = createWithPail({ pail: logger });
|
|
1340
|
+
```
|
|
1341
|
+
|
|
1342
|
+
```typescript
|
|
1343
|
+
// app/api/users/route.ts
|
|
1344
|
+
import { withPail } from "@/lib/pail";
|
|
1345
|
+
import { useLogger } from "@visulima/pail/middleware/next";
|
|
1346
|
+
|
|
1347
|
+
export const GET = withPail(async (request: Request) => {
|
|
1348
|
+
const log = useLogger();
|
|
1349
|
+
log.set({ user: { id: 1 } });
|
|
1350
|
+
return Response.json({ ok: true });
|
|
1351
|
+
});
|
|
1352
|
+
```
|
|
1353
|
+
|
|
1354
|
+
**Exports:** `pailMiddleware`, `createWithPail`, `useLogger`, `WideEvent`
|
|
1355
|
+
|
|
1356
|
+
### Route Configuration
|
|
1357
|
+
|
|
1358
|
+
All adapters support glob-based route configuration:
|
|
1359
|
+
|
|
1360
|
+
```typescript
|
|
1361
|
+
pailMiddleware({
|
|
1362
|
+
pail: logger,
|
|
1363
|
+
service: "api-gateway",
|
|
1364
|
+
exclude: ["/health", "/metrics", "/_next/**"],
|
|
1365
|
+
include: ["/api/**"],
|
|
1366
|
+
routes: {
|
|
1367
|
+
"/api/auth/**": { service: "auth-service" },
|
|
1368
|
+
"/api/payments/**": { service: "payments-service" },
|
|
1369
|
+
},
|
|
1370
|
+
});
|
|
1371
|
+
```
|
|
1372
|
+
|
|
1373
|
+
- **Exclusions take precedence** over inclusions
|
|
1374
|
+
- **Glob patterns** support `*` (any non-`/`), `**` (any including `/`), and `?` (single char)
|
|
1375
|
+
- **Route service overrides** apply to the first matching pattern
|
|
1376
|
+
|
|
1377
|
+
### Sensitive Header Filtering
|
|
1378
|
+
|
|
1379
|
+
All adapters automatically filter sensitive headers before logging:
|
|
1380
|
+
|
|
1381
|
+
| Filtered Headers |
|
|
1382
|
+
| --------------------- |
|
|
1383
|
+
| `authorization` |
|
|
1384
|
+
| `cookie` |
|
|
1385
|
+
| `set-cookie` |
|
|
1386
|
+
| `x-api-key` |
|
|
1387
|
+
| `x-auth-token` |
|
|
1388
|
+
| `proxy-authorization` |
|
|
1389
|
+
|
|
1067
1390
|
## Integrations
|
|
1068
1391
|
|
|
1069
1392
|
### Use with @visulima/boxen
|
package/dist/error.d.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for creating a PailError.
|
|
3
|
+
*
|
|
4
|
+
* Extends standard error properties with self-documenting fields
|
|
5
|
+
* that provide actionable context for debugging, particularly useful
|
|
6
|
+
* for AI-assisted log analysis.
|
|
7
|
+
*/
|
|
8
|
+
interface PailErrorOptions {
|
|
9
|
+
/** The root cause of this error */
|
|
10
|
+
cause?: unknown;
|
|
11
|
+
/** A suggested resolution or steps to fix the issue */
|
|
12
|
+
fix?: string;
|
|
13
|
+
/** A link to relevant documentation or issue tracker */
|
|
14
|
+
link?: string;
|
|
15
|
+
/** The error message */
|
|
16
|
+
message: string;
|
|
17
|
+
/** HTTP status code associated with this error */
|
|
18
|
+
status?: number;
|
|
19
|
+
/** An explanation of what caused the failure */
|
|
20
|
+
why?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Self-documenting error class for structured logging.
|
|
24
|
+
*
|
|
25
|
+
* Inspired by evlog's structured error approach, PailError extends the native
|
|
26
|
+
* Error class with additional fields that make log entries immediately actionable:
|
|
27
|
+
* - `why`: Explains what caused the failure
|
|
28
|
+
* - `fix`: Suggests how to resolve the issue
|
|
29
|
+
* - `link`: Points to relevant documentation
|
|
30
|
+
* - `status`: HTTP status code for request-related errors
|
|
31
|
+
*
|
|
32
|
+
* These fields are preserved through the logging pipeline and displayed by reporters,
|
|
33
|
+
* making it easier for both humans and AI agents to understand and resolve issues.
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* import { PailError, createPailError } from "@visulima/pail/error";
|
|
37
|
+
*
|
|
38
|
+
* // Using the class directly
|
|
39
|
+
* throw new PailError({
|
|
40
|
+
* message: "Payment processing failed",
|
|
41
|
+
* status: 402,
|
|
42
|
+
* why: "The customer's card was declined by the payment provider",
|
|
43
|
+
* fix: "Retry with a different payment method or contact the card issuer",
|
|
44
|
+
* link: "https://docs.example.com/payments/declined",
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* // Using the factory function
|
|
48
|
+
* throw createPailError("Connection timeout");
|
|
49
|
+
*
|
|
50
|
+
* // With full options
|
|
51
|
+
* throw createPailError({
|
|
52
|
+
* message: "Database connection failed",
|
|
53
|
+
* why: "Connection pool exhausted after 30s timeout",
|
|
54
|
+
* fix: "Increase pool size or check for connection leaks",
|
|
55
|
+
* });
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare class PailError extends Error {
|
|
59
|
+
/** HTTP status code (defaults to 500) */
|
|
60
|
+
readonly status: number;
|
|
61
|
+
/** Explanation of what caused the failure */
|
|
62
|
+
readonly why: string | undefined;
|
|
63
|
+
/** Suggested resolution steps */
|
|
64
|
+
readonly fix: string | undefined;
|
|
65
|
+
/** Link to relevant documentation */
|
|
66
|
+
readonly link: string | undefined;
|
|
67
|
+
constructor(options: PailErrorOptions | string);
|
|
68
|
+
/**
|
|
69
|
+
* Converts the error to a JSON-serializable object.
|
|
70
|
+
*
|
|
71
|
+
* Includes all self-documenting fields in the output for structured logging.
|
|
72
|
+
* @returns A plain object representation of the error
|
|
73
|
+
*/
|
|
74
|
+
toJSON(): Record<string, unknown>;
|
|
75
|
+
/**
|
|
76
|
+
* Returns a formatted string representation including self-documenting fields.
|
|
77
|
+
* @returns Formatted error string with why/fix/link context
|
|
78
|
+
*/
|
|
79
|
+
toString(): string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Factory function for creating PailError instances.
|
|
83
|
+
*
|
|
84
|
+
* A convenient shorthand for creating PailError objects. Accepts either
|
|
85
|
+
* a string message or a full PailErrorOptions object.
|
|
86
|
+
* @param options Error message string or PailErrorOptions object
|
|
87
|
+
* @returns A new PailError instance
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* import { createPailError } from "@visulima/pail/error";
|
|
91
|
+
*
|
|
92
|
+
* throw createPailError("Something went wrong");
|
|
93
|
+
*
|
|
94
|
+
* throw createPailError({
|
|
95
|
+
* message: "Auth failed",
|
|
96
|
+
* status: 401,
|
|
97
|
+
* why: "Token expired",
|
|
98
|
+
* fix: "Refresh the authentication token",
|
|
99
|
+
* });
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
declare const createPailError: (options: PailErrorOptions | string) => PailError;
|
|
103
|
+
export { createPailError, PailError };
|
|
104
|
+
export type { PailErrorOptions };
|
package/dist/error.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
class PailError extends Error {
|
|
2
|
+
/** HTTP status code (defaults to 500) */
|
|
3
|
+
status;
|
|
4
|
+
/** Explanation of what caused the failure */
|
|
5
|
+
why;
|
|
6
|
+
/** Suggested resolution steps */
|
|
7
|
+
fix;
|
|
8
|
+
/** Link to relevant documentation */
|
|
9
|
+
link;
|
|
10
|
+
constructor(options) {
|
|
11
|
+
const resolvedOptions = typeof options === "string" ? { message: options } : options;
|
|
12
|
+
super(resolvedOptions.message, resolvedOptions.cause === void 0 ? void 0 : { cause: resolvedOptions.cause });
|
|
13
|
+
this.name = "PailError";
|
|
14
|
+
this.status = resolvedOptions.status ?? 500;
|
|
15
|
+
this.why = resolvedOptions.why;
|
|
16
|
+
this.fix = resolvedOptions.fix;
|
|
17
|
+
this.link = resolvedOptions.link;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Converts the error to a JSON-serializable object.
|
|
21
|
+
*
|
|
22
|
+
* Includes all self-documenting fields in the output for structured logging.
|
|
23
|
+
* @returns A plain object representation of the error
|
|
24
|
+
*/
|
|
25
|
+
toJSON() {
|
|
26
|
+
const json = {
|
|
27
|
+
message: this.message,
|
|
28
|
+
name: this.name,
|
|
29
|
+
status: this.status
|
|
30
|
+
};
|
|
31
|
+
if (this.why) {
|
|
32
|
+
json.why = this.why;
|
|
33
|
+
}
|
|
34
|
+
if (this.fix) {
|
|
35
|
+
json.fix = this.fix;
|
|
36
|
+
}
|
|
37
|
+
if (this.link) {
|
|
38
|
+
json.link = this.link;
|
|
39
|
+
}
|
|
40
|
+
if (this.stack) {
|
|
41
|
+
json.stack = this.stack;
|
|
42
|
+
}
|
|
43
|
+
if (this.cause !== void 0) {
|
|
44
|
+
json.cause = this.cause instanceof Error ? { message: this.cause.message, name: this.cause.name, stack: this.cause.stack } : this.cause;
|
|
45
|
+
}
|
|
46
|
+
return json;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Returns a formatted string representation including self-documenting fields.
|
|
50
|
+
* @returns Formatted error string with why/fix/link context
|
|
51
|
+
*/
|
|
52
|
+
toString() {
|
|
53
|
+
let output = `${this.name} [${this.status}]: ${this.message}`;
|
|
54
|
+
if (this.why) {
|
|
55
|
+
output += `
|
|
56
|
+
Why: ${this.why}`;
|
|
57
|
+
}
|
|
58
|
+
if (this.fix) {
|
|
59
|
+
output += `
|
|
60
|
+
Fix: ${this.fix}`;
|
|
61
|
+
}
|
|
62
|
+
if (this.link) {
|
|
63
|
+
output += `
|
|
64
|
+
Link: ${this.link}`;
|
|
65
|
+
}
|
|
66
|
+
if (this.cause !== void 0) {
|
|
67
|
+
const causeMessage = this.cause instanceof Error ? this.cause.message : String(this.cause);
|
|
68
|
+
output += `
|
|
69
|
+
Cause: ${causeMessage}`;
|
|
70
|
+
}
|
|
71
|
+
return output;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const createPailError = (options) => new PailError(options);
|
|
75
|
+
|
|
76
|
+
export { PailError, createPailError };
|
package/dist/index.browser.d.ts
CHANGED
|
@@ -44,4 +44,6 @@ export declare const createPail: <T extends string = string, L extends string =
|
|
|
44
44
|
*/
|
|
45
45
|
export declare const pail: PailBrowserType<string, string>;
|
|
46
46
|
export type { PailBrowserType as Pail } from "./pail.d.ts";
|
|
47
|
+
export type { PailErrorOptions } from "./error.d.ts";
|
|
48
|
+
export { createPailError, PailError } from "./error.d.ts";
|
|
47
49
|
export type { ConstructorOptions, DefaultLoggerTypes, DefaultLogTypes, ExtendedRfc5424LogLevels, LoggerConfiguration, LoggerFunction, LoggerTypesAwareReporter, LoggerTypesConfig, Processor, Reporter, StreamAwareReporter, } from "./types.d.ts";
|
package/dist/index.browser.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { a as EXTENDED_RFC_5424_LOG_LEVELS, E as EMPTY_SYMBOL, L as LOG_TYPES } from './packem_shared/constants-omsTHUWB.js';
|
|
2
2
|
import { w as writeConsoleLogBasedOnLevel } from './packem_shared/write-console-log-based-on-level-DBmRYXpj.js';
|
|
3
3
|
import { g as getLongestLabel, b as build } from './packem_shared/index-DnkF86LQ.js';
|
|
4
|
-
import JsonReporter from './packem_shared/JsonReporter-
|
|
4
|
+
import JsonReporter from './packem_shared/JsonReporter-p_BXg6Sj.js';
|
|
5
|
+
export { PailError, createPailError } from './packem_shared/createPailError-B11aRfrT.js';
|
|
5
6
|
|
|
6
7
|
function getDefaultExportFromCjs (x) {
|
|
7
8
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
package/dist/index.server.d.ts
CHANGED
|
@@ -62,4 +62,6 @@ export type { MultiBarOptions, ProgressBarOptions, ProgressBarPayload, ProgressB
|
|
|
62
62
|
export { getBarChar, MultiProgressBar, ProgressBar } from "./progress-bar.d.ts";
|
|
63
63
|
export type { SpinnerIcons, SpinnerOptions, SpinnerStartOptions, SpinnerStyle } from "./spinner.d.ts";
|
|
64
64
|
export { MultiSpinner, Spinner } from "./spinner.d.ts";
|
|
65
|
+
export type { PailErrorOptions } from "./error.d.ts";
|
|
66
|
+
export { createPailError, PailError } from "./error.d.ts";
|
|
65
67
|
export type { ConstructorOptions, DefaultLoggerTypes, DefaultLogTypes, ExtendedRfc5424LogLevels, LoggerConfiguration, LoggerFunction, LoggerTypesAwareReporter, LoggerTypesConfig, Processor, Reporter, StreamAwareReporter, } from "./types.d.ts";
|
package/dist/index.server.js
CHANGED
|
@@ -22,14 +22,15 @@ const {
|
|
|
22
22
|
stderr,
|
|
23
23
|
env
|
|
24
24
|
} = __cjs_getProcess;
|
|
25
|
-
import { t as terminalSize, w as wordWrap, W as WrapMode, g as getLongestLabel, E as EXTENDED_RFC_5424_LOG_LEVELS, a as EMPTY_SYMBOL, L as LOG_TYPES, i as inspect, b as writeStream, A as AbstractPrettyReporter, d as defaultInspectorConfig, c as getLongestBadge, e as getStringWidth, f as formatLabel, r as renderError } from './packem_shared/abstract-pretty-reporter-
|
|
25
|
+
import { t as terminalSize, w as wordWrap, W as WrapMode, g as getLongestLabel, E as EXTENDED_RFC_5424_LOG_LEVELS, a as EMPTY_SYMBOL, L as LOG_TYPES, i as inspect, b as writeStream, A as AbstractPrettyReporter, d as defaultInspectorConfig, c as getLongestBadge, e as getStringWidth, f as formatLabel, r as renderError } from './packem_shared/abstract-pretty-reporter-jU8WL_6c.js';
|
|
26
26
|
const {
|
|
27
27
|
StringDecoder
|
|
28
28
|
} = __cjs_getBuiltinModule("node:string_decoder");
|
|
29
29
|
import { ProgressBar, applyStyleToOptions, MultiProgressBar } from './packem_shared/getBarChar-BWj1UrH3.js';
|
|
30
30
|
export { getBarChar } from './packem_shared/getBarChar-BWj1UrH3.js';
|
|
31
|
-
import { Spinner, MultiSpinner } from './packem_shared/Spinner-
|
|
32
|
-
import colorize, { red, greenBright, cyan, green, grey,
|
|
31
|
+
import { Spinner, MultiSpinner } from './packem_shared/Spinner-DIdVcfWq.js';
|
|
32
|
+
import colorize, { red, greenBright, cyan, green, grey, bgGrey, underline, white } from '@visulima/colorize';
|
|
33
|
+
export { PailError, createPailError } from './packem_shared/createPailError-B11aRfrT.js';
|
|
33
34
|
|
|
34
35
|
class InteractiveManager {
|
|
35
36
|
#stream;
|
|
@@ -2946,7 +2947,8 @@ class MessageFormatterProcessor {
|
|
|
2946
2947
|
}
|
|
2947
2948
|
}
|
|
2948
2949
|
|
|
2949
|
-
const
|
|
2950
|
+
const PAIL_DIST_REGEX = /[\\/]pail[\\/]dist/;
|
|
2951
|
+
const pailFileFilter = (line) => !PAIL_DIST_REGEX.test(line);
|
|
2950
2952
|
class PrettyReporter extends AbstractPrettyReporter {
|
|
2951
2953
|
#stdout;
|
|
2952
2954
|
#stderr;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { default as InteractiveManager } from '../packem_shared/InteractiveManager-
|
|
1
|
+
export { default as InteractiveManager } from '../packem_shared/InteractiveManager-Cd6A14ZK.js';
|
|
2
2
|
export { default as InteractiveStreamHook } from '../packem_shared/InteractiveStreamHook-ePIURL_U.js';
|