---
title: Examples
description: Real-world examples and use cases for Elogs
---

下面所有示例都基于 [`apps/elysia/src/routers/`](https://github.com/eastgold15/elogs/tree/main/apps/elysia/src/routers) 的真实代码——`createElogs` 已在 `index.ts` 里挂上，这些 router 都按 `<App extends CreateElogs>(app: App) => ...` 模式写，TypeScript 能精确推断 `store.logger` / `log` 的类型。

## 基础

### 最小化

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

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

### 显示启动 banner

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

const app = new Elysia()
  .use(
    createElogs({
      config: {
        showStartupMessage: true,
        startupMessageFormat: 'banner',
      },
    })
  )
  .get('/', () => 'Hello World')
  .listen(3000)
```

`dev` preset 默认就开 banner，等价于上面这段。

## 请求作用域 context

### 累积字段到 access log

不调用任何 `logger.info`，只要在 handler 里 `mergeContext`，字段会自动出现在最终的 access log 的 `context` 节点下：

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

export const requestContextRouter = <App extends CreateElogs>(app: App) =>
  app.get('/checkout', ({ request, store }) => {
    store.logger.mergeContext(request, { userId: 'usr_demo' })
    store.logger.mergeContext(request, { cart: { items: 2, total: 4999 } })
    return { ok: true }
  })
```

### 深调用栈：AsyncLocalStorage

开启 `useAsyncLocalStorage: true` 后，service / helper 不用逐层传 `request`，直接 `useLogger()` 拿同一份 logger：

```ts
import type { CreateElogs } from '@eastgold15/elogs'
import { createElogs, useLogger } from '@eastgold15/elogs'

const dbQueryHelper = async () => {
  const log = useLogger()
  log.mergeContext({ query: 'SELECT * FROM users' })
  await Promise.resolve()
  log.info('Running database query in nested service')
}

export const requestContextRouter = <App extends CreateElogs>(app: App) =>
  app
    .use(createElogs({ config: { useAsyncLocalStorage: true } }))
    .get('/async-context', async ({ log }) => {
      log.mergeContext({ userId: 'usr_async' })
      log.info('Starting async request processing')
      await dbQueryHelper()
      return { ok: true }
    })
```

### 请求作用域 `log`（不传 request）

handler context 上 derive 出来的 `log` 已经绑定了当前请求，省掉一个参数：

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

export const requestContextRouter = <App extends CreateElogs>(app: App) =>
  app.get('/profile', ({ log }) => {
    log.info('profile accessed')                  // info, 自动绑 request
    log.mergeContext({ stage: 'view' })           // 累积 context
    return { ok: true }
  })
```

## 自定义日志

### 结构化日志

两种姿势：直接走 `store.logger.info`（Elogs 路径）或者走 `store.pino.info`（原始 Pino）：

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

export const customRouter = <App extends CreateElogs>(app: App) =>
  app.get('/users/:id', ({ request, store, params }) => {
    // Elogs 路径：自动关联 request context
    store.logger.info(request, 'User profile accessed', {
      userId: params.id,
      feature: 'custom-route-log',
    })

    // 原始 Pino 路径（不进 request context）
    store.pino.info(
      { userId: params.id, action: 'view_profile' },
      'User profile accessed'
    )

    return { ok: true }
  })
```

### Child Loggers

用 `pino.child(...)` 创建带绑定字段的子 logger：

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

export const pinoRouter = <App extends CreateElogs>(app: App) =>
  app.get('/api/orders/:id', ({ store, params }) => {
    const orderLogger = store.pino.child({
      orderId: params.id,
      module: 'order-service',
    })

    orderLogger.debug('Fetching order details')
    orderLogger.info({ status: 'processing' }, 'Order retrieved')

    return { ok: true }
  })
```

### 性能埋点

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

export const pinoRouter = <App extends CreateElogs>(app: App) =>
  app.post('/api/process', async ({ store, body }) => {
    const startTime = Date.now()
    const result = await processData(body)

    store.pino.info(
      {
        operation: 'process_data',
        duration: Date.now() - startTime,
        itemsProcessed: result.count,
        memory: process.memoryUsage().heapUsed / 1024 / 1024,
        success: true,
      },
      'Data processing completed'
    )

    return result
  })
```

## 错误处理

### 自动错误日志

Elogs 在单点 `onError` 钩子里记录所有进入错误管道的 error，**不** return value——错误继续按原路径传播：

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

export const boomRouter = <App extends CreateElogs>(app: App) =>
  app.get('/boom', () => {
    // 这个 Error 会被 Elogs 自动记录为 ERROR 级别
    // 响应格式仍由 Elysia 默认的 application/problem+json 处理
    throw new Error('Boom!')
  })
```

### 自定义响应格式

用自己的 `.error()` 钩子接管响应（Elogs 仍然负责日志）：

```ts
import { problem } from 'elysia'

export const errorRouter = <App extends CreateElogs>(app: App) =>
  app
    .error(({ code, error }) => {
      return problem(500, {
        detail: error.message,
        status: code,
      })
    })
    .get('/risky', ({ store }) => {
      try {
        return performRiskyOperation()
      } catch (error) {
        store.pino.error(
          {
            err: error,
            operation: 'risky_operation',
            context: { attempted: true, timestamp: Date.now() },
          },
          'Operation failed'
        )
        throw error
      }
    })
```

### JSON 格式的 access log

`customLogFormat` 一行换成 JSON：

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

const app = new Elysia()
  .use(
    createElogs({
      config: {
        customLogFormat:
          '{"level": "{level}", "message": "{message}", "method": "{method}", "pathname": "{pathname}", "status": "{status}"}',
      },
    })
  )
  .get('/error', () => {
    throw new Error('Validation failed')
  })
```

正常请求和错误请求都会被渲染成 JSON：

```json
{"level": "INFO", "message": "", "method": "GET", "pathname": "/hello", "status": "200"}
{"level": "ERROR", "message": "Validation failed", "method": "GET", "pathname": "/error", "status": "500"}
```

## 数据库错误翻译

`autoTranslate: { db: 'drizzle' }` 让 Drizzle driver 错误（唯一约束 / 外键违反 / 连接失败）按映射规则决定日志级别。响应格式完全交给用户的 `.error("DrizzleError", ...)`：

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

// 模拟 Drizzle 抛的错误（真实代码里是 drizzle 自己的 DrizzleError）
const makeDrizzleError = (code: string) => {
  const e = new Error(`PG driver reported: ${code}`) as Error & {
    code: string
    name: string
  }
  e.name = 'DrizzleError'
  e.code = code
  return e
}

export const dbRouter = <App extends CreateElogs>(app: App) =>
  app
    // 用户完全控制响应格式
    .error('DrizzleError', (ctx) => {
      const code = (ctx.error as { code?: string }).code ?? 'UNKNOWN'
      if (code === '23505') {
        return problem(409, { detail: 'Duplicate key — that value already exists' })
      }
      if (code === '23503') {
        return problem(400, { detail: 'Foreign key violation — referenced row missing' })
      }
      if (code === '08006') {
        return problem(503, { detail: 'Database unavailable — try again later' })
      }
      return problem(500, { detail: `Unhandled DB error (code=${code})` })
    })
    .get('/demo/db-error/duplicate', () => {
      throw makeDrizzleError('23505')
    })
    .get('/demo/db-error/foreign-key', () => {
      throw makeDrizzleError('23503')
    })
    .get('/demo/db-error/connect', () => {
      throw makeDrizzleError('08006')
    })

const app = new Elysia()
  .use(
    createElogs({
      autoTranslate: { db: 'drizzle' },
      config: { /* ... */ },
    })
  )
  .use(dbRouter)
  .listen(3000)
```

也可以不依赖 `autoTranslate`，在 `try/catch` 里手动调 `translateDrizzleError`：

```ts
import { translateDrizzleError } from '@eastgold15/elogs/translator'

app.post('/users', async ({ store, body }) => {
  try {
    return await db.insert(users).values(body)
  } catch (err) {
    // 翻译后抛回去——status / message 已经按 driver code 选好
    throw translateDrizzleError(err)
  }
})
```

详见 [Database errors](/docs/features/database-errors) 和 [translator API](/api/exports#translate-drizzle-error)。

## Custom Transports

### Elasticsearch Transport

```ts
const elasticsearchTransport = {
  log: async (level, message, meta) => {
    try {
      await fetch('http://elasticsearch:9200/logs/_doc', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          level,
          message,
          ...meta,
          timestamp: new Date().toISOString(),
        }),
      })
    } catch (err) {
      console.error('Elasticsearch transport error', err)
    }
  },
}

