2. 工业设备软件日志体系设计
工业设备软件的日志与互联网服务日志有本质差异:它面对的是实时控制回路、现场总线、PLC 扫描周期和毫秒级时序约束。日志写得太细会拖慢控制线程,写得太粗则在故障复现时无从下手。本节围绕日志分级、结构化规范、工业协议与设备事件日志、采集点选择、生命周期管理、面向诊断的字段设计六个方面展开,并最终落到一个可执行的实战任务:为某 PLC 上位机软件设计日志规范。
一、日志分级:按“谁来看、多久看”划分
工业上位机常见的分级是 TRACE / DEBUG / INFO / WARN / ERROR / FATAL 六级,但真正决定分级是否可用的是判定标准,而不是级别名称。建议用“动作主体 + 影响范围”来定级:
- FATAL:进程无法继续,如与控制器的通信线程崩溃、许可证校验失败导致主功能不可用。
- ERROR:一次业务操作失败但进程存活,如写 PLC 寄存器返回超时、配方下发校验不通过。
- WARN:指标越界或降级运行,如通信重连成功、扫描周期超过设定阈值但仍完成。
- INFO:状态迁移与关键业务动作,如“连接建立”“进入自动模式”“批次 B20240513 开始”。
- DEBUG:单次交互的输入输出,如一条 Modbus 请求的报文摘要。
- TRACE:逐字节/逐帧细节,仅用于短时抓取,默认关闭。
关键约束是:INFO 及以上必须默认开启且可长期保留,DEBUG/TRACE 通过运行时开关按模块动态打开,避免为了排障而重启上位机——重启往往会让现场状态丢失,故障无法复现。
二、结构化日志规范
工业现场排障常需要按“设备 + 点位 + 时间窗”检索,纯文本日志几乎无法支撑。结构化日志的核心是固定字段名与取值约定。推荐以 JSON Lines 输出,每行一条记录,字段分三层:
- 公共层:ts(ISO8601 带毫秒与时区)、level、logger、pid、thread、host、app、app_ver。
- 设备层:device_id、device_type、protocol、endpoint(如 192.168.1.10:502)、unit_id。
- 业务层:event_code、point(如 DB1.DBD0)、value、duration_ms、result、trace_id。
其中 event_code 建议采用“模块.对象.动作”的稳定编码,例如 comm.modbus.write_timeout、recipe.download.checksum_mismatch。编码一旦发布不得随意改名,否则历史日志的聚合统计会断裂。trace_id 用于把一次“操作员点击下发配方”引发的通信、校验、写库多条日志串成一条链路。
三、工业协议与设备事件日志
协议层日志不能只记“通信失败”,要能回答“哪一帧、哪个字段、什么异常码”。以 Modbus TCP 为例,一次写保持寄存器失败,应记录功能码 0x10、起始地址、寄存器数量、异常码(如 0x02 非法数据地址、0x0B 网关目标设备无响应)以及耗时。S7 通信则应记录 PDU 参考号、区域(DB/I/Q/M)、字节偏移与返回状态码。
设备事件日志与协议日志要分开:前者描述设备自身状态变化(急停按下、伺服使能丢失、安全门打开),来源可能是 PLC 的报警位、OPC UA 事件订阅或设备自带诊断缓冲区;后者描述上位机与设备之间的交互。二者用同一 trace_id 关联,才能在“设备报警 → 上位机响应 → 操作员处置”之间建立因果链。
四、采集点选择
采集点不是越多越好。选择原则是“能定位到责任边界”:通信驱动层记录帧级交互与异常码,用于判断是网络、设备还是上位机的问题;业务逻辑层记录配方、批次、校验结果,用于判断业务规则是否被正确执行;设备事件层记录报警与安全信号,用于还原现场工况;UI/操作层记录操作员动作,用于区分人为操作与系统异常;系统资源层记录 CPU、内存、日志队列积压,用于发现日志本身成为瓶颈。
需要特别注意的是日志写入不能阻塞控制线程。推荐异步队列 + 独立写盘线程,队列水位超过阈值时降级丢弃 TRACE/DEBUG 并产生一条 WARN,而不是让通信线程等待磁盘 IO。
五、日志生命周期管理
工业现场上位机常部署在无外网的工控机上,磁盘容量有限。建议采用三级生命周期:热存储保留 7 天全量日志,支持秒级检索;温存储保留 90 天 INFO 及以上级别,用于月度分析;归档保留 2 年,仅保留 ERROR/FATAL 与关键业务事件,用于质量追溯。轮转策略采用“按天 + 按大小”双触发,单文件 50 MB。磁盘占用超过 80% 时,优先淘汰 DEBUG/TRACE,ERROR/FATAL 单独文件且禁止被轮转覆盖。
六、面向诊断的日志字段设计
字段设计要服务于“排障时你会怎么问”。常见问题与对应字段:
| 排障问题 | 必需字段 |
|---|---|
| 哪台设备出问题? | device_id、endpoint、unit_id |
| 什么时候开始? | ts(毫秒精度)、duration_ms |
| 是通信还是业务问题? | protocol、event_code、result |
| 影响哪些点位? | point、value |
| 能否还原完整链路? | trace_id、thread、pid |
实战任务:为某 PLC 上位机软件设计日志规范
假设对象为一套注塑机 PLC 上位机软件,通过 Modbus TCP 与 3 台 PLC 通信,周期 100 ms 采集温度与压力,支持配方下发。规范要点如下:
- 分级:INFO 默认开启;通信超时、配方校验失败记 ERROR;扫描周期超 150 ms 记 WARN;帧级细节记 DEBUG,默认关闭。
- 格式:JSON Lines,字段固定为 ts、level、logger、device_id、protocol、endpoint、unit_id、event_code、point、value、duration_ms、result、trace_id。
- 事件编码:
comm.modbus.read_timeout、comm.modbus.write_exception、recipe.download.checksum_mismatch、device.alarm.over_temp。 - 采集点:通信驱动、配方服务、报警订阅、操作日志、资源监控五处。
- 生命周期:热 7 天、温 90 天、归档 2 年;单文件 50 MB;磁盘 80% 触发降级。
示例日志行(配方下发校验失败):
{"ts":"2024-05-13T09:21:07.482+08:00","level":"ERROR","logger":"recipe.service",
"device_id":"IMM-01","protocol":"modbus_tcp","endpoint":"192.168.1.10:502","unit_id":1,
"event_code":"recipe.download.checksum_mismatch","point":"DB1.DBD0","value":null,
"duration_ms":38,"result":"fail","trace_id":"a3f9c1e2"}
验证方式:人为制造一次校验失败,确认该行日志出现且 trace_id 能关联到同一次操作的下发请求与 PLC 返回帧;随后按 device_id 与时间窗检索,应能还原“操作员下发 → 校验失败 → 未写入 PLC”的完整链路。若 trace_id 缺失或 event_code 不统一,则规范未达标,需回到字段设计阶段修正。