Modify sourceLevel programatically Logging

Dec 30, 2011 at 1:00 PM

 

Hi,

 

is it possible change logging sourceLevel by code? I need a user can edit this value in a management web page and changes will have effect immediatlly.

How can i achieve this behavior?

 

Thanks!!

Dec 30, 2011 at 8:24 PM

Yes, you can change logging settings programmatically.

In this example I configure logging programmatically using the Enterprise Library 5.0 Fluent Interface and wrap the UnityContainer in a singleton (LoggingConfigurator).  SourceLevels can be updated when requested and LogWriters can be returned from the container.  There may be other ways to do this as well (perhaps using the EnterpriseLibraryContainer directly).

using System;
using System.Diagnostics;

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ContainerModel;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ContainerModel.Unity;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.Unity;

public sealed class LoggingConfigurator
{
    private static readonly Lazy<LoggingConfigurator> lazy =
        new Lazy<LoggingConfigurator>(() => new LoggingConfigurator());

    private IUnityContainer container;
    private IContainerConfigurator configurator;

    private LoggingConfigurator()
    {
        container = new UnityContainer();
        configurator = new UnityContainerConfigurator(container);

        Initialize(SourceLevels.Error);
    }

    public static LoggingConfigurator Instance 
    { 
        get 
        { 
            return lazy.Value; 
        } 
    }

    public void SetSourceLevels(SourceLevels level)
    {
        Initialize(level);
    }

    public LogWriter GetLogWriter()
    {
        return container.Resolve<LogWriter>();
    }

    private void Initialize(SourceLevels level)
    {
        var builder = new ConfigurationSourceBuilder();

        builder.ConfigureLogging()
                .WithOptions
                    .DoNotRevertImpersonation()
                .LogToCategoryNamed("General")
                    .WithOptions
                    .ToSourceLevels(level)
                    .SendTo.FlatFile("Trace File")
                    .FormatWith(new FormatterBuilder()
                        .TextFormatterNamed("Text Formatter")
                        .UsingTemplate("{message}    Timestamp: {timestamp}...{newline})}"))
                        .ToFile("trace.log");

        var configSource = new DictionaryConfigurationSource();
        builder.UpdateConfigurationWithReplace(configSource);

        // It appears that we do not to to synchronize this call since UnityContainerConfigurator will
        // perform a lock while performing the registrations
        EnterpriseLibraryContainer.ConfigureContainer(configurator, configSource); 
    }
}

And here is an example on how to use the LoggingConfigurator:

var loggingConfigurator = LoggingConfigurator.Instance;

var loggerNoLog = loggingConfigurator.GetLogWriter();

// This does not log since the default level set in LoggingConfiguration is Error
loggerNoLog.Write("loggerNoLog", "General", 0, 0, TraceEventType.Information);

// Change our logging level from Error to Verbose
loggingConfigurator.SetSourceLevels(SourceLevels.Verbose);

var loggerWillLog = loggingConfigurator.GetLogWriter();

// Since the logging level is now set to Verbose Information messages will now log
loggerWillLog.Write("loggerWillLog", "General", 0, 0, TraceEventType.Information);

// This will still not log
loggerNoLog.Write("loggerNoLog", "General", 0, 0, TraceEventType.Information);

I hope that helps.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Feb 11, 2013 at 7:05 PM
I had an issue putting the last block of code in Application_Startup. It appears the call to SetSourceLevels causes an attempt to read the ISO storage lock file which causes a locking issue, so I had to comment it out. Not sure if this is a bug or not.
Dec 2, 2014 at 4:31 PM
Here is a similar example using Enterprise Library 6 programmatic configuration (and reconfiguration):
public sealed class LoggingConfigurator
{
    private static readonly Lazy<LoggingConfigurator> lazy =
        new Lazy<LoggingConfigurator>(() => new LoggingConfigurator());

    private LogWriter currentLogWriter;

    private LoggingConfigurator()
    {
        Initialize(SourceLevels.Error);
    }

    public static LoggingConfigurator Instance
    {
        get
        {
            return lazy.Value;
        }
    }

    public void SetSourceLevels(SourceLevels level)
    {
        this.currentLogWriter.Configure(cfg =>
        {
            foreach (var logSource in cfg.LogSources)
            {
                logSource.Level = level;
            }
        });
    }

    public LogWriter GetLogWriter()
    {
        return this.currentLogWriter;
    }

    private void Initialize(SourceLevels level)
    {
        // Formatter
        TextFormatter briefFormatter = new TextFormatter("{message}    Timestamp: {timestamp}...{newline}"); ;

        // Trace Listener
        var flatFileTraceListener = new FlatFileTraceListener(
            @"trace.log",
            "----------------------------------------", 
            "----------------------------------------", 
            briefFormatter);

        // Build Configuration
        var config = new LoggingConfiguration();

        config.AddLogSource("General", level, true)
            .AddTraceListener(flatFileTraceListener);

        this.currentLogWriter = new LogWriter(config);
    }
}
// Get singleton
var loggingConfigurator = LoggingConfigurator.Instance;

// LogWriter will also be a singleton
LogWriter logger = loggingConfigurator.GetLogWriter();

// This does not log since the default level set in LoggingConfiguration is Error
logger.Write("loggerNoLog", "General", 0, 0, TraceEventType.Information);

// Change our logging level from Error to Verbose
loggingConfigurator.SetSourceLevels(SourceLevels.Verbose);

// Since the logging level is now set to Verbose Information messages will now log
// without getting a new LogWriter since logger is the same LogWriter instance that was reconfigured
logger.Write("loggerWillLog", "General", 0, 0, TraceEventType.Information);

Some notes:
  • This is slightly different than the first example since only one LogWriter instance is created, returned and modified (which is more of a typical use case).
  • In this simpler approach a LoggingConfigurator seems like overkill (LogWriter.Configure is thread-safe so we don't need to take that into account). Basically at any time Configure can be called on the LogWriter.
See Reconfiguring Logging at Run Time.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to