create-evlog-framework-integration

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Create evlog Framework Integration

创建 evlog 框架集成

Add a new framework integration to evlog. Every integration follows the same architecture built on the shared
createMiddlewareLogger
utility. This skill walks through all touchpoints. Every single touchpoint is mandatory -- do not skip any.
为 evlog 新增一个框架集成。所有集成都基于共享的
createMiddlewareLogger
工具函数遵循相同的架构。本技能指南会覆盖所有需要修改的接触点。每一个接触点都是必填项——不要跳过任何步骤。

PR Title

PR 标题

Recommended format for the pull request title:
feat({framework}): add {Framework} middleware integration
拉取请求标题的推荐格式:
feat({framework}): add {Framework} middleware integration

Touchpoints Checklist

接触点检查清单

#FileAction
1
packages/evlog/src/{framework}/index.ts
Create integration source
2
packages/evlog/tsdown.config.ts
Add build entry + external
3
packages/evlog/package.json
Add
exports
+
typesVersions
+ peer dep + keyword
4
packages/evlog/test/{framework}.test.ts
Create tests
5
apps/docs/content/1.getting-started/2.installation.md
Add framework section
6
apps/docs/content/6.examples/{N}.{framework}.md
Create dedicated example page
7
apps/docs/content/0.landing.md
Add framework code snippet
8
apps/docs/app/components/features/FeatureFrameworks.vue
Add framework tab
9
AGENTS.md
Add framework to integration section
10
examples/{framework}/
Create example app with test UI
11
package.json
(root)
Add
example:{framework}
script
12
.changeset/{framework}-integration.md
Create changeset (
minor
)
13
.github/workflows/semantic-pull-request.yml
Add
{framework}
scope
14
.github/pull_request_template.md
Add
{framework}
scope
Important: Do NOT consider the task complete until all 14 touchpoints have been addressed.
#文件操作
1
packages/evlog/src/{framework}/index.ts
创建集成源代码
2
packages/evlog/tsdown.config.ts
新增构建入口 + 外部依赖
3
packages/evlog/package.json
新增
exports
+
typesVersions
+ 对等依赖 + 关键词
4
packages/evlog/test/{framework}.test.ts
创建测试用例
5
apps/docs/content/1.getting-started/2.installation.md
新增对应框架的文档章节
6
apps/docs/content/6.examples/{N}.{framework}.md
创建专属的示例页面
7
apps/docs/content/0.landing.md
新增框架代码片段
8
apps/docs/app/components/features/FeatureFrameworks.vue
新增框架标签页
9
AGENTS.md
在集成章节添加该框架
10
examples/{framework}/
创建带测试UI的示例应用
11根目录
package.json
新增
example:{framework}
脚本
12
.changeset/{framework}-integration.md
创建变更集(
minor
版本)
13
.github/workflows/semantic-pull-request.yml
新增
{framework}
作用域
14
.github/pull_request_template.md
新增
{framework}
作用域
重要提示:只有完成所有14个接触点的修改,才能视为任务完成。

Naming Conventions

命名规范

Use these placeholders consistently:
PlaceholderExample (Hono)Usage
{framework}
hono
Directory names, import paths, file names
{Framework}
Hono
PascalCase in type/interface names
请一致使用以下占位符:
占位符示例(Hono)使用场景
{framework}
hono
目录名、导入路径、文件名
{Framework}
Hono
类型/接口名的大驼峰命名

Shared Utilities

共享工具函数

All integrations share the same core utilities. Never reimplement logic that exists in shared/.
UtilityLocationPurpose
createMiddlewareLogger
../shared/middleware
Full lifecycle: logger creation, route filtering, tail sampling, emit, enrich, drain
extractSafeHeaders
../shared/headers
Convert Web API
Headers
→ filtered
Record<string, string>
(Hono, Elysia, etc.)
extractSafeNodeHeaders
../shared/headers
Convert Node.js
IncomingHttpHeaders
→ filtered
Record<string, string>
(Express, Fastify, NestJS)
MiddlewareLoggerOptions
../shared/middleware
Base options type with
drain
,
enrich
,
keep
,
include
,
exclude
,
routes
,
headers
所有集成共享相同的核心工具函数。永远不要重写
shared/
目录下已存在的逻辑
工具函数位置用途
createMiddlewareLogger
../shared/middleware
完整生命周期:日志创建、路由过滤、尾部采样、上报、信息丰富、排空
extractSafeHeaders
../shared/headers
将Web API
Headers
转换为过滤后的
Record<string, string>
(适用于Hono、Elysia等)
extractSafeNodeHeaders
../shared/headers
将Node.js
IncomingHttpHeaders
转换为过滤后的
Record<string, string>
(适用于Express、Fastify、NestJS)
MiddlewareLoggerOptions
../shared/middleware
基础选项类型,包含
drain
enrich
keep
include
exclude
routes
headers

