Skip to content
Elogs
Esc
navigateopen⌘Jpreview
On this page

Pino

用 Pino 做高性能结构化日志

Elogs 集成了 Pino 作为底层日志引擎,挂载在 Elysia store 的 pino 字段上 —— 所有 pino.LoggerOptions 都通过 config.pino 透传,不需要单独 import。

为什么选 Pino

  • 高性能 —— 业界最快的 Node.js logger 之一
  • 结构化日志 —— JSON 优先
  • 生产验证 —— 大规模生产环境使用
  • 灵活 —— 丰富的选项和 transport 支持

基本用法

在 Elysia route 里

在 route handler 里通过 store 拿 Pino:

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

const app = new Elysia().use(createElogs())

app.get('/users/:id', ({ store, params }) => {
  const { pino } = store

  pino.info({
    userId: params.id,
    action: 'view_profile',
  }, 'User profile accessed')

  return { user: 'data' }
})

或者通过请求作用域 log(useAsyncLocalStorage: true 时,log 自动绑定到当前请求):

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' }
})

在 Elysia route 之外(standalone)

在服务、后台任务、或任何 HTTP 请求上下文外的地方用 Pino:

// logger.ts
import { createElogs } from '@eastgold15/elogs'

export const elogsIns = createElogs({
  config: {
    logFilePath: './logs/app.log',
    pino: {
      level: 'info',
      base: {
        service: 'my-api',
        version: '1.0.0',
      },
    },
  },
})

// 导出 Pino 实例给 standalone 用
export const logger = elogsIns.store.pino
// services/order.service.ts
import { logger } from '../logger'

export class OrderService {
  async processOrder(orderId: string) {
    logger.info({ orderId }, 'Processing order')

    try {
      // ... 业务逻辑
      logger.info({ orderId }, 'Order processed successfully')
    } catch (error) {
      logger.error({ orderId, error }, 'Order processing failed')
      throw error
    }
  }
}
// index.ts
import { Elysia } from 'elysia'
import { elogsIns } from './logger'
import { OrderService } from './services/order.service'

const orderService = new OrderService()

new Elysia()
  .use(elogsIns)
  .post('/orders/:id/process', async ({ params }) => {
    await orderService.processOrder(params.id)
    return { success: true }
  })
  .listen(3000)

注意: 在 route 外用 Pino 没有 HTTP context(method、pathname、IP 等)。HTTP 请求日志请在 route handler 里用 store.loggerstore.pino

配置

config.pino 字段是 PinoConfig 类型,实际定义是:

interface PinoConfig {
  /** 显式禁用 pino(测试中常用) */
  enabled?: boolean
  /** Pretty 打印(pino-pretty) */
  prettyPrint?: boolean
  /** 其他 pino options 透传(走 [key: string]: unknown) */
  [key: string]: unknown
}

[key: string]: unknown 索引签名让你能透传任何 pino.LoggerOptions(levelredacttransportbasetimestampformattersserializers 等)。下面是常用配置:

日志级别

import { createElogs } from '@eastgold15/elogs'

createElogs({
  config: {
    pino: {
      level: 'debug', // 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace' | 'silent'
    },
  },
})

美化输出

import { createElogs } from '@eastgold15/elogs'

createElogs({
  config: {
    pino: {
      prettyPrint: true,
    },
  },
})

或者自定义:

import { createElogs } from '@eastgold15/elogs'

createElogs({
  config: {
    pino: {
      prettyPrint: {
        colorize: true,
        translateTime: 'HH:MM:ss Z',
        ignore: 'pid,hostname',
      },
    },
  },
})

prettyPrint 透传给 pino-pretty。要本地开发用漂亮输出,装一下 pino-pretty(peer dep,可选):

bun add pino-pretty

脱敏

Pino 自己的 redact 跟 Elogs 的 autoRedact 独立,可以同时跑:

import { createElogs } from '@eastgold15/elogs'

