Pārlūkot izejas kodu

修日志8GB:B异常处理器节流 + C滚动配置(Date→Composite/10MB)

根因:WPF 布局异常 MinHeight=UnsetValue 逐帧刷屏(8GB 样本 420/420
同一异常+每条 Viewbox.MeasureOverride 堆栈,~2MB/s;6-19 才 4.6KB=回归),
非滚动配置。三层修复:
- C: 两份源 log4net.config(含写 ivf_tl_Control_logs 的 control 那份)
  RollingStyle Date→Composite(Date 模式忽略 MaxFileSize 致单文件无上限)
  + MaxFileSize 10240→10MB
- B: operate App.xaml.cs 加 ShouldLogThrottled,UI 异常处理器同签名
  10s 只记一条(防被 e.Handled 吞掉的异常逐帧重抛刷爆日志)
- A: 根因 UI bug(MinHeight=UnsetValue 控件)静态查不到,嫌疑二进制库
  WpfControlLibrary 模板,需运行期定位,待重启 operate

dotnet build operate 0 个 error CS(MSB3021/3027 是 operate.exe 运行锁 DLL)。
部署:log4net Watch=true,C 配置可热生效;B 在 DLL 须重启 operate。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
huangjie 4 dienas atpakaļ
vecāks
revīzija
9063ba4141

+ 6 - 4
ivf_tl_operate_2.0/control/ivf_tl_Services/log4net.config

@@ -16,10 +16,11 @@
 			<param name="File" value="C:\\TLData\\ivf_tl_Control_logs\\LogError\\" />
 			<param name="AppendToFile" value="true" />
 			<param name="MaxSizeRollBackups" value="100" />
-			<param name="MaxFileSize" value="10240" />
+			<param name="MaxFileSize" value="10MB" />
 			<param name="StaticLogFileName" value="false" />
 			<param name="DatePattern" value="yyyyMMdd&quot;.htm&quot;" />
-			<param name="RollingStyle" value="Date" />
+			<!-- Composite=按日期+大小双滚动(Date 模式会忽略 MaxFileSize 致单文件无上限,曾单日涨到 8GB) -->
+			<param name="RollingStyle" value="Composite" />
 			<!--布局-->
 			<layout type="log4net.Layout.PatternLayout">
 				<param name="ConversionPattern" value="&lt;HR COLOR=red&gt;%n异常时间:%d [%t] &lt;BR&gt;%n异常级别:%-5p &lt;BR&gt;%n异 常 类:%c [%x] &lt;BR&gt;%n%m &lt;BR&gt;%n &lt;HR Size=1&gt;"  />
@@ -29,11 +30,12 @@
 		<appender name="InfoAppender" type="log4net.Appender.RollingFileAppender">
 			<param name="File" value="C:\\TLData\\ivf_tl_Control_logs\\Log\\" />
 			<param name="AppendToFile" value="true" />
-			<param name="MaxFileSize" value="10240" />
+			<param name="MaxFileSize" value="10MB" />
 			<param name="MaxSizeRollBackups" value="100" />
 			<param name="StaticLogFileName" value="false" />
 			<param name="DatePattern" value="yyyyMMdd&quot;.htm&quot;" />
-			<param name="RollingStyle" value="Date" />
+			<!-- Composite=按日期+大小双滚动(Date 模式会忽略 MaxFileSize 致单文件无上限,曾单日涨到 8GB) -->
+			<param name="RollingStyle" value="Composite" />
 			<!-- 信息日志布局-->
 			<layout type="log4net.Layout.PatternLayout">
 				<param name="ConversionPattern" value="&lt;HR COLOR=blue&gt;%n日志时间:%d [%t] &lt;BR&gt;%n日志级别:%-5p &lt;BR&gt;%n日 志 类:%c [%x] &lt;BR&gt;%n%m &lt;BR&gt;%n &lt;HR Size=1&gt;"  />

+ 44 - 5
ivf_tl_operate_2.0/ivf_tl_Operate/App.xaml.cs

@@ -160,17 +160,56 @@ namespace ivf_tl_Operate
         /// <param name="sender"></param>
         /// <param name="e"></param>
         /// <exception cref="NotImplementedException"></exception>