const app = new Elysia()
  .use(
    createElogs({
      config: {
        transports: [elasticsearchTransport],
      },
    })
  )
  .listen(3000)
```

### Slack Transport（仅 ERROR）

```ts
const slackTransport = {
  log: async (level, message, meta) => {
    if (level !== 'ERROR') return
    const webhook = process.env.SLACK_WEBHOOK_URL
    if (!webhook) return

    try {
      await fetch(webhook, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          text: `[ERROR] ${message}\n\`\`\`${JSON.stringify(meta, null, 2)}\`\`\``,
        }),
      })
    } catch (err) {
      console.error('Slack transport error', err)
    }
  },
}
```

### MongoDB Transport

```ts
import { MongoClient } from 'mongodb'

const client = new MongoClient(process.env.MONGODB_URI)
await client.connect()
const db = client.db('logs')

const mongodbTransport = {
  log: async (level, message, meta) => {
    try {
      await db.collection('logs').insertOne({
        level,
        message,
        ...meta,
        timestamp: new Date(),
      })
    } catch (err) {
      console.error('MongoDB transport error', err)
    }
  },
}
```

## 完整生产配置

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

const elasticsearchTransport = { /* ... */ }
const slackTransport = { /* ... */ }

const app = new Elysia()
  .use(
    createElogs({
      preset: 'prod',
      autoTranslate: { db: 'drizzle' },
      config: {
        service: 'my-api',
        showStartupMessage: false,

        // 文件日志 + 轮转
        logFilePath: './logs/production.log',
        logRotation: {
          maxSize: '100m',
          interval: '1d',
          maxFiles: '30d',
          compress: true,
        },

        // 只输出 ERROR / WARNING
        logFilter: { level: ['ERROR', 'WARNING'] },

        // Pino 配置
        pino: {
          level: 'info',
          redact: ['password', 'token', 'apiKey', 'creditCard'],
          base: {
            service: 'my-api',
            version: process.env.APP_VERSION,
            environment: 'production',
          },
        },

        // 外部 transport
        useTransportsOnly: false,
        transports: [elasticsearchTransport, slackTransport],
      },
    })
  )
  .listen(3000)
```