createElogs({
  config: {
    autoRedact: true,                 // Elogs: 改写 meta + headers
    pino: { redact: ['password'] },   // Pino: 擦掉 JSON 输出
  },
})

paths + remove 删掉整个 key:

createElogs({
  config: {
    pino: {
      redact: {
        paths: ['user.password', 'req.headers.authorization'],
        remove: true,
      },
    },
  },
})

base 字段

import { createElogs } from '@eastgold15/elogs'

createElogs({
  config: {
    pino: {
      base: {
        service: 'my-api',
        version: '1.0.0',
        environment: process.env.NODE_ENV,
      },
    },
  },
})

Pino transport

Pino 有自己的 transport 体系,处理高级日志目标。用 transport 选项配置:

import { createElogs } from '@eastgold15/elogs'

createElogs({
  config: {
    pino: {
      transport: {
        target: 'pino/file',
        options: { destination: './logs/pino-example.log' },
      },
    },
  },
})

重要: Pino transport 和 Elogs transport 是独立的:

  • Pino transport(config.pino.transport)—— 影响 store.pino.info()store.pino.error() 等写出的日志
  • Elogs transport(config.transports)—— 影响 HTTP access log 管道
import { createElogs } from '@eastgold15/elogs'

const customTransport = {
  log(level, message, meta) {
    // 你的实现
  },
}

createElogs({
  config: {
    // Pino transport —— 只对 store.pino.* 日志生效
    pino: {
      transport: { target: 'pino/file', options: { destination: './logs/pino.log' } },
    },
    // Elogs transport —— 只对 HTTP access log 生效
    transports: [customTransport],
  },
})

app.get('/example', ({ store }) => {
  store.pino.info({ feature: 'pino' }, 'pino log example')
  // → 走 pino/file transport
  // HTTP access log 走 Elogs transport
})

进阶用法

结构化日志

app.get('/users/:id', ({ store, params }) => {
  const { pino } = store

  pino.info({
    userId: params.id,
    action: 'view_profile',
    timestamp: Date.now(),
  }, 'User profile viewed')

  return user
})

Child logger

派生绑定 context 的 child logger:

app.get('/api/orders/:id', ({ store, params }) => {
  const { pino } = store

  const orderLogger = pino.child({
    orderId: params.id,
    module: 'order-service',
  })

  orderLogger.info('Fetching order details')
  orderLogger.debug({ query: 'SELECT * FROM orders WHERE id = ?' })

  return order
})

错误日志

app.get('/api/risky', ({ store }) => {
  const { pino } = store

  try {
    performRiskyOperation()
  } catch (error) {
    pino.error({ err: error, operation: 'risky_operation' }, 'Operation failed')
    throw error
  }
})

按环境配置

import { createElogs } from '@eastgold15/elogs'

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

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

dev preset 已经预设了 pino: { prettyPrint: true },开发环境通常直接用:

import { createElogs } from '@eastgold15/elogs'

createElogs({ preset: 'dev' })

最佳实践

  1. 用结构化日志 —— 传对象,不要字符串拼接

    // ✅
    pino.info({ userId: 123, action: 'login' }, 'User logged in')
    // ❌
    pino.info(`User ${userId} logged in`)
  2. 派生 child logger 来绑 context

  3. 记性能指标 —— 关键操作

    pino.info({ duration: 150, operation: 'db_query' }, 'Query completed')
  4. 脱敏 —— autoRedact(Elogs)和 pino.redact(Pino)都开

  5. 正确选级别:

    • fatal —— 应用即将崩溃
    • error —— 需要关注的错误
    • warn —— 警告,应用继续
    • info —— 一般信息(默认)
    • debug —— 详细调试
    • trace —— 极详细调试
    • silent —— 完全关掉
  6. 导出 Pino 给 standalone 用 —— 在服务、job、工具里

    export const logger = elogsIns.store.pino

扩展阅读

Last updated on August 15, 2026

Was this page helpful?