Logging with Log4Net on the Azure platform

Log4Net on Azure

I have always been a big fan of the Log4Net framework for all of my logging needs in applications and I would like to use this framework in my azure projects as well. And to be honest it isn’t even that difficult: just configure it to the TraceAppender as everything written to the tracelog will be transferred by the diagnostics manager to azure table storage. 

But there are two problems, or inconveniences, with this approach. First of all, you have to both configure log4net and the diagnostics monitor. This is a pretty repetitive task that you don’t want to perform over and over again for every application you create.  Secondly log4net is configured using the application’s configuration file, which is rather inconvenient on azure as it isn’t editable.

AzureAppender

To solve both of these problems we can instead create a custom appender that both sets up the diagnostics monitor as well as read the log4net configuration from the service configuration file instead of the application configuration file.

In order to do so, you have to inherit a class from AppenderSkeleton.

public sealed class AzureAppender : AppenderSkeleton

First thing that needs to be done is ensure that the configuration values are read from the service configuration file, if they are present. This is a bit clumbsy on azure as you cannot check for the presence of a configuration key, all you can do is act on the exception thrown when the key is not present. Make sure to set all values before the ActivateOptions method is called.

The following example shows you how to read the error level from config and apply it to the log4net environment.

private static string GetLevel()        
{            
       try            
       {                
           return RoleEnvironment.GetConfigurationSettingValue(LevelKey);            
       }           
       catch (Exception)            
       {                
            return "Error";            
       }        
}

 private void ConfigureThreshold()
 {
     var rootRepository = (Hierarchy) log4net.LogManager.GetRepository();
     Threshold = rootRepository.LevelMap[GetLevel()];
 }

The appender for this article supports the following configuration settings:

  • Diagnostics.ConnectionString Sets the connection string to be used when transferring the log entries to table storage
  • Diagnostics.Level Sets the threshold that log4net will use to filter the logs to output.
  • Diagnostics.Layout Defines the layout and content that log4net will use to create the log entries
  • Diagnostics.ScheduledTransferPeriod Specifies the interval, in minutes, that will be used by the diagnostics manager to transfer logs to azure table storage
  • Diagnostics.EventLogs Configures which of the event log sections will be transferred from the azure instance to azure table storage

When the options have been set and activated, the log4net environment has been completely configured to make proper use of our custom appender and we can start azure diagnostics monitor. Note that the diagnostics monitor also has a threshold that allows you to filter the logs written to storage. But as log4net is already filtering, we don’t need to do it here anymore so we set the filter to Verbose.

 private void ConfigureAzureDiagnostics()
{
    var traceListener = new DiagnosticMonitorTraceListener();
    Trace.Listeners.Add(traceListener);

    var dmc = DiagnosticMonitor.GetDefaultInitialConfiguration();

    //set threshold to verbose, what gets logged is controled by the log4net level
    dmc.Logs.ScheduledTransferLogLevelFilter = LogLevel.Verbose;

    ScheduleTransfer(dmc);

    ConfigureWindowsEventLogsToBeTransferred(dmc);

    DiagnosticMonitor.Start(ConnectionStringKey, dmc);
}

private void ScheduleTransfer(DiagnosticMonitorConfiguration dmc)
{
    var transferPeriod = TimeSpan.FromMinutes(ScheduledTransferPeriod);
    dmc.Logs.ScheduledTransferPeriod = transferPeriod;
    dmc.WindowsEventLog.ScheduledTransferPeriod = transferPeriod;
}

private static void ConfigureWindowsEventLogsToBeTransferred(DiagnosticMonitorConfiguration dmc)
{
    var eventLogs = GetEventLogs().Split(';');
    foreach (var log in eventLogs)
    {
         dmc.WindowsEventLog.DataSources.Add(log);
    }
}

That’s all there is to it basically, the only thing we need to do now is to apply the appender to the environment. This is done by creating an instance of the appender, configure it either in code or using the settings in the service configuration file, and finally configure the log4net environment.

var appender = new AzureAppender();
appender.ActivateOptions();
BasicConfigurator.Configure(appender);

You can find the source for this appender in the NServiceBus project and feel free to use it in your projects. I also want to give special credits to Andreas Ohlund for creating the first version of this appender.

Advertisements