---
title: Usage
description: 在 Elysia 应用中使用 Elogs
---

## 安装

```bash
bun add @eastgold15/elogs elysia@next
```

`next` 标签对应 [Elysia 2 open beta](/docs/elysia-2)。如果你还在 Elysia 1.4，请见 [Elysia 2 支持](/docs/elysia-2)。

## 基本用法

```ts
import { Elysia } from 'elysia'
import { createElogs } from '@eastgold15/elogs'

const app = new Elysia()
  .use(createElogs())
  .get('/', () => 'Hello World')
  .listen(3000)
```

`createElogs()` 返回一个 Elysia 插件。`Logger` 挂在 Elysia store 上，可以通过 `store.logger` 访问；请求作用域的 `log` 也会派生到 handler context 上（不需要再手动传 `request`）。插件同时注册 `.request`、`.afterHandle`、`.error` 和 `.setup` 钩子，你不需要手动接线。

## 预设

三个 preset 一行设好环境默认。显式 `config` 字段永远覆盖 preset。

```ts
createElogs({ preset: 'dev' })   // 美化 console + 启动 banner
createElogs({ preset: 'prod' })  // JSON console + autoRedact + requestId
createElogs({ preset: 'json' })  // 极简 JSON console，无附加
```

也可以通过 `registerPreset('staging', { ... })` 注册自己的预设。完整默认值见 [Presets](/docs/features/presets)。

## 配置

### 最小化

```ts
createElogs({
  config: {
    showStartupMessage: true,
    ip: true,
    autoRedact: false,
  },
})
```

### 请求作用域 context

在请求过程中累积字段，最终合并到 access log 的 `context` 下：

```ts
.get('/users/:id', ({ request, store, params }) => {
  store.logger.mergeContext(request, { userId: params.id })
  return { ok: true }
})
```

完整 API 含深调用栈用的 `useLogger()`，见 [Request context](/docs/features/request-context)。

### Request ID

开启 `X-Request-Id` 头透传用 `requestId: true`。插件入口读这个头，缺失时自动生成，出口回显——包括错误响应。

```ts
createElogs({ config: { requestId: true } })
```

见 [Request ID](/docs/features/request-id)。

### 自定义日志格式

通过 `customLogFormat` 自定义 access log：

```ts
createElogs({
  config: {
    customLogFormat: '{now} {level} {duration}ms {method} {pathname} {status} {requestId}',
  },
})
```

可用的占位符：

| 占位符 | 描述 | 示例 |
| --- | --- | --- |
| `{now}` | 当前时间戳 | `2026-08-15 10:00:00` |
| `{epoch}` | Unix 时间戳 | `1734729600` |
| `{level}` | 日志级别（`DEBUG`、`INFO`、`WARNING`、`ERROR`） | `INFO` |
| `{duration}` | 请求耗时（已格式化） | `12ms`、`1.5s` |
| `{method}` | HTTP 方法 | `GET` |
| `{pathname}` | 请求路径（别名：`{path}`）；`logQueryParams: true` 时带 query | `/users` |
| `{query}` | 原始 query 字符串 | `?id=123` |
| `{status}` | 响应状态码 | `200` |
| `{statusText}` | HTTP 状态文本 | `Not Found` |
| `{message}` | 自定义消息 | `User profile accessed` |
| `{icon}` | Elogs 狐狸 `🦊`（颜色 + TTY 时为级别色块） | `🦊` |
| `{speed}` | 慢请求徽章（耗时 ≥ `verySlowThreshold`） | ` ⚡ slow` |
| `{service}` | `config.service` 服务名前缀 | `[my-api] ` |
| `{ip}` | 客户端 IP 地址 | `127.0.0.1` |
| `{context}` | context 的 JSON 表示（树关掉或空时显示在主行） | `{"id":1}` |
| `{requestId}` | `X-Request-Id` 值（未开启时为空） | `8c2f…` |

## 日志过滤

`logFilter.level` 接受单个级别或数组：

```ts
createElogs({
  config: {
    logFilter: { level: ['ERROR', 'WARNING'] },
  },
})
```

根级有一个等价的 `logLevel` 简写：

```ts
createElogs({ logLevel: ['ERROR', 'WARNING'] })
```

见 [Log Levels](/docs/features/log-levels)。

## Pino 集成

Elogs 由 Pino 驱动。Pino 实例可以通过 `store.pino` 访问，也可以从请求作用域 `log` 拿到：

```ts
app.get('/users/:id', async ({ log, params, store }) => {
  log.info({ userId: params.id, action: 'view_profile' }, 'profile accessed')
  // 或者想用原始 Pino：store.pino.info({ ... }, '...')
  return { user: 'data' }
})
```

配置 Pino 选项：

```ts
createElogs({
  config: {
    pino: {
      level: 'debug',
      prettyPrint: true,
      base: { service: 'my-api' },
    },
  },
})
```

Pino 在首次访问时按需构造 —— 错误的 Pino 选项只在真正 log 时才报错。如果想启动时立即校验，把 `config.pino` 设为真值，proxy 就会立刻初始化。

见 [Pino](/docs/integrations/pino)。

## 文件日志

```ts
createElogs({
  config: {
    logFilePath: './logs/app.log',
    logRotation: {
      maxSize: '10m',
      interval: '1d',
      maxFiles: '7d',
      compress: true,
    },
  },
})
```