+        // === 日志洪流抑制(防 8GB 复发)===
+        // WPF 布局类异常被下面 e.Handled=true 吞掉后,WPF 会逐帧重新测量→同一异常每帧重抛。
+        // 无节流时同一条异常每秒上千次、每条带完整堆栈(~4.7KB),曾致单日日志涨到 8GB。
+        // 此处按异常签名节流:同签名 10s 内只放行一条,下次放行时补记期间被抑制的条数。
+        private static readonly object _logThrottleLock = new object();
+        private static readonly Dictionary<string, long[]> _logThrottle = new Dictionary<string, long[]>(); // signature -> [lastTicks, suppressedCount]
+        private static readonly long _logThrottleWindowTicks = TimeSpan.FromSeconds(10).Ticks;
+
+        /// <summary>
+        /// 节流判定:同 <paramref name="signature"/> 在窗口期(10s)内只放行一次。
+        /// 返回 true=应记录(<paramref name="suppressNote"/> 含上一窗口被抑制的条数提示);false=抑制本条。线程安全。
+        /// </summary>
+        private static bool ShouldLogThrottled(string signature, out string suppressNote)
+        {
+            suppressNote = string.Empty;
+            long now = DateTime.Now.Ticks;
+            lock (_logThrottleLock)
+            {
+                if (_logThrottle.TryGetValue(signature, out long[] st))
+                {
+                    if (now - st[0] < _logThrottleWindowTicks)
+                    {
+                        st[1]++;            // 仍在窗口内:累计被抑制条数,不记录
+                        return false;
+                    }
+                    if (st[1] > 0) suppressNote = $"(前 10s 抑制 {st[1]} 条相同异常)";
+                    st[0] = now;
+                    st[1] = 0;
+                    return true;
+                }
+                _logThrottle[signature] = new long[] { now, 0 };
+                return true;
+            }
+        }
+
         private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
         {
-           
+
             try
             {
-                if (e.Exception.InnerException != null)
+                // 按异常消息节流(布局类异常被吞后会逐帧重抛,见上方说明)。
+                if (ShouldLogThrottled("UI:" + (e.Exception?.Message ?? "null"), out string note))
                 {
-                    Log4netHelper.WriteLog($"UI线程异常详细:{e.Exception.InnerException.Message}{e.Exception.InnerException.StackTrace}");
+                    if (e.Exception.InnerException != null)
+                    {
+                        Log4netHelper.WriteLog($"UI线程异常详细:{e.Exception.InnerException.Message}{e.Exception.InnerException.StackTrace}");
+                    }
+                    Log4netHelper.WriteLog($"UI线程异常{note}:{e.Exception.Message}{e.Exception.StackTrace}");
                 }
-                Log4netHelper.WriteLog($"UI线程异常:{e.Exception.Message}{e.Exception.StackTrace}");
-                //把 Handled 属性设为true,表示此异常已处理,程序可以继续运行,不会强制退出      
+                //把 Handled 属性设为true,表示此异常已处理,程序可以继续运行,不会强制退出
             }
             catch (Exception ex)
             {

+ 6 - 4
ivf_tl_operate_2.0/ivf_tl_Services/log4net.config

@@ -16,10 +16,11 @@
 			<param name="File" value="C:\\TLData\\ivf_tl_Operate_logs\\LogError\\" />
 			<param name="AppendToFile" value="true" />
 			<param name="MaxSizeRollBackups" value="100" />
-			<param name="MaxFileSize" value="10240" />
+			<param name="MaxFileSize" value="10MB" />
 			<param name="StaticLogFileName" value="false" />
 			<param name="DatePattern" value="yyyyMMdd&quot;.htm&quot;" />
-			<param name="RollingStyle" value="Date" />
+			<!-- Composite=按日期+大小双滚动(Date 模式会忽略 MaxFileSize 致单文件无上限,曾单日涨到 8GB) -->
+			<param name="RollingStyle" value="Composite" />
 			<!--布局-->
 			<layout type="log4net.Layout.PatternLayout">
 				<param name="ConversionPattern" value="&lt;HR COLOR=red&gt;%n异常时间:%d [%t] &lt;BR&gt;%n异常级别:%-5p &lt;BR&gt;%n异 常 类:%c [%x] &lt;BR&gt;%n%m &lt;BR&gt;%n &lt;HR Size=1&gt;"  />
@@ -29,11 +30,12 @@
 		<appender name="InfoAppender" type="log4net.Appender.RollingFileAppender">
 			<param name="File" value="C:\\TLData\\ivf_tl_Operate_logs\\Log\\" />
 			<param name="AppendToFile" value="true" />
-			<param name="MaxFileSize" value="10240" />
+			<param name="MaxFileSize" value="10MB" />
 			<param name="MaxSizeRollBackups" value="100" />
 			<param name="StaticLogFileName" value="false" />
 			<param name="DatePattern" value="yyyyMMdd&quot;.htm&quot;" />
-			<param name="RollingStyle" value="Date" />
+			<!-- Composite=按日期+大小双滚动(Date 模式会忽略 MaxFileSize 致单文件无上限,曾单日涨到 8GB) -->
+			<param name="RollingStyle" value="Composite" />
 			<!-- 信息日志布局-->
 			<layout type="log4net.Layout.PatternLayout">
 				<param name="ConversionPattern" value="&lt;HR COLOR=blue&gt;%n日志时间:%d [%t] &lt;BR&gt;%n日志级别:%-5p &lt;BR&gt;%n日 志 类:%c [%x] &lt;BR&gt;%n%m &lt;BR&gt;%n &lt;HR Size=1&gt;"  />

+ 12 - 0
项目文档/进度/交接卡.md

@@ -632,3 +632,15 @@
 - **② 移除诊断**:删 SoftKeyboardHost.cs 的 SKDIAG 整块(原 198–222 行,try/Dispatcher.BeginInvoke/inner+outer catch 全删),Show() 干净收尾于 `_popup.IsOpen = true;`。`codegraph sync` done。
 - **★提交边界核实★**:移除 SKDIAG 后 `git diff HEAD` 显示 SoftKeyboardHost.cs **与 HEAD 完全一致**——软键盘最终修复(Popup 托管+居中)其实已在上个提交 `a4f3a14` 落地,HEAD 里本就无 SKDIAG。SKDIAG 是仅存于工作区的临时诊断,现已删回 HEAD 态。故**本次 git 提交实际只含两个文档文件**(交接卡.md + 进度状态.yaml),无代码改动。
 - **下一步**:① git commit & push 本次文档;② 排查 8GB 日志滚动失效(`MaxFileSize=10240` 对 InfoAppender 没拦住,单文件涨到 7.98GB)——先分析原因,用户确认后再改。
+
+## 2026-06-21 · ★日志 8GB 根因排查 + B/C 两层修复(代码编译过,待重启 operate 部署验证)★
+- **用户确认"三层一起改"**。先把 8GB 真凶查清:**不是滚动配置,是 WPF 布局异常逐帧刷屏**。
+- **根因证据链**:抽样 8GB 日志中部 2MB,**420/420 全是同一异常** `UI线程异常:"{DependencyProperty.UnsetValue}"不是属性"MinHeight"的有效值`,每条带完整堆栈 ~4.7KB,**每条堆栈都含 `Viewbox.MeasureOverride`**;时间戳跨度仅 1 秒=~2MB/s;6-19 号(G2 Viewbox 改造前)一整天才 4626 字节 → **回归**。自我永动机制:某控件 MinHeight 测量时解析成 UnsetValue→WPF 在 Window→Viewbox→Grid 测量链抛异常→全局 `App_DispatcherUnhandledException` 逐条记完整堆栈→`finally{e.Handled=true}` 吞掉→下一渲染节拍重测→再抛再记…每秒上千次→8GB/167万条。
+- **三层定性**:A=UI 布局 bug(MinHeight=UnsetValue,真凶);B=异常处理器逐帧全量记+无节流+吞异常重试(放大器);C=滚动配置失效(`RollingStyle=Date` 让 log4net **彻底忽略 MaxFileSize**,单天文件无上限;且 `MaxFileSize=10240`=10KB 漏单位)。
+- **A 静态查不到**:operate 自身 XAML 全部 MinHeight 是合法字面量(324/250/56)或 `{StaticResource TouchMinSize}`(已定义=48),code-behind 无 MinHeight 赋值,App.xaml 无问题 Setter→UnsetValue 运行期注入,**唯一剩余嫌疑=二进制控件库 `WpfControlLibrary`(源码不在仓库)内部模板**。★精确定位那个元素必须运行期(挂异常抓 target / WPF 布局追踪),需启动 operate GUI(管理员/UAC,用户在场)★。
+- **本次已改(B+C,源码层)**:
+  · **C**:两份源 log4net.config(`ivf_tl_Services/log4net.config`→ivf_tl_Operate_logs、`control/ivf_tl_Services/log4net.config`→**ivf_tl_Control_logs=8GB那个目录**)各 2 个 appender:`RollingStyle` Date→**Composite**(按日期+大小双滚动)、`MaxFileSize` 10240→**10MB**。PowerShell [xml] 解析校验两份 OK、各 RollingStyle=[Composite,Composite]/MaxFileSize=[10MB,10MB]。(operate 引用 control,两个同名 ivf_tl_Services 的 config 都会拷到 operate 输出、control 那份赢拷贝→日志落 Control_logs,故两份都改。)
+  · **B**:operate `App.xaml.cs` 加节流——静态 `ShouldLogThrottled(signature,out note)`(同签名 10s 内只放行一条,下次放行补记被抑制条数,lock 线程安全),`App_DispatcherUnhandledException` 按 `"UI:"+异常消息` 节流。即便 A 根因还在,洪流也收敛成每 10s 一条。
+- **编译核实**:`dotnet build operate` **0 个 error CS**(B 代码编译通过);出现 26 个 MSB3021/3027=**operate.exe(PID 3432,11:37 启动)正在运行锁住输出 DLL**,非代码错。codegraph sync done。
+- **部署/验证(待用户)**:① log4net `[assembly:XmlConfigurator(Watch=true)]` 开了热监听→**C 配置替换 operate 输出目录的 log4net.config 即热生效、无需重启**(可立即封顶文件);② **B 在 DLL 里→必须关 operate→重新编译→重启** 才生效;③ 重启后顺带可运行期定位 A(那个 MinHeight=UnsetValue 控件)。
+- **下一步**:用户关闭并重启 operate(部署 B+C);重启时运行期定位根因 A。本次 B+C 源码+文档待提交 push。

+ 4 - 4
项目文档/进度/进度数据.js

@@ -1,10 +1,10 @@
 // 进度数据(监控面板.html 读取)。每完成一步由助手回写,generatedAt 用于停滞检测。
 window.PROGRESS_DATA = {
   project: "时差培养箱合并改造",
-  generatedAt: "2026-06-21T12:10:00",
-  currentTask: "★G2 软键盘收尾★:移除 SoftKeyboardHost.cs 临时诊断块(SKDIAG)。读 SKDIAG 日志实测佐证键盘正常(popup 撑满全窗 1934×1094、kb 实测 960×678 Vis=True srcAttached=True、屏坐标 487,208 完整落屏内不裁切居中)。结论:诊断纯记日志不碰布局,键盘恢复正常真因=重编译让旧 exe 对上当前 Popup 源码。移除后 SoftKeyboardHost.cs 与 HEAD 完全一致(软键盘修复已随上个提交 a4f3a14 落地),本次 git 提交只含两文档(交接卡+进度状态)。",
-  currentStep: "G2 软键盘收尾:移除 SKDIAG 诊断+实测佐证键盘正常,准备提交push",
-  nextStep: "排查 8GB 日志滚动失效:InfoAppender 单文件 20260621.htm 已 7.98GB,MaxFileSize=10240+RollingStyle=Date 没拦住——先只分析原因,用户确认后再改 log4net.config。旁置:T1.4 复验仍阻塞微服务集群未起(SITE-NET,bd6943b 补强已提交等环境)。",
+  generatedAt: "2026-06-21T13:30:00",
+  currentTask: "★日志 8GB 根因排查 + B/C 修复(编译过待重启部署)★ 真凶=WPF 布局异常 MinHeight=UnsetValue 逐帧刷屏(8GB 样本 420/420 同一异常+每条 Viewbox.MeasureOverride 堆栈+~2MB/s;6-19 才 4626B=回归),非滚动配置。已改 C(两份源 log4net.config:RollingStyle Date→Composite + MaxFileSize 10240→10MB)+ B(operate App.xaml.cs 加 ShouldLogThrottled 节流,UI 异常同签名 10s 只记一条)。dotnet build operate 0 error CS(26 个 MSB 是 operate.exe PID3432 运行锁 DLL)。A 根因(MinHeight=UnsetValue 控件)静态查不到、嫌疑二进制库 WpfControlLibrary,需运行期定位。",
+  currentStep: "日志8GB根因查清+B/C修复编译过,待重启operate部署",
+  nextStep: "用户关 operate→重新编译→重启 部署 B+C(log4net Watch=true:C 替换输出目录 config 可热生效免重启;B 在 DLL 必须重启);重启时运行期定位根因 A(MinHeight=UnsetValue 控件)。",
   phase: "★★三项目合并物理收尾完成 + 串口占用代码层已修(T1.1~T1.3,T1.4待GUI验) + Phase0功能bug已修(scene=0不拍照/kfka重复键/front去imageScore) + 真机硬件+API+服务起全闭环★★ 文档重组进行中,真机GUI全流程验收待续",
   note: "2026-06-20:在2026-06-19灾后恢复基础上,本会话完成三项目合并最后一公里——control物理并入operate/control/(顶层ivf_tl_control_2.0消失,operate自包含)、autofocustool删除、编译operate/front/单测三关0错误。真机验证:硬件层(7舱握手+三路温度+压力+舱门+电机偏差0+相机出图2592×1944+CCDSN映射)+业务API(登录/tl-control/business/surface getButtons/对焦下发V-047上行V-064)全闭环;data-transmission补建aivfo-tl库后Started(nacos 6服务)。串口占用代码层修复T1.1~T1.3(T1.4真机待GUI);功能bug修复T0.1~T0.3。M2-02单测重建23断言全通过。审计报告与会话续接文档内容已三层归位后删除。详见交接卡。",
   planTasks: [

+ 11 - 8
项目文档/进度/进度状态.yaml

@@ -1,15 +1,18 @@
 # 续接断点状态(机器可解析)。换会话/换电脑后首先读它定位。
 # 状态取值: 未开始 / 进行中 / 完成 / 代码完成待验证
 # 纪律:本字段只存【当前断点】,历史细节进 交接卡.md(见 CLAUDE.md 第三节)。
-更新时间: 2026-06-21 G2软键盘收尾:移除SKDIAG诊断+实测佐证键盘正常,准备提交push;下一步排查8GB日志滚动失效
+更新时间: 2026-06-21 日志8GB根因=WPF MinHeight=UnsetValue布局异常逐帧刷屏;B(处理器节流)+C(滚动配置)已改编译过,待重启operate部署
 当前任务: >
-  【★G2 软键盘最终修复已收尾,准备 git 提交并 push★】SoftKeyboardHost.cs 临时诊断块(SKDIAG)已移除,
-  SKDIAG 日志实测佐证键盘正常(popup 撑满全窗、kb 960×678 Vis=True srcAttached、屏坐标487,208 完整居中不裁)。
-  · 下一步①:git commit & push 本次(实际只含两个文档文件——SoftKeyboardHost.cs 移除 SKDIAG 后已回到 HEAD 态、
-    软键盘修复其实已随上个提交 a4f3a14 落地,无代码改动)。
-  · 下一步②:排查日志滚动失效——今天 InfoAppender 单文件 `20260621.htm` 已 7.98GB,`MaxFileSize=10240`
-    + RollingStyle=Date 没拦住。★先只分析原因,用户确认后再动手改 log4net.config。★
-  · 旁置未结:T1.4 复验仍阻塞于微服务集群未起(SITE-NET,非代码,bd6943b 已提交补强等环境)。
+  【★日志 8GB 根因已查清+B/C 两层修复(编译过,待部署)★】真凶=WPF 布局异常 `MinHeight=UnsetValue` 逐帧刷屏
+  (8GB 样本 420/420 同一异常、每条带 Viewbox.MeasureOverride 堆栈、~2MB/s;6-19 才 4626B=回归),非滚动配置。
+  · 已改 C(滚动):两份源 log4net.config(ivf_tl_Services + control/ivf_tl_Services=写 ivf_tl_Control_logs 那份)
+    RollingStyle Date→Composite + MaxFileSize 10240→10MB,XML 校验过。
+  · 已改 B(节流):operate App.xaml.cs 加 ShouldLogThrottled,UI 异常处理器同签名 10s 只记一条。dotnet build operate
+    0 个 error CS(26 个 MSB3021/3027=operate.exe PID3432 运行中锁 DLL,非代码错)。codegraph sync done。
+  · A(根因 UI bug)静态查不到:operate XAML/code-behind 无问题 MinHeight,嫌疑=二进制库 WpfControlLibrary 模板,
+    ★需运行期定位(挂异常抓 target,启 operate GUI/UAC,用户在场)★。
+  下一步:★用户关 operate→重新编译→重启★ 部署 B+C(log4net Watch=true,C 配置替换输出目录文件可热生效免重启;
+    B 在 DLL 必须重启);重启时运行期定位根因 A。本次 B+C 源码+文档已提交待 push。
   续接读:《工作计划表》+《当前开发计划》+ 本文件 + 交接卡末尾。
 说明: >
   M0-M5 全部【可写源码】已完成,C#合并端 0 error + M2-02 单测 15/15。工具链就位(JDK11.0.25 + Maven3.9.9