如何在log4net日志中添加堆栈跟踪、类及方法名?
解决log4net日志无法显示类/方法名及堆栈跟踪的问题
问题根源
你的封装Logger类直接调用log4net的ILog方法时,log4net默认捕获的是Logger类内部的方法(如Debug/Info),而非业务代码中实际调用日志的位置。同时默认的%M(方法名)、%C(类名)转换符只能识别直接调用log4net API的层级,导致日志显示的是封装类的信息,而非业务代码的调用位置。
解决方案
方案1:修改Logger类,传递真实调用者信息
利用C#的CallerMemberName特性或StackFrame获取业务代码的调用位置,手动注入到log4net的LoggingEvent中:
using System; using System.IO; using System.Reflection; using log4net; using log4net.Core; using System.Runtime.CompilerServices; public class Logger : ILogger { private readonly ILog _logger; private readonly Type _declaringType; public Logger() { _declaringType = MethodBase.GetCurrentMethod()?.DeclaringType ?? typeof(Logger); _logger = LogManager.GetLogger(_declaringType); } public void Debug(string message, [CallerMemberName] string memberName = "", [CallerFilePath] string filePath = "", [CallerLineNumber] int lineNumber = 0) { Log(Level.Debug, message, null, memberName, filePath, lineNumber); } public void Info(string message, [CallerMemberName] string memberName = "", [CallerFilePath] string filePath = "", [CallerLineNumber] int lineNumber = 0) { Log(Level.Info, message, null, memberName, filePath, lineNumber); } public void Error(string message, Exception? ex = null, [CallerMemberName] string memberName = "", [CallerFilePath] string filePath = "", [CallerLineNumber] int lineNumber = 0) { Log(Level.Error, message, ex, memberName, filePath, lineNumber); } public void Warning(string message, [CallerMemberName] string memberName = "", [CallerFilePath] string filePath = "", [CallerLineNumber] int lineNumber = 0) { Log(Level.Warn, message, null, memberName, filePath, lineNumber); } private void Log(Level level, string message, Exception? ex, string memberName, string filePath, int lineNumber) { if (!_logger.IsEnabledFor(level)) return; var log4netLogger = _logger.Logger; var loggingEvent = new LoggingEvent(_declaringType, log4netLogger.Repository, log4netLogger.Name, level, message, ex); // 通过StackFrame获取真实的业务类名和方法名(偏移2:跳过Log和Debug/Info等封装方法) var stackFrame = new StackFrame(2, false); var method = stackFrame.GetMethod(); if (method != null) { loggingEvent.Properties["CallerClass"] = method.DeclaringType?.Name; loggingEvent.Properties["CallerMethod"] = method.Name; } // 注入调用文件和行号 loggingEvent.Properties["CallerFile"] = Path.GetFileName(filePath); loggingEvent.Properties["CallerLine"] = lineNumber; log4netLogger.Log(loggingEvent); } }
方案2:调整log4net配置,输出自定义信息
修改log4net.config中的conversionPattern,使用自定义属性输出真实调用位置,同时添加%exception显示异常堆栈:
<configuration> <log4net> <appender name="Console" type="log4net.Appender.ConsoleAppender"> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%-4thread] %-5level %logger [%property{CallerClass}.%property{CallerMethod} (%property{CallerFile}:%property{CallerLine})] - %message%newline%exception" /> </layout> </appender> <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender"> <file value="Log.txt" /> <appendToFile value="false" /> <rollingStyle value="Size" /> <maxSizeRollBackups value="10" /> <maximumFileSize value="50MB" /> <staticLogFileName value="true" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%-4thread] %-5level %logger [%property{CallerClass}.%property{CallerMethod} (%property{CallerFile}:%property{CallerLine})] - %message%newline%exception" /> </layout> </appender> <root> <level value="INFO" /> <appender-ref ref="Console" /> <appender-ref ref="RollingFileAppender" /> </root> </log4net> </configuration>
简化方案(无需修改代码)
如果不想改动Logger类,可以直接使用%stacktrace转换符并指定偏移量,跳过封装层:
<conversionPattern value="%date [%-4thread] %-5level %logger %stacktrace{2} - %message%newline%exception" />
%stacktrace{2}表示跳过堆栈中的前2层(即Logger类的封装方法),直接显示业务代码的调用位置,输出格式类似:at MyApp.MqttService.Connect() in D:\Projects\MyApp\Services\MqttService.cs:line 45
效果验证
修改后日志输出会显示类似内容:
2022-11-08 10:02:41,161 [1234] WARN Logger [MqttService.Disconnect (MqttService.cs:67)] - Disconnected from the MQTT broker 2022-11-08 10:02:41,161 [1234] ERROR Logger [MqttService.Connect (MqttService.cs:45)] - Failed to connect to the MQTT broker System.Net.Sockets.SocketException: 连接失败 at System.Net.Sockets.TcpClient.Connect(String hostname, Int32 port) at MyApp.MqttService.Connect() in D:\Projects\MyApp\Services\MqttService.cs:line 42
内容的提问来源于stack exchange,提问作者fuba
相关产品推荐
相关产品推荐

