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.
Files changed (37) hide show
  1. package/README.md +105 -25
  2. package/dist/elysia/index.d.mts +7 -24
  3. package/dist/elysia/index.d.mts.map +1 -1
  4. package/dist/elysia/index.mjs +14 -4
  5. package/dist/elysia/index.mjs.map +1 -1
  6. package/dist/express/index.d.mts +4 -39
  7. package/dist/express/index.d.mts.map +1 -1
  8. package/dist/express/index.mjs +2 -21
  9. package/dist/express/index.mjs.map +1 -1
  10. package/dist/fastify/index.d.mts +37 -0
  11. package/dist/fastify/index.d.mts.map +1 -0
  12. package/dist/fastify/index.mjs +73 -0
  13. package/dist/fastify/index.mjs.map +1 -0
  14. package/dist/headers-CXOd5EyZ.mjs.map +1 -1
  15. package/dist/hono/index.d.mts +3 -24
  16. package/dist/hono/index.d.mts.map +1 -1
  17. package/dist/hono/index.mjs.map +1 -1
  18. package/dist/middleware-BoVCgsfQ.d.mts +36 -0
  19. package/dist/middleware-BoVCgsfQ.d.mts.map +1 -0
  20. package/dist/nestjs/index.d.mts +83 -0
  21. package/dist/nestjs/index.d.mts.map +1 -0
  22. package/dist/nestjs/index.mjs +109 -0
  23. package/dist/nestjs/index.mjs.map +1 -0
  24. package/dist/next/index.d.mts +3 -34
  25. package/dist/next/index.d.mts.map +1 -1
  26. package/dist/nitro/module.d.mts +1 -1
  27. package/dist/nitro/v3/module.d.mts +1 -1
  28. package/dist/{nitro-Nxg6qcXd.d.mts → nitro-BRisWfGy.d.mts} +1 -1
  29. package/dist/{nitro-Nxg6qcXd.d.mts.map → nitro-BRisWfGy.d.mts.map} +1 -1
  30. package/dist/nuxt/module.mjs +1 -1
  31. package/dist/storage-Dd3PHiMh.mjs +29 -0
  32. package/dist/storage-Dd3PHiMh.mjs.map +1 -0
  33. package/dist/sveltekit/index.d.mts +128 -0
  34. package/dist/sveltekit/index.d.mts.map +1 -0
  35. package/dist/sveltekit/index.mjs +163 -0
  36. package/dist/sveltekit/index.mjs.map +1 -0
  37. 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 { createRequestLogger, initLogger } from 'evlog'
381
+ import { initLogger } from 'evlog'
382
+ import { evlog, type EvlogVariables } from 'evlog/hono'
385
383
 
