日志写入方法中执行new DirectoryInfo触发StackOverflowException,如何解决?
问题分析与解决方案
核心原因
触发StackOverflowException的关键是无限递归调用:当writeLogToFile抛出栈溢出异常后,catch块中调用AppExceptionHandler.writeException(sofex)记录异常,若这个异常处理方法内部又调用了writeLogToFile(或间接触发日志写入逻辑),就会形成循环——写异常日志→触发异常→再写异常日志,往复直到栈空间耗尽。
另外,newLogFile中重复调用Monitor.Enter(m_lock)属于冗余操作:writeLogToFile已持有同一锁,虽然Monitor支持线程重入,但这种写法会增加复杂度,甚至可能引发锁泄漏(原代码newLogFile未在finally中释放锁)。
具体修复步骤
1. 切断异常处理的递归链
修改StackOverflowException的捕获逻辑,改用无递归风险的基础方式记录异常,彻底避免循环调用:
catch (StackOverflowException sofex) { try { // 直接写入固定的应急日志文件,不调用当前日志系统 File.AppendAllText(@"C:\emergency_log_error.txt", $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 栈溢出异常: {sofex.Message}\r\n"); // 控制台应用可同时输出到控制台 Console.WriteLine($"栈溢出异常: {sofex.Message}"); } catch { } // 极端情况忽略二次异常,避免问题扩大 }
2. 移除newLogFile中的冗余锁
由于writeLogToFile已持有锁,newLogFile无需再次获取同一锁,同时补充锁释放的finally块(原代码缺失导致锁泄漏):
private void newLogFile() { try { if (m_streamWriter != null) { endLogging(); m_streamWriter.Flush(); m_streamWriter.Close(); m_streamWriter = null; // 显式置空避免后续错误 } m_logFileNameSuffix = getLogFileNewSuffix(); m_streamWriter = File.AppendText(getLogFileName()); startLogging(); } }
3. 提前校验日志目录
在类初始化阶段完成目录校验与创建,避免每次写入时重复执行:
// 类构造函数或初始化方法中添加 public YourLoggerClass() { if (string.IsNullOrWhiteSpace(m_logFileDirectory)) { throw new ArgumentNullException(nameof(m_logFileDirectory), "日志目录路径不能为空"); } // 提前创建目录,不存在则自动创建 Directory.CreateDirectory(m_logFileDirectory); }
关键注意事项
StackOverflowException属于.NET严重异常,抛出时栈空间已极度紧张,此时复杂操作大概率失败,异常处理逻辑必须极简。- 绝对禁止在捕获该异常后调用可能触发递归的方法,尤其是当前的日志写入逻辑。
内容的提问来源于stack exchange,提问作者Sujin Lee
相关产品推荐
相关产品推荐

