【问题标题】:How to know if log4net has logged an error如何知道 log4net 是否记录了错误
【发布时间】:2014-03-12 08:43:14
【问题描述】:

我在控制台应用程序中使用 log4net,在 if/else 结构中多次使用 log.Error("Message to log");
我的应用程序必须返回一个代码,指定在应用程序运行期间是否发生错误(0 → 正常,1 → 至少发生 1 个错误)。
有没有办法询问 log4net 是否记录了错误,例如:

bool b = LogManager.AtLeastOneErrorHasBeenLogged;

【问题讨论】:

  • 为什么不检查日志看看是否抛出了错误?
  • @Nappa,你是什么意思?打开日志文件(例如)并检查是否记录了错误?
  • 首先检查您是否已正确设置。您是否设置了 log4net 配置?您是在 web.config 文件中进行的,还是创建了配置文件?
  • @RicardoAppleton,我设置正确。我在 app.config 文件中设置了 log4net 配置。你有解决我的问题的方法吗?我不明白你的评论的意义。
  • 那只是为了确保它被覆盖。然后我想如果你想要的只是有一个指示错误已被记录的标志,你可以采用 Nicholas Carey 的解决方案,只是更简单:你创建一个带有布尔标志的派生类,当 .Error (...) 被称为。您可能还希望为您的应用程序提供一个通用超类,以便在那里设置您的 log4net,从而在整个应用程序中访问

标签: c# log4net


【解决方案1】:

如果您使用唯一目的是跟踪是否发生错误的自定义附加程序,这似乎很简单。

这个(未经测试的)示例使用仅在日志级别为错误时调用附加程序的技术,使用LevelRangeFilter,但您可以在 Append 方法中轻松检查日志级别(或其他条件),或者在根记录器配置。该示例假设您使用的是 XML 配置而不是编程配置,但可以通过编程方式添加附加程序,以便所有记录器都使用它们。

附加器:

public namespace MyApp
{
    public class ErrorFlagAppender : AppenderSkeleton
    {
        public bool ErrorOccurred { get; set; }

        protected override void Append(LoggingEvent loggingEvent)
        {
            ErrorOccurred = true;
        }
    }
} 

配置:

<appender name="ErrorFlagAppender" type="MyApp.ErrorFlagAppender,MyApp" >
    <filter type="log4net.Filter.LevelRangeFilter">
        <levelMin value="ERROR"/>
        <levelMax value="ERROR"/>
    </filter>
</appender>

<root>
  <appender-ref ref="[your existingappender]" />
  <appender-ref ref="ErrorFlagAppender" />
</root>

然后你可以有一个扩展方法来查看是否发生错误:

public static class LogManagerExtensions
{
    public static bool AtLeastOneErrorHasBeenLogged(this LogManager logManager)
    {
        var flagAppenders = logManager.GetRepository()
                                      .GetAppenders()
                                      .OfType<ErrorFlagAppender>();

        return flagAppenders.Any(f => f. ErrorOccurred);
    }
}

【讨论】:

  • 我真的很喜欢你的解决方案。我想创建自定义附加程序来添加代码以指定是否记录了错误。但是创建一个特定的 appender 会更好。
【解决方案2】:

我认为大多数人会考虑询问 log4net(或人们可能使用的任何日志框架)是否记录了错误是非常奇怪的想法。您可以控制调用 log4net,所以如果发生错误,为什么不自己跟踪。如果您的程序的用户关闭了配置文件的日志记录怎么办?如果在关闭日志记录时发生错误,并且您取决于是否记录了错误,那么您无法知道发生了错误。 Nicholas Carey 建议在你的 catch 块中添加逻辑来跟踪是否发生了异常。即使您不使用异常,您也可以在 if/then/else 逻辑中执行类似的操作:

(请注意,我是借用 Nicholas Carey 的样本作为我回答的基础)

