14-全量操作日志方案.md 26 KB

14 · 全量操作日志方案

状态:设计已确认(2026-06-18 brainstorming),待实现。 关联:本文件是「全量操作日志」需求的权威设计文档(=spec);实现计划另出(开发计划/ M8 子计划)。 一句话:给 C# 与 Java 两端所有「操作」留下完整、可读、可串联的记录(谁/功能/输入/输出/报错/结果/耗时),让测试期能靠日志快速定位、上线后易排障。


1. 背景与目标

  • 痛点:现在调试(如对焦"串口失败")要人工逐个翻界面报错、读源码猜,定位慢。
  • 目标:所有操作可观测——出问题时,按 traceId 把一次操作的全过程(跨 C#→Java)拉成时间线,找到 result=失败 那条,读它的 input + error 即可定位。人能看懂、AI 能自动分析。
  • 原则:纯增量,不碰业务逻辑;记日志失败绝不影响业务。

2. 范围

项目 是否埋点 说明
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 全部微服务 复用现有框架
  • "全埋"口径:operate/front 里所有"有业务含义的操作"100% 不漏——命令入口(按钮/菜单)、串口/相机每个调用、对焦流程、HTTP 调后端、数据库读写、关键业务方法。不埋纯噪声(UI 属性 getter/setter、纯界面刷新、日志方法自身、简单工具函数)。
  • C# 一次性全埋,不分批(组件先做好,埋点一次铺完,便于测试全程可定位)。
  • 开发规约(强制):见 §13。

3. 总体架构与数据流

[C# operate / front]  ┐
[Java 各微服务]        ├─异步→ Kafka(topic: tl-oplog) → [日志微服务 aivfo-oplog] → log 库 operation_log
                      ┘                                       ↘ 大对象只存文件名/FastDFS路径/关键字段
本地文件:① 启动阶段日志(连不上Kafka时)  ② Kafka 发不出去时降级兜底  ③ 调试级详细日志(见 §4)
  • 业务端只把日志丢进内存队列(非阻塞、微秒级)→ 后台线程批量发 Kafka,业务线程不等
  • 日志微服务从 Kafka 消费 → 写 operation_log;负责分级归类、保留期清理。
  • 走 Kafka 的理由:量大、要稳——业务端零等待;日志微服务/库挂了或慢了不影响业务、不丢日志(Kafka 持久化缓冲,恢复后接着消费);C# 与 Java 同一条通道、格式统一;集中控制写库压力。

4. 两级日志(关键设计)

操作级(默认开) 调试级(默认关,按需热开)
记什么 "打开串口 COM3 失败-超时" 等有业务含义的操作 串口原始收发 hex 字节、相机每帧返回码、握手每步时序
目的 定位哪一步出问题 看那一步内部细节、复现 bug(开发调试用)
载体 结构化 → Kafka → operation_log(长期、可查、AI 分析) 本地文件(滚动,临时,不进 Kafka/库,避免冲垮)
可控 高频、巨大 → 故不入库
  • 调试级走本地文件、不入库:既能拿到逐字节细节,又不污染日志库。
  • 现有基础可收编:Java 的 async-rollingFile(logback 文件 appender);C# LogHelper 已有的 PortComRecord(串口通信记录)/HouseComRecord(舱室通信记录)文件日志——直接收编为"调试级"通道。
  • 实操(以串口失败为例):平时只开操作级 → 要深挖时把"串口模块"调 DEBUG(可只针对 3 号舱)→ 重现一次 → 本地文件拿到完整收发时序 → 分析 → 调回 INFO。

5. 统一日志模型(C#/Java 共用 — 操作级 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 用人能看懂的描述(中英文均可)。
  • 不建操作字典表(YAGNI):可读串直接落库。

6. 传输与存储

  • Kafka:单 topic tl-oplog,JSON 统一 schema,分区可按 project/module。C# 用现有 Kafka 客户端、Java 用 Kafka 生产。
  • 日志微服务 aivfo-oplog:复用 framework、注册 Nacos;消费 tl-oplog → 写 logoperation_log 表。
  • 日志库:用现有 log 库(本就是"日志专用库"),新增 operation_log,与现有 system_log(技术日志)并列。两表共用 trace_id:看到一条操作"失败",用 trace_id 可 join 出 system_log 里对应代码行的异常堆栈。
    • 不并入 system_log:技术日志(logger/类/方法/行号/异常)与操作审计(谁/输入/输出/结果)字段、视角不同,分表更干净,且不影响现有 system_log。
  • 大对象:图片等只存文件名/FastDFS 路径/尺寸等关键信息,不存内容,控制库负荷。
  • 分级管理:按 项目 > 模块 > 时间 组织(索引/分区 + 查询维度)。
  • 保留期:在日志微服务配置保留天数,定时(quartz)清理过期。

7. traceId + parentId 全链路

  • 一次操作生成一个 trace_idparent_id 记父子调用层级(比单 traceId 更细)。
  • Java 已有完整体系com.aivfo.el.starter.base.contex.Trace(TransmittableThreadLocal 跨线程传递)、TraceIdAspect/ParentIdAspect、TTL MDC。直接复用。
  • C#→Java 透传:C# 生成 trace_id,调后端时放 HTTP header;Kafka 消息也带 trace_id。同 trace_id 串起跨端全过程。
  • C# 端对齐生成 + 在进程内(含跨线程/异步)传递 trace_id。

7.1 两表合查排障(operation_log + system_log)——同一 trace_id 缝合

operation_logsystem_log 谁也不是谁的子集,但共用同一个 trace_id,合查能更完整还原一次业务流程:

  • 同源保证TraceIdAspect(入口挂 @BuildTraceId)一次性把同一 traceId 同时写入 Trace.context()(operation_log 切面读它)与 MDC(logback/LoggerDBAppender 写 system_log 读它)——所以两表 trace_id同一个字符串
  • 分工(互补,不重复)
    • operation_log = 跨端主线/骨架:C#+Java 所有 @OperateLog/埋点操作,成功+失败都有,结构化(谁/输入/输出/耗时/result)。
    • system_log = Java 端 ERROR 补充/血肉:链路里所有 log.error(含没埋 @OperateLog 处、框架/第三方库抛的异常栈),补上 operation_log 没盖到的根因。
  • 排障姿势:operation_log 找 result=失败 那条定位哪一步坏 → 用同 trace_id 查 system_log 拿那一刻底层异常堆栈
  • 三个边界(否则串不起来/查空)
    1. 得有 traceId:链路入口须挂 @BuildTraceId(或上游已透传);否则两表 trace_id 皆空,无法 join。
    2. system_log 只有 ERROR(LoggerDBAppenderLevelFilter level=error):流程全程成功时 system_log 一条都没有,只查 operation_log——它只在出错时补充价值。
    3. C# 段只有 operation_log:system_log 是纯 Java logback 落库,C# 端不写;跨端流程里 C# 的腿只能靠 operation_log,Java 的腿才两表都有。

8. Java 端方案(复用现有框架,改动小)

  • 现状aivfo-log-spring-boot 已有 @OperateLog 注解 + OperateLogAspect 切面 + traceId/parentId + LoggerDBAppender(随 logback enable=true 开启、LevelFilter 只记 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 的服务不受影响)。
    • Kafka 实现独立成新 starter 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 正确。

9. C# 端方案(主要工作量)

  • 组件化(关键):做一个可复用类库 Aivfo.OperationLog(对称 Java 的 aivfo-log-spring-boot)——封装:异步内存队列、发 Kafka、trace_id 生成与透传、配置开关、脱敏、容错兜底、调试级本地文件。
    • 现有 operate/front 引用它以后新 C# 项目引用同一个库 + 一段标准配置即具备完整日志能力("新项目要加"= 引一个包)。
  • 采集
    • 关键边界统一封装:HTTP 调用(HttpHelper)、命令入口(按钮/RelayCommand)、串口/相机调用、对焦流程 —— 边界包一层自动记录。
    • 重点方法手动埋点补充,达成 §2 的"操作全埋"。
  • 异步管道:业务线程 →(非阻塞入队)内存队列 → 后台线程批量发 Kafka。队列有上限,满了降级(丢弃低级别 / 落本地兜底)。
  • 序列化安全:序列化 input/output 全程 try 兜底(防大对象/循环引用抛异常),记日志绝不拖垮业务
  • 收编现有日志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(门面)、OperationScopeOperationLogContext(traceId/parentId/operator/houseSn/wellSn 用 AsyncLocal 跨 async/线程透传 + BeginScope 父子链)、OperationLogPipeline(有界 System.Threading.ChannelsDropWrite 非阻塞入队,后台单线程批量发送)、KafkaOplogTransportIOplogTransport 抽象 + Confluent.Kafka 实现)、LocalFileWriter(调试级 + 兜底 + 自身错误,按天滚动)、SafeSerializer(循环引用忽略、大对象/byte[]/Stream 只记类型+长度、超 4000 字截断、全 try)、OperationLogOptions(全局/模块级 开关+级别,运行时可改)。
    • APIOperationLogger.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 取。
    • Kafka 发送:复用 control 已用的 Confluent.Kafka 2.1.1(+ librdkafka.redist 2.1.1),NuGet 离线缓存命中、还原通过ProducerBuilder<Null,string> 发 UTF-8 JSON 到 tl-oplog真发 Kafka 成功(非降级)。
    • 两级日志:操作级(Info)发 Kafka 入库;调试级(Debug)只写本地文件、不入 Kafka(除非该模块 MinLevel 显式调到 Debug)。Kafka 门槛与"模块启用"解耦:模块关闭则连本地都不写。
    • 可靠性:入队非阻塞/不抛;队列满或 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 条调试级正确只落本地文件未入库。验证后已删除测试数据。
    • operate/front 未改动(组件独立、二者工程零改动,编译不受影响)。全埋(铺点)属 P3b 后续,本期不做
  • P3b 已落地(2026-06-18)—— operate 接入组件 + 关键边界统一封装埋点(铺开第一步)

    • 引用组件ivf_tl_Operateivf_tl_Entity 各加 ProjectReference → ..\..\Aivfo.OperationLog(组件 net6.0 与两工程 net6.0-windows 兼容)。Entity 含串口(ComBin/Channel)/相机(Camera)边界故须直接引用;Services 经 Entity 传递可见组件类型。
    • 启动 InitApp.xaml.csApp_StartupOperationLogger.Init(project="operate", KafkaBootstrapServers=App.config kfkaIP:kfkaPort(当前 127.0.0.1:9092), Topic="tl-oplog"),全 try 兜底,初始化失败不影响启动。
    • operator 来源AppData.AppDataInit(HttpHelper 构造 + 登录用户来源处)登录成功后 SetOperationLogContext()OperationLogContext.Operator = CurrentUserInfo.username(无则 account),并设 OperationLogger.Options.TlSn = TlSn 作设备默认。AsyncLocal 不跨 UI 事件传播,故另存静态 AppData.CurrentOperator 作全局默认备用。
    • 关键边界统一封装埋点(覆盖面大),module 列表:
    • HTTPivf_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 自动捕异常。挑关键几个,非每个。
    • traceId 透传(C#→Java 链路串联):HttpHelper 原本每次 Guid.NewGuid() 生成 traceId 写入请求 header "traceId";改为取 OperationLogContext.TraceId(无则 NewTraceId()),HttpClientSendAsyncHttpClientSendAsyncStream 两条 HTTP 路径同步对齐。header 名 "traceId" 与 Java 网关读取完全一致:核对 BasicConstant.TRACE_ID = "traceId",servlet TraceInterceptor 与 reactive TraceFiltergetHeader("traceId"),缺省才新建——无待对齐项
    • 兜底:所有埋点调用 try 包裹,绝不因记日志抛异常影响业务(组件本身已兜底,调用处再保一层)。
    • 编译验证:每加一类边界后 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 透传机制可用)。验证后已删除测试数据 + 探针。
    • 剩余(记为后续):① 逐方法手动埋点(§9"重点方法手动埋点",达成完整"操作全埋",属后续规约);② front 工程接入;③ Java 端逐 controller 铺 @OperateLog 注解;④ 真机验证(operate GUI 跑起来看实时日志,需用户在现场)。
  • M8-Pjava 已落地(2026-06-18)—— Java 端关键微服务接入 + 真实链路自测(铺开第一步)

    • 接入服务:全仓 grep aivfo-kafka-spring-boot-starteraivfo-data-transmission(收图/串口服务)有 kafka;aivof-tl-controlaivfo-business-manage 均无 kafka,本期不接(无 ProducerNormal 即使引 starter 也不装配,留待其加 kafka 后或改用其它发送通道)。aivfo-data-transmission-lanucher/pom.xmlaivfo-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
    • 自测(真实 HTTP 请求触发切面,非 standalone 探针):重启 data-transmission(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() 取得真实值。
    • 结论:相比 P2 standalone(operator 仅匿名兜底、traceId 走探针构造),真实微服务链路下 operator 被正确填充为真实登录用户、trace_id 为真实请求链路值——验证 P2 机制在真实服务里端到端有效。module/operation 中文经 utf8mb4 正确存储,project=aivfo-data-transmission、elapsed_ms 计时正确、result=success。KafkaOperationLogSender 启动日志确认装配(OplogClientAutoConfiguration topic=tl-oplog)。验证后已删除测试数据(DELETE WHERE project='aivfo-data-transmission')。
    • 踩坑:① 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 路径)启动稳定。
    • 剩余:① data-transmission 其余 controller/service 方法逐方法埋点(本期仅 4 个代表性入口);② aivof-tl-control / aivfo-business-manage 等无 kafka 的服务,待其引入 kafka 后再接 aivfo-oplog-client,或评估非 kafka 发送通道;③ 业务 service 层(非 controller 入口)关键方法埋点。

10. 可配置(运行时开关,集中可调、热生效)

  • 模块级开关 + 级别:每个模块可配 开/关/详略;全局级别(INFO 操作级 / DEBUG 调试级)。
  • 按设备过滤:调试级可按舱/按 well/按设备 SN 过滤开启(只开 3 号舱 debug,不被其它舱噪声淹没)。
  • 集中管理:日志微服务集中配置 + 各端读取;改配置即生效、不用改代码重编。
  • 平时只开在测模块(日志干净、量可控);出问题临时调高某模块重现一次拿全量细节;上线默认精简、排障远程调高。
  • 注:配置控制的是"已埋的点要不要记/记多细",不能替代先把点埋进去(尤其 C# 无全局 AOP,须先埋后配)。

11. 可靠性与降级

  • 启动阶段(连不上 Kafka):写本地启动日志。
  • 运行期 Kafka 发不出去:本地文件兜底(恢复后可选补送)。
  • 日志失败不影响业务:全 try 包裹、入队非阻塞、队列有上限。
  • 不丢(业务诉求):靠 Kafka 持久化缓冲 + 日志微服务消费;本地兜底防极端。

12. 性能

  • 业务线程只做"非阻塞入队",序列化与发送在后台线程;业务无感
  • 操作级粒度(非逐帧逐字节),量可控;高频底层走调试级本地文件、默认关。
  • Kafka 削峰 + 日志微服务集中控写库压力。

13. 开发规约(强制)

以后新写的代码(C# 和 Java)都必须加操作日志埋点。

  • Java:关键 Controller/Service 方法加 @OperateLog(module/operation 可读)。
  • C#:新功能的命令入口、外部调用(HTTP/串口/相机/DB)、关键业务方法,必须经 Aivfo.OperationLog 组件埋点。
  • 新增 C# 项目:引用 Aivfo.OperationLog 库 + 标准配置。
  • Code Review 检查项:有业务操作但无对应日志埋点的,不予合并。

14. 落地里程碑(M8 · 全量操作日志)

  1. 机制先行log 库建 operation_log 表 → 日志微服务 aivfo-oplog(消费 Kafka 入库 + 保留期清理)→ Kafka topic tl-oplog → 统一 schema → Java 端操作日志切面发 Kafka → C# 端 Aivfo.OperationLog 组件。
  2. C# 全埋(一次性):operate + front 两项目按"操作全埋"口径铺点 + 调试级通道 + 配置开关。
  3. Java 铺注解:各微服务关键方法加 @OperateLog
  4. 验证:见 §15。

15. 验证项(纳入待验证清单)

  • 操作级日志经 Kafka 入 operation_log,字段完整(谁/操作/输入/输出/结果/耗时)。
  • 同一操作的 C#→Java 多条日志 trace_id 一致、可拉成时间线。
  • trace_id 可 join operation_logsystem_log
  • 调试级按模块/按舱热开 → 本地文件出详细收发;调回即停。
  • 配置改模块开关/级别热生效、不重编。
  • 日志微服务/库临时不可用时业务不受影响、恢复后不丢。
  • 保留期到期自动清理。
  • 性能:高频路径开操作级时业务无明显延迟。

16. 决策记录(ADR 摘要)

# 决策 理由
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 开发规约强制新代码埋点 保证长期一致、不退化