设备交付以后,开发者面对的通常不是一个可调试的异常,而是一句话:“昨天夜班偶尔停了一次,重启后好了。”如果软件没有把命令、状态、设备观测和配置版本关联起来,现场只能靠猜;如果升级又缺少兼容检查和回退路径,一次修复还可能制造更大的停机。
本系列最后一篇把可维护性视为架构能力:日志、指标、追踪、报警和审计各自记录不同事实,诊断包把证据安全地带离现场,升级流程则让软件、Recipe、固件和数据结构能够协同演进。
1. 五类信息各司其职
| 类型 | 回答的问题 | 典型内容 |
|---|---|---|
| 日志(Log) | 某个时刻发生了什么 | 命令接收、设备响应、异常与上下文 |
| 指标(Metric) | 一段时间内系统怎样变化 | 周期时间、失败数、队列深度、温度趋势 |
| 追踪(Trace) | 一次操作经过了哪些组件 | UI 命令 → 流程 → 轴/相机 → 数据保存 |
| 报警(Alarm) | 现在需要人采取什么行动 | 联锁未满足、位置未知、关键设备失联 |
| 审计(Audit) | 谁在何时改变了受控对象 | Recipe 发布、权限变更、维护复位、升级 |
日志不是审计日志,异常也不自动成为报警。把所有内容写进一个文本文件,会同时损害检索、保留策略和访问控制。
.NET 原生提供 ILogger、System.Diagnostics.Metrics 和 ActivitySource;OpenTelemetry 可以收集这些信号并导出到不同后端。是否部署集中式平台取决于现场网络和运维条件,但应用内部的事件结构与关联方式应尽早统一。半导体设备侧分级报警与可观测性落地的领域实践见《半导体设备软件(七):日志、报警与可观测性》。
2. 从一次操作的关联 ID 开始
每个外部命令生成或接收一个 CorrelationId,长流程再分配 RunId 和步骤 ID。它们应贯穿日志、状态转换、设备调用和结果数据:
| |
关联 ID 用于串起证据,不代表鉴权或幂等。外部传入的值要校验长度和字符范围,日志中也不能无条件信任用户输入。
结构化日志保留字段语义,便于查询和聚合:
| |
LoggerMessage 源生成器可以在编译时生成高效日志代码并提供诊断。事件 ID 要稳定,消息模板字段名也应保持一致;否则仪表盘和现场脚本会随着文字修改失效。
3. 日志要能解释状态转换
有用的状态日志至少包含:
- 旧状态、新状态和触发事件;
- 命令、运行和设备标识;
- 守卫条件拒绝的明确原因;
- 设备返回的原始码和稳定故障分类;
- Recipe、校准、软件和固件版本;
- 使用单调计时测得的持续时间。
不要在高频循环中无条件记录每次采样。可以把原始数据送入专门数据管线,把日志保留给状态变化和异常;趋势使用指标,特定故障前后的短窗口再通过环形缓冲保存。日志量控制不能简单丢弃所有 Information,应保证命令开始、状态转换和失败证据能够关联。
墙上时间用于跨系统对齐和审计,持续时间则应使用单调计时来源,例如 Stopwatch 或 TimeProvider.GetTimestamp(),避免系统时间校准造成负耗时。
4. 指标关注可行动的趋势
设备软件常见指标包括:
- 命令和流程的成功数、失败数与耗时分布;
- 当前命令队列深度、丢弃或等待次数;
- 设备断连次数和恢复耗时;
- 报警发生次数、持续时间和重复量;
- 数据采集速率、处理积压与磁盘剩余空间;
- 设备温度、压力等健康信号。
标签维度必须受控。把 RunId、序列号或完整错误消息作为指标标签,会产生近乎无限的时间序列;这些高基数字段应该进入日志或追踪。指标适合发现“最近一小时相机断连增加”,日志再回答具体是哪几次。
阈值不要凭经验写成通用常数。先采集基线,再结合设备规格、工艺窗口和响应流程定义报警条件,并记录适用版本。
5. 诊断包保存足够但不过量的证据
现场导出的诊断包可以包含:
- 软件、操作系统、驱动、固件和硬件标识;
- 当前状态快照与最近状态转换;
- 生效的 Recipe/配置/校准版本及摘要;
- 故障前后受限时间窗口内的日志与关键遥测;
- 活动报警、原始错误码和通信统计;
- 存储、内存和线程等运行健康信息;
- 清单文件、生成时间和文件摘要。
默认不要包含凭据、私钥、访问令牌、完整客户数据和无限量原始图像。导出前执行脱敏,诊断包设置访问权限和保留期限;跨组织传输时按双方的数据规则处理。生成诊断包本身也要有容量上限,避免磁盘已满时再次写入大量数据。
6. 审计记录受控变更
以下动作通常值得审计:
- 登录、角色和权限变更;
- Recipe 的创建、审核、发布、激活和停用;
- 校准与设备常数更新;
- 报警禁用、阈值和优先级修改;
- 维护模式、强制输出和故障复位;
- 软件、驱动和固件升级。
审计事件记录操作者身份、动作、对象、旧值/新值或版本、时间、理由和结果。仅写“修改成功”无法还原变化。审计存储应限制修改和删除,并将时间同步异常本身纳入监控;具体保存期限由行业、合同和组织制度决定,不能从通用架构直接给出一个年限。
7. 升级是一项受控设备变更
一次升级可能同时改变应用、数据库、Recipe Schema、厂商 SDK 和控制器固件。升级包至少应明确:
- 来源、版本、摘要或数字签名;
- 支持的操作系统、架构、驱动和固件组合;
- 配置与数据结构的迁移路径;
- 升级前检查、预计停机和空间需求;
- 安装后健康检查与验收步骤;
- 可回退组件、回退前提和不可逆步骤。
“复制新文件覆盖旧目录”会让旧 DLL、配置和新程序混杂。更稳健的方式是部署到独立版本目录,停止控制入口,备份受控数据,执行迁移,再切换启动目标。切换是否能做到原子性取决于操作系统和部署机制,不能只凭重命名目录就假定整个升级事务原子。
数据库或固件迁移可能不可逆。此时所谓回退不能只恢复旧可执行文件,还必须有向前修复、数据备份恢复或整机维护方案。升级前应阻止新任务,等待当前任务到安全检查点,并记录设备实际状态。
8. 用健康检查决定是否接管设备
进程启动成功不等于升级成功。自动验证至少包括:
- 读取应用版本和部署清单;
- 加载并验证配置;
- 检查数据结构版本;
- 建立设备会话并核对身份/固件;
- 验证关键联锁和只读状态;
- 在允许条件下执行受控自检;
- 确认日志、数据和磁盘路径可写;
- 最后才允许进入
Ready。
验证失败时,系统应保持不可生产状态并输出可诊断原因,而不是为了“服务已启动”强行忽略。对无人值守设备,还要验证 Windows Service、看门狗或外部监管器的重启策略不会形成无限崩溃循环。
9. 可维护性从设计阶段开始
日志字段、故障分类、配置摘要、命令 ID 和版本清单都依赖前几篇建立的边界。若 UI 直接调用 SDK、流程没有唯一 RunId、Recipe 可以运行中被修改,再先进的日志平台也只能收集互相矛盾的信息。
可以用一次“离线故障演练”检验体系:不给开发者远程调试权限,只提供诊断包、操作时间和现象描述,看能否还原使用的版本、命令路径、设备状态、首个根因和恢复动作。无法回答的问题,就是下一轮需要补充的可观测性需求。
总结
日志记录离散事实,指标显示趋势,追踪串起一次操作,报警要求人采取行动,审计保护受控变更。诊断包把这些证据以安全、有限的方式带离现场;升级则是一条包含兼容检查、迁移、验证和回退边界的设备变更流程。
至此,系列从职责边界出发,依次建立 HAL、生命周期、状态机与队列、Recipe、故障恢复、测试和可维护性闭环。它们共同指向同一个原则:上位机架构的核心不是界面有多少功能,而是每个命令、状态、参数、故障和资源都有清晰且可验证的所有者。