3

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

Log4Net 示例:

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

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

4

6 回答 6

7

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

我个人不喜欢使用单例记录器,因为您无法在程序的不同部分调整日志记录级别。如果您正在处理多个组件,您可能希望为一个组件打开信息级别的日志记录,但只为其他组件打开警告日志记录(或根本不记录)。使用单例记录器,所有应用程序中的所有日志记录都将处于同一级别。

当您错误地包装 log4net 以及使用单个记录器来覆盖整个应用程序时,您会拒绝使用 log4net 的许多内置(和强大)功能。

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

使用带有 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<string> 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 方法并将包装器的类型作为第一个参数传递。

如果您要编写一个包装器以实现您所询问的功能(在日志调用中延迟执行任何昂贵的代码而不明确检查是否启用了所需的日志级别),那么您不妨把它包装器中的代码,而不是将其实现为扩展方法(这也将遭受与我上面描述的相同的呼叫站点丢失问题)。

祝你好运!

于 2013-10-25T21:46:11.343 回答
4

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

于 2016-08-31T08:34:44.717 回答
3

您可以使用 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<string> formattingCallback )
    {
        if( log.IsDebugEnabled )
        {
            log.Debug( formattingCallback() );
        }
    }
    // .. other methods as required...
}

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

于 2013-10-25T13:39:52.700 回答
3

但也不想在每个日志语句之前检查

当您发现自己一遍又一遍地重复相同的代码时,听起来可能需要一个通用的抽象。例如,在这种情况下,您可以为 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);
    }
}

在这种情况下,我将记录器实例设为静态。我不是100%确定这将始终按预期工作。通常我在依赖注入框架后面使用它,并将记录器依赖配置为单例,由框架处理。您可以改为将其设为具有实例方法的实例类,并将其放在静态工厂类后面。根据需要进行测试和调整。

这里有几个额外的好处:

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

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

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());
}

这会从运行时系统中获得更有意义的调试信息(尽管可能会影响性能,但对于大容量系统来说,值得测试)。所以我的直通错误日志功能可能如下所示:

public void Error(string message, Exception ex)
{
    _log.Error(string.Format("{0}:{1}", GetLocation(), message), ex);
}
于 2013-10-25T13:41:52.793 回答
2

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

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

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

于 2017-07-03T08:15:59.843 回答
-2

我会看看预处理器(预编译?)指令。

#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

于 2013-10-25T13:41:25.790 回答