Test Helpers

测试辅助工具

UtilityLocationPurpose
createPipelineSpies()
test/helpers/framework
Creates mock drain/enrich/keep callbacks
assertDrainCalledWith()
test/helpers/framework
Validates drain was called with expected event shape
assertEnrichBeforeDrain()
test/helpers/framework
Validates enrich runs before drain
assertSensitiveHeadersFiltered()
test/helpers/framework
Validates sensitive headers are excluded
assertWideEventShape()
test/helpers/framework
Validates standard wide event fields
工具函数位置用途
createPipelineSpies()
test/helpers/framework
创建模拟的drain/enrich/keep回调
assertDrainCalledWith()
test/helpers/framework
验证drain被调用时传入的事件结构符合预期
assertEnrichBeforeDrain()
test/helpers/framework
验证enrich在drain之前执行
assertSensitiveHeadersFiltered()
test/helpers/framework
验证敏感头信息已被排除
assertWideEventShape()
test/helpers/framework
验证标准宽事件字段符合要求

Step 1: Integration Source

步骤1:集成源代码

Create
packages/evlog/src/{framework}/index.ts
.
The integration file should be minimal — typically 50-80 lines of framework-specific glue. All pipeline logic (enrich, drain, keep, header filtering) is handled by
createMiddlewareLogger
.
创建
packages/evlog/src/{framework}/index.ts
集成文件应该尽可能精简——通常是50-80行框架专属的粘合代码。所有管道逻辑(enrich、drain、keep、头过滤)都由
createMiddlewareLogger
处理。

Template Structure

模板结构

typescript
import { AsyncLocalStorage } from 'node:async_hooks'
import type { DrainContext, EnrichContext, RequestLogger, RouteConfig, TailSamplingContext } from '../types'
import { createMiddlewareLogger } from '../shared/middleware'
import { extractSafeHeaders } from '../shared/headers'       // for Web API Headers (Hono, Elysia)
// OR
import { extractSafeNodeHeaders } from '../shared/headers'    // for Node.js headers (Express, Fastify)

const storage = new AsyncLocalStorage<RequestLogger>()

export interface Evlog{Framework}Options {
  include?: string[]
  exclude?: string[]
  routes?: Record<string, RouteConfig>
  drain?: (ctx: DrainContext) => void | Promise<void>
  enrich?: (ctx: EnrichContext) => void | Promise<void>
  keep?: (ctx: TailSamplingContext) => void | Promise<void>
}

// Type augmentation for typed logger access (framework-specific)
// For Express: declare module 'express-serve-static-core' { interface Request { log: RequestLogger } }
// For Hono: export type EvlogVariables = { Variables: { log: RequestLogger } }

/**
 * Get the request-scoped logger from anywhere in the call stack.
 * Uses AsyncLocalStorage — works across async boundaries.
 */
export function useLogger<T extends object = Record<string, unknown>>(): RequestLogger<T> {
  const logger = storage.getStore()
  if (!logger) {
    throw new Error(
      '[evlog] useLogger() was called outside of an evlog middleware context. '
      + 'Make sure the evlog middleware is registered before your routes.',
    )
  }
  return logger as RequestLogger<T>
}

export function evlog(options: Evlog{Framework}Options = {}): FrameworkMiddleware {
  return async (frameworkContext, next) => {
    const { logger, finish, skipped } = createMiddlewareLogger({
      method: /* extract from framework context */,
      path: /* extract from framework context */,
      requestId: /* extract x-request-id or crypto.randomUUID() */,
      headers: extractSafeHeaders(/* framework request Headers object */),
      ...options,
    })

    if (skipped) {
      await next()
      return
    }

    // Store logger in framework-specific context
    // e.g., c.set('log', logger) for Hono
    // e.g., req.log = logger for Express

    // Wrap next() in AsyncLocalStorage.run() for useLogger() support
    // Express: storage.run(logger, () => next())
    // Hono: await storage.run(logger, () => next())
  }
}
typescript
import { AsyncLocalStorage } from 'node:async_hooks'
import type { DrainContext, EnrichContext, RequestLogger, RouteConfig, TailSamplingContext } from '../types'
import { createMiddlewareLogger } from '../shared/middleware'
import { extractSafeHeaders } from '../shared/headers'       // for Web API Headers (Hono, Elysia)
// OR
import { extractSafeNodeHeaders } from '../shared/headers'    // for Node.js headers (Express, Fastify)

