日志系统 #

一、日志概述 #

Fastify内置了Pino日志库,这是一个极高性能的Node.js日志库。

1.1 Pino特点 #

特点 说明
高性能 比其他日志库快5倍以上
低开销 对应用性能影响极小
结构化 JSON格式日志
可扩展 支持子日志、自定义格式

1.2 基本配置 #

javascript
const fastify = require('fastify')({
  logger: true
})

fastify.get('/', async (request, reply) => {
  request.log.info('Hello World')
  return { message: 'Hello' }
})

二、日志配置 #

2.1 基本配置 #

javascript
const fastify = require('fastify')({
  logger: {
    level: 'info',
    file: './logs/app.log'
  }
})

2.2 完整配置 #

javascript
const fastify = require('fastify')({
  logger: {
    level: process.env.LOG_LEVEL || 'info',
    prettyPrint: process.env.NODE_ENV === 'development',
    redact: ['req.headers.authorization', 'req.headers.cookie'],
    serializers: {
      req(req) {
        return {
          method: req.method,
          url: req.url,
          headers: req.headers
        }
      },
      res(res) {
        return {
          statusCode: res.statusCode
        }
      }
    },
    genReqId: (req) => require('crypto').randomUUID()
  }
})

2.3 开发环境配置 #

javascript
const fastify = require('fastify')({
  logger: {
    level: 'debug',
    transport: {
      target: 'pino-pretty',
      options: {
        colorize: true,
        translateTime: 'SYS:standard',
        ignore: 'pid,hostname'
      }
    }
  }
})

2.4 生产环境配置 #

javascript
const fastify = require('fastify')({
  logger: {
    level: 'info',
    formatters: {
      level: (label) => {
        return { level: label.toUpperCase() }
      }
    },
    timestamp: () => `,"time":"${new Date().toISOString()}"`,
    redact: {
      paths: ['req.headers.authorization', 'req.headers.cookie'],
      censor: '***'
    }
  }
})

三、日志级别 #

3.1 日志级别 #

javascript
fastify.get('/levels', async (request, reply) => {
  fastify.log.trace('Trace message')
  fastify.log.debug('Debug message')
  fastify.log.info('Info message')
  fastify.log.warn('Warning message')
  fastify.log.error('Error message')
  fastify.log.fatal('Fatal message')
  
  return { logged: true }
})

3.2 级别优先级 #

级别 优先级 说明
trace 10 最详细日志
debug 20 调试信息
info 30 一般信息
warn 40 警告信息
error 50 错误信息
fatal 60 致命错误

3.3 动态调整级别 #

javascript
fastify.get('/log-level/:level', async (request, reply) => {
  const { level } = request.params
  fastify.log.level = level
  return { level: fastify.log.level }
})

四、请求日志 #

4.1 自动请求日志 #

Fastify自动记录请求日志:

json
{
  "level": 30,
  "time": 1709500000000,
  "pid": 12345,
  "hostname": "localhost",
  "reqId": "req-1",
  "req": {
    "method": "GET",
    "url": "/users",
    "headers": {}
  },
  "msg": "incoming request"
}

4.2 请求日志钩子 #

javascript
fastify.addHook('onRequest', async (request, reply) => {
  request.log.info({
    type: 'request',
    method: request.method,
    url: request.url,
    ip: request.ip
  })
})

fastify.addHook('onResponse', async (request, reply) => {
  request.log.info({
    type: 'response',
    method: request.method,
    url: request.url,
    statusCode: reply.statusCode,
    responseTime: reply.elapsedTime
  })
})

4.3 自定义请求ID #

javascript
const { v4: uuidv4 } = require('uuid')

const fastify = require('fastify')({
  logger: true,
  genReqId: (req) => req.headers['x-request-id'] || uuidv4()
})

五、结构化日志 #

5.1 对象日志 #

javascript
fastify.get('/user/:id', async (request, reply) => {
  request.log.info({
    action: 'get_user',
    userId: request.params.id,
    ip: request.ip
  })
  
  const user = await getUser(request.params.id)
  
  request.log.info({
    action: 'user_found',
    userId: user.id,
    userName: user.name
  })
  
  return user
})

5.2 错误日志 #

javascript
fastify.get('/error', async (request, reply) => {
  try {
    await riskyOperation()
  } catch (error) {
    request.log.error({
      action: 'risky_operation',
      error: {
        name: error.name,
        message: error.message,
        stack: error.stack
      }
    })
    
    throw error
  }
})

5.3 性能日志 #

javascript
fastify.addHook('onRequest', async (request, reply) => {
  request.startTime = Date.now()
})

fastify.addHook('onResponse', async (request, reply) => {
  const duration = Date.now() - request.startTime
  
  request.log.info({
    type: 'performance',
    method: request.method,
    url: request.url,
    duration,
    statusCode: reply.statusCode
  })
})

