Question

I use log4net in my console application, I use log.Error("Message to log"); multiple times in if/else structures.
My application has to return a code which specifies if an error has occurred during the run of the application (0 → ok, 1 → at least 1 error occurred).
Is there a way to ask log4net if it logged an error, for instance:

bool b = LogManager.AtLeastOneErrorHasBeenLogged;
Was it helpful?

Solution

This seems like it would be straightforward if you use a custom appender whose only purpose is to track if an error occurred.

This (untested) example uses the technique of only calling the appender if the log level is Error by using a LevelRangeFilter but you could just as easily check the log level (or other criteria) in the Append method, or in the root logger config. The example assumes you are using XML config rather than programmatic config, but it is possible to add appenders programatically so they are used by all loggers.

The appender:

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

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

The config:

<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>

Then you can have an extension method to see if an error occurred:

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

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

OTHER TIPS

I think that most people would consider the idea of asking log4net (or whatever logging framework one might use) if an error was logged to be very odd. You are in control of calling log4net, so why not keep track yourself if an error occurred. What if the user of your program turns off logging in the config file? If an error happens when logging is turned off and you are depending on whether or not an error was logged, then you have no way of knowing that an error occurred. Nicholas Carey suggested to add logic inside your catch blocks to keep track of whether or not an exception occurred. Even if you don't use exceptions, you could do something similar inside your if/then/else logic:

(Note that I am borrowing Nicholas Carey's sample as a basis for my answer)

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

Having posted that code, I have to say that I think that it likely you could come up with a more robust solution than what I posted.

My final advice is to NOT make your program logic depend on something that the logging framework does (or does not). Generally speaking, your program should run the same way whether logging is enabled or not (notwithstanding that when logging is enabled, messages are logged).

I don't believe so...but it would be easy enough to use the decorator pattern and decorate the ILog interface to gather that information. The problem is that the log4net standard way of instantiating a logger is to have every class instantiate its own class-specific logger instance via LogManager.GetLogger(Type t).

However, shouldn't you just be catching the exception in your Main() method, logging it and exiting with the condition code set to zero or one as appropriate?

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

That's the point of exceptions. If you can't actually handle it, you shouldn't be catching it: just let it bubble up to the top.

Edited to note: a sample ILog decorator and factory:

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

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top