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)