static int Main( string[] argv )
{
  static int errorOccurred = 0;
  try
  {
    log.Info("Begin");
    if (SomeImportantPreconditionExists())
    {
      DoSomeStuff();
      if (DoingSomeStuffWorkedRight())
      {
        Hooray();
      }
      else
      {
        log.Error("DoSomeStuff seems to have failed");
        ErrorOccurred();
      }
    }
    else
    {
      log.Error("SomeImportantPrecondition does not exist");
      ErrorOccurred();
    }
  }
  catch( Exception e )
  {
     log.Error("Exception of some sort", e);
     ErrorOccurred();
  }
  return ErrorStatus();
}

private static void InitializeErrorOccurred()
{
  errorOccurred = 0;
}

private static void ErrorOccurred()
{
  errorOccurred = 1;
}

private static int ErrorStatus()
{
  return errorOccurred;
}

发布了该代码之后,我不得不说,我认为您可能会想出一个比我发布的更强大的解决方案。

我最后的建议是不要让你的程序逻辑依赖于日志框架做(或不做)的事情。一般来说,无论是否启用日志记录,您的程序都应该以相同的方式运行(尽管启用日志记录时会记录消息)。

【讨论】:

  • 感谢您指出我必须将日志与错误系统分开。但是每次记录错误并实施错误系统似乎是浪费时间。也许我应该将它们分组并让日志可选。
【解决方案3】:

我不这么认为...但是使用 装饰器模式 并装饰 ILog 界面来收集这些信息会很容易。问题是实例化记录器的 log4net 标准方法是让每个类通过 LogManager.GetLogger(Type t) 实例化其自己的特定于类的记录器实例。

但是,您不应该只是在您的 Main() 方法中捕获异常,记录它并在条件代码设置为零或一的情况下退出吗?

static int Main( string[] argv )
{
  int cc ;
  try
  {
     ExecApplicationCore( argv ) ;
     cc = 0 ;
  }
  catch( Exception e )
  {
     ILog log = LogManager.GetLogger(this.GetType()) ;
     log.Fatal(e) ;
     cc = 1 ;
  }
  return cc ;
}

这就是例外的意义所在。如果你不能真正处理它,你不应该抓住它:让它冒泡到顶部。

编辑注释:ILog 装饰器和工厂示例:

using log4net;
using log4net.Core;
using log4net.Repository;

public class CountingLogger : ILog , ILogger
{
  public IDictionary<Level , int> Counts { get; private set; }
  private ILog Log4Net { get; set; }

  public CountingLogger( ILog logger )
  {
    this.Log4Net = logger ;
    this.Counts  = this.Log4Net.Logger.Repository.LevelMap.AllLevels.Cast<Level>().ToDictionary( x => x , x => 0 ) ;
    return;
  }
  private void Count( Level level )
  {
    int count;
    bool success = this.Counts.TryGetValue( level , out count );
    this.Counts[level] = ++count;
    return;
  }


  public bool IsDebugEnabled { get { return Log4Net.IsDebugEnabled ; } }
  public bool IsErrorEnabled { get { return Log4Net.IsErrorEnabled ; } }
  public bool IsFatalEnabled { get { return Log4Net.IsFatalEnabled ; } }
  public bool IsInfoEnabled  { get { return Log4Net.IsInfoEnabled  ; } }
  public bool IsWarnEnabled  { get { return Log4Net.IsWarnEnabled  ; } }

  public ILogger Logger { get { return this ; } }

  public void Debug( object message , Exception exception ) { Count( Level.Debug ) ; Log4Net.Debug( message , exception ) ; }
  public void Info(  object message , Exception exception ) { Count( Level.Info  ) ; Log4Net.Info(  message , exception ) ; }
  public void Warn(  object message , Exception exception ) { Count( Level.Warn  ) ; Log4Net.Warn(  message , exception ) ; }
  public void Error( object message , Exception exception ) { Count( Level.Error ) ; Log4Net.Error( message , exception ) ; }
  public void Fatal( object message , Exception exception ) { Count( Level.Fatal ) ; Log4Net.Fatal( message , exception ) ; }