const storage = new AsyncLocalStorage<RequestLogger>()

export interface Evlog{Framework}Options {
  include?: string[]
  exclude?: string[]
  routes?: Record<string, RouteConfig>
  drain?: (ctx: DrainContext) => void | Promise<void>
  enrich?: (ctx: EnrichContext) => void | Promise<void>
  keep?: (ctx: TailSamplingContext) => void | Promise<void>
}

// Type augmentation for typed logger access (framework-specific)
// For Express: declare module 'express-serve-static-core' { interface Request { log: RequestLogger } }
// For Hono: export type EvlogVariables = { Variables: { log: RequestLogger } }

/**
 * 从调用栈的任意位置获取请求作用域的日志实例。
 * 基于AsyncLocalStorage实现——可跨异步边界工作。
 */
export function useLogger<T extends object = Record<string, unknown>>(): RequestLogger<T> {
  const logger = storage.getStore()
  if (!logger) {
    throw new Error(
      '[evlog] useLogger() was called outside of an evlog middleware context. '
      + 'Make sure the evlog middleware is registered before your routes.',
    )
  }
  return logger as RequestLogger<T>
}

export function evlog(options: Evlog{Framework}Options = {}): FrameworkMiddleware {
  return async (frameworkContext, next) => {
    const { logger, finish, skipped } = createMiddlewareLogger({
      method: /* extract from framework context */,
      path: /* extract from framework context */,
      requestId: /* extract x-request-id or crypto.randomUUID() */,
      headers: extractSafeHeaders(/* framework request Headers object */),
      ...options,
    })

    if (skipped) {
      await next()
      return
    }

    // Store logger in framework-specific context
    // e.g., c.set('log', logger) for Hono
    // e.g., req.log = logger for Express

    // Wrap next() in AsyncLocalStorage.run() for useLogger() support
    // Express: storage.run(logger, () => next())
    // Hono: await storage.run(logger, () => next())
  }
}

Reference Implementations

参考实现

  • Hono (~40 lines):
    packages/evlog/src/hono/index.ts
    — Web API Headers,
    c.set('log', logger)
    , wraps
    next()
    in try/catch
  • Express (~80 lines):
    packages/evlog/src/express/index.ts
    — Node.js headers,
    req.log
    ,
    res.on('finish')
    ,
    AsyncLocalStorage
    for
    useLogger()
  • Elysia (~70 lines):
    packages/evlog/src/elysia/index.ts
    — Web API Headers,
    derive()
    plugin,
    onAfterHandle
    /
    onError
    ,
    AsyncLocalStorage
    for
    useLogger()
  • Hono(~40行):
    packages/evlog/src/hono/index.ts
    —— Web API Headers,
    c.set('log', logger)
    ,在try/catch中包裹
    next()
  • Express(~80行):
    packages/evlog/src/express/index.ts
    —— Node.js headers,
    req.log
    res.on('finish')
    ,基于
    AsyncLocalStorage
    实现
    useLogger()
  • Elysia(~70行):
    packages/evlog/src/elysia/index.ts
    —— Web API Headers,
    derive()
    插件,
    onAfterHandle
    /
    onError
    ,基于
    AsyncLocalStorage
    实现
    useLogger()

Key Architecture Rules

核心架构规则

  1. Use
    createMiddlewareLogger
    — never call
    createRequestLogger
    directly
  2. Use the right header extractor
    extractSafeHeaders
    for Web API
    Headers
    ,
    extractSafeNodeHeaders
    for Node.js
    IncomingHttpHeaders
  3. Spread user options into
    createMiddlewareLogger
    drain
    ,
    enrich
    ,
    keep
    are handled automatically by
    finish()
  4. Store logger in the framework's idiomatic context (e.g.,
    c.set()
    for Hono,
    req.log
    for Express,
    .derive()
    for Elysia)
  5. Export
    useLogger()
    — backed by
    AsyncLocalStorage
    so the logger is accessible from anywhere in the call stack
  6. Call
    finish()
    in both success and error paths — it handles emit + enrich + drain
  7. Re-throw errors after
    finish()
    so framework error handlers still work
  8. Export options interface with drain/enrich/keep for feature parity across all frameworks
  9. Export type helpers for typed context access (e.g.,
    EvlogVariables
    for Hono)
  10. Framework SDK is a peer dependency — never bundle it
  11. Never duplicate pipeline logic
    callEnrichAndDrain
    is internal to
    createMiddlewareLogger
  1. 使用
    createMiddlewareLogger
    —— 永远不要直接调用
    createRequestLogger
  2. 使用正确的头提取函数 —— Web API
    Headers
    extractSafeHeaders
    ,Node.js
    IncomingHttpHeaders
    extractSafeNodeHeaders
  3. 将用户选项展开传入
    createMiddlewareLogger
    ——
    drain
    enrich
    keep
    会被
    finish()
    自动处理
  4. 将日志实例存储到框架的惯用上下文对象中(例如Hono用
    c.set()
    ,Express用
    req.log
    ,Elysia用
    .derive()
  5. 导出
    useLogger()
    —— 基于
    AsyncLocalStorage
    实现,因此可以在调用栈的任意位置访问日志实例
  6. 在成功和错误路径中都调用
    finish()
    —— 它会处理上报、信息丰富和排空逻辑
  7. finish()
    执行后重新抛出错误
    ,保证框架的错误处理逻辑仍然可以正常工作
  8. 导出选项接口,包含drain/enrich/keep,保证所有框架的功能一致性
  9. 导出类型辅助工具,用于类型化的上下文访问(例如Hono的
    EvlogVariables
  10. 框架SDK作为对等依赖 —— 永远不要打包进产物
  11. 永远不要复制管道逻辑 ——
    callEnrichAndDrain
    createMiddlewareLogger
    的内部逻辑

Framework-Specific Patterns

框架专属模式

Hono: Use
MiddlewareHandler
return type,
c.set('log', logger)
,
c.res.status
for status,
c.req.raw.headers
for headers.
Express: Standard
(req, res, next)
middleware,
res.on('finish')
for response end,
storage.run(logger, () => next())
for
useLogger()
. Type augmentation targets
express-serve-static-core
(NOT
express
). Error handler uses
ErrorRequestHandler
type.
Elysia: Return
new Elysia({ name: 'evlog' })
plugin, use
.derive({ as: 'global' })
to create logger and attach
log
to context,
onAfterHandle
for success path,
onError
for error path. Use
storage.enterWith(logger)
in
derive
for
useLogger()
support. Note:
onAfterResponse
is fire-and-forget and may not complete before
app.handle()
returns in tests — use
onAfterHandle
instead.
Fastify: Use
fastify-plugin
wrapper,
fastify.decorateRequest('log', null)
,
onRequest
/
onResponse
hooks.
NestJS:
NestInterceptor
with
intercept()
,
tap()
/
catchError()
on observable,
forRoot()
dynamic module.
Hono:使用
MiddlewareHandler
返回类型,
c.set('log', logger)
,用
c.res.status
获取状态码,
c.req.raw.headers
获取请求头。
Express:标准
(req, res, next)
中间件,
res.on('finish')
监听响应结束,
storage.run(logger, () => next())
实现
useLogger()
支持。类型增强目标是
express-serve-static-core
(不是
express
)。错误处理使用
ErrorRequestHandler
类型。
Elysia:返回
new Elysia({ name: 'evlog' })
插件,使用
.derive({ as: 'global' })
创建日志实例并将
log
挂载到上下文,
onAfterHandle
处理成功路径,
onError
处理错误路径。在
derive
中使用
storage.enterWith(logger)
实现
useLogger()
支持。注意:
onAfterResponse
是 fire-and-forget 模式,在测试中可能在
app.handle()
返回前未完成执行——请改用
onAfterHandle
Fastify:使用
fastify-plugin
包装,
fastify.decorateRequest('log', null)
onRequest
/
onResponse
钩子。
NestJS
NestInterceptor
搭配
intercept()
,在observable上使用
tap()
/
catchError()
forRoot()
动态模块。

Step 2: Build Config

步骤2:构建配置

Add a build entry in
packages/evlog/tsdown.config.ts
:
typescript
'{framework}/index': 'src/{framework}/index.ts',
Place it after the existing framework entries (workers, next, hono, express).
Also add the framework SDK to the
external
array:
typescript
external: [
  // ... existing externals
  '{framework-package}',  // e.g., 'elysia', 'fastify', 'express'
],
packages/evlog/tsdown.config.ts
中新增构建入口:
typescript
'{framework}/index': 'src/{framework}/index.ts',
放在已有的框架入口(workers、next、hono、express)之后。
同时将框架SDK添加到
external
数组中:
typescript
external: [
  // ... existing externals
  '{framework-package}',  // e.g., 'elysia', 'fastify', 'express'
],

Step 3: Package Exports

步骤3:包导出配置

In
packages/evlog/package.json
, add four entries:
In
exports
(after the last framework entry):
json
"./{framework}": {
  "types": "./dist/{framework}/index.d.mts",
  "import": "./dist/{framework}/index.mjs"
}
In
typesVersions["*"]
:
json
"{framework}": [
  "./dist/{framework}/index.d.mts"
]
In
peerDependencies
(with version range):
json
"{framework-package}": "^{latest-major}.0.0"
In
peerDependenciesMeta
(mark as optional):
json
"{framework-package}": {
  "optional": true
}
In
keywords
— add the framework name to the keywords array.
packages/evlog/package.json
中新增四个配置项:
exports
(放在最后一个框架入口之后):
json
"./{framework}": {
  "types": "./dist/{framework}/index.d.mts",
  "import": "./dist/{framework}/index.mjs"
}
typesVersions["*"]
json
"{framework}": [
  "./dist/{framework}/index.d.mts"
]
peerDependencies
(带版本范围):
json
"{framework-package}": "^{latest-major}.0.0"
peerDependenciesMeta
(标记为可选):
json
"{framework-package}": {
  "optional": true
}
keywords
—— 将框架名称添加到关键词数组。

Step 4: Tests

步骤4:测试用例

Create
packages/evlog/test/{framework}.test.ts
.
Import shared test helpers from
./helpers/framework
:
typescript
import {
  assertDrainCalledWith,
  assertEnrichBeforeDrain,
  assertSensitiveHeadersFiltered,
  createPipelineSpies,
} from './helpers/framework'
Required test categories:
  1. Middleware creates logger — verify
    c.get('log')
    or
    req.log
    returns a
    RequestLogger
  2. Auto-emit on response — verify event includes status, method, path, duration
  3. Error handling — verify errors are captured and event has error level + error details
  4. Route filtering — verify skipped routes don't create a logger
  5. Request ID forwarding — verify
    x-request-id
    header is used when present
  6. Context accumulation — verify
    logger.set()
    data appears in emitted event
  7. Drain callback — use
    assertDrainCalledWith()
    helper
  8. Enrich callback — use
    assertEnrichBeforeDrain()
    helper
  9. Keep callback — verify tail sampling callback receives context and can force-keep logs
  10. Sensitive header filtering — use
    assertSensitiveHeadersFiltered()
    helper
  11. Drain/enrich error resilience — verify errors in drain/enrich do not break the request
  12. Skipped routes skip drain/enrich — verify drain/enrich are not called for excluded routes
  13. useLogger() returns same logger — verify
    useLogger() === req.log
    (or framework equivalent)
  14. useLogger() throws outside context — verify error thrown when called without middleware
  15. useLogger() works across async — verify logger accessible in async service functions
Use the framework's test utilities when available (e.g., Hono's
app.request()
, Express's
supertest
, Fastify's
inject()
).
创建
packages/evlog/test/{framework}.test.ts
./helpers/framework
导入共享测试辅助工具
typescript
import {
  assertDrainCalledWith,
  assertEnrichBeforeDrain,
  assertSensitiveHeadersFiltered,
  createPipelineSpies,
} from './helpers/framework'
必填测试分类:
  1. 中间件创建日志实例 —— 验证
    c.get('log')
    req.log
    返回
    RequestLogger
    实例
  2. 响应时自动上报 —— 验证事件包含状态码、请求方法、路径、耗时
  3. 错误处理 —— 验证错误被捕获,事件包含错误级别和错误详情
  4. 路由过滤 —— 验证跳过的路由不会创建日志实例
  5. 请求ID透传 —— 验证存在
    x-request-id
    头时会被使用
  6. 上下文累积 —— 验证
    logger.set()
    的数据会出现在上报的事件中
  7. Drain回调 —— 使用
    assertDrainCalledWith()
    辅助工具验证
  8. Enrich回调 —— 使用
    assertEnrichBeforeDrain()
    辅助工具验证
  9. Keep回调 —— 验证尾部采样回调收到上下文,并且可以强制保留日志
  10. 敏感头过滤 —— 使用
    assertSensitiveHeadersFiltered()
    辅助工具验证
  11. Drain/Enrich错误容错 —— 验证drain/enrich中的错误不会中断请求
  12. 跳过的路由不触发Drain/Enrich —— 验证被排除的路由不会调用drain/enrich
  13. useLogger()
    返回相同的日志实例
    —— 验证
    useLogger() === req.log
    (或对应框架的等价实现)
  14. useLogger()
    在上下文外调用会抛出错误
    —— 验证未使用中间件时调用会抛出错误
  15. useLogger()
    跨异步调用正常工作
    —— 验证在异步服务函数中可以正常访问日志实例
如果框架有提供测试工具请使用(例如Hono的
app.request()
,Express的
supertest
,Fastify的
inject()
)。

Step 5: Installation Docs

步骤5:安装文档

Add a section in
apps/docs/content/1.getting-started/2.installation.md
for the framework.
Follow the pattern of existing framework sections (Nuxt, Next.js, Nitro, Hono, Express). Include:
  1. One-liner description mentioning
    req.log
    /
    c.get('log')
    and
    useLogger()
    access
  2. Install command
    npm install evlog {framework}
  3. Setup code — minimal working example with imports and configuration
  4. Usage in routes — how to access the logger in route handlers
  5. Full pipeline example — show drain + enrich + keep configuration
  6. Error handling — how structured errors work with the framework
  7. Callout linking to the dedicated example page
apps/docs/content/1.getting-started/2.installation.md
中为该框架新增一个章节。
遵循现有框架章节(Nuxt、Next.js、Nitro、Hono、Express)的模式,包含:
  1. 一行描述,说明
    req.log
    /
    c.get('log')
    useLogger()
    的访问方式
  2. 安装命令 ——
    npm install evlog {framework}
  3. 配置代码 —— 包含导入和配置的最小可运行示例
  4. 路由中使用 —— 如何在路由处理函数中访问日志实例
  5. 完整管道示例 —— 展示drain + enrich + keep的配置
  6. 错误处理 —— 结构化错误如何与该框架配合工作
  7. 提示框,链接到专属的示例页面

Step 6: Example Docs Page

步骤6:示例文档页面

Create
apps/docs/content/6.examples/{N}.{framework}.md
with a comprehensive guide.
Frontmatter:
yaml
---
title: {Framework}
description: Using evlog with {Framework} — automatic wide events, structured errors, drain adapters, enrichers, and tail sampling.
navigation:
  title: {Framework}
  icon: i-simple-icons-{framework}
links:
  - label: Source Code
    icon: i-simple-icons-github
    to: https://github.com/HugoRCD/evlog/tree/main/examples/{framework}
    color: neutral
    variant: subtle
---
Sections (follow the Express/Hono example pages):
  1. Setup — install + register middleware
  2. Wide Events
    req.log.set()
    /
    c.get('log').set()
    usage
  3. useLogger() — accessing logger from services without passing req
  4. Error Handling
    createError()
    + framework error handler
  5. Drain & Enrichers — middleware options
  6. Tail Sampling
    keep
    callback
  7. Route Filtering
    include
    /
    exclude
    /
    routes
  8. Run Locally — clone +
    bun run example:{framework}
  9. Card group linking to GitHub source
创建
apps/docs/content/6.examples/{N}.{framework}.md
,包含完整的使用指南。
Frontmatter
yaml
---
title: {Framework}
description:{Framework}中使用evlog —— 自动宽事件、结构化错误、drain适配器、enricher和尾部采样。
navigation:
  title: {Framework}
  icon: i-simple-icons-{framework}
links:
  - label: 源代码
    icon: i-simple-icons-github
    to: https://github.com/HugoRCD/evlog/tree/main/examples/{framework}
    color: neutral
    variant: subtle
---
章节(遵循Express/Hono示例页面的结构):
  1. 配置 —— 安装 + 注册中间件
  2. 宽事件 ——
    req.log.set()
    /
    c.get('log').set()
    的使用
  3. useLogger()
    —— 无需传递req即可在服务中访问日志实例
  4. 错误处理 ——
    createError()
    + 框架错误处理
  5. Drain & Enrichers —— 中间件选项
  6. 尾部采样 ——
    keep
    回调
  7. 路由过滤 ——
    include
    /
    exclude
    /
    routes
  8. 本地运行 —— 克隆仓库 +
    bun run example:{framework}
  9. 卡片组,链接到GitHub源代码

Step 7: Landing Page

步骤7:首页

Add a code snippet in
apps/docs/content/0.landing.md
for the framework.
Find the
FeatureFrameworks
MDC component usage (the section with
#nuxt
,
#nextjs
,
#hono
,
#express
, etc.) and add a new slot:
markdown
  #{framework}
  ```ts [src/index.ts]
  // Framework-specific code example showing evlog usage

Place the snippet in the correct order relative to existing frameworks.
apps/docs/content/0.landing.md
中为该框架新增代码片段。
找到
FeatureFrameworks
MDC组件的使用位置(包含
#nuxt
#nextjs
#hono
#express
等的章节),新增一个插槽:
markdown
  #{framework}
  ```ts [src/index.ts]
  // 展示evlog用法的框架专属代码示例

放在现有框架的正确排序位置。

Step 8: FeatureFrameworks Component

步骤8:FeatureFrameworks组件

Update
apps/docs/app/components/features/FeatureFrameworks.vue
:
  1. Add the framework to the
    frameworks
    array with its icon and the next available tab index
  2. Add a
    <div v-show="activeTab === {N}">
    with
    <slot name="{framework}" />
    in the template
  3. Increment tab indices for any frameworks that come after the new one
Icons use Simple Icons format:
i-simple-icons-{name}
(e.g.,
i-simple-icons-express
,
i-simple-icons-hono
).
更新
apps/docs/app/components/features/FeatureFrameworks.vue
  1. 将框架添加到
    frameworks
    数组,配置对应的图标和下一个可用的标签索引
  2. 在模板中添加
    <div v-show="activeTab === {N}">
    ,内部包含
    <slot name="{framework}" />
  3. 为所有排在新框架之后的现有框架增加标签索引
图标使用Simple Icons格式:
i-simple-icons-{name}
(例如
i-simple-icons-express
i-simple-icons-hono
)。

Step 9: Update AGENTS.md

步骤9:更新AGENTS.md

In the root
AGENTS.md
file:
  1. Add the framework to the "Framework Integration" section
  2. Add import path and basic setup example (showing both
    req.log
    and
    useLogger()
    )
  3. Show drain/enrich/keep usage
在根目录的
AGENTS.md
文件中:
  1. 将框架添加到**「框架集成」**章节
  2. 添加导入路径和基础配置示例(同时展示
    req.log
    useLogger()
    的用法)
  3. 展示drain/enrich/keep的用法

Step 10: Example App

步骤10:示例应用

Create
examples/{framework}/
with a runnable app that demonstrates all evlog features.
The app must include:
  1. evlog()
    middleware
    with
    drain
    (PostHog) and
    enrich
    callbacks
  2. Health route — basic
    log.set()
    usage
  3. Data route — context accumulation with user/business data, using
    useLogger()
    in a service function
  4. Error route
    createError()
    with status/why/fix/link
  5. Error handler — framework's error handler with
    parseError()
    + manual
    log.error()
  6. Test UI — served at
    /
    , a self-contained HTML page with buttons to hit each route and display JSON responses
Drain must use PostHog (
createPostHogDrain()
from
evlog/posthog
). The
POSTHOG_API_KEY
env var is already set in the root
.env
. This ensures every example tests a real external drain adapter.
Pretty printing should be enabled so the output is readable when testing locally.
Type the
enrich
callback parameter explicitly
— use
type EnrichContext
from
evlog
to avoid implicit
any
:
typescript
import { type EnrichContext } from 'evlog'

app.use(evlog({
  enrich: (ctx: EnrichContext) => {
    ctx.event.runtime = 'node'
  },
}))
创建
examples/{framework}/
目录,包含可运行的应用,展示所有evlog功能。
应用必须包含:
  1. evlog()
    中间件
    ,配置
    drain
    (PostHog)和
    enrich
    回调
  2. 健康检查路由 —— 基础的
    log.set()
    用法
  3. 数据路由 —— 结合用户/业务数据的上下文累积,在服务函数中使用
    useLogger()
  4. 错误路由 —— 包含状态/原因/修复方案/链接的
    createError()
  5. 错误处理 —— 框架的错误处理,搭配
    parseError()
    + 手动
    log.error()
  6. 测试UI —— 部署在
    /
    路径,是一个自包含的HTML页面,包含按钮可以访问每个路由并展示JSON响应
Drain必须使用PostHog(从
evlog/posthog
导入
createPostHogDrain()
)。根目录的
.env
中已经配置了
POSTHOG_API_KEY
环境变量。这保证每个示例都测试了真实的外部drain适配器。
应启用格式化输出,方便本地测试时阅读输出内容。
显式标注
enrich
回调参数的类型
—— 使用从
evlog
导入的
EnrichContext
类型,避免隐式
any
typescript
import { type EnrichContext } from 'evlog'

app.use(evlog({
  enrich: (ctx: EnrichContext) => {
    ctx.event.runtime = 'node'
  },
}))

Test UI

测试UI

Every example must serve a test UI at
GET /
— a self-contained HTML page (no external deps) that lets the user click routes and see responses without curl.
The UI must:
  • List all available routes with method badge + path + description
  • Send the request on click and display the JSON response with syntax highlighting
  • Show status code (color-coded 2xx/4xx/5xx) and response time
  • Use a dark theme with monospace font
  • Be a single
    .ts
    file (
    src/ui.ts
    ) exporting a
    testUI()
    function returning an HTML string
  • The root
    /
    route must be registered before the evlog middleware so it doesn't get logged
Reference:
examples/hono/src/ui.ts
for the canonical pattern. Copy and adapt for each framework.
每个示例都必须在
GET /
路径提供测试UI —— 一个自包含的HTML页面(无外部依赖),用户可以点击路由查看响应,无需使用curl。
UI必须满足:
  • 列出所有可用路由,包含方法标识、路径、描述
  • 点击时发送请求,展示带语法高亮的JSON响应
  • 展示状态码(2xx/4xx/5xx对应不同颜色)和响应耗时
  • 使用深色主题和等宽字体
  • 是单个
    .ts
    文件(
    src/ui.ts
    ),导出
    testUI()
    函数返回HTML字符串
  • /
    路由必须在evlog中间件之前注册,避免被日志记录
参考:
examples/hono/src/ui.ts
是标准实现模式,可以复制适配到每个框架。

Required files

必填文件

FilePurpose
src/index.ts
App with all features demonstrated
src/ui.ts
Test UI —
testUI()
returning self-contained HTML
package.json
dev
and
start
scripts
tsconfig.json
TypeScript config (if needed)
README.md
How to run + link to the UI
文件用途
src/index.ts
展示所有功能的应用代码
src/ui.ts
测试UI ——
testUI()
返回自包含的HTML
package.json
dev
start
脚本
tsconfig.json
TypeScript配置(如果需要)
README.md
运行说明 + UI链接

Package scripts

包脚本

json
{
  "scripts": {
    "dev": "bun --watch src/index.ts",
    "start": "bun src/index.ts"
  }
}
json
{
  "scripts": {
    "dev": "bun --watch src/index.ts",
    "start": "bun src/index.ts"
  }
}

Step 11: Root Package Script

步骤11:根目录包脚本

Add a root-level script in the monorepo
package.json
:
json
"example:{framework}": "dotenv -- turbo run dev --filter=evlog-{framework}-example"
The
dotenv --
prefix loads the root
.env
file (containing
POSTHOG_API_KEY
and other adapter keys) into the process before turbo starts. Turborepo does not load
.env
files —
dotenv-cli
handles this at the root level so individual examples need no env configuration.
在monorepo的根
package.json
中新增根级脚本:
json
"example:{framework}": "dotenv -- turbo run dev --filter=evlog-{framework}-example"
dotenv --
前缀会在turbo启动前将根目录的
.env
文件(包含
POSTHOG_API_KEY
和其他适配器密钥)加载到进程中。Turborepo不会自动加载
.env
文件——根级别使用
dotenv-cli
处理,因此单个示例不需要额外的环境配置。

Step 12: Changeset

步骤12:变更集

Create
.changeset/{framework}-integration.md
:
markdown
---
"evlog": minor
---

Add {Framework} middleware integration (`evlog/{framework}`) with automatic wide-event logging, drain, enrich, and tail sampling support
创建
.changeset/{framework}-integration.md
markdown
---
"evlog": minor
---

Add {Framework} middleware integration (`evlog/{framework}`) with automatic wide-event logging, drain, enrich, and tail sampling support

Step 13 & 14: PR Scopes

步骤13 & 14:PR作用域

Add the framework name as a valid scope in both files so PR title validation passes:
.github/workflows/semantic-pull-request.yml
— add
{framework}
to the
scopes
list:
yaml
scopes: |
  # ... existing scopes
  {framework}
.github/pull_request_template.md
— add
{framework}
to the Scopes section:
markdown
- {framework} ({Framework} integration)
将框架名称作为有效作用域添加到两个文件中,保证PR标题校验通过:
.github/workflows/semantic-pull-request.yml
—— 将
{framework}
添加到
scopes
列表:
yaml
scopes: |
  # ... existing scopes
  {framework}
.github/pull_request_template.md
—— 将
{framework}
添加到Scopes章节:
markdown
- {framework} ({Framework} integration)

Verification

验证

After completing all steps, run from the repo root:
bash
cd packages/evlog
bun run build    # Verify build succeeds with new entry
bun run test     # Verify unit tests pass
bun run lint     # Verify no lint errors
Then type-check the example:
bash
cd examples/{framework}
npx tsc --noEmit  # Verify no TS errors in the example
完成所有步骤后,在仓库根目录执行:
bash
cd packages/evlog
bun run build    # 验证新增入口后构建成功
bun run test     # 验证单元测试通过
bun run lint     # 验证无lint错误
然后对示例进行类型检查:
bash
cd examples/{framework}
npx tsc --noEmit  # 验证示例中无TS错误