跳到主要内容

日志分析全攻略

概述

日志是 DarraRT 唯一不依赖现场复现的故障线索。PLC 产品不提供断点/单步调试, 日志系统承担了"事后还原"的全部职责。本文档系统梳理 DarraRT 的四类日志统一等级体系时间戳对齐规则按模块过滤技巧以及常见异常根因速查表

日志质量直接决定故障处置速度。一个优秀的日志系统应当让工程师在 10 分钟内, 仅凭日志 就能还原异常发生前 30 秒的全部状态

适用场景

  • 现场无法复现的偶发崩溃
  • 产线恢复后的根因分析
  • 升级版本前的回归验证
  • 远程技术支持协作

四类日志体系

DarraRT 日志按进程归属分为四类, 路径各异, 必须同步分析才能拼出完整时间线。

类别路径进程典型内容
IDE 日志%AppData%/DarraPLC/logs/ide_*.logDarra.PLC.IDE.exeUI 事件 / 编译 / 下载
Service 日志<Service.exe 目录>/logs/service_*.logDarra.PLC.Service.exe永驻中间层 / 健康 / 部署
EtherCAT 日志Darra_EtherCAT_Master/debug_info.txtEtherCAT SDK 宿主主站状态 / WKC / DC
驱动日志DbgView.exe 实时抓取内核态三驱动DbgPrint 输出

日志路径速查

路径环境变量展开备注
%AppData%/DarraPLC/logs/C:\Users\<user>\AppData\Roaming\DarraPLC\logs\当前用户
%LocalAppData%/DarraPLC/dumps/C:\Users\<user>\AppData\Local\DarraPLC\dumps\崩溃转储
<Service.exe>/logs/C:\Program Files\DarraRT\Service\logs\系统服务
<EtherCAT.exe>/debug_info.txt根据部署位置追加模式
%ProgramData%/DarraPLC/last_layout.xmlDock 布局持久化布局异常看这个

统一日志等级

所有 DarraRT 日志使用 LogManager.Get("模块名"), 共 6 个等级:

等级数值用途生产环境
Trace0细粒度追踪 (每次函数进入/退出)关闭
Debug1调试信息 (变量值/分支判定)关闭
Info2正常事件 (启动/连接/下载)默认开
Warn3可恢复异常 (重连/降级)默认开
Error4业务错误 (未抛到顶层)默认开
Fatal5进程级崩溃默认开

日志格式标准

[2026-04-18 09:15:32.481] [Info] [MainForm] IDE 启动完成, 版本 25.2.0
[2026-04-18 09:15:32.512] [Warn] [DockManager] 布局文件版本不匹配, 已重建
[2026-04-18 09:15:33.127] [Error] [Compiler] 语法错误 FB_Motor:12 expected ';'

字段定义:

  1. 时间戳: ISO 8601 + 毫秒, 本地时区
  2. 等级: Trace/Debug/Info/Warn/Error/Fatal
  3. 模块: 方括号, 与 LogManager.Get("xxx") 参数一致
  4. 消息: 自由文本, 允许多行 (换行前置 4 空格)

前置条件与工具

工具用途
DbgView.exe (SysInternals)捕获三驱动 DbgPrint
BareTail / glogg实时尾部跟踪
ripgrep (rg)快速跨文件搜索
PowerShell 5.1+Select-String 过滤
Timeline ExplorerEric Zimmerman, 时间线对齐神器

诊断步骤

步骤 1: 定位时间窗

故障发生时间 ± 5 分钟 = 有效时间窗。过大则噪音太多, 过小易遗漏前置原因。

# 示例: 故障发生在 2026-04-18 09:15:00
$start = [datetime]"2026-04-18 09:14:30"
$end = [datetime]"2026-04-18 09:15:30"

步骤 2: 按等级初筛 (Fatal → Error → Warn)

# IDE 日志
Select-String -Path "$env:APPDATA\DarraPLC\logs\ide_*.log" `
-Pattern "\[Fatal\]|\[Error\]" | Select-Object -Last 200

预期输出: 若有 Fatal, 跳步骤 4 看堆栈; 若只有 Error, 进入步骤 3。

步骤 3: 按模块收窄

IDE 常用模块名:

MainForm          — 主窗口生命周期
DockManager — DevExpress Dock 布局
RibbonController — Ribbon 按钮绑定
Compiler — IEC 编译器
Downloader — 程序下载
OnlineMonitor — 在线监视
VariableBus — 变量总线
# 只看 DockManager 异常
Select-String -Path "$env:APPDATA\DarraPLC\logs\ide_*.log" `
-Pattern "\[DockManager\]" -Context 0,2

步骤 4: 拉异常堆栈 (Fatal/Error)

DarraRT 异常写出规范:

[2026-04-18 09:15:33.481] [Fatal] [MainForm] 未处理异常
类型: System.InvalidOperationException
消息: Collection was modified; enumeration operation may not execute.
栈:
at System.Collections.Generic.List`1.Enumerator.MoveNext()
at Darra.PLC.IDE.Panels.ProjectTree.RefreshAll() in ProjectTree.cs:line 213
at Darra.PLC.IDE.MainForm.OnProjectChanged() in MainForm.cs:line 487

判定规则:

  • 堆栈第一行 = 最内层触发点, 通常就是 bug 所在文件/行号
  • 堆栈最后一行 = 消息传入顶层, 看调用来源
  • 忽略 System.* / Microsoft.* 直到看见 Darra.* 才是代码

步骤 5: 四日志时间线对齐

故障涉及 PLC + EtherCAT + 驱动时, 必须把四份日志的相同时间窗放一起看。

# 把四份日志裁剪到同一分钟窗口, 合并成一个大文件按时间排序
$window = "2026-04-18 09:15"
Get-Content ide_*.log, service_*.log, debug_info.txt |
Where-Object { $_ -match [regex]::Escape($window) } |
Sort-Object |
Out-File merged_09_15.log

预期输出: 合并后一行一事件, 肉眼即可看出"IDE 先 Warn, 2 秒后 Service Error, 再 200ms 后驱动 DbgPrint: NDIS send fail"这种因果链。

步骤 6: 驱动日志 (DbgView) 抓取

1. 以管理员启动 DbgView.exe
2. Capture → Capture Kernel (勾选)
3. Capture → Capture Events (勾选)
4. Edit → Filter/Highlight → Include: Darra
5. File → Log to File... → kernel_YYYYMMDD.log

三驱动的 DbgPrint 前缀:

  • [DarraRT] — Global 驱动
  • [DarraRT_Eth] — Eth 驱动
  • [DarraRT_PLC] — PLC 驱动

常见异常根因速查表

异常消息根因定位方法解决
NullReferenceException字段未初始化 / 时序错位堆栈第一行定位变量构造函数初始化 or null 防护
Object reference not set同上同上同上
Collection was modifiedforeach 中改集合栈里找 MoveNext.ToArray() 快照再遍历
InvalidOperationException: Cross-threadUI 线程越界Control.InvokeRequiredBeginInvoke 封装
DockPanel.DockTo ... not supportedDevExpress API 过时报错版本号DockController.Dock
MSB3027 The process cannot access文件被 Service 锁handle.exe <dll>pre-build taskkill
The type initializer for 'X' threw静态构造函数异常InnerException看被初始化类的 static ctor
A task was canceledCancellationToken 触发谁 Cancel 的延长超时 or 合理 Cancel
The operation has timed out网络/Service 未响应netstat 看端口重启 Service / 扩大超时
ThreadAbortExceptionThread.Abort 调用栈里找 Abort禁用 Abort, 用 Cancel
OutOfMemoryException真泄漏 / 大对象堆碎片dotnet-dumpDispose 及时 / 分批处理
EntryPointNotFoundExceptionDLL 版本不匹配Fusion Log清理 bin + rebuild
BadImageFormatExceptionx86/x64 混用查项目 Platform统一 x64
FileNotFoundException: X.dll依赖缺失ProcMon NAME NOT FOUND补 NuGet 包
TypeLoadException接口不匹配Fusion Log重新编译依赖方

日志时间戳对齐规则

DarraRT 四类日志使用的时间源不同, 分析时要清楚差异:

日志时间源精度跨机一致性
IDEDateTime.Now (用户进程)ms不一致
ServiceDateTime.Now (系统服务)ms不一致
EtherCATDateTime.UtcNow + 毫秒msUTC, 需+时区
驱动TSC (KeQueryPerformanceCounter)100ns单机精确

对齐技巧: 在 IDE 日志里故意打一条"锚点日志":

[2026-04-18 09:15:00.000] [Info] [Anchor] ANCHOR TSC=12345678901234

然后用这个 TSC 值反推驱动日志中的相对时间, 即可做毫秒级对齐。

按模块过滤技巧

推荐每个模块独立 LogManager 实例, 分析时按模块过滤。常见过滤模式:

# 只看下载流程
rg "\[Downloader\]|\[Compiler\]" ide_*.log

# 看 EtherCAT 在 Init -> OP 过程
rg "\[ECAT\].*(Init|PreOP|SafeOP|OP)" debug_info.txt

# 看 PLC 扫描周期 (每次扫描都有 Debug)
rg "\[PlcCycle\] cycle=\d+" service_*.log | Select-Object -Last 100

高级技巧

  • 日志反向切片: Select-String -Tail 0 -Context 30,0 可以抓到 Error 前的 30 行上下文, 还原事故前状态机。
  • 结构化日志: Service 支持 JsonLogEnabled=true, 输出 logs/json/*.ndjson, 可直接导入 Kibana / DuckDB 做 SQL 查询。
  • 崩溃转储: Fatal 级日志同时触发 MiniDumpWriteDump, 文件落在 %LocalAppData%/DarraPLC/dumps/<pid>_<ts>.dmp, 用 WinDbg 打开看线程栈。
  • 对比分析: 把正常运行一小时的日志与异常运行一小时并列打开, 看模块日志频率差异, 异常模块的日志频率会有明显突变。

禁忌

  • 禁止: 生产环境开 Trace/Debug 级 — 日志洪水会填满 SSD
  • 禁止: 在 catch { } 写空块 — 至少 log.Error(ex)
  • 禁止: 捕获 Exception 后继续抛裸 throw — 用 throw; 保持栈
  • 禁止: 用 Console.WriteLine — 无时间戳/模块/等级, 不可检索

相关文档

日志时间线合并示例