log4net自定义ILog中间类日志行号显示为0问题求助
解决自定义LoggerManager日志行号显示为0的问题
问题根源
日志框架默认会抓取直接调用日志方法的代码位置,也就是你的LoggerManager.Print方法所在行,但你需要的是上层UsingClass.Print的调用行号。虽然堆栈追踪里能看到正确行号,但框架没自动定位到那一层,所以显示0。
解决方案
方案1:手动遍历堆栈帧获取调用方信息
在LoggerManager的Print方法里,通过StackTrace跳过当前类的方法,找到第一个外部调用的堆栈帧,提取行号后传入日志方法:
using System.Diagnostics; public class LoggerManager : ILog { public void Print(object message) { var stackTrace = new StackTrace(true); // true表示捕获文件名和行号 StackFrame callerFrame = null; // 遍历堆栈帧,跳过LoggerManager自身的方法 foreach (StackFrame frame in stackTrace.GetFrames()) { var declaringType = frame.GetMethod()?.DeclaringType; if (declaringType != null && declaringType != typeof(LoggerManager)) { callerFrame = frame; break; } } int lineNumber = callerFrame?.GetFileLineNumber() ?? 0; Type callerType = callerFrame?.GetMethod()?.DeclaringType ?? typeof(UsingClass); // 将调用方类型和行号传入日志方法 Log.Debug($"[{callerType.Name}] {message}:{lineNumber}"); } }
方案2:利用编译器特性自动注入调用方信息
使用.NET的CallerLineNumber、CallerFilePath等特性,让编译器自动把调用方的行号、文件名等信息注入到方法参数中,无需手动遍历堆栈:
using System.Runtime.CompilerServices; public class LoggerManager : ILog { // 添加Caller相关特性,编译器会自动填充参数值 public void Print(object message, [CallerLineNumber] int lineNumber = 0, [CallerMemberName] string callerMethod = "", [CallerFilePath] string callerFile = "") { // 获取调用方类型 var callerType = new StackTrace().GetFrame(1).GetMethod().DeclaringType; // 输出日志时带上行号 Log.Debug($"[{callerType.Name}] {message}:{lineNumber}"); } }
调用时无需额外传参,编译器会自动处理:
// UsingClass中的调用,直接传消息即可 loggerManager.Print("Hello");
关键注意事项
- 确保项目编译时启用调试符号生成(生成PDB文件):在项目属性的“生成”选项卡中,勾选“调试信息”为“完整”或“pdb-only”,否则无法获取行号信息。
- 如果使用第三方日志框架(如NLog、Log4net),部分框架支持配置忽略中间类,让框架自动跳过
LoggerManager直接获取调用方信息。比如NLog可以在配置中添加skipFrames="1"来跳过当前方法层。
内容的提问来源于stack exchange,提问作者Omer Somekh
相关产品推荐
相关产品推荐