六、子日志 #

6.1 创建子日志 #

javascript
const childLogger = fastify.log.child({ module: 'user-service' })

childLogger.info('User service started')

6.2 模块日志 #

javascript
const userService = fastify.log.child({ service: 'user' })
const emailService = fastify.log.child({ service: 'email' })

userService.info('User created')
emailService.info('Email sent')

6.3 请求级子日志 #

javascript
fastify.addHook('onRequest', async (request, reply) => {
  request.log = request.log.child({
    requestId: request.id,
    userId: request.user?.id
  })
})

七、日志输出 #

7.1 控制台输出 #

javascript
const fastify = require('fastify')({
  logger: {
    level: 'info',
    transport: {
      target: 'pino-pretty',
      options: {
        colorize: true
      }
    }
  }
})

7.2 文件输出 #

javascript
const fastify = require('fastify')({
  logger: {
    level: 'info',
    file: './logs/app.log'
  }
})

7.3 多输出 #

javascript
const pino = require('pino')
const fs = require('fs')

const streams = [
  { level: 'info', stream: process.stdout },
  { level: 'error', stream: fs.createWriteStream('./logs/error.log') },
  { level: 'debug', stream: fs.createWriteStream('./logs/debug.log') }
]

const fastify = require('fastify')({
  logger: pino({ level: 'debug' }, pino.multistream(streams))
})

八、日志格式 #

8.1 自定义格式 #

javascript
const fastify = require('fastify')({
  logger: {
    formatters: {
      level: (label) => {
        return { level: label.toUpperCase() }
      },
      bindings: (bindings) => {
        return { pid: bindings.pid }
      }
    }
  }
})

8.2 时间格式 #

javascript
const fastify = require('fastify')({
  logger: {
    timestamp: () => `,"time":"${new Date().toISOString()}"`
  }
})

8.3 自定义序列化 #

javascript
const fastify = require('fastify')({
  logger: {
    serializers: {
      req(req) {
        return {
          method: req.method,
          url: req.url,
          headers: {
            'content-type': req.headers['content-type'],
            'user-agent': req.headers['user-agent']
          }
        }
      },
      res(res) {
        return {
          statusCode: res.statusCode,
          headers: res.getHeaders()
        }
      },
      err(err) {
        return {
          type: err.name,
          message: err.message,
          stack: err.stack
        }
      }
    }
  }
})

九、敏感信息处理 #

9.1 字段隐藏 #

javascript
const fastify = require('fastify')({
  logger: {
    redact: {
      paths: [
        'req.headers.authorization',
        'req.headers.cookie',
        'req.body.password',
        'req.body.creditCard'
      ],
      censor: '[REDACTED]'
    }
  }
})

9.2 条件隐藏 #

javascript
const fastify = require('fastify')({
  logger: {
    redact: {
      paths: ['req.headers.authorization'],
      censor: (value, path) => {
        if (path.join('.') === 'req.headers.authorization') {
          return value.substring(0, 10) + '...'
        }
        return '[REDACTED]'
      }
    }
  }
})

十、日志最佳实践 #

10.1 日志级别使用 #

javascript
fastify.log.trace('详细调试信息')
fastify.log.debug('调试信息')
fastify.log.info('一般信息')
fastify.log.warn('警告信息')
fastify.log.error('错误信息')
fastify.log.fatal('致命错误')

10.2 结构化日志 #

javascript
request.log.info({
  action: 'user_action',
  userId: request.user.id,
  data: { key: 'value' }
})

10.3 请求追踪 #

javascript
fastify.addHook('onRequest', async (request, reply) => {
  request.log = request.log.child({
    requestId: request.id,
    traceId: request.headers['x-trace-id']
  })
})

10.4 错误日志 #

javascript
fastify.setErrorHandler((error, request, reply) => {
  request.log.error({
    error: {
      name: error.name,
      message: error.message,
      stack: error.stack
    },
    request: {
      method: request.method,
      url: request.url,
      headers: request.headers
    }
  })
  
  reply.code(error.statusCode || 500).send({
    error: error.message
  })
})

十一、总结 #

本章我们学习了:

  1. 日志概述:Pino特点和基本配置
  2. 日志配置:基本配置、开发环境、生产环境
  3. 日志级别:trace、debug、info、warn、error、fatal
  4. 请求日志:自动日志、钩子、请求ID
  5. 结构化日志:对象日志、错误日志、性能日志
  6. 子日志:创建子日志、模块日志
  7. 日志输出:控制台、文件、多输出
  8. 日志格式:自定义格式、时间格式、序列化
  9. 敏感信息:字段隐藏、条件隐藏
  10. 最佳实践:级别使用、结构化、追踪、错误处理

接下来让我们学习性能优化!

最后更新:2026-03-28