386
- initLogger({
387
- env: { service: 'hono-api' },
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
- const app = new Hono()
396
+ See the full [hono example](https://github.com/HugoRCD/evlog/tree/main/examples/hono) for a complete working project.
391
397
 
392
- app.use('*', async (c, next) => {
393
- const startedAt = Date.now()
394
- const log = createRequestLogger({ method: c.req.method, path: c.req.path })
398
+ ## Express
395
399
 
396
- try {
397
- await next()
398
- } catch (error) {
399
- log.error(error as Error)
400
- throw error
401
- } finally {
402
- log.emit({
403
- status: c.res.status,
404
- duration: Date.now() - startedAt,
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.get('/health', (c) => c.json({ ok: true }))
431
+ const app = Fastify({ logger: false })
432
+ await app.register(evlog)
410
433
 
411
- serve({ fetch: app.fetch, port: 3000 })
434
+ app.get('/api/users', async (request) => {
435
+ request.log.set({ users: { count: 42 } })
436
+ return { users: [] }
437
+ })
412
438
  ```
413
439
 
414
- See the full [hono example](https://github.com/HugoRCD/evlog/tree/main/examples/hono) for a complete working project.
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
 
@@ -1,34 +1,17 @@
1
- import { DrainContext, EnrichContext, RequestLogger, RouteConfig, TailSamplingContext } from "../types.mjs";
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
- interface EvlogElysiaOptions {
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":";;;;UAQiB,kBAAA;;EAEf,OAAA;EAFiC;EAIjC,OAAA;EAEwB;EAAxB,MAAA,GAAS,MAAA,SAAe,WAAA;EAKV;;;;EAAd,KAAA,IAAS,GAAA,EAAK,YAAA,YAAwB,OAAA;EAUM;;;;EAL5C,MAAA,IAAU,GAAA,EAAK,aAAA,YAAyB,OAAA;EAVxC;;;;EAeA,IAAA,IAAQ,GAAA,EAAK,mBAAA,YAA+B,OAAA;AAAA;;;;;;;;;;;;AAiB9C;;;iBAAgB,SAAA,oBAA6B,MAAA,kBAAA,CAAA,GAA4B,aAAA,CAAc,CAAA;AAAA,iBAuCvE,KAAA,CAAM,OAAA,GAAS,kBAAA,GAAuB,MAAA"}
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"}
@@ -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
- if (logger) logger.error(err);
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 { DrainContext, EnrichContext, RequestLogger, RouteConfig, TailSamplingContext } from '../types'\nimport { createMiddlewareLogger } from '../shared/middleware'\nimport { extractSafeHeaders } from '../shared/headers'\n\nconst storage = new AsyncLocalStorage<RequestLogger>()\n\nexport interface EvlogElysiaOptions {\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 * 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 * @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) {\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}\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 storage.enterWith(logger)\n requestState.set(request, { finish, skipped })\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 })\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 logger = storage.getStore()\n const err = error instanceof Error ? error : new Error(String(error))\n if (logger) logger.error(err)\n await state.finish({ error: err })\n })\n}\n"],"mappings":";;;;;AAMA,MAAM,UAAU,IAAI,mBAAkC;;;;;;;;;;;;;;;AAwCtD,SAAgB,YAA0E;CACxF,MAAM,SAAS,QAAQ,UAAU;AACjC,KAAI,CAAC,OACH,OAAM,IAAI,MACR,kIAED;AAEH,QAAO;;AA+BT,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,UAAQ,UAAU,OAAO;AACzB,eAAa,IAAI,SAAS;GAAE;GAAQ;GAAS,CAAC;AAE9C,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;GAC3D,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,SAAS,QAAQ,UAAU;EACjC,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;AACrE,MAAI,OAAQ,QAAO,MAAM,IAAI;AAC7B,QAAM,MAAM,OAAO,EAAE,OAAO,KAAK,CAAC;GAClC"}
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"}
@@ -1,50 +1,15 @@
1
- import { DrainContext, EnrichContext, RequestLogger, RouteConfig, TailSamplingContext } from "../types.mjs";
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
- interface EvlogExpressOptions {
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
+ 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":";;;;UAQiB,mBAAA;;EAEf,OAAA;EAFkC;EAIlC,OAAA;EAEwB;EAAxB,MAAA,GAAS,MAAA,SAAe,WAAA;EAKV;;;;EAAd,KAAA,IAAS,GAAA,EAAK,YAAA,YAAwB,OAAA;EAUM;;;;EAL5C,MAAA,IAAU,GAAA,EAAK,aAAA,YAAyB,OAAA;EAVxC;;;;EAeA,IAAA,IAAQ,GAAA,EAAK,mBAAA,YAA+B,OAAA;AAAA;AAAA;EAAA,UAIlC,OAAA;IACR,GAAA,EAAK,aAAA;EAAA;AAAA;;;;;;;AAJR;;;;;;;;iBAsBe,SAAA,oBAA6B,MAAA,kBAAA,CAAA,GAA4B,aAAA,CAAc,CAAA;;AAAvF;;;;;;;;;;;;;;AA6BA;;;iBAAgB,KAAA,CAAM,OAAA,GAAS,mBAAA,GAA2B,cAAA"}
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"}
@@ -1,27 +1,8 @@
1
1
  import { n as extractSafeNodeHeaders, r as createMiddlewareLogger } from "../headers-CXOd5EyZ.mjs";
2
- import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { t as createLoggerStorage } from "../storage-Dd3PHiMh.mjs";
3
3
 
4
4
  //#region src/express/index.ts
5
- const storage = new AsyncLocalStorage();
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 { AsyncLocalStorage } from 'node:async_hooks'\nimport type { Request, Response, NextFunction, RequestHandler } from 'express'\nimport type { DrainContext, EnrichContext, RequestLogger, RouteConfig, TailSamplingContext } from '../types'\nimport { createMiddlewareLogger } from '../shared/middleware'\nimport { extractSafeNodeHeaders } from '../shared/headers'\n\nconst storage = new AsyncLocalStorage<RequestLogger>()\n\nexport interface EvlogExpressOptions {\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\ndeclare module 'express-serve-static-core' {\n interface Request {\n log: RequestLogger\n }\n}\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()` middleware.\n *\n * @example\n * ```ts\n * import { useLogger } from 'evlog/express'\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) {\n throw new Error(\n '[evlog] useLogger() was called outside of an evlog middleware 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 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,UAAU,IAAI,mBAAkC;;;;;;;;;;;;;;;AA8CtD,SAAgB,YAA0E;CACxF,MAAM,SAAS,QAAQ,UAAU;AACjC,KAAI,CAAC,OACH,OAAM,IAAI,MACR,sIAED;AAEH,QAAO;;;;;;;;;;;;;;;;;;;;AAqBT,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"}
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 * Each framework adapter spreads its user-facing options into this.\n */\nexport interface MiddlewareLoggerOptions {\n method: string\n path: string\n requestId?: string\n include?: string[]\n exclude?: string[]\n routes?: Record<string, RouteConfig>\n /** Tail sampling callback set `ctx.shouldKeep = true` to force-keep */\n keep?: (ctx: TailSamplingContext) => void | Promise<void>\n /** Drain callback send events to external services */\n drain?: (ctx: DrainContext) => void | Promise<void>\n /** Enrich callback add derived context after emit, before drain */\n enrich?: (ctx: EnrichContext) => void | Promise<void>\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":";;;;;;AAgCA,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;;;;;;;;;;AC3J3C,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"}
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"}
@@ -1,30 +1,9 @@
1
- import { DrainContext, EnrichContext, RequestLogger, RouteConfig, TailSamplingContext } from "../types.mjs";
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
- interface EvlogHonoOptions {
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":";;;;UAKiB,gBAAA;;EAEf,OAAA;EAF+B;EAI/B,OAAA;EAEwB;EAAxB,MAAA,GAAS,MAAA,SAAe,WAAA;EAKV;;;;EAAd,KAAA,IAAS,GAAA,EAAK,YAAA,YAAwB,OAAA;EAUM;;;;EAL5C,MAAA,IAAU,GAAA,EAAK,aAAA,YAAyB,OAAA;EAVxC;;;;EAeA,IAAA,IAAQ,GAAA,EAAK,mBAAA,YAA+B,OAAA;AAAA;;;;;;;;;;;;AAiB9C;;;KAAY,cAAA;EAAmB,SAAA;IAAa,GAAA,EAAK,aAAA;EAAA;AAAA;;AAoBjD;;;;;;;;;;;;;;;;;iBAAgB,KAAA,CAAM,OAAA,GAAS,gBAAA,GAAwB,iBAAA"}
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"}