如何在Unity中配置log4net实现基于来源的上下文特定日志捕获
解决Unity日志上下文动态绑定问题的方案
核心思路是通过解析调用栈定位日志的实际来源类/方法,为每个来源生成对应log4net Logger实例,同时保持对Unity标准Debug.Log系列方法的兼容。
1. 从调用栈中提取真实日志来源
Unity的Application.logMessageReceivedThreaded回调会传入日志的调用栈字符串,我们可以解析这个字符串,过滤掉Unity内部类和LoggerService自身的调用帧,找到实际触发日志的类:
private void OnLogMessageReceivedThreaded(string logString, string stackTrace, LogType logType) { var stackFrames = new StackTrace(stackTrace, true).GetFrames(); foreach (var frame in stackFrames) { var method = frame.GetMethod(); var declaringClass = method.DeclaringType; // 跳过Unity内部类和当前Logger服务类 if (declaringClass?.Namespace != null && !declaringClass.Namespace.StartsWith("UnityEngine") && declaringClass != typeof(LoggerService)) { // 用类的全名作为Logger名称,确保上下文唯一 var logger = LogManager.GetLogger(declaringClass.FullName); // 映射Unity日志类型到log4net级别 switch (logType) { case LogType.Log: logger.Info(logString); break; case LogType.Warning: logger.Warn(logString); break; case LogType.Error: logger.Error(logString); break; case LogType.Exception: logger.Fatal(logString); break; case LogType.Assert: logger.Debug(logString); break; } break; } } }
2. 优化性能避免重复开销
频繁创建StackTrace和Logger实例会有性能损耗,加个缓存就能解决:
// 缓存已创建的Logger,避免重复调用LogManager.GetLogger private static readonly Dictionary<string, ILog> _loggerCache = new Dictionary<string, ILog>(); // 预定义需要过滤的命名空间,减少判断逻辑 private static readonly HashSet<string> _filteredNamespaces = new HashSet<string> { "UnityEngine", "UnityEditor", typeof(LoggerService).Namespace }; private ILog GetTargetLogger(string stackTrace) { var stackFrames = new StackTrace(stackTrace, true).GetFrames(); foreach (var frame in stackFrames) { var declaringClass = frame.GetMethod().DeclaringType; if (declaringClass == null) continue; var className = declaringClass.FullName; // 跳过过滤名单里的类 if (_filteredNamespaces.Any(ns => className.StartsWith(ns))) continue; // 缓存命中直接返回,否则创建新Logger并存入缓存 if (!_loggerCache.TryGetValue(className, out var logger)) { logger = LogManager.GetLogger(className); _loggerCache[className] = logger; } return logger; } // 兜底:找不到有效来源时用默认Logger return LogManager.GetLogger("LoggerService"); }
3. 配置log4net输出上下文信息
在你的log4net配置文件里,修改PatternLayout,加入%logger字段就能输出类全名,还可以加%method显示方法名(需要调用栈支持):
<layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread] %-5level %logger - %message %method%newline" /> </layout>
4. 处理特殊场景注意事项
- 异步代码日志:异步方法的调用栈可能丢失原始上下文,要是遇到这种情况,可以给开发者提供扩展方法手动指定上下文,但尽量别改他们原来用
Debug.Log的习惯 - IL2CPP编译:IL2CPP会优化调用栈,要在PlayerSettings里开启
Enable Script Debugging,Release模式下也得保留必要的调试信息 - 性能敏感场景:如果是对性能要求极高的游戏,可以考虑用编译期注入(类似AOP的方式)来提前绑定日志上下文,避免运行时解析调用栈的开销
内容的提问来源于stack exchange,提问作者user2204328
相关产品推荐
相关产品推荐

