不调试时Log4Net消息的构建成本

根据这篇 log4net文章,您应该检查是否在任何Log.Debug语句之前启用了调试以消除语句构造成本。 是否有更好的替代方法,始终必须在任何日志语句之前检查是否(Log.IsDebugEnabled)?

Log4Net示例:

if (log.IsDebugEnabled) { log.Debug("This is entry number: " + i ); } 

我不想支付语句构造的开销成本,但也不想在每个日志语句之前检查。

但也不想在每个日志声明之前检查

当你发现自己一遍又一遍地重复相同的代码时,听起来像是一个普通的抽象可能是有序的。 在这种情况下,您可以为Log4Net创建自定义包装器。 简单的事情:

 public static class Logger { private static ILog _log; static Logger() { log4net.Config.XmlConfigurator.Configure(); _log = log4net.LogManager.GetLogger("Log4Net"); } public static void Debug(string message) { if (_log.IsDebugEnabled) _log.Debug(message); } public static void Info(string message) { _log.Info(message); } public static void Warn(string message) { _log.Warn(message); } public static void Error(string message) { _log.Error(message); } public static void Error(string message, Exception ex) { _log.Error(message, ex); } public static void Fatal(string message) { _log.Fatal(message); } public static void Fatal(string message, Exception ex) { _log.Fatal(message, ex); } } 

在这种情况下,我使记录器实例静态。 我不是百分百肯定会一直按预期工作。 通常我在dependency injection框架后面使用它,并将logger依赖项配置为单例,由框架处理。 您可以使用实例方法将其设置为实例类,并将其放在静态工厂类之后。 根据需要进行测试和调整。

这里有几个额外的好处:

  1. 您在Log4Net中的依赖关系被隔离到一个类。 因此,如果您想使用其他记录器,则只需更改一个类而不是整个项目中的所有内容。
  2. 您可以轻松地在dependency injection器后面抽象它。
  3. 您希望包含在所有日志记录语句中的任何其他常用function都可以轻松地全局包含在此处。

我通常用于第三个好处的一个例子可能是这样的:

 private static string GetLocation() { var frame = new StackTrace(1).GetFrame(1); var method = frame.GetMethod(); return string.Format("{0}:{1}.{2}({3})", Environment.MachineName, method.ReflectedType.FullName, method.Name, frame.GetFileLineNumber().ToString()); } 

这从运行时系统获得了更有意义的调试信息(尽管可能存在性能损失,对于大批量系统而言,它值得测试)。 所以我的传递错误记录function可能如下所示:

 public void Error(string message, Exception ex) { _log.Error(string.Format("{0}:{1}", GetLocation(), message), ex); } 

@Grhm和@David都有很好的想法,但我不认为大卫的包装是尽可能好的。 以这种方式包装log4net。 只需在包装器上实现Debug,Info等,并将它们委托给log4net的Debug,Info等方法,就可以破解log4net记录调用站点信息的能力。 如果你用这种方式包装并告诉log4net记录呼叫站点信息,log4net将在包装器中写出呼叫站点,而不是实际代码中的呼叫站点,这就是你想要的。

我个人不喜欢使用单例记录器,因为你失去了调整程序不同部分的日志记录级别的能力。 如果您正在处理多个组件,则可能需要为一个组件启用信息级别日志记录,但仅对其他组件发出警告日志(或根本不警告)。 使用单例记录器,所有应用程序中的所有日志记录都将处于同一级别。

当您错误地包装log4net以及使用单个记录器覆盖整个应用程序时,您正在否认自己很多log4net的内置(和强大)function。

我在这里回答了一个类似的问题(关于维护呼叫站点信息):

在使用包含Log4net的包装类时如何记录方法名称

为了节省时间,我在这里包含了一个代码示例(未编译和未经测试,但应该关闭)…

 public class MyLog4NetWrapper { ILog log; public MyLog4NetWrapper(string loggerName) { log = LogManager.GetLogger(loggerName) } public MyLog4NetWrapper(type loggerType) { log = LogManager.GetLogger(loggerType) } public void Info(string message) { if (log.IsInfoEnabled) log.Logger.Log(typeof(MyLog4NetWrapper), LogLevel.Info, message, null); } //Defer expensive calculations unless logging is enabled - thanks Grhm for the example public void Info(Func formattingCallback ) { if(log.IsInfoEnabled) { log.Logger.Log(typeof(MyLog4NetWrapper), LogLevel.Info, formattingCallback(), null); } } //Debug, Warn, Trace, etc are left as an exercise. } 

您可以在代码中创建这些记录器,如下所示:

 public class MyClass { private static readonly ILog log = new MyLoggerWrapper(typeof(MyClass)); public void DoSomething() { log.Info("Hello world!"); } } 

编写保留调用站点信息的log4net包装器的技巧是使用Log方法并将包装器的类型作为第一个参数传递。

如果您要编写一个包装器以实现您询问的function(在日志调用中推迟执行任何昂贵的代码而不显式检查是否启用了所需的日志记录级别),那么您可以将其设置为包装器中的代码而不是将其作为扩展方法实现(它也会遭受我上面描述的相同的呼叫站点丢失问题)。

祝好运!

你可以使用lambda表达式。 喜欢:

 log.Debug(() => "This is entry number:" + i); 

这样,lambda仅在.IsDebugEnabled调用之后进行评估。

我们定义了一个扩展类(取自http://www.beefycode.com/post/Extension-Methods-for-Deferred-Message-Formatting-in-Log4Net.aspx ),其扩展方法如下:

 public static class Log4NetExtensionMethods { public static void Debug( this ILog log, Func formattingCallback ) { if( log.IsDebugEnabled ) { log.Debug( formattingCallback() ); } } // .. other methods as required... } 

我不确定log4net是否在最近的版本中添加了lamda类型支持 – 但这对我有用。

最简单和最干净的方法可能是使用DebugFormat方法,它实际上检查调试级别是否已启用(请参阅log4net的Github-Code )。

如果包含命名空间log4net.Util,则可以在log4net ILog上调用以下扩展方法:

 public static void ErrorExt(this ILog logger, Func callback) 

这只会在启用日志记录错误级别时调用lambda函数。 无需编写自己的扩展方法。 它还通过将创建包装在try catch方法中来防止在构造实际日志消息时创建错误。

我会看一下预处理器(precompile?)指令。

 #if DEBUG {your logging code here} #endif 

这样的东西应该为你做,然后代码只在调试模式下编译。

您还可以在这样的方法上使用Conditional属性:[System.Diagnostics.Conditional(“DEBUG”)] private void YourMethodNameHere(YourMethodSignatureHere)

请查看这个旧问题,了解有关何时/为何/如何使用它们的更多信息。

http://stackoverflow.com/questions/3788605/if-debug-vs-conditionaldebug