You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Fastify+PostgreSQL后端中JS错误对象的字段访问异常问题

Fastify+PostgreSQL错误对象字段访问异常问题解析

问题背景

基于Fastify+PostgreSQL开发后端时,用户注册功能向数据库写入数据阶段,优化错误处理时发现:pg库抛出的错误对象在特定日志调用方式下,会出现字段丢失的异常,尤其是type字段无法在日志中正常输出。

错误对象结构

pg库抛出的DatabaseError对象完整结构如下:

err: {
  "type": "DatabaseError",
  "message": "duplicate key value violates unique constraint \"users_login_key\"",
  "stack": "error: duplicate key value violates unique constraint \"users_login_key\"\n    at C:\\Users\\galec\\Dev\\Lottery\\backend-lottery\\node_modules\\pg\\lib\\client.js:526:17\n    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n    at async file:///C:/Users/galec/Dev/Lottery/backend-lottery/src/routes/register.js:46:13",
  "length": 202,
  "name": "error",
  "severity": "ERROR",
  "code": "23505",
  "detail": "Key (login)=(alaverdyan) already exists.",
  "schema": "public",
  "table": "users",
  "constraint": "users_login_key",
  "file": "nbtinsert.c",
  "line": "649",
  "routine": "_bt_check_unique"
}

错误捕获代码

当前捕获错误并输出日志的代码片段:

try {
  // 数据库事务逻辑
} catch (err) {
  app.log.error({
    msg: `Error adding user '${login}' to table 'users'`,
    'Error code': err.code,
    'Error type': err.type,
    'Message': err.message,
    'Detail': err.detail,
    'DB table': err.table,
    'DB constraint': err.constraint,
    'Stack': err.stack,
  })
}

日志输出差异

不同日志调用方式的结果存在明显差异:

  • app.log.error(err):输出包含type字段的完整err对象
  • app.log.error({ err: err }):输出包含type字段的完整err对象
  • app.log.error({ error: err }):输出的err对象丢失type、message、stack等字段

问题原因

核心原因是Pino日志库对键名为error的对象有特殊序列化逻辑:

  1. Pino内部会自动识别键名为error的Error实例,并使用内置的错误序列化器处理
  2. 内置序列化器仅保留标准Error对象的属性(name、message、stack),而pg库抛出的DatabaseError属于自定义Error,其扩展的type、code、detail等属性不在Pino默认序列化白名单中,因此被过滤
  3. 当使用err作为键名或直接传入err对象时,Pino不会触发这个特殊序列化逻辑,因此能保留所有字段

解决方案

针对该问题,可采用以下几种解决方式:

  • 避免使用error作为键名:将日志对象中的键名改为dbError或其他名称,比如app.log.error({ dbError: err })
  • 自定义Pino序列化器:配置Fastify时添加自定义错误序列化逻辑,保留需要的字段:
    const fastify = require('fastify')({
      logger: {
        serializers: {
          error: (err) => ({
            type: err.type,
            message: err.message,
            stack: err.stack,
            code: err.code,
            detail: err.detail,
            table: err.table,
            constraint: err.constraint
          })
        }
      }
    })
    
  • 手动提取字段:在日志输出前手动提取需要的错误字段,再传入日志对象,比如:
    app.log.error({
      msg: `Error adding user '${login}' to table 'users'`,
      error: {
        type: err.type,
        code: err.code,
        message: err.message,
        detail: err.detail
      }
    })
    

内容的提问来源于stack exchange,提问作者Georgii Galechyan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 01:03:44