Programmatically adding and removing log appenders in log4net

27,708

Solution 1

I have been using the BasicConfigurator configured with a MemoryAppender. This appender lets you get to the in-memory messages logged during your test.

Solution 2

Using the BasicConfigurator is fine for unit testing (what the OP asked for, but not what's in the subject line). The other answers grab output for a specific logger.

I wanted it all (this was a 'self test' page within a website). In the end I did basically the following:

var root = ((log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository()).Root;
var attachable = root as IAppenderAttachable;

var appender = new log4net.Appender.MemoryAppender();
if(attachable!=null)
    attachable.AddAppender(appender);
// do stuff
var loggingEvents = appender.GetEvents();
foreach (var loggingEvent in loggingEvents)
    loggingEvent.WriteRenderedMessage(writer);
if(attachable!=null)
    attachable.RemoveAppender(appender);

...but wrapped up as a Disposable as per @Pawel's approach

UPDATE: Pawel's answer was deleted, so I am adding his link here: Programmatically check Log4Net log.

Solution 3

The following code was originally found on the apache mailing list archives and should solve the problem of adding and removing log4net appenders in code

/// <summary>
/// dataLog
/// </summary>
protected static readonly IDeviceCommunicationsLog dataLog =
DeviceCommunicationsLogManager.GetLogger("LIS3.Data");


Each connection adds and removes a file appender programmatically:

/// <summary>
/// add connection specific appender
/// </summary>
void AddAppender()
{
    // check if logging is endabled
    if( this.IsLoggingEnabled() )
    {
        try
        {
            // get the interface
            IAppenderAttachable connectionAppender = (IAppenderAttachable)this.DataLog.Logger;
            // need some application configuration settings
            NameValueCollection appSettings = ConfigurationSettings.AppSettings;
            // get the layout string
            string log4netLayoutString = appSettings["log4net.LIS3.LayoutString"];
            if( log4netLayoutString == null )
            {
                // use default setting
                log4netLayoutString = "%d [%x]%n   %m%n  %P MessageData}%n%n";
            }
            // get logging path
            string log4netPath = appSettings["log4net.Path"];
            if( log4netPath == null )
            {
                // use default path
                log4netPath = ".\\";
            }
            // create the appender
            this.rollingFileAppender = new RollingFileAppender();
            // setup the appender
            this.rollingFileAppender.MaxFileSize = 10000000;
            this.rollingFileAppender.MaxSizeRollBackups = 2;
            this.rollingFileAppender.RollingStyle =   RollingFileAppender.RollingMode.Size;
            this.rollingFileAppender.StaticLogFileName = true;
            string appenderPath = LogSourceName + ".log";
            // log source name may have a colon - if soreplace with underscore
            appenderPath = appenderPath.Replace( ':', '_' );
            // now add to log4net path
            appenderPath = Path.Combine( log4netPath, appenderPath );
            // update file property of appender
            this.rollingFileAppender.File = appenderPath;
            // add the layout
            PatternLayout patternLayout = new PatternLayout(   log4netLayoutString );
            this.rollingFileAppender.Layout = patternLayout;
            // add the filter for the log source
            NDCFilter sourceFilter = new NDCFilter();
            sourceFilter.StringToMatch = this.LogSourceName;
            this.rollingFileAppender.AddFilter( sourceFilter);
            // now add the deny all filter to end of the chain
            DenyAllFilter denyAllFilter = new DenyAllFilter();
            this.rollingFileAppender.AddFilter( denyAllFilter );
            // activate the options
            this.rollingFileAppender.ActivateOptions();
            // add the appender
            connectionAppender.AddAppender( this.rollingFileAppender );
        }
        catch( Exception x )
        {
            this.ErrorLog.Error( "Error creating LIS3 data log appender for " + LogSourceName, x );
        }
    }
}
/// <summary>
/// remove connection specific appender
/// </summary>
void RemoveAppender()
{
    // check if we have one
    if( this.rollingFileAppender != null )
    {
        // cast to required interface
        IAppenderAttachable connectionAppender = (IAppenderAttachable)this.DataLog.Logger;
        // remove the appendier
        connectionAppender.RemoveAppender( rollingFileAppender );
        // set to null
        this.rollingFileAppender = null;
    }
}

Solution 4

How about:

((log4net.Repository.Hierarchy.Logger) theLogger.Logger).RemoveAppender("SomeAppender");

same for add.

Share:
27,708

Related videos on Youtube

Pete
Author by

Pete

I have worked professionally with software development since 1997. Since year 2000 I have worked as an independent contractor, helping various business in developing their internal and external IT systems. Since 2002 I have worked almost exclusively with the .NET framework. Of notable work can be mentioned IT-Jobbank, Denmark's largest online job board for IT professionals, where I was the lead developer and architect. SOreadytohelp

Updated on July 09, 2022

Comments

  • Pete
    Pete almost 2 years

    I have a component that uses log4net. I want to create unit tests, that validate that certain error conditions result in the correct logging.

    I was thinking that the best way to do this is to create an ILogAppender implementation, for example a mock. I would then add the log appender to log4net during test setup, inspect what was written during test validation, and remove it again during test teardown.

    Is this possible?

  • Pete
    Pete over 14 years
    I finally got to implementing this, and it solves the problem very nicely
  • Aasmund Eldhuset
    Aasmund Eldhuset about 13 years
    +1; saved me from making a stub implementation of ILog and injecting it into all of my classes :-)
  • Mark W
    Mark W over 8 years
    Do you know how to make the add actually work? I've written a unit test to test the implementation of a custom appender I created, but unfortunately the Add method of Logger is a lie, because the ILog implementation that LogManager.GetLogger(string) returns has a read only collection of appenders. It ends up silently consuming the exception it throws internally, and never really adds the appender.
  • Vivelin
    Vivelin almost 8 years
    Note that if you do not use the BasicConfigurator or XmlConfigurator anywhere, you need to set Hierarchy.Configured to true to make log4net actually do anything.