Files
web-table/docs/superpowers/specs/2026-06-24-structured-logging-design.md

5.0 KiB
Raw Blame History

结构化日志系统(稳定性诊断)设计

  • 日期2026-06-24
  • 状态:设计(已口头确认,待实现)
  • 范围v1 聚焦稳定性诊断——捕获错误与崩溃(尤其 /api/export-excel 的 OOM 路径)。

1. 背景与问题

当前服务端只有 3 处 ad-hoc console.error/warn,且只记 err.message丢失堆栈)。 输出被 NSSM 原样灌进 logs/stdout.log/stderr.log,追加、不轮转——导致崩溃堆栈堆积、不可检索。 诊断 export 的 OOM单次全量导出峰值 ~1.3GB 堆)时,缺少"哪次导出、多少行、耗时多久"的现场。

约束114 不通外网 → 自包含文件日志,排除 SaaS单 Node 进程、内部工具、低流量。

2. 目标

在不改动业务逻辑的前提下,新增一层结构化、分级、同步落盘、自带轮转的应用日志, 让"出错/崩溃时能查到完整现场"。

3. 非目标YAGNI

  • 不做请求级全量审计 / 用户行为追踪(属"运行可见性/审计",非 v1
  • 不做客户端(浏览器)错误采集。
  • 不做集中式日志聚合ELK 等)。
  • 不重构 dbConfig 两处重复(不属于本次范围)。

4. 方案

4.1 选型log4js fileSync

  • fileSync appender 同步写fs.appendFileSync)→ 进程 OOM/被杀前日志已落盘,满足崩溃诊断刚需。
  • 内置按大小轮转(maxLogSize + backups),无需手写。
  • 分级、category、pattern layout成熟轻量。
  • 引入依赖 log4js(约 ~3 个小间接依赖)。

4.2 分层

应用日志(新增)  log4js fileSync → logs/app/app.log同步 + 10MB×5 轮转)  ← 查错误/崩溃
框架/致命日志     NSSM 的 stdout/stderrNext 启动、V8 崩溃栈)            ← 兜底,保持不动

4.3 logger 模块 src/server/logger.ts

import log4js from "log4js";

log4js.configure({
  appenders: {
    app: {
      type: "fileSync",
      filename: "logs/app/app.log",
      maxLogSize: 10 * 1024 * 1024, // 10MB
      backups: 5,
      layout: { type: "pattern", pattern: "[%d{ISO8601}] [%p] [%c] %m" },
    },
  },
  categories: { default: { appenders: ["app"], level: "info" } },
});

export const apiLogger = log4js.getLogger("api");
export const exportLogger = log4js.getLogger("export");
  • filename 用相对路径,next startNSSM 的 AppDirectory/next dev 的 cwd 均为项目根,解析正确。
  • 输出样例: [2026-06-24T10:30:01.123] [ERROR] [export] Excel export failed · rows=33142 · dur=18234ms · RangeError: Invalid string length\n at ...

4.4 记什么(诊断聚焦,不给常规成功查询加噪)

事件 级别 字段
production-data 查询失败 ERROR workshopNo、完整堆栈
production-data 慢查询(>3s WARN workshopNo、耗时
export 锁被并发占用429 WARN
export 锁超时回收 WARN 取代现有 console.warn
export 开始 / 成功 INFO rows、bytes、耗时OOM 诊断核心线索)
export 失败 ERROR 完整堆栈、rows、耗时
进程启动 INFO logger ready

要点:现有 3 处 console.error/warn 全部替换为 logger 调用;err.message 升级为完整 err.stack

4.5 路由改造

  • production-data/route.tscatch 分支用 apiLogger.error(...) 带栈 + workshopNo查询耗时 >3s 时 apiLogger.warn(...)
  • export-excel/route.ts:锁占用/回收 exportLogger.warn;开始记 rows、成功记 rows/bytes/dur、失败 exportLogger.error 带栈 + dur保留现有返回逻辑不变。
  • 用一个纯函数 formatError(err): string 统一提取 err.stack ?? String(err),便于单测。

4.6 涉及文件

文件 动作
src/server/logger.ts 新增log4js 配置 + 导出 logger + formatError
src/app/api/production-data/route.ts 改:失败 ERROR带栈+ 慢查询 WARN
src/app/api/export-excel/route.ts 改:锁/开始/成功/失败 全套日志(带栈)
package.json +log4js 依赖

5. 边界情况

  • OOM 落盘fileSync 同步写,每行即时 flush崩溃前已写入。
  • 首次写入log4js 自动创建 logs/app/ 目录与文件。
  • 轮转:达到 maxLogSize 自动滚动为 app.log.1..5,更早的删除。
  • 本地 dev vs 114 生产:均写入项目根下 logs/app/app.log;不影响 NSSM 的 stdout/stderr。

6. 测试

  • 纯函数 formatError(err):用 vitest 单测Error→stack、非 Error→字符串、null/undefined 安全)。
  • 路由日志为副作用,以手验为准:本地断开 DB 触发 production-data 失败 → 确认 logs/app/app.log 有 ERROR 带栈行;触发一次导出 → 确认 INFOrows/bytes/dur/ 失败 ERROR 行。
  • 不引入 log4js 行为的单测(第三方,按项目惯例不测)。

7. 部署注意

新增 log4js 依赖114 部署须执行 npm installCLAUDE.local.md 第 3 步),再 next build + 重启。