## 按环境动态切换

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

const isDev = process.env.NODE_ENV === 'development'
const isProd = process.env.NODE_ENV === 'production'

const app = new Elysia()
  .use(
    createElogs({
      preset: isProd ? 'prod' : 'dev',
      autoTranslate: isProd ? { db: 'drizzle' } : undefined,
      config: {
        showStartupMessage: isDev,
        startupMessageFormat: isDev ? 'banner' : 'simple',

        logFilePath: isProd ? './logs/production.log' : undefined,
        logRotation: isProd
          ? { maxSize: '100m', interval: '1d', maxFiles: '30d', compress: true }
          : undefined,

        pino: {
          level: isDev ? 'debug' : 'info',
          prettyPrint: isDev,
          redact: isProd ? ['password', 'token', 'apiKey'] : [],
          base: {
            service: 'my-api',
            version: process.env.APP_VERSION,
            environment: process.env.NODE_ENV,
          },
        },

        transports: isProd ? [elasticsearchTransport, slackTransport] : [],
      },
    })
  )
  .listen(3000)
```

## REST API 完整示例

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

const app = new Elysia()
  .use(
    createElogs({
      config: {
        pino: {
          level: 'info',
          base: { service: 'user-api' },
        },
      },
    })
  )
  .get('/users', ({ store }) => {
    store.pino.info({ action: 'list_users' }, 'Fetching users')
    return { users: [] }
  })
  .get('/users/:id', ({ store, params }) => {
    const userLogger = store.pino.child({ userId: params.id })
    userLogger.info('Fetching user')
    return { user: { id: params.id } }
  })
  .post('/users', ({ store, body }) => {
    store.pino.info({ action: 'create_user' }, 'Creating user')
    return { user: body }
  })
  .put('/users/:id', ({ store, params, body }) => {
    store.pino.info({ userId: params.id, action: 'update_user' }, 'Updating user')
    return { user: { id: params.id, ...body } }
  })
  .delete('/users/:id', ({ store, params }) => {
    store.pino.info({ userId: params.id, action: 'delete_user' }, 'Deleting user')
    return { success: true }
  })
  .listen(3000)
```
