log4net programmatically change target from text to database to event log

1.3k Views Asked by At

Is there a way to have log4net change the target location of the intended log thru certain conditions in the program, without having to update and redeploy the xml config?

I dont want any configuration dependent solution. It should work purely thru code. I have certain conditions based on which I will be able to tell my program which target to choose. On some condition the program will automatically switch between a text file on a pre-defined path or to SQL Server on a predefined server/database or to an event log under my application suite's name. File path, sql connection string and event log parameters will be the only thing stored in my applications config file.

Is this even doable on log4net? I couldn't find anything relevant with what I searched so I don't have any code I can show yet. Most answers were on how to change the default directory location, but thats not what I want.

Thanks

2

There are 2 best solutions below

0
On BEST ANSWER

I know you talked about changing your appender in code only, but I really think you would benefit more by using appenders chosen depending on a context property set in the code

An example would be to do something like this in your configuration file:

<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender,log4net">

  <filter type="log4net.Filter.PropertyFilter">
    <key value="target" />
    <stringToMatch value="DB" />
    <acceptOnMatch value="true" />
  </filter>
  <filter type="log4net.Filter.DenyAllFilter" />

<!-- your ado configuration -->


<appender name="AnotherAppender" type="log4net.Appender.EventLogAppender,log4net">

  <filter type="log4net.Filter.PropertyFilter">
    <key value="target" />
    <stringToMatch value="EventLog" />
    <acceptOnMatch value="true" />
  </filter>
  <filter type="log4net.Filter.DenyAllFilter" />

<!-- your Eventlog configuration -->

Then in your code, you can set the logger target property to the destination you want:

var isDB = true;
var loggingEvent = new LoggingEvent(typeof(Program), Log.Logger.Repository, Log.Logger.Name, Level.Info, "message", null);
loggingEvent.Properties["target"] = isDB ? "DB" : "EventLog";
Log.Logger.Log(loggingEvent);

This way you don't have magic logging outputs hidden in your code, the outputs are driven through configuration so you can change it easily, and the routing is the only thing you have to take care of inside the application.

If you don't need the context per message (for example you know that once logging routing changes, it won't switch again for a long time), you can use one of the three other contexts that can be set globally:

  • log4net.GlobalContext is used for all loggers in the app
  • log4net.ThreadContext is shared between loggers in the same thread
  • log4net.ThreadLogicalContext is shared between loggers in the same logical boundaries for a thread (more info on the difference)
1
On

It is possible to programatically toggle logging per appender. You could control the logging threshold based on conditions you define.

For example, this is some code which toggles appenders which have a name prefixed with "Audit".

        private static void ToggleAuditing(bool enabled)
    {
        log4net.Appender.IAppender[] appenders = log4net.LogManager.GetRepository().GetAppenders();

        foreach (log4net.Appender.IAppender app in appenders)
        {
            log4net.Appender.AppenderSkeleton skel = app as log4net.Appender.AppenderSkeleton;

            if (skel != null && app.Name.StartsWith("Audit"))
            {
                skel.Threshold = enabled ? Level.All : Level.Off;
            }
        }
    }