  public void Debug( object message ) { Count( Level.Debug ) ; Log4Net.Debug( message ) ; }
  public void Info(  object message ) { Count( Level.Info  ) ; Log4Net.Info(  message ) ; }
  public void Warn(  object message ) { Count( Level.Warn  ) ; Log4Net.Warn(  message ) ; }
  public void Error( object message ) { Count( Level.Error ) ; Log4Net.Error( message ) ; }
  public void Fatal( object message ) { Count( Level.Fatal ) ; Log4Net.Fatal( message ) ; }

  public void DebugFormat( IFormatProvider provider , string format , params object[] args ) { Count( Level.Debug ) ; Log4Net.DebugFormat( provider , format , args ) ; }
  public void InfoFormat(  IFormatProvider provider , string format , params object[] args ) { Count( Level.Info  ) ; Log4Net.InfoFormat(  provider , format , args ) ; }
  public void WarnFormat(  IFormatProvider provider , string format , params object[] args ) { Count( Level.Warn  ) ; Log4Net.WarnFormat(  provider , format , args ) ; }
  public void ErrorFormat( IFormatProvider provider , string format , params object[] args ) { Count( Level.Error ) ; Log4Net.ErrorFormat( provider , format , args ) ; }
  public void FatalFormat( IFormatProvider provider , string format , params object[] args ) { Count( Level.Fatal ) ; Log4Net.FatalFormat( provider , format , args ) ; }

  public void DebugFormat( string format , object arg0 , object arg1 , object arg2 ) { Count( Level.Debug ) ; Log4Net.DebugFormat( format , arg0 , arg1 , arg2 ) ; }
  public void InfoFormat(  string format , object arg0 , object arg1 , object arg2 ) { Count( Level.Info  ) ; Log4Net.InfoFormat(  format , arg0 , arg1 , arg2 ) ; }
  public void WarnFormat(  string format , object arg0 , object arg1 , object arg2 ) { Count( Level.Warn  ) ; Log4Net.WarnFormat(  format , arg0 , arg1 , arg2 ) ; }
  public void ErrorFormat( string format , object arg0 , object arg1 , object arg2 ) { Count( Level.Error ) ; Log4Net.ErrorFormat( format , arg0 , arg1 , arg2 ) ; }
  public void FatalFormat( string format , object arg0 , object arg1 , object arg2 ) { Count( Level.Fatal ) ; Log4Net.FatalFormat( format , arg0 , arg1 , arg2 ) ; }

  public void DebugFormat( string format , object arg0 , object arg1 ) { Count( Level.Debug ) ; Log4Net.DebugFormat( format , arg0 , arg1 ) ; }
  public void InfoFormat(  string format , object arg0 , object arg1 ) { Count( Level.Info  ) ; Log4Net.InfoFormat(  format , arg0 , arg1 ) ; }
  public void WarnFormat(  string format , object arg0 , object arg1 ) { Count( Level.Warn  ) ; Log4Net.WarnFormat(  format , arg0 , arg1 ) ; }
  public void ErrorFormat( string format , object arg0 , object arg1 ) { Count( Level.Error ) ; Log4Net.ErrorFormat( format , arg0 , arg1 ) ; }
  public void FatalFormat( string format , object arg0 , object arg1 ) { Count( Level.Fatal ) ; Log4Net.FatalFormat( format , arg0 , arg1 ) ; }

  public void DebugFormat( string format , object arg0 ) { Count( Level.Debug ) ; Log4Net.DebugFormat( format , arg0 ) ; }
  public void InfoFormat(  string format , object arg0 ) { Count( Level.Info  ) ; Log4Net.InfoFormat(  format , arg0 ) ; }
  public void WarnFormat(  string format , object arg0 ) { Count( Level.Warn  ) ; Log4Net.WarnFormat(  format , arg0 ) ; }
  public void ErrorFormat( string format , object arg0 ) { Count( Level.Error ) ; Log4Net.ErrorFormat( format , arg0 ) ; }
  public void FatalFormat( string format , object arg0 ) { Count( Level.Fatal ) ; Log4Net.FatalFormat( format , arg0 ) ; }

