evlog 2.3.0 → 2.4.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/README.md +105 -25
- package/dist/elysia/index.d.mts +7 -24
- package/dist/elysia/index.d.mts.map +1 -1
- package/dist/elysia/index.mjs +14 -4
- package/dist/elysia/index.mjs.map +1 -1
- package/dist/express/index.d.mts +4 -39
- package/dist/express/index.d.mts.map +1 -1
- package/dist/express/index.mjs +2 -21
- package/dist/express/index.mjs.map +1 -1
- package/dist/fastify/index.d.mts +37 -0
- package/dist/fastify/index.d.mts.map +1 -0
- package/dist/fastify/index.mjs +73 -0
- package/dist/fastify/index.mjs.map +1 -0
- package/dist/headers-CXOd5EyZ.mjs.map +1 -1
- package/dist/hono/index.d.mts +3 -24
- package/dist/hono/index.d.mts.map +1 -1
- package/dist/hono/index.mjs.map +1 -1
- package/dist/middleware-BoVCgsfQ.d.mts +36 -0
- package/dist/middleware-BoVCgsfQ.d.mts.map +1 -0
- package/dist/nestjs/index.d.mts +83 -0
- package/dist/nestjs/index.d.mts.map +1 -0
- package/dist/nestjs/index.mjs +109 -0
- package/dist/nestjs/index.mjs.map +1 -0
- package/dist/next/index.d.mts +3 -34
- package/dist/next/index.d.mts.map +1 -1
- package/dist/nitro/module.d.mts +1 -1
- package/dist/nitro/v3/module.d.mts +1 -1
- package/dist/{nitro-Nxg6qcXd.d.mts → nitro-BRisWfGy.d.mts} +1 -1
- package/dist/{nitro-Nxg6qcXd.d.mts.map → nitro-BRisWfGy.d.mts.map} +1 -1
- package/dist/nuxt/module.mjs +1 -1
- package/dist/storage-Dd3PHiMh.mjs +29 -0
- package/dist/storage-Dd3PHiMh.mjs.map +1 -0
- package/dist/sveltekit/index.d.mts +128 -0
- package/dist/sveltekit/index.d.mts.map +1 -0
- package/dist/sveltekit/index.mjs +163 -0
- package/dist/sveltekit/index.mjs.map +1 -0
- package/package.json +39 -2
package/README.md
CHANGED
|
@@ -375,43 +375,116 @@ Notes:
|
|
|
375
375
|
|
|
376
376
|
## Hono
|
|
377
377
|
|
|
378
|
-
Use the standalone API to create one wide event per request from a Hono middleware.
|
|
379
|
-
|
|
380
378
|
```typescript
|
|
381
379
|
// src/index.ts
|
|
382
|
-
import { serve } from '@hono/node-server'
|
|
383
380
|
import { Hono } from 'hono'
|
|
384
|
-
import {
|
|
381
|
+
import { initLogger } from 'evlog'
|
|
382
|
+
import { evlog, type EvlogVariables } from 'evlog/hono'
|
|
385
383
|
|
|
386
|
-
initLogger({
|
|
387
|
-
|
|
384
|
+
initLogger({ env: { service: 'hono-api' } })
|
|
385
|
+
|
|
386
|
+
const app = new Hono<EvlogVariables>()
|
|
387
|
+
app.use(evlog())
|
|
388
|
+
|
|
389
|
+
app.get('/api/users', (c) => {
|
|
390
|
+
const log = c.get('log')
|
|
391
|
+
log.set({ users: { count: 42 } })
|
|
392
|
+
return c.json({ users: [] })
|
|
388
393
|
})
|
|
394
|
+
```
|
|
389
395
|
|
|
390
|
-
|
|
396
|
+
See the full [hono example](https://github.com/HugoRCD/evlog/tree/main/examples/hono) for a complete working project.
|
|
391
397
|
|
|
392
|
-
|
|
393
|
-
const startedAt = Date.now()
|
|
394
|
-
const log = createRequestLogger({ method: c.req.method, path: c.req.path })
|
|
398
|
+
## Express
|
|
395
399
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
400
|
+
```typescript
|
|
401
|
+
// src/index.ts
|
|
402
|
+
import express from 'express'
|
|
403
|
+
import { initLogger } from 'evlog'
|
|
404
|
+
import { evlog, useLogger } from 'evlog/express'
|
|
405
|
+
|
|
406
|
+
initLogger({ env: { service: 'express-api' } })
|
|
407
|
+
|
|
408
|
+
const app = express()
|
|
409
|
+
app.use(evlog())
|
|
410
|
+
|
|
411
|
+
app.get('/api/users', (req, res) => {
|
|
412
|
+
req.log.set({ users: { count: 42 } })
|
|
413
|
+
res.json({ users: [] })
|
|
407
414
|
})
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Use `useLogger()` to access the logger from anywhere in the call stack without passing `req`.
|
|
418
|
+
|
|
419
|
+
See the full [express example](https://github.com/HugoRCD/evlog/tree/main/examples/express) for a complete working project.
|
|
420
|
+
|
|
421
|
+
## Fastify
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
// src/index.ts
|
|
425
|
+
import Fastify from 'fastify'
|
|
426
|
+
import { initLogger } from 'evlog'
|
|
427
|
+
import { evlog, useLogger } from 'evlog/fastify'
|
|
428
|
+
|
|
429
|
+
initLogger({ env: { service: 'fastify-api' } })
|
|
408
430
|
|
|
409
|
-
app
|
|
431
|
+
const app = Fastify({ logger: false })
|
|
432
|
+
await app.register(evlog)
|
|
410
433
|
|
|
411
|
-
|
|
434
|
+
app.get('/api/users', async (request) => {
|
|
435
|
+
request.log.set({ users: { count: 42 } })
|
|
436
|
+
return { users: [] }
|
|
437
|
+
})
|
|
412
438
|
```
|
|
413
439
|
|
|
414
|
-
|
|
440
|
+
`request.log` is the evlog wide-event logger (shadows Fastify's built-in pino logger on the request). Use `useLogger()` to access the logger from anywhere in the call stack.
|
|
441
|
+
|
|
442
|
+
See the full [fastify example](https://github.com/HugoRCD/evlog/tree/main/examples/fastify) for a complete working project.
|
|
443
|
+
|
|
444
|
+
## Elysia
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
// src/index.ts
|
|
448
|
+
import { Elysia } from 'elysia'
|
|
449
|
+
import { initLogger } from 'evlog'
|
|
450
|
+
import { evlog, useLogger } from 'evlog/elysia'
|
|
451
|
+
|
|
452
|
+
initLogger({ env: { service: 'elysia-api' } })
|
|
453
|
+
|
|
454
|
+
const app = new Elysia()
|
|
455
|
+
.use(evlog())
|
|
456
|
+
.get('/api/users', ({ log }) => {
|
|
457
|
+
log.set({ users: { count: 42 } })
|
|
458
|
+
return { users: [] }
|
|
459
|
+
})
|
|
460
|
+
.listen(3000)
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
Use `useLogger()` to access the logger from anywhere in the call stack.
|
|
464
|
+
|
|
465
|
+
See the full [elysia example](https://github.com/HugoRCD/evlog/tree/main/examples/elysia) for a complete working project.
|
|
466
|
+
|
|
467
|
+
## NestJS
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
// src/app.module.ts
|
|
471
|
+
import { Module } from '@nestjs/common'
|
|
472
|
+
import { EvlogModule } from 'evlog/nestjs'
|
|
473
|
+
|
|
474
|
+
@Module({
|
|
475
|
+
imports: [EvlogModule.forRoot()],
|
|
476
|
+
})
|
|
477
|
+
export class AppModule {}
|
|
478
|
+
|
|
479
|
+
// In any controller or service:
|
|
480
|
+
import { useLogger } from 'evlog/nestjs'
|
|
481
|
+
const log = useLogger()
|
|
482
|
+
log.set({ users: { count: 42 } })
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
`EvlogModule.forRoot()` registers a global middleware that creates a request-scoped logger for every request. Use `useLogger()` to access it anywhere in the call stack, or `req.log` directly. Supports `forRootAsync()` for async configuration.
|
|
486
|
+
|
|
487
|
+
See the full [nestjs example](https://github.com/HugoRCD/evlog/tree/main/examples/nestjs) for a complete working project.
|
|
415
488
|
|
|
416
489
|
## Browser
|
|
417
490
|
|
|
@@ -991,12 +1064,19 @@ try {
|
|
|
991
1064
|
|-----------|-------------|
|
|
992
1065
|
| **Nuxt** | `modules: ['evlog/nuxt']` |
|
|
993
1066
|
| **Next.js** | `createEvlog()` factory with `import { createEvlog } from 'evlog/next'` ([example](./examples/nextjs)) |
|
|
1067
|
+
| **SvelteKit** | `export const { handle, handleError } = createEvlogHooks()` with `import { createEvlogHooks } from 'evlog/sveltekit'` ([example](./examples/sveltekit)) |
|
|
994
1068
|
| **Nitro v3** | `modules: [evlog()]` with `import evlog from 'evlog/nitro/v3'` |
|
|
995
1069
|
| **Nitro v2** | `modules: [evlog()]` with `import evlog from 'evlog/nitro'` |
|
|
1070
|
+
| **TanStack Start** | Nitro v3 module setup ([example](./examples/tanstack-start)) |
|
|
1071
|
+
| **NestJS** | `EvlogModule.forRoot()` with `import { EvlogModule } from 'evlog/nestjs'` ([example](./examples/nestjs)) |
|
|
1072
|
+
| **Express** | `app.use(evlog())` with `import { evlog } from 'evlog/express'` ([example](./examples/express)) |
|
|
1073
|
+
| **Hono** | `app.use(evlog())` with `import { evlog } from 'evlog/hono'` ([example](./examples/hono)) |
|
|
1074
|
+
| **Fastify** | `app.register(evlog)` with `import { evlog } from 'evlog/fastify'` ([example](./examples/fastify)) |
|
|
1075
|
+
| **Elysia** | `.use(evlog())` with `import { evlog } from 'evlog/elysia'` ([example](./examples/elysia)) |
|
|
1076
|
+
| **Cloudflare Workers** | Manual setup with `import { initLogger, createRequestLogger } from 'evlog'` ([example](./examples/workers)) |
|
|
996
1077
|
| **Analog** | Nitro v2 module setup |
|
|
997
1078
|
| **Vinxi** | Nitro v2 module setup |
|
|
998
1079
|
| **SolidStart** | Nitro v2 module setup ([example](./examples/solidstart)) |
|
|
999
|
-
| **TanStack Start** | Nitro v3 module setup ([example](./examples/tanstack-start)) |
|
|
1000
1080
|
|
|
1001
1081
|
## Agent Skills
|
|
1002
1082
|
|
package/dist/elysia/index.d.mts
CHANGED
|
@@ -1,34 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RequestLogger } from "../types.mjs";
|
|
2
|
+
import { t as BaseEvlogOptions } from "../middleware-BoVCgsfQ.mjs";
|
|
2
3
|
import { Elysia } from "elysia";
|
|
3
4
|
|
|
4
5
|
//#region src/elysia/index.d.ts
|
|
5
|
-
|
|
6
|
-
/** Route patterns to include in logging (glob). If not set, all routes are logged */
|
|
7
|
-
include?: string[];
|
|
8
|
-
/** Route patterns to exclude from logging. Exclusions take precedence over inclusions */
|
|
9
|
-
exclude?: string[];
|
|
10
|
-
/** Route-specific service configuration */
|
|
11
|
-
routes?: Record<string, RouteConfig>;
|
|
12
|
-
/**
|
|
13
|
-
* Drain callback called with every emitted event.
|
|
14
|
-
* Use with drain adapters (Axiom, OTLP, Sentry, etc.) or custom endpoints.
|
|
15
|
-
*/
|
|
16
|
-
drain?: (ctx: DrainContext) => void | Promise<void>;
|
|
17
|
-
/**
|
|
18
|
-
* Enrich callback called after emit, before drain.
|
|
19
|
-
* Use to add derived context (geo, deployment info, user agent, etc.).
|
|
20
|
-
*/
|
|
21
|
-
enrich?: (ctx: EnrichContext) => void | Promise<void>;
|
|
22
|
-
/**
|
|
23
|
-
* Custom tail sampling callback.
|
|
24
|
-
* Set `ctx.shouldKeep = true` to force-keep the log regardless of head sampling.
|
|
25
|
-
*/
|
|
26
|
-
keep?: (ctx: TailSamplingContext) => void | Promise<void>;
|
|
27
|
-
}
|
|
6
|
+
type EvlogElysiaOptions = BaseEvlogOptions;
|
|
28
7
|
/**
|
|
29
8
|
* Get the request-scoped logger from anywhere in the call stack.
|
|
30
9
|
* Must be called inside a request handled by the `evlog()` plugin.
|
|
31
10
|
*
|
|
11
|
+
* Unlike other frameworks, Elysia uses `storage.enterWith()` which persists
|
|
12
|
+
* beyond the request lifecycle. This accessor additionally checks `activeLoggers`
|
|
13
|
+
* to ensure the logger belongs to an in-flight request.
|
|
14
|
+
*
|
|
32
15
|
* @example
|
|
33
16
|
* ```ts
|
|
34
17
|
* import { useLogger } from 'evlog/elysia'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/elysia/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/elysia/index.ts"],"mappings":";;;;;KAcY,kBAAA,GAAqB,gBAAA;;AAAjC;;;;;AAoBA;;;;;;;;;;;;iBAAgB,SAAA,oBAA6B,MAAA,kBAAA,CAAA,GAA4B,aAAA,CAAc,CAAA;AAAA,iBAwCvE,KAAA,CAAM,OAAA,GAAS,kBAAA,GAAuB,MAAA"}
|
package/dist/elysia/index.mjs
CHANGED
|
@@ -4,10 +4,15 @@ import { Elysia } from "elysia";
|
|
|
4
4
|
|
|
5
5
|
//#region src/elysia/index.ts
|
|
6
6
|
const storage = new AsyncLocalStorage();
|
|
7
|
+
const activeLoggers = /* @__PURE__ */ new WeakSet();
|
|
7
8
|
/**
|
|
8
9
|
* Get the request-scoped logger from anywhere in the call stack.
|
|
9
10
|
* Must be called inside a request handled by the `evlog()` plugin.
|
|
10
11
|
*
|
|
12
|
+
* Unlike other frameworks, Elysia uses `storage.enterWith()` which persists
|
|
13
|
+
* beyond the request lifecycle. This accessor additionally checks `activeLoggers`
|
|
14
|
+
* to ensure the logger belongs to an in-flight request.
|
|
15
|
+
*
|
|
11
16
|
* @example
|
|
12
17
|
* ```ts
|
|
13
18
|
* import { useLogger } from 'evlog/elysia'
|
|
@@ -20,7 +25,7 @@ const storage = new AsyncLocalStorage();
|
|
|
20
25
|
*/
|
|
21
26
|
function useLogger() {
|
|
22
27
|
const logger = storage.getStore();
|
|
23
|
-
if (!logger) throw new Error("[evlog] useLogger() was called outside of an evlog plugin context. Make sure app.use(evlog()) is registered before your routes.");
|
|
28
|
+
if (!logger || !activeLoggers.has(logger)) throw new Error("[evlog] useLogger() was called outside of an evlog plugin context. Make sure app.use(evlog()) is registered before your routes.");
|
|
24
29
|
return logger;
|
|
25
30
|
}
|
|
26
31
|
function evlog(options = {}) {
|
|
@@ -35,10 +40,12 @@ function evlog(options = {}) {
|
|
|
35
40
|
headers: extractSafeHeaders(request.headers),
|
|
36
41
|
...options
|
|
37
42
|
});
|
|
43
|
+
if (!skipped) activeLoggers.add(logger);
|
|
38
44
|
storage.enterWith(logger);
|
|
39
45
|
requestState.set(request, {
|
|
40
46
|
finish,
|
|
41
|
-
skipped
|
|
47
|
+
skipped,
|
|
48
|
+
logger
|
|
42
49
|
});
|
|
43
50
|
return { log: logger };
|
|
44
51
|
}).onAfterHandle({ as: "global" }, async ({ request, set }) => {
|
|
@@ -46,14 +53,17 @@ function evlog(options = {}) {
|
|
|
46
53
|
if (!state || state.skipped || emitted.has(request)) return;
|
|
47
54
|
emitted.add(request);
|
|
48
55
|
await state.finish({ status: set.status || 200 });
|
|
56
|
+
activeLoggers.delete(state.logger);
|
|
57
|
+
storage.enterWith(void 0);
|
|
49
58
|
}).onError({ as: "global" }, async ({ request, error }) => {
|
|
50
59
|
const state = requestState.get(request);
|
|
51
60
|
if (!state || state.skipped || emitted.has(request)) return;
|
|
52
61
|
emitted.add(request);
|
|
53
|
-
const logger = storage.getStore();
|
|
54
62
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
55
|
-
|
|
63
|
+
state.logger.error(err);
|
|
56
64
|
await state.finish({ error: err });
|
|
65
|
+
activeLoggers.delete(state.logger);
|
|
66
|
+
storage.enterWith(void 0);
|
|
57
67
|
});
|
|
58
68
|
}
|
|
59
69
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/elysia/index.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks'\nimport { Elysia } from 'elysia'\nimport type {
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/elysia/index.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks'\nimport { Elysia } from 'elysia'\nimport type { RequestLogger } from '../types'\nimport { createMiddlewareLogger, type BaseEvlogOptions } from '../shared/middleware'\nimport { extractSafeHeaders } from '../shared/headers'\n\nconst storage = new AsyncLocalStorage<RequestLogger>()\n\n// Tracks loggers that are currently active (within a live request).\n// Elysia uses storage.enterWith() which persists in the async context\n// even after the request ends, so we use this set to distinguish\n// an in-flight logger from a stale one.\nconst activeLoggers = new WeakSet<RequestLogger>()\n\nexport type EvlogElysiaOptions = BaseEvlogOptions\n\n/**\n * Get the request-scoped logger from anywhere in the call stack.\n * Must be called inside a request handled by the `evlog()` plugin.\n *\n * Unlike other frameworks, Elysia uses `storage.enterWith()` which persists\n * beyond the request lifecycle. This accessor additionally checks `activeLoggers`\n * to ensure the logger belongs to an in-flight request.\n *\n * @example\n * ```ts\n * import { useLogger } from 'evlog/elysia'\n *\n * function findUser(id: string) {\n * const log = useLogger()\n * log.set({ user: { id } })\n * }\n * ```\n */\nexport function useLogger<T extends object = Record<string, unknown>>(): RequestLogger<T> {\n const logger = storage.getStore()\n if (!logger || !activeLoggers.has(logger)) {\n throw new Error(\n '[evlog] useLogger() was called outside of an evlog plugin context. '\n + 'Make sure app.use(evlog()) is registered before your routes.',\n )\n }\n return logger as RequestLogger<T>\n}\n\n/**\n * Create an evlog plugin for Elysia.\n *\n * @example\n * ```ts\n * import { Elysia } from 'elysia'\n * import { evlog } from 'evlog/elysia'\n * import { createAxiomDrain } from 'evlog/axiom'\n *\n * const app = new Elysia()\n * .use(evlog({\n * drain: createAxiomDrain(),\n * enrich: (ctx) => {\n * ctx.event.region = process.env.FLY_REGION\n * },\n * }))\n * .get('/health', ({ log }) => {\n * log.set({ route: 'health' })\n * return { ok: true }\n * })\n * .listen(3000)\n * ```\n */\ninterface RequestState {\n finish: (opts?: { status?: number; error?: Error }) => Promise<unknown>\n skipped: boolean\n logger: RequestLogger\n}\n\nexport function evlog(options: EvlogElysiaOptions = {}) {\n const emitted = new WeakSet<Request>()\n const requestState = new WeakMap<Request, RequestState>()\n\n return new Elysia({ name: 'evlog' })\n .derive({ as: 'global' }, ({ request }) => {\n const url = new URL(request.url)\n\n const { logger, finish, skipped } = createMiddlewareLogger({\n method: request.method,\n path: url.pathname,\n requestId: request.headers.get('x-request-id') || crypto.randomUUID(),\n headers: extractSafeHeaders(request.headers),\n ...options,\n })\n\n if (!skipped) activeLoggers.add(logger)\n storage.enterWith(logger)\n requestState.set(request, { finish, skipped, logger })\n\n return { log: logger }\n })\n .onAfterHandle({ as: 'global' }, async ({ request, set }) => {\n const state = requestState.get(request)\n if (!state || state.skipped || emitted.has(request)) return\n emitted.add(request)\n await state.finish({ status: set.status as number || 200 })\n activeLoggers.delete(state.logger)\n storage.enterWith(undefined as unknown as RequestLogger)\n })\n .onError({ as: 'global' }, async ({ request, error }) => {\n const state = requestState.get(request)\n if (!state || state.skipped || emitted.has(request)) return\n emitted.add(request)\n const err = error instanceof Error ? error : new Error(String(error))\n state.logger.error(err)\n await state.finish({ error: err })\n activeLoggers.delete(state.logger)\n storage.enterWith(undefined as unknown as RequestLogger)\n })\n}\n"],"mappings":";;;;;AAMA,MAAM,UAAU,IAAI,mBAAkC;AAMtD,MAAM,gCAAgB,IAAI,SAAwB;;;;;;;;;;;;;;;;;;;AAsBlD,SAAgB,YAA0E;CACxF,MAAM,SAAS,QAAQ,UAAU;AACjC,KAAI,CAAC,UAAU,CAAC,cAAc,IAAI,OAAO,CACvC,OAAM,IAAI,MACR,kIAED;AAEH,QAAO;;AAgCT,SAAgB,MAAM,UAA8B,EAAE,EAAE;CACtD,MAAM,0BAAU,IAAI,SAAkB;CACtC,MAAM,+BAAe,IAAI,SAAgC;AAEzD,QAAO,IAAI,OAAO,EAAE,MAAM,SAAS,CAAC,CACjC,OAAO,EAAE,IAAI,UAAU,GAAG,EAAE,cAAc;EACzC,MAAM,MAAM,IAAI,IAAI,QAAQ,IAAI;EAEhC,MAAM,EAAE,QAAQ,QAAQ,YAAY,uBAAuB;GACzD,QAAQ,QAAQ;GAChB,MAAM,IAAI;GACV,WAAW,QAAQ,QAAQ,IAAI,eAAe,IAAI,OAAO,YAAY;GACrE,SAAS,mBAAmB,QAAQ,QAAQ;GAC5C,GAAG;GACJ,CAAC;AAEF,MAAI,CAAC,QAAS,eAAc,IAAI,OAAO;AACvC,UAAQ,UAAU,OAAO;AACzB,eAAa,IAAI,SAAS;GAAE;GAAQ;GAAS;GAAQ,CAAC;AAEtD,SAAO,EAAE,KAAK,QAAQ;GACtB,CACD,cAAc,EAAE,IAAI,UAAU,EAAE,OAAO,EAAE,SAAS,UAAU;EAC3D,MAAM,QAAQ,aAAa,IAAI,QAAQ;AACvC,MAAI,CAAC,SAAS,MAAM,WAAW,QAAQ,IAAI,QAAQ,CAAE;AACrD,UAAQ,IAAI,QAAQ;AACpB,QAAM,MAAM,OAAO,EAAE,QAAQ,IAAI,UAAoB,KAAK,CAAC;AAC3D,gBAAc,OAAO,MAAM,OAAO;AAClC,UAAQ,UAAU,OAAsC;GACxD,CACD,QAAQ,EAAE,IAAI,UAAU,EAAE,OAAO,EAAE,SAAS,YAAY;EACvD,MAAM,QAAQ,aAAa,IAAI,QAAQ;AACvC,MAAI,CAAC,SAAS,MAAM,WAAW,QAAQ,IAAI,QAAQ,CAAE;AACrD,UAAQ,IAAI,QAAQ;EACpB,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;AACrE,QAAM,OAAO,MAAM,IAAI;AACvB,QAAM,MAAM,OAAO,EAAE,OAAO,KAAK,CAAC;AAClC,gBAAc,OAAO,MAAM,OAAO;AAClC,UAAQ,UAAU,OAAsC;GACxD"}
|
package/dist/express/index.d.mts
CHANGED
|
@@ -1,50 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RequestLogger } from "../types.mjs";
|
|
2
|
+
import { t as BaseEvlogOptions } from "../middleware-BoVCgsfQ.mjs";
|
|
2
3
|
import { RequestHandler } from "express";
|
|
3
4
|
|
|
4
5
|
//#region src/express/index.d.ts
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
include?: string[];
|
|
8
|
-
/** Route patterns to exclude from logging. Exclusions take precedence over inclusions */
|
|
9
|
-
exclude?: string[];
|
|
10
|
-
/** Route-specific service configuration */
|
|
11
|
-
routes?: Record<string, RouteConfig>;
|
|
12
|
-
/**
|
|
13
|
-
* Drain callback called with every emitted event.
|
|
14
|
-
* Use with drain adapters (Axiom, OTLP, Sentry, etc.) or custom endpoints.
|
|
15
|
-
*/
|
|
16
|
-
drain?: (ctx: DrainContext) => void | Promise<void>;
|
|
17
|
-
/**
|
|
18
|
-
* Enrich callback called after emit, before drain.
|
|
19
|
-
* Use to add derived context (geo, deployment info, user agent, etc.).
|
|
20
|
-
*/
|
|
21
|
-
enrich?: (ctx: EnrichContext) => void | Promise<void>;
|
|
22
|
-
/**
|
|
23
|
-
* Custom tail sampling callback.
|
|
24
|
-
* Set `ctx.shouldKeep = true` to force-keep the log regardless of head sampling.
|
|
25
|
-
*/
|
|
26
|
-
keep?: (ctx: TailSamplingContext) => void | Promise<void>;
|
|
27
|
-
}
|
|
6
|
+
declare const useLogger: <T extends object = Record<string, unknown>>() => RequestLogger<T>;
|
|
7
|
+
type EvlogExpressOptions = BaseEvlogOptions;
|
|
28
8
|
declare module 'express-serve-static-core' {
|
|
29
9
|
interface Request {
|
|
30
10
|
log: RequestLogger;
|
|
31
11
|
}
|
|
32
12
|
}
|
|
33
|
-
/**
|
|
34
|
-
* Get the request-scoped logger from anywhere in the call stack.
|
|
35
|
-
* Must be called inside a request handled by the `evlog()` middleware.
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* ```ts
|
|
39
|
-
* import { useLogger } from 'evlog/express'
|
|
40
|
-
*
|
|
41
|
-
* function findUser(id: string) {
|
|
42
|
-
* const log = useLogger()
|
|
43
|
-
* log.set({ user: { id } })
|
|
44
|
-
* }
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
declare function useLogger<T extends object = Record<string, unknown>>(): RequestLogger<T>;
|
|
48
13
|
/**
|
|
49
14
|
* Create an evlog middleware for Express.
|
|
50
15
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/express/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/express/index.ts"],"mappings":";;;;;cAMiB,SAAA,sBAAS,MAAA,wBAAA,aAAA,CAAA,CAAA;AAAA,KAId,mBAAA,GAAsB,gBAAA;AAAA;EAAA,UAKtB,OAAA;IACR,GAAA,EAAK,aAAA;EAAA;AAAA;;;;;;;;;AANT;;;;;AAEoB;;;;;iBA0BJ,KAAA,CAAM,OAAA,GAAS,mBAAA,GAA2B,cAAA"}
|
package/dist/express/index.mjs
CHANGED
|
@@ -1,27 +1,8 @@
|
|
|
1
1
|
import { n as extractSafeNodeHeaders, r as createMiddlewareLogger } from "../headers-CXOd5EyZ.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { t as createLoggerStorage } from "../storage-Dd3PHiMh.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/express/index.ts
|
|
5
|
-
const storage =
|
|
6
|
-
/**
|
|
7
|
-
* Get the request-scoped logger from anywhere in the call stack.
|
|
8
|
-
* Must be called inside a request handled by the `evlog()` middleware.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts
|
|
12
|
-
* import { useLogger } from 'evlog/express'
|
|
13
|
-
*
|
|
14
|
-
* function findUser(id: string) {
|
|
15
|
-
* const log = useLogger()
|
|
16
|
-
* log.set({ user: { id } })
|
|
17
|
-
* }
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
function useLogger() {
|
|
21
|
-
const logger = storage.getStore();
|
|
22
|
-
if (!logger) throw new Error("[evlog] useLogger() was called outside of an evlog middleware context. Make sure app.use(evlog()) is registered before your routes.");
|
|
23
|
-
return logger;
|
|
24
|
-
}
|
|
5
|
+
const { storage, useLogger } = createLoggerStorage("middleware context. Make sure app.use(evlog()) is registered before your routes.");
|
|
25
6
|
/**
|
|
26
7
|
* Create an evlog middleware for Express.
|
|
27
8
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/express/index.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/express/index.ts"],"sourcesContent":["import type { Request, Response, NextFunction, RequestHandler } from 'express'\nimport type { RequestLogger } from '../types'\nimport { createMiddlewareLogger, type BaseEvlogOptions } from '../shared/middleware'\nimport { extractSafeNodeHeaders } from '../shared/headers'\nimport { createLoggerStorage } from '../shared/storage'\n\nconst { storage, useLogger } = createLoggerStorage(\n 'middleware context. Make sure app.use(evlog()) is registered before your routes.',\n)\n\nexport type EvlogExpressOptions = BaseEvlogOptions\n\nexport { useLogger }\n\ndeclare module 'express-serve-static-core' {\n interface Request {\n log: RequestLogger\n }\n}\n\n/**\n * Create an evlog middleware for Express.\n *\n * @example\n * ```ts\n * import express from 'express'\n * import { evlog } from 'evlog/express'\n * import { createAxiomDrain } from 'evlog/axiom'\n *\n * const app = express()\n * app.use(evlog({\n * drain: createAxiomDrain(),\n * enrich: (ctx) => {\n * ctx.event.region = process.env.FLY_REGION\n * },\n * }))\n * ```\n */\nexport function evlog(options: EvlogExpressOptions = {}): RequestHandler {\n return (req: Request, res: Response, next: NextFunction) => {\n const { logger, finish, skipped } = createMiddlewareLogger({\n method: req.method,\n path: req.path,\n requestId: req.get('x-request-id') || crypto.randomUUID(),\n headers: extractSafeNodeHeaders(req.headers),\n ...options,\n })\n\n if (skipped) {\n next()\n return\n }\n\n req.log = logger\n\n res.on('finish', () => {\n finish({ status: res.statusCode }).catch(() => {})\n })\n\n storage.run(logger, () => next())\n }\n}\n"],"mappings":";;;;AAMA,MAAM,EAAE,SAAS,cAAc,oBAC7B,mFACD;;;;;;;;;;;;;;;;;;;AA8BD,SAAgB,MAAM,UAA+B,EAAE,EAAkB;AACvE,SAAQ,KAAc,KAAe,SAAuB;EAC1D,MAAM,EAAE,QAAQ,QAAQ,YAAY,uBAAuB;GACzD,QAAQ,IAAI;GACZ,MAAM,IAAI;GACV,WAAW,IAAI,IAAI,eAAe,IAAI,OAAO,YAAY;GACzD,SAAS,uBAAuB,IAAI,QAAQ;GAC5C,GAAG;GACJ,CAAC;AAEF,MAAI,SAAS;AACX,SAAM;AACN;;AAGF,MAAI,MAAM;AAEV,MAAI,GAAG,gBAAgB;AACrB,UAAO,EAAE,QAAQ,IAAI,YAAY,CAAC,CAAC,YAAY,GAAG;IAClD;AAEF,UAAQ,IAAI,cAAc,MAAM,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { RequestLogger } from "../types.mjs";
|
|
2
|
+
import { t as BaseEvlogOptions } from "../middleware-BoVCgsfQ.mjs";
|
|
3
|
+
import { FastifyPluginCallback } from "fastify";
|
|
4
|
+
|
|
5
|
+
//#region src/fastify/index.d.ts
|
|
6
|
+
declare const useLogger: <T extends object = Record<string, unknown>>() => RequestLogger<T>;
|
|
7
|
+
type EvlogFastifyOptions = BaseEvlogOptions;
|
|
8
|
+
declare module 'fastify' {
|
|
9
|
+
interface FastifyRequest {
|
|
10
|
+
log: any;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create an evlog plugin for Fastify.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import Fastify from 'fastify'
|
|
19
|
+
* import { initLogger } from 'evlog'
|
|
20
|
+
* import { evlog } from 'evlog/fastify'
|
|
21
|
+
* import { createAxiomDrain } from 'evlog/axiom'
|
|
22
|
+
*
|
|
23
|
+
* initLogger({ env: { service: 'fastify-api' } })
|
|
24
|
+
*
|
|
25
|
+
* const app = Fastify()
|
|
26
|
+
* await app.register(evlog, {
|
|
27
|
+
* drain: createAxiomDrain(),
|
|
28
|
+
* enrich: (ctx) => {
|
|
29
|
+
* ctx.event.region = process.env.FLY_REGION
|
|
30
|
+
* },
|
|
31
|
+
* })
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
declare const evlog: FastifyPluginCallback<BaseEvlogOptions>;
|
|
35
|
+
//#endregion
|
|
36
|
+
export { EvlogFastifyOptions, evlog, useLogger };
|
|
37
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/fastify/index.ts"],"mappings":";;;;;cAMiB,SAAA,sBAAS,MAAA,wBAAA,aAAA,CAAA,CAAA;AAAA,KAId,mBAAA,GAAsB,gBAAA;AAAA;EAAA,UAKtB,cAAA;IAER,GAAA;EAAA;AAAA;;;;;;;;;AAPJ;;;;;AAEoB;;;;;;AAyFpB;;cAAa,KAAA,EAAK,qBAAA,CAAA,gBAAA"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { n as extractSafeNodeHeaders, r as createMiddlewareLogger } from "../headers-CXOd5EyZ.mjs";
|
|
2
|
+
import { t as createLoggerStorage } from "../storage-Dd3PHiMh.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/fastify/index.ts
|
|
5
|
+
const { storage, useLogger } = createLoggerStorage("plugin context. Make sure app.register(evlog) is called before your routes.");
|
|
6
|
+
const evlogPlugin = (fastify, options, done) => {
|
|
7
|
+
const emitted = /* @__PURE__ */ new WeakSet();
|
|
8
|
+
const requestState = /* @__PURE__ */ new WeakMap();
|
|
9
|
+
fastify.addHook("onRequest", (request, _reply, done) => {
|
|
10
|
+
const headers = extractSafeNodeHeaders(request.headers);
|
|
11
|
+
const path = new URL(request.url, "http://localhost").pathname;
|
|
12
|
+
const { logger, finish, skipped } = createMiddlewareLogger({
|
|
13
|
+
method: request.method,
|
|
14
|
+
path,
|
|
15
|
+
requestId: headers["x-request-id"] || crypto.randomUUID(),
|
|
16
|
+
headers,
|
|
17
|
+
...options
|
|
18
|
+
});
|
|
19
|
+
if (skipped) {
|
|
20
|
+
done();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const req = request;
|
|
24
|
+
req.log = logger;
|
|
25
|
+
requestState.set(request, { finish });
|
|
26
|
+
storage.run(logger, () => done());
|
|
27
|
+
});
|
|
28
|
+
fastify.addHook("onResponse", async (request, reply) => {
|
|
29
|
+
const state = requestState.get(request);
|
|
30
|
+
if (!state || emitted.has(request)) return;
|
|
31
|
+
emitted.add(request);
|
|
32
|
+
await state.finish({ status: reply.statusCode });
|
|
33
|
+
});
|
|
34
|
+
fastify.addHook("onError", async (request, _reply, error) => {
|
|
35
|
+
const state = requestState.get(request);
|
|
36
|
+
if (!state || emitted.has(request)) return;
|
|
37
|
+
emitted.add(request);
|
|
38
|
+
const logger = request.log;
|
|
39
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
40
|
+
if (logger && typeof logger.error === "function") logger.error(err);
|
|
41
|
+
await state.finish({ error: err });
|
|
42
|
+
});
|
|
43
|
+
done();
|
|
44
|
+
};
|
|
45
|
+
const plugin = evlogPlugin;
|
|
46
|
+
plugin[Symbol.for("skip-override")] = true;
|
|
47
|
+
plugin[Symbol.for("fastify.display-name")] = "evlog";
|
|
48
|
+
/**
|
|
49
|
+
* Create an evlog plugin for Fastify.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* import Fastify from 'fastify'
|
|
54
|
+
* import { initLogger } from 'evlog'
|
|
55
|
+
* import { evlog } from 'evlog/fastify'
|
|
56
|
+
* import { createAxiomDrain } from 'evlog/axiom'
|
|
57
|
+
*
|
|
58
|
+
* initLogger({ env: { service: 'fastify-api' } })
|
|
59
|
+
*
|
|
60
|
+
* const app = Fastify()
|
|
61
|
+
* await app.register(evlog, {
|
|
62
|
+
* drain: createAxiomDrain(),
|
|
63
|
+
* enrich: (ctx) => {
|
|
64
|
+
* ctx.event.region = process.env.FLY_REGION
|
|
65
|
+
* },
|
|
66
|
+
* })
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
const evlog = evlogPlugin;
|
|
70
|
+
|
|
71
|
+
//#endregion
|
|
72
|
+
export { evlog, useLogger };
|
|
73
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/fastify/index.ts"],"sourcesContent":["import type { FastifyPluginCallback } from 'fastify'\nimport type { RequestLogger } from '../types'\nimport { createMiddlewareLogger, type BaseEvlogOptions } from '../shared/middleware'\nimport { extractSafeNodeHeaders } from '../shared/headers'\nimport { createLoggerStorage } from '../shared/storage'\n\nconst { storage, useLogger } = createLoggerStorage(\n 'plugin context. Make sure app.register(evlog) is called before your routes.',\n)\n\nexport type EvlogFastifyOptions = BaseEvlogOptions\n\nexport { useLogger }\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n // Overrides Fastify's built-in pino logger on the request with evlog's RequestLogger.\n log: any\n }\n}\n\ninterface RequestState {\n finish: (opts?: { status?: number; error?: Error }) => Promise<unknown>\n}\n\nconst evlogPlugin: FastifyPluginCallback<EvlogFastifyOptions> = (fastify, options, done) => {\n const emitted = new WeakSet<object>()\n const requestState = new WeakMap<object, RequestState>()\n\n fastify.addHook('onRequest', (request, _reply, done) => {\n const headers = extractSafeNodeHeaders(request.headers)\n const path = new URL(request.url, 'http://localhost').pathname\n\n const { logger, finish, skipped } = createMiddlewareLogger({\n method: request.method,\n path,\n requestId: headers['x-request-id'] || crypto.randomUUID(),\n headers,\n ...options,\n })\n\n if (skipped) {\n done()\n return\n }\n\n // Shadow Fastify's built-in pino logger with evlog's request-scoped logger\n const req = request as any\n req.log = logger\n requestState.set(request, { finish })\n\n storage.run(logger, () => done())\n })\n\n fastify.addHook('onResponse', async (request, reply) => {\n const state = requestState.get(request)\n if (!state || emitted.has(request)) return\n emitted.add(request)\n await state.finish({ status: reply.statusCode })\n })\n\n fastify.addHook('onError', async (request, _reply, error) => {\n const state = requestState.get(request)\n if (!state || emitted.has(request)) return\n emitted.add(request)\n const logger = (request as any).log\n const err = error instanceof Error ? error : new Error(String(error))\n if (logger && typeof logger.error === 'function') logger.error(err)\n await state.finish({ error: err })\n })\n\n done()\n}\n\n// Break Fastify plugin encapsulation without a runtime dependency on fastify-plugin.\n// This is the same mechanism fastify-plugin uses internally.\nconst plugin = evlogPlugin as any\nplugin[Symbol.for('skip-override')] = true\nplugin[Symbol.for('fastify.display-name')] = 'evlog'\n\n/**\n * Create an evlog plugin for Fastify.\n *\n * @example\n * ```ts\n * import Fastify from 'fastify'\n * import { initLogger } from 'evlog'\n * import { evlog } from 'evlog/fastify'\n * import { createAxiomDrain } from 'evlog/axiom'\n *\n * initLogger({ env: { service: 'fastify-api' } })\n *\n * const app = Fastify()\n * await app.register(evlog, {\n * drain: createAxiomDrain(),\n * enrich: (ctx) => {\n * ctx.event.region = process.env.FLY_REGION\n * },\n * })\n * ```\n */\nexport const evlog = evlogPlugin\n"],"mappings":";;;;AAMA,MAAM,EAAE,SAAS,cAAc,oBAC7B,8EACD;AAiBD,MAAM,eAA2D,SAAS,SAAS,SAAS;CAC1F,MAAM,0BAAU,IAAI,SAAiB;CACrC,MAAM,+BAAe,IAAI,SAA+B;AAExD,SAAQ,QAAQ,cAAc,SAAS,QAAQ,SAAS;EACtD,MAAM,UAAU,uBAAuB,QAAQ,QAAQ;EACvD,MAAM,OAAO,IAAI,IAAI,QAAQ,KAAK,mBAAmB,CAAC;EAEtD,MAAM,EAAE,QAAQ,QAAQ,YAAY,uBAAuB;GACzD,QAAQ,QAAQ;GAChB;GACA,WAAW,QAAQ,mBAAmB,OAAO,YAAY;GACzD;GACA,GAAG;GACJ,CAAC;AAEF,MAAI,SAAS;AACX,SAAM;AACN;;EAIF,MAAM,MAAM;AACZ,MAAI,MAAM;AACV,eAAa,IAAI,SAAS,EAAE,QAAQ,CAAC;AAErC,UAAQ,IAAI,cAAc,MAAM,CAAC;GACjC;AAEF,SAAQ,QAAQ,cAAc,OAAO,SAAS,UAAU;EACtD,MAAM,QAAQ,aAAa,IAAI,QAAQ;AACvC,MAAI,CAAC,SAAS,QAAQ,IAAI,QAAQ,CAAE;AACpC,UAAQ,IAAI,QAAQ;AACpB,QAAM,MAAM,OAAO,EAAE,QAAQ,MAAM,YAAY,CAAC;GAChD;AAEF,SAAQ,QAAQ,WAAW,OAAO,SAAS,QAAQ,UAAU;EAC3D,MAAM,QAAQ,aAAa,IAAI,QAAQ;AACvC,MAAI,CAAC,SAAS,QAAQ,IAAI,QAAQ,CAAE;AACpC,UAAQ,IAAI,QAAQ;EACpB,MAAM,SAAU,QAAgB;EAChC,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;AACrE,MAAI,UAAU,OAAO,OAAO,UAAU,WAAY,QAAO,MAAM,IAAI;AACnE,QAAM,MAAM,OAAO,EAAE,OAAO,KAAK,CAAC;GAClC;AAEF,OAAM;;AAKR,MAAM,SAAS;AACf,OAAO,OAAO,IAAI,gBAAgB,IAAI;AACtC,OAAO,OAAO,IAAI,uBAAuB,IAAI;;;;;;;;;;;;;;;;;;;;;;AAuB7C,MAAa,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headers-CXOd5EyZ.mjs","names":[],"sources":["../src/shared/middleware.ts","../src/shared/headers.ts"],"sourcesContent":["import type { DrainContext, EnrichContext, RequestLogger, RouteConfig, TailSamplingContext, WideEvent } from '../types'\nimport { createRequestLogger, isEnabled, shouldKeep } from '../logger'\nimport { extractErrorStatus } from '../nitro'\nimport { shouldLog, getServiceForPath } from './routes'\n\n/**\n * Base options shared by all framework integrations.\n *
|
|
1
|
+
{"version":3,"file":"headers-CXOd5EyZ.mjs","names":[],"sources":["../src/shared/middleware.ts","../src/shared/headers.ts"],"sourcesContent":["import type { DrainContext, EnrichContext, RequestLogger, RouteConfig, TailSamplingContext, WideEvent } from '../types'\nimport { createRequestLogger, isEnabled, shouldKeep } from '../logger'\nimport { extractErrorStatus } from '../nitro'\nimport { shouldLog, getServiceForPath } from './routes'\n\n/**\n * Base options shared by all framework integrations.\n *\n * Every framework-specific options interface (e.g. `EvlogExpressOptions`)\n * extends this type. If a framework needs extra fields it can add them\n * on top; otherwise the base is used as-is.\n */\nexport interface BaseEvlogOptions {\n /** Route patterns to include in logging (glob). If not set, all routes are logged */\n include?: string[]\n /** Route patterns to exclude from logging. Exclusions take precedence over inclusions */\n exclude?: string[]\n /** Route-specific service configuration */\n routes?: Record<string, RouteConfig>\n /**\n * Drain callback called with every emitted event.\n * Use with drain adapters (Axiom, OTLP, Sentry, etc.) or custom endpoints.\n */\n drain?: (ctx: DrainContext) => void | Promise<void>\n /**\n * Enrich callback called after emit, before drain.\n * Use to add derived context (geo, deployment info, user agent, etc.).\n */\n enrich?: (ctx: EnrichContext) => void | Promise<void>\n /**\n * Custom tail sampling callback.\n * Set `ctx.shouldKeep = true` to force-keep the log regardless of head sampling.\n */\n keep?: (ctx: TailSamplingContext) => void | Promise<void>\n}\n\n/**\n * Internal options consumed by `createMiddlewareLogger`.\n * Extends `BaseEvlogOptions` with the request-specific fields\n * that framework adapters must provide.\n */\nexport interface MiddlewareLoggerOptions extends BaseEvlogOptions {\n method: string\n path: string\n requestId?: string\n /** Pre-filtered safe request headers (used for enrich/drain context) */\n headers?: Record<string, string>\n}\n\nexport interface MiddlewareLoggerResult {\n logger: RequestLogger\n finish: (opts?: { status?: number; error?: Error }) => Promise<WideEvent | null>\n skipped: boolean\n}\n\nconst noopResult: MiddlewareLoggerResult = {\n logger: {\n set() {},\n error() {},\n info() {},\n warn() {},\n emit() {\n return null \n },\n getContext() {\n return {} \n },\n },\n finish: () => Promise.resolve(null),\n skipped: true,\n}\n\nasync function runEnrichAndDrain(\n emittedEvent: WideEvent,\n options: MiddlewareLoggerOptions,\n requestInfo: { method: string; path: string; requestId?: string },\n responseStatus?: number,\n): Promise<void> {\n if (options.enrich) {\n const enrichCtx: EnrichContext = {\n event: emittedEvent,\n request: requestInfo,\n headers: options.headers,\n response: { status: responseStatus },\n }\n try {\n await options.enrich(enrichCtx)\n } catch (err) {\n console.error('[evlog] enrich failed:', err)\n }\n }\n\n if (options.drain) {\n const drainCtx: DrainContext = {\n event: emittedEvent,\n request: requestInfo,\n headers: options.headers,\n }\n try {\n await options.drain(drainCtx)\n } catch (err) {\n console.error('[evlog] drain failed:', err)\n }\n }\n}\n\n/**\n * Create a middleware-aware request logger with full lifecycle management.\n *\n * Handles the complete pipeline shared across all framework integrations:\n * route filtering, logger creation, service overrides, duration tracking,\n * tail sampling evaluation, event emission, enrichment, and draining.\n *\n * Framework adapters only need to:\n * 1. Extract method/path/requestId/headers from the framework request\n * 2. Call `createMiddlewareLogger()` with those + user options\n * 3. Check `skipped` — if true, skip to next middleware\n * 4. Store `logger` in framework-specific context (e.g., `c.set('log', logger)`)\n * 5. Call `finish({ status })` or `finish({ error })` at response end\n */\nexport function createMiddlewareLogger(options: MiddlewareLoggerOptions): MiddlewareLoggerResult {\n if (!isEnabled()) return noopResult\n\n const { method, path, requestId, include, exclude, routes, keep } = options\n\n if (!shouldLog(path, include, exclude)) {\n return noopResult\n }\n\n const resolvedRequestId = requestId || crypto.randomUUID()\n\n const logger = createRequestLogger({\n method,\n path,\n requestId: resolvedRequestId,\n })\n\n const routeService = getServiceForPath(path, routes)\n if (routeService) {\n logger.set({ service: routeService })\n }\n\n const startTime = Date.now()\n const requestInfo = { method, path, requestId: resolvedRequestId }\n\n const finish = async (opts?: { status?: number; error?: Error }): Promise<WideEvent | null> => {\n const { status, error } = opts ?? {}\n\n if (error) {\n logger.error(error)\n const errorStatus = extractErrorStatus(error)\n logger.set({ status: errorStatus })\n } else if (status !== undefined) {\n logger.set({ status })\n }\n\n const durationMs = Date.now() - startTime\n\n const resolvedStatus = error\n ? extractErrorStatus(error)\n : status ?? (logger.getContext().status as number | undefined)\n\n const tailCtx: TailSamplingContext = {\n status: resolvedStatus,\n duration: durationMs,\n path,\n method,\n context: logger.getContext(),\n shouldKeep: false,\n }\n\n if (keep) {\n await keep(tailCtx)\n }\n\n const forceKeep = tailCtx.shouldKeep || shouldKeep(tailCtx)\n const emittedEvent = logger.emit({ _forceKeep: forceKeep })\n\n if (emittedEvent && (options.enrich || options.drain)) {\n await runEnrichAndDrain(emittedEvent, options, requestInfo, resolvedStatus)\n }\n\n return emittedEvent\n }\n\n return { logger, finish, skipped: false }\n}\n","import { filterSafeHeaders } from '../utils'\n\n/**\n * Extract headers from a Web API `Headers` object and filter out sensitive ones.\n * Works with any runtime that supports the standard `Headers` API (Hono, Elysia,\n * Nitro v3, Cloudflare Workers, Bun, Deno, etc.).\n */\nexport function extractSafeHeaders(headers: Headers): Record<string, string> {\n const raw: Record<string, string> = {}\n headers.forEach((value, key) => {\n raw[key] = value\n })\n return filterSafeHeaders(raw)\n}\n\n/**\n * Extract headers from Node.js `IncomingHttpHeaders` and filter out sensitive ones.\n * Works with Express, Fastify, and any Node.js HTTP server using `req.headers`.\n */\nexport function extractSafeNodeHeaders(headers: Record<string, string | string[] | undefined>): Record<string, string> {\n const raw: Record<string, string> = {}\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined) continue\n raw[key] = Array.isArray(value) ? value.join(', ') : value\n }\n return filterSafeHeaders(raw)\n}\n"],"mappings":";;;;;;AAuDA,MAAM,aAAqC;CACzC,QAAQ;EACN,MAAM;EACN,QAAQ;EACR,OAAO;EACP,OAAO;EACP,OAAO;AACL,UAAO;;EAET,aAAa;AACX,UAAO,EAAE;;EAEZ;CACD,cAAc,QAAQ,QAAQ,KAAK;CACnC,SAAS;CACV;AAED,eAAe,kBACb,cACA,SACA,aACA,gBACe;AACf,KAAI,QAAQ,QAAQ;EAClB,MAAM,YAA2B;GAC/B,OAAO;GACP,SAAS;GACT,SAAS,QAAQ;GACjB,UAAU,EAAE,QAAQ,gBAAgB;GACrC;AACD,MAAI;AACF,SAAM,QAAQ,OAAO,UAAU;WACxB,KAAK;AACZ,WAAQ,MAAM,0BAA0B,IAAI;;;AAIhD,KAAI,QAAQ,OAAO;EACjB,MAAM,WAAyB;GAC7B,OAAO;GACP,SAAS;GACT,SAAS,QAAQ;GAClB;AACD,MAAI;AACF,SAAM,QAAQ,MAAM,SAAS;WACtB,KAAK;AACZ,WAAQ,MAAM,yBAAyB,IAAI;;;;;;;;;;;;;;;;;;AAmBjD,SAAgB,uBAAuB,SAA0D;AAC/F,KAAI,CAAC,WAAW,CAAE,QAAO;CAEzB,MAAM,EAAE,QAAQ,MAAM,WAAW,SAAS,SAAS,QAAQ,SAAS;AAEpE,KAAI,CAAC,UAAU,MAAM,SAAS,QAAQ,CACpC,QAAO;CAGT,MAAM,oBAAoB,aAAa,OAAO,YAAY;CAE1D,MAAM,SAAS,oBAAoB;EACjC;EACA;EACA,WAAW;EACZ,CAAC;CAEF,MAAM,eAAe,kBAAkB,MAAM,OAAO;AACpD,KAAI,aACF,QAAO,IAAI,EAAE,SAAS,cAAc,CAAC;CAGvC,MAAM,YAAY,KAAK,KAAK;CAC5B,MAAM,cAAc;EAAE;EAAQ;EAAM,WAAW;EAAmB;CAElE,MAAM,SAAS,OAAO,SAAyE;EAC7F,MAAM,EAAE,QAAQ,UAAU,QAAQ,EAAE;AAEpC,MAAI,OAAO;AACT,UAAO,MAAM,MAAM;GACnB,MAAM,cAAc,mBAAmB,MAAM;AAC7C,UAAO,IAAI,EAAE,QAAQ,aAAa,CAAC;aAC1B,WAAW,OACpB,QAAO,IAAI,EAAE,QAAQ,CAAC;EAGxB,MAAM,aAAa,KAAK,KAAK,GAAG;EAEhC,MAAM,iBAAiB,QACnB,mBAAmB,MAAM,GACzB,UAAW,OAAO,YAAY,CAAC;EAEnC,MAAM,UAA+B;GACnC,QAAQ;GACR,UAAU;GACV;GACA;GACA,SAAS,OAAO,YAAY;GAC5B,YAAY;GACb;AAED,MAAI,KACF,OAAM,KAAK,QAAQ;EAGrB,MAAM,YAAY,QAAQ,cAAc,WAAW,QAAQ;EAC3D,MAAM,eAAe,OAAO,KAAK,EAAE,YAAY,WAAW,CAAC;AAE3D,MAAI,iBAAiB,QAAQ,UAAU,QAAQ,OAC7C,OAAM,kBAAkB,cAAc,SAAS,aAAa,eAAe;AAG7E,SAAO;;AAGT,QAAO;EAAE;EAAQ;EAAQ,SAAS;EAAO;;;;;;;;;;AClL3C,SAAgB,mBAAmB,SAA0C;CAC3E,MAAM,MAA8B,EAAE;AACtC,SAAQ,SAAS,OAAO,QAAQ;AAC9B,MAAI,OAAO;GACX;AACF,QAAO,kBAAkB,IAAI;;;;;;AAO/B,SAAgB,uBAAuB,SAAgF;CACrH,MAAM,MAA8B,EAAE;AACtC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,EAAE;AAClD,MAAI,UAAU,OAAW;AACzB,MAAI,OAAO,MAAM,QAAQ,MAAM,GAAG,MAAM,KAAK,KAAK,GAAG;;AAEvD,QAAO,kBAAkB,IAAI"}
|
package/dist/hono/index.d.mts
CHANGED
|
@@ -1,30 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RequestLogger } from "../types.mjs";
|
|
2
|
+
import { t as BaseEvlogOptions } from "../middleware-BoVCgsfQ.mjs";
|
|
2
3
|
import { MiddlewareHandler } from "hono";
|
|
3
4
|
|
|
4
5
|
//#region src/hono/index.d.ts
|
|
5
|
-
|
|
6
|
-
/** Route patterns to include in logging (glob). If not set, all routes are logged */
|
|
7
|
-
include?: string[];
|
|
8
|
-
/** Route patterns to exclude from logging. Exclusions take precedence over inclusions */
|
|
9
|
-
exclude?: string[];
|
|
10
|
-
/** Route-specific service configuration */
|
|
11
|
-
routes?: Record<string, RouteConfig>;
|
|
12
|
-
/**
|
|
13
|
-
* Drain callback called with every emitted event.
|
|
14
|
-
* Use with drain adapters (Axiom, OTLP, Sentry, etc.) or custom endpoints.
|
|
15
|
-
*/
|
|
16
|
-
drain?: (ctx: DrainContext) => void | Promise<void>;
|
|
17
|
-
/**
|
|
18
|
-
* Enrich callback called after emit, before drain.
|
|
19
|
-
* Use to add derived context (geo, deployment info, user agent, etc.).
|
|
20
|
-
*/
|
|
21
|
-
enrich?: (ctx: EnrichContext) => void | Promise<void>;
|
|
22
|
-
/**
|
|
23
|
-
* Custom tail sampling callback.
|
|
24
|
-
* Set `ctx.shouldKeep = true` to force-keep the log regardless of head sampling.
|
|
25
|
-
*/
|
|
26
|
-
keep?: (ctx: TailSamplingContext) => void | Promise<void>;
|
|
27
|
-
}
|
|
6
|
+
type EvlogHonoOptions = BaseEvlogOptions;
|
|
28
7
|
/**
|
|
29
8
|
* Hono variables type for typed `c.get('log')` access.
|
|
30
9
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/hono/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/hono/index.ts"],"mappings":";;;;;KAKY,gBAAA,GAAmB,gBAAA;;AAA/B;;;;;AAgBA;;;;;;;;KAAY,cAAA;EAAmB,SAAA;IAAa,GAAA,EAAK,aAAA;EAAA;AAAA;;;;;;;;;;;;;;;;;;;iBAoBjC,KAAA,CAAM,OAAA,GAAS,gBAAA,GAAwB,iBAAA"}
|