文件 sink 通过 `queueMicrotask` 批写、缓存文件句柄，跨文件 rotation 用 keyed mutex 串行，不同文件之间不会互相阻塞。文件创建时默认 `0o600`（文件）和 `0o700`（目录）；可以用 `logFileMode` / `logDirMode` 覆盖。

见 [File Logging](/docs/features/file-logging) 和 [Log Rotation](/docs/features/log-rotation)。

## Transports

```ts
const consoleTransport = {
  log: (level, message, meta) => {
    console.log(`[${level}] ${message}`, meta)
  },
}

createElogs({
  config: {
    transports: [consoleTransport],
  },
})
```

transport 抛出的同步异常和异步 reject 会被捕获并打印到 `console.error`，同一 transport 5s 窗口内只输出一次，坏的 target 不会拖慢请求路径。

见 [Transports](/docs/features/transports)。

## OpenTelemetry

```bash
bun add @opentelemetry/api
```

```ts
import { injectTraceContext } from '@eastgold15/elogs/otel'

app.request(({ request, store }) => {
  injectTraceContext(store.logger, request)
})
```

当存在 active span 时，`trace_id` 和 `span_id` 会合并进请求 context 包，出现在 access log 上。Elogs 不启动 OpenTelemetry SDK —— 你需要自己装 tracer provider 和 instrumentation。

见 [OpenTelemetry](/docs/integrations/otel)。

## AI SDK 指标

```ts
import { mergeAIMetrics } from '@eastgold15/elogs/ai'

app.post('/chat', async ({ request, store }) => {
  // 在 AI SDK 调用之后：
  mergeAIMetrics(store.logger, request, {
    model: 'claude-sonnet',
    provider: 'anthropic',
    inputTokens: 1200,
    outputTokens: 400,
    totalTokens: 1600,
    msToFinish: 2300,
  })

  return { ok: true }
})
```

access log 的 `context.ai` 字段沿用 evlog wide event 形态，方便两边仪表盘直接对比迁移。

见 [AI SDK](/docs/integrations/ai)。

## 数据库错误翻译（Drizzle）

在 `createElogs` 上开 `autoTranslate: { db: 'drizzle' }`，单点 onError 钩子就会把 Drizzle 抛出的 driver 错误（唯一约束、外键违反、连接失败等）翻译成合适的 HTTP 状态码来决定日志级别。**翻译只影响日志输出，不劫持错误响应格式**——原 error 继续传播，你的 `.error("DrizzleError", ...)` / Elysia 默认 `application/problem+json` 响应都按原逻辑走。

```ts
createElogs({
  autoTranslate: { db: 'drizzle' },
  config: { /* ... */ },
})
```

也可以手动在 `try/catch` 里调 `translateDrizzleError(error)`（来自 `@eastgold15/elogs/translator`）。详见 [Database errors](/docs/features/database-errors)。

## AsyncLocalStorage

深调用栈（服务层、helper、请求里的子任务）想拿到请求作用域 logger，不用逐层传 `request`，开启 ALS 即可：

```ts
createElogs({ config: { useAsyncLocalStorage: true } })

// 在任意位置：
import { useLogger } from '@eastgold15/elogs'

const log = useLogger()
log.mergeContext({ stage: 'db' })
log.info('query started')
```

见 [Request context](/docs/features/request-context#async-localstorage)。

## 输出控制

```ts
createElogs({
  config: {
    disableInternalLogger: false, // console 输出
    disableFileLogging: false,     // logFilePath 输出
    useTransportsOnly: false,      // 同时禁用上面两个，只走 transport
    transports: [/* ... */],
  },
})
```

## 路由拆分模式

跟 Elysia 2 推荐写法一致，把每个子路由写成 `<App extends CreateElogs>(app: App) => app` 的高阶函数，TypeScript 就能精确推断 `store.logger` / `log` 的类型：

```ts
import type { CreateElogs } from '@eastgold15/elogs'
import { createElogs } from '@eastgold15/elogs'
import { Elysia } from 'elysia'

const chatRouter = <App extends CreateElogs>(app: App) =>
  app.post('/chat', ({ request, store }) => {
    store.logger.info(request, 'chat received')
    return { ok: true }
  })

const app = new Elysia()
  .use(createElogs())
  .use(chatRouter)
  .listen(3000)
```

参考 [`apps/elysia/src/routers/`](https://github.com/eastgold15/elogs/tree/main/apps/elysia/src/routers) 下的真实用法。

## 示例

### 生产配置

```ts
import { Elysia } from 'elysia'
import { createElogs } from '@eastgold15/elogs'

const app = new Elysia().use(
  createElogs({
    preset: 'prod',
    autoTranslate: { db: 'drizzle' },
    config: {
      service: 'my-api',
      logFilePath: './logs/production.log',
      logRotation: { maxSize: '100m', interval: '1d', maxFiles: '30d', compress: true },
      logFilter: { level: ['ERROR', 'WARNING'] },
      pino: { level: 'info', redact: ['password', 'token', 'apiKey'] },
    },
  })
)
```

### 开发配置

```ts
import { Elysia } from 'elysia'
import { createElogs } from '@eastgold15/elogs'

const app = new Elysia().use(
  createElogs({
    preset: 'dev',
    config: {
      service: 'my-api',
      pino: { level: 'debug', prettyPrint: true },
    },
  })
)
```
