客户打电话说"昨晚三点设备停了一会儿",你打开程序想找原因——什么都没有。这是没有日志系统的上位机最常见的窘境。日志不是调试期用完就扔的东西,而是交付后唯一的现场证据链。但日志也不是越多越好:全量 DEBUG 一天能写几个 G,把磁盘撑爆反而误事。我们把日志设计拆成三件事:分类、分级、滚动写入,这篇讲透。
先分类:三种日志写三个文件,别混在一起
运行日志 run.log
程序自身行为:启动/退出、模块加载、异常堆栈、重连过程。排"程序为什么崩"看这个。
操作日志 op.log
人的行为:谁在几点登录、改了哪个参数、下发了什么配方、确认了哪条报警。排"谁动了设备"看这个。
通信日志 comm.log
设备交互:收发报文十六进制、超时、重连、校验失败。排"设备为什么没应答"看这个。
混在一个文件里的后果是:查一次通信问题要在几十万行运行日志里翻,最后没人愿意查。分开之后每个文件职责单一,排查时直奔目标文件。通信日志量大,默认只记"异常帧 + 收发计数",需要时通过界面开关临时打开全量报文记录——这个开关我们做在诊断页面上,现场远程指导时让客户打开,比跑一趟现场快得多。
再分级:五级够用,关键是定清楚"什么进哪级"
| 级别 | 记录内容 | 现场默认 |
|---|---|---|
| Debug | 变量快照、流程分支走向 | 关闭 |
| Info | 启动/停止、登录登出、配方切换、批次开始结束 | 开启 |
| Warn | 重连成功、队列水位高、单次超时后恢复、磁盘空间低于阈值 | 开启 |
| Error | 功能失败但不致命:写库失败重试、单设备通信中断 | 开启 |
| Fatal | 程序级故障:未捕获异常、数据库彻底连不上、主流程崩溃 | 开启 |
▲ 判断口诀:Info 记"发生了什么正常大事",Warn 记"出了问题但自己恢复了",Error 记"出了问题需要人看一眼",Fatal 记"程序干不下去了"。级别定错最典型的是把重连成功记成 Error——重连成功是好事,记 Warn 就够。
写盘不能阻塞采集:异步队列是标配
日志写入是磁盘 IO,机械盘上单次写入几十毫秒很常见。如果在采集线程里同步写日志,一次磁盘抖动就能拖慢采集周期。我们的做法和采集数据入库一样:日志进内存队列,独立线程批量落盘:
public sealed class FileLogger { private readonly BlockingCollection<string> _q = new(10000); private readonly string _dir; public void Log(LogLevel lv, string category, string msg) { if (lv < MinLevel) return; var line = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} [{lv}] [{category}] {msg}"; if (!_q.TryAdd(line)) Interlocked.Increment(ref _dropped); // 队列满直接丢并计数,日志永远不能反过来拖死业务线程 } private void WriterLoop() // 独立线程 { var buf = new List<string>(256); foreach (var line in _q.GetConsumingEnumerable()) { buf.Add(line); if (buf.Count >= 256 || _q.Count == 0) { AppendBatch(buf); // 按类别路由到 run/op/comm 文件 buf.Clear(); } } } }
格式上固定为 时间(毫秒) [级别] [分类] 内容 四段,毫秒精度必须有——两条报警之间差 200ms 还是差 2 秒,判断结论完全不同。
滚动与清理:别让日志把工控机磁盘吃光
工控机通常只有 128G 固态,日志不管控就是定时炸弹。我们的固定策略:
- 按天分文件:run_20260912.log,跨天自动切换,单文件天然不会太大;
- 单文件超 50MB 追加序号:防止某天报警风暴把单文件撑到打不开;
- 保留 30 天,启动时清理:程序启动时扫一遍日志目录,删掉超期的——比定时器清理可靠,因为程序可能中途没运行;
- 清理前压缩:文本日志压缩比极高,7 天以上的先 zip 再留 90 天,追溯期更长;
- 磁盘水位监控:剩余空间低于 10% 记 Warn 并上屏提示,这是运维层面的最后一道保险。
收尾:日志的价值在"查得到"
最后说两个让日志真正发挥作用的实践。一是关键事件带上下文:记"配方下发失败"没用,要记"配方#17 下发到 2#炉 失败,错误码 0x06,重试 2/3"——排查时不用再问任何人。二是程序里内置日志查看页,按日期、级别、关键字过滤,客户现场出问题先自己翻一遍,电话里就能定位一半。
如果你的上位机现在还没有日志,最小起步方案:先把 Info/Error 两级、按天滚动写进 run.log,一周之内你就会庆幸——第一次客户说"昨晚出过问题"时,你终于有东西可查了。
