状态:设计已确认(2026-06-18 brainstorming),待实现。 关联:本文件是「全量操作日志」需求的权威设计文档(=spec);实现计划另出(开发计划/ M8 子计划)。 一句话:给 C# 与 Java 两端所有「操作」留下完整、可读、可串联的记录(谁/功能/输入/输出/报错/结果/耗时),让测试期能靠日志快速定位、上线后易排障。
traceId 把一次操作的全过程(跨 C#→Java)拉成时间线,找到 result=失败 那条,读它的 input + error 即可定位。人能看懂、AI 能自动分析。| 端 | 项目 | 是否埋点 | 说明 |
|---|---|---|---|
| C# | ivf_tl_operate_2.0(合并端 operate) |
✅ 全埋 | 当前主战场 |
| C# | aivfo-front-manament-2.0(前端管理) |
✅ 全埋 | |
| C# | ivf_tl_control_2.0(control) |
❌ 不埋 | 要并入 operate、以后删 |
| C# | autofocustool(对焦标定工具) |
❌ 不埋 | 要并入 operate、以后删 |
| Java | 全部微服务 | ✅ | 复用现有框架 |
[C# operate / front] ┐
[Java 各微服务] ├─异步→ Kafka(topic: tl-oplog) → [日志微服务 aivfo-oplog] → log 库 operation_log
┘ ↘ 大对象只存文件名/FastDFS路径/关键字段
本地文件:① 启动阶段日志(连不上Kafka时) ② Kafka 发不出去时降级兜底 ③ 调试级详细日志(见 §4)
operation_log;负责分级归类、保留期清理。| 操作级(默认开) | 调试级(默认关,按需热开) | |
|---|---|---|
| 记什么 | "打开串口 COM3 失败-超时" 等有业务含义的操作 | 串口原始收发 hex 字节、相机每帧返回码、握手每步时序 |
| 目的 | 定位哪一步出问题 | 看那一步内部细节、复现 bug(开发调试用) |
| 载体 | 结构化 → Kafka → operation_log 库(长期、可查、AI 分析) |
本地文件(滚动,临时,不进 Kafka/库,避免冲垮) |
| 量 | 可控 | 高频、巨大 → 故不入库 |
async-rollingFile(logback 文件 appender);C# LogHelper 已有的 PortComRecord(串口通信记录)/HouseComRecord(舱室通信记录)文件日志——直接收编为"调试级"通道。operation_log 表)| 字段 | 含义 |
|---|---|
trace_id |
全链路串联 ID(机器码,唯一) |
parent_id |
父子调用链 ID(复用现有 parentId 体系,串调用层级) |
time |
操作时间(毫秒) |
project |
端/服务(operate / front / tl-control / business-manage / data-transmission …) |
module |
功能模块(可读:对焦 / 串口 / 患者 / 图片 …) |
operation |
操作(可读:一键标定 / 打开端口 / 保存手调 …,不要像 trace_id 那样看不懂) |
operator |
谁(登录用户 / 工程师 / 系统 / 设备 SN)。Java 现有 system_log.create_by 已记登录用户,对齐复用 |
input |
输入(JSON;大对象只存文件名/路径/尺寸等关键信息) |
output |
输出(JSON;同上) |
result |
结果(成功 / 失败) |
error |
报错(消息 + 堆栈摘要) |
elapsed_ms |
耗时 |
level |
级别(操作级默认;调试级走文件不入此表) |
house_sn/well_sn/tl_sn |
设备上下文(便于按舱/按 well 过滤定位) |
host/ip |
来源 |
| 标准审计列 | create_by/create_time/update_by/update_time/deleted(随框架约定) |
module/operation/result/error 用人能看懂的描述(中英文均可)。tl-oplog,JSON 统一 schema,分区可按 project/module。C# 用现有 Kafka 客户端、Java 用 Kafka 生产。aivfo-oplog:复用 framework、注册 Nacos;消费 tl-oplog → 写 log 库 operation_log 表。log 库(本就是"日志专用库"),新增 operation_log 表,与现有 system_log(技术日志)并列。两表共用 trace_id:看到一条操作"失败",用 trace_id 可 join 出 system_log 里对应代码行的异常堆栈。
system_log:技术日志(logger/类/方法/行号/异常)与操作审计(谁/输入/输出/结果)字段、视角不同,分表更干净,且不影响现有 system_log。trace_id;parent_id 记父子调用层级(比单 traceId 更细)。com.aivfo.el.starter.base.contex.Trace(TransmittableThreadLocal 跨线程传递)、TraceIdAspect/ParentIdAspect、TTL MDC。直接复用。aivfo-log-spring-boot 已有 @OperateLog 注解 + OperateLogAspect 切面 + traceId/parentId + LoggerDBAppender(但默认关、只记 ERROR、同步写 system_log)。@OperateLog 注解语义(module/operation/记入参出参/耗时/operator/result),切面采集后走 Kafka(新增 Kafka 生产),而非现有同步 DBAppender。补齐 §5 缺的结构化字段(input/output/result/operation 可读名/elapsed)。system_log + LoggerDBAppender 保持现状(可选开启),通过 trace_id 与操作日志关联。覆盖 Controller/Service 关键方法;注解为主,改动小。
P2 已落地(2026-06-18):
@OperateLog 增加 module() / operation()(默认空,旧无参用法兼容)。aivfo-log-spring-boot-core 新增 OperationLogMessage(字段对齐 §5 / oplog 消费端)+ OperationLogSender 接口。OperateLogAspect 改为环绕采集 traceId(Trace)/parentId(MDC)/project(spring.application.name)/operator(AuthThreadLocal)/input/output/result/error/elapsedMs/host,组装消息;容器存在 OperationLogSender bean 则发送(try 兜底,失败不影响业务),否则保持原 log.info(向后兼容,未引 kafka 的服务不受影响)。aivfo-oplog-client(依赖 log + kafka starter),仅当容器存在 ProducerNormal 时装配 KafkaOperationLogSender(发 JSON 到 tl-oplog,启动尝试建 topic)。理由:不让 aivfo-log-spring-boot 反向依赖 kafka——有 kafka 的服务引此 starter 即接入,其余服务零影响。tl-oplog → aivfo-oplog 消费 → operation_log 落库,module/operation/project/result/elapsedMs 正确。Aivfo.OperationLog(对称 Java 的 aivfo-log-spring-boot)——封装:异步内存队列、发 Kafka、trace_id 生成与透传、配置开关、脱敏、容错兜底、调试级本地文件。
收编现有日志:LogHelper(同步写文件)收编进新组件——操作级走 Kafka,本地文件保留为"启动日志 + 兜底 + 调试级"角色。PortComRecord 等收编为调试级通道。
P3a 已落地(2026-06-18)—— 组件 + 端到端验证(不含全埋):
Aivfo.OperationLog/(net6.0,仓库根目录,operate/front 均 net6.0-windows 可直接引用)。核心文件:OperationLogMessage(字段对齐 §5 / tl-oplog camelCase schema:traceId/parentId/time/project/module/operation/operator/input/output/result/error/elapsedMs/level/houseSn/wellSn/tlSn/host)、OperationLogger(门面)、OperationScope、OperationLogContext(traceId/parentId/operator/houseSn/wellSn 用 AsyncLocal 跨 async/线程透传 + BeginScope 父子链)、OperationLogPipeline(有界 System.Threading.Channels,DropWrite 非阻塞入队,后台单线程批量发送)、KafkaOplogTransport(IOplogTransport 抽象 + Confluent.Kafka 实现)、LocalFileWriter(调试级 + 兜底 + 自身错误,按天滚动)、SafeSerializer(循环引用忽略、大对象/byte[]/Stream 只记类型+长度、超 4000 字截断、全 try)、OperationLogOptions(全局/模块级 开关+级别,运行时可改)。OperationLogger.Init(o => …) 启动一次;OperationLogger.Log(module, operation, input, output, result, error, elapsedMs, level, …);using (OperationLogger.Begin(module, operation)) { op.Input(x).Success(y); } 自动计时;OperationLogger.Run(module, operation, () => {…}) 自动捕获异常→result=失败/error。operator 从入参或 OperationLogContext.Operator 取。ProducerBuilder<Null,string> 发 UTF-8 JSON 到 tl-oplog。真发 Kafka 成功(非降级)。MinLevel 显式调到 Debug)。Kafka 门槛与"模块启用"解耦:模块关闭则连本地都不写。fallback 文件兜底;组件自身错误写 error 文件;序列化全 try。C:\TLData\_setup\m8-test(独立、不进仓库)记 4 条(含中文 module="测试"/operation="P3a验证"/operator="工程师张三")→ tl-oplog → aivfo-oplog 消费 → operation_log。查得 3 条操作级入库(trace_id 非空、project=operate、house_sn/well_sn/tl_sn 透传、elapsed_ms 计时正确)、中文经 HEX 校验存储正确;1 条调试级正确只落本地文件未入库。验证后已删除测试数据。P3b 已落地(2026-06-18)—— operate 接入组件 + 关键边界统一封装埋点(铺开第一步):
ivf_tl_Operate 与 ivf_tl_Entity 各加 ProjectReference → ..\..\Aivfo.OperationLog(组件 net6.0 与两工程 net6.0-windows 兼容)。Entity 含串口(ComBin/Channel)/相机(Camera)边界故须直接引用;Services 经 Entity 传递可见组件类型。App.xaml.cs → App_Startup 中 OperationLogger.Init(project="operate", KafkaBootstrapServers=App.config kfkaIP:kfkaPort(当前 127.0.0.1:9092), Topic="tl-oplog"),全 try 兜底,初始化失败不影响启动。AppData.AppDataInit(HttpHelper 构造 + 登录用户来源处)登录成功后 SetOperationLogContext() 设 OperationLogContext.Operator = CurrentUserInfo.username(无则 account),并设 OperationLogger.Options.TlSn = TlSn 作设备默认。AsyncLocal 不跨 UI 事件传播,故另存静态 AppData.CurrentOperator 作全局默认备用。ivf_tl_Services/HttpHelper.cs):统一收口方法 HttpClientSendAsync 埋点(module="HTTP",operation=接口 url,记成功/失败/超时/异常 + elapsedMs + output 截断 2000 字)。所有 HTTP 调用经 callWebService* → HttpClientSendAsync 单一收口,一处即全覆盖。ivf_tl_Entity/ComEntitys/ComBin.cs):OpenPort/ClosePort 操作级入库(module="串口",打开/关闭端口);SendCommand(单条指令收发,高频)走调试级 Debug(落本地文件、默认不入库,符合 §4),operation=指令-{commandType}。ivf_tl_Entity/CameraEntitys/Camera.cs):Init/UnInit 边界埋点(module="相机",初始化/卸载,input/output/result)。ivf_tl_Operate/ViewModel/HouseDebugPageViewModel.cs,module="对焦调试"):OneClickCalibrate(一键标定,用 Begin scope 统一 traceId 串联本次标定内的 HTTP/串口/相机子埋点,完成记 okCount/total,异常 Fail);SaveWellHor(手调保存)、HorizontalMotorReset/VerticalMotorReset(电机控制)用 Run 自动捕异常。挑关键几个,非每个。Guid.NewGuid() 生成 traceId 写入请求 header "traceId";改为取 OperationLogContext.TraceId(无则 NewTraceId()),HttpClientSendAsync 与 HttpClientSendAsyncStream 两条 HTTP 路径同步对齐。header 名 "traceId" 与 Java 网关读取完全一致:核对 BasicConstant.TRACE_ID = "traceId",servlet TraceInterceptor 与 reactive TraceFilter 均 getHeader("traceId"),缺省才新建——无待对齐项。dotnet build ivf_tl_Operate/ivf_tl_Operate.csproj -c Debug,全程 0 error(仅 DLL 拷贝重试警告,因现场 operate.exe 在运行锁定输出目录,非编译错误)。C:\TLData\_setup\m8-test\p3b-probe,独立不进仓库)模拟 operate Init(project=operate, 127.0.0.1:9092, tl-oplog),在 Begin("对焦调试","一键标定") scope 内记 HTTP/串口/相机 3 条 → tl-oplog → aivfo-oplog 消费 → operation_log。查得 4 条 project=operate、operator 透传、house_sn=3、elapsed_ms 正确、中文 module(相机/对焦调试/串口)utf8mb4 存储正确,4 条共享同一 trace_id(证明 scope 父子链串联生效,即 traceId header 透传机制可用)。验证后已删除测试数据 + 探针。@OperateLog 注解;④ 真机验证(operate GUI 跑起来看实时日志,需用户在现场)。M8-Pjava 已落地(2026-06-18)—— Java 端关键微服务接入 + 真实链路自测(铺开第一步):
aivfo-kafka-spring-boot-starter 仅 aivfo-data-transmission(收图/串口服务)有 kafka;aivof-tl-control、aivfo-business-manage 均无 kafka,本期不接(无 ProducerNormal 即使引 starter 也不装配,留待其加 kafka 后或改用其它发送通道)。aivfo-data-transmission-lanucher/pom.xml 加 aivfo-oplog-client 依赖(版本随 ${revision} BOM 管理,无显式版本)。@OperateLog 的方法(4 个,data-transmission-controller):PrepareApiController.showCache(数据传输/查询缓存数据,@NoToken)、PrepareApiController.initCache(数据传输/重新初始化缓存,需鉴权)、EventManageApiController.tlSettingUpdate(数据传输/设备设置更新)、UploadApiController.upload(数据传输/上传图片,收图入口)。挑代表性业务入口,非全铺。aivfo-framework install、aivfo-data-transmission install 均 0 error。java -jar,端口 10030,context /api/data/transmission/server),起服务踩坑见下。两次请求对比验证:POST /prepare/showCache(@NoToken,匿名) → 入库 operator=anonymous_anonymous、trace_id=724b...(真实链路 traceId 非空)。POST /prepare/initCache 带真实 JWT token(header token) → 入库 operator=张医生_doctor_zhang、trace_id=b82d...。token 用项目内置 AuthInfoUtils.generateTokenExpireInDay(RsaUtils 内置私钥)对真实 CurrentUserInfo 签发;下游 TokenInterceptor 解析后填 AuthThreadLocal,切面 getOperatorInformation() 取得真实值。aivfo-data-transmission、elapsed_ms 计时正确、result=success。KafkaOperationLogSender 启动日志确认装配(OplogClientAutoConfiguration topic=tl-oplog)。验证后已删除测试数据(DELETE WHERE project='aivfo-data-transmission')。-Djna.library.path 指向 aivfo-data-transmission/lib 外,还须把该 lib 目录加入 进程 PATH——JavaImageDLL.dll 依赖 opencv_world3416.dll,Windows DLL 依赖链走 PATH 解析(不走 jna.library.path),否则 UnsatisfiedLinkError: Unable to load library 'JavaImageDLL' 导致 pictureProcessing bean 创建失败、上下文启动失败。② 仓库根目录含中文 时差项目源代码,用 .bat(cmd GBK 解析)或嵌套 PowerShell Start-Process 启动 java 易因路径编码截断失败;用 bash nohup java(PATH 头部加 lib 的 MSYS 路径)启动稳定。aivof-tl-control / aivfo-business-manage 等无 kafka 的服务,待其引入 kafka 后再接 aivfo-oplog-client,或评估非 kafka 发送通道;③ 业务 service 层(非 controller 入口)关键方法埋点。以后新写的代码(C# 和 Java)都必须加操作日志埋点。
@OperateLog(module/operation 可读)。Aivfo.OperationLog 组件埋点。Aivfo.OperationLog 库 + 标准配置。log 库建 operation_log 表 → 日志微服务 aivfo-oplog(消费 Kafka 入库 + 保留期清理)→ Kafka topic tl-oplog → 统一 schema → Java 端操作日志切面发 Kafka → C# 端 Aivfo.OperationLog 组件。@OperateLog。operation_log,字段完整(谁/操作/输入/输出/结果/耗时)。trace_id 一致、可拉成时间线。trace_id 可 join operation_log ↔ system_log。| # | 决策 | 理由 |
|---|---|---|
| 1 | 异步通道走 Kafka | 量大、要稳不丢;业务零等待;C#/Java 统一通道(项目已有 Kafka) |
| 2 | 复用现有 aivfo-log-spring-boot(@OperateLog/traceId/parentId),不另起炉灶 |
框架几乎就是所需;跟随现有模式 |
| 3 | 操作日志建独立表 operation_log(与 system_log 并列同库、共享 trace_id) |
操作审计与技术日志字段/视角不同;分表干净、可 join |
| 4 | 两级日志:操作级入库 / 调试级本地文件 | 兼顾"定位哪步"与"看内部细节",互不污染 |
| 5 | C# 组件化 Aivfo.OperationLog |
现有项目复用、未来新项目引包即得;与 Java starter 对称 |
| 6 | C# 全埋 operate+front,不埋 control+autofocustool | 后两者要并入 operate、以后删 |
| 7 | 可配置 = 运行时模块级开关+级别+按舱热开,先埋后配 | 平时干净、排障可调;配置不替代埋点 |
| 8 | 不建操作字典表 | YAGNI,可读串直接落库 |
| 9 | 开发规约强制新代码埋点 | 保证长期一致、不退化 |