  public void DebugFormat( string format , params object[] args ) { Count( Level.Debug ) ; Log4Net.DebugFormat( format , args ) ; }
  public void InfoFormat(  string format , params object[] args ) { Count( Level.Info  ) ; Log4Net.InfoFormat(  format , args ) ; }
  public void WarnFormat(  string format , params object[] args ) { Count( Level.Warn  ) ; Log4Net.WarnFormat(  format , args ) ; }
  public void ErrorFormat( string format , params object[] args ) { Count( Level.Error ) ; Log4Net.ErrorFormat( format , args ) ; }
  public void FatalFormat( string format , params object[] args ) { Count( Level.Fatal ) ; Log4Net.FatalFormat( format , args ) ; }

  #region ILogger implementation

  bool ILogger.IsEnabledFor( Level level )
  {
    return this.Log4Net.Logger.IsEnabledFor( level ) ;
  }

  void ILogger.Log( LoggingEvent logEvent )
  {
    Count( logEvent.Level ) ;
    this.Log4Net.Logger.Log( logEvent ) ;
    return ;
  }

  void ILogger.Log( Type callerStackBoundaryDeclaringType , Level level , object message , Exception exception )
  {
    Count( level ) ;
    this.Log4Net.Logger.Log( callerStackBoundaryDeclaringType , level , message , exception ) ;
    return ;
  }

  string            ILogger.Name       { get { return this.Log4Net.Logger.Name       ; } }
  ILoggerRepository ILogger.Repository { get { return this.Log4Net.Logger.Repository ; } }

  #endregion

}

static class LoggerFactory
{

  public static ILog GetLogger( string name )
  {
    ILog log             = LogManager.GetLogger( name );
    ILog decoratedLogger = new CountingLogger( log );
    return decoratedLogger;
  }
  public static ILog GetLogger( Type type )
  {
    ILog log             = LogManager.GetLogger( type );
    ILog decoratedLogger = new CountingLogger( log );
    return decoratedLogger;
  }

  public static ILog GetLogger( string repository , string name )
  {
    ILog log             = LogManager.GetLogger( repository , name );
    ILog decoratedLogger = new CountingLogger( log );
    return decoratedLogger;
  }
  public static ILog GetLogger( string repository , Type type )
  {
    ILog log             = LogManager.GetLogger( repository , type );
    ILog decoratedLogger = new CountingLogger( log );
    return decoratedLogger;
  }

  public static ILog GetLogger( Assembly repositoryAssembly , string name )
  {
    ILog log             = LogManager.GetLogger( repositoryAssembly , name );
    ILog decoratedLogger = new CountingLogger( log );
    return decoratedLogger;
  }
  public static ILog GetLogger( Assembly repositoryAssembly , Type type )
  {
    ILog log             = LogManager.GetLogger( repositoryAssembly , type );
    ILog decoratedLogger = new CountingLogger( log );
    return decoratedLogger;
  }

}

【讨论】:

  • 装饰器模式似乎是一个解决方案。你有 log4net 的明确例子吗?
  • 关于您的 try/catch 解决方案,它在我的应用程序中不起作用,因为我不抛出异常。如果发生错误,我不想停止该过程,我只想记录它。
  • 好主意(尽管我仍然认为 OP 最好不使用他的记录器来跟踪是否没有发生错误)。我会注意到您包装 log4net 的方法将破坏 log4net 记录呼叫站点的能力。如果调用站点日志记录打开,您将从包装器中获取调用站点,而不是从程序代码中获取调用站点。有关如何包装 log4net 以保留呼叫站点stackoverflow.com/questions/1149844/… 的简短示例,请参见我的答案
  • @wageoghe:我同意你的观点,这颠覆了(如果不是完全违反)单一责任原则,但这是达到目的的一种手段。但是,很多人并没有考虑装饰器:它们在使事情变得灵活和可配置方面非常有用,尤其是在与依赖注入结合使用时。我们使用 MSEL 日志记录块;我想让我们使用 Log4Net,但由于诸如获取记录器所需的静态方法调用之类的事情而受到阻力,这使得依赖注入和模拟变得困难。一个好的包装纸和工厂可能会降低那里的阻力。感谢您的建议!
  • 我个人的看法是,装饰器模式极其丑陋。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-10-03
  • 2016-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-20
相关资源
最近更新 更多