Log4Net: Programmatically Specify Multiple Loggers (With Multiple File Appenders)

Log4Net: Programmatically specify multiple loggers (with multiple file appenders)

This thread at the log4net Dashboard details an approach.

To summarize a little, hopefully without ripping off too much code:

using log4net;
using log4net.Appender;
using log4net.Layout;
using log4net.Repository.Hierarchy;

// Set the level for a named logger
public static void SetLevel(string loggerName, string levelName)
{
ILog log = LogManager.GetLogger(loggerName);
Logger l = (Logger)log.Logger;

l.Level = l.Hierarchy.LevelMap[levelName];
}

// Add an appender to a logger
public static void AddAppender(string loggerName, IAppender appender)
{
ILog log = LogManager.GetLogger(loggerName);
Logger l = (Logger)log.Logger;

l.AddAppender(appender);
}

// Create a new file appender
public static IAppender CreateFileAppender(string name, string fileName)
{
FileAppender appender = new
FileAppender();
appender.Name = name;
appender.File = fileName;
appender.AppendToFile = true;

PatternLayout layout = new PatternLayout();
layout.ConversionPattern = "%d [%t] %-5p %c [%x] - %m%n";
layout.ActivateOptions();

appender.Layout = layout;
appender.ActivateOptions();

return appender;
}

// In order to set the level for a logger and add an appender reference you
// can then use the following calls:
SetLevel("Log4net.MainForm", "ALL");
AddAppender("Log4net.MainForm", CreateFileAppender("appenderName", "fileName.log"));

// repeat as desired

log4net logging to multiple defined appenders using class name loggers

If you want to have multiple loggers with the same name but using different appenders, then you must set up multiple repositories to contain them.

As it says in the (somewhat scanty) documentation on repositories:

Named logging repositories can be created using the LogManager.CreateRepository method. The repository for can be retrieved using the LogManager.GetRepository method. A repository created in this way will need to be configured programmatically.

However, this does not mean the actual config must be in code, but that you should do something like this:

// or wherever
string configPath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

var repository = log4net.LogManager.CreateRepository("file1repo");
log4net.XmlConfigurator.ConfigureAndWatch(repository, new FileInfo(configPath));

repository = log4net.LogManager.CreateRepository("file2repo");
log4net.XmlConfigurator.ConfigureAndWatch(repository, new FileInfo(configPath));

Then you would write code to amend the 'file2' appender to write to file2:

var appender = LogManager.GetRepository("file2repo").GetAppenders()
.OfType<RollingFileAppender>().Single();

appender.File = "file2.log";
appender.ActivateOptions();

Your code then looks like this:

public static ILog GetLogger(Type classType, bool shouldLogToFile2)
{
return LogManager.GetLogger(shouldLogToFile2 ? "file2repo" : "file1repo", classType);
}

Configure Log4net to write to multiple files

Yes, just add multiple FileAppenders to your logger. For example:

<log4net>
<appender name="File1Appender" type="log4net.Appender.FileAppender">
<file value="log-file-1.txt" />
<appendToFile value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %message%newline" />
</layout>
</appender>
<appender name="File2Appender" type="log4net.Appender.FileAppender">
<file value="log-file-2.txt" />
<appendToFile value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %message%newline" />
</layout>
</appender>

<root>
<level value="DEBUG" />
<appender-ref ref="File1Appender" />
<appender-ref ref="File2Appender" />
</root>
</log4net>

Automatically create appenders with log4net

You can programmatically configure multiple loggers. See the link below:

Log4Net: Programmatically specify multiple loggers

Multiple appender in same Log4net file

You can configure multiple appenders very easily. For example like this:

<appender name="ConsoleAppender" ...
...
</appender>

<appender name="RollingFileAppender" ...
...
</appender>

<root>
<level value="ALL" />
<appender-ref ref="ConsoleAppender" />
<appender-ref ref="RollingFileAppender" />
</root>

The second part is harder to answer: The idea is not to select an appender and write to it. Normally you use loggers and simply write whatever you think is useful at an appropriate log level. You do not care where your log messages are written to: This is for the system administrator to decide (IMHO).

It is actually possible to use different appenders for different loggers. A system administrator might decide to log all errors in a YourApp.Security namespace to a SMTP appender while the rest simply goes to a database or a file. More information on loggers can be found in this excellent tutorial:
http://www.beefycode.com/post/Log4Net-Tutorial-pt-5-Using-Logger-Objects.aspx

It is however possible to create appenders in code (sample code can be found here) but the question is if you really want to do this. Example: If you want to send an email in certain situations then I suggest not use log4net. Instead you simply create and send the email in your own code.

Log4Net Multiple loggers

You have several typos on your example. First is you don't close the configuration tag, and why you're getting only in one file, is because you call:

logger.Info("started async");

and after that you surprisingly do:

logger.Info("started sync");

You will not get writing in the second file because you actually don't log to it.

And in console I get from your code:

Logger: Async
Appenders: FileInfoAppenderS, FileInfoAppenderA
Logger: Sync
Appenders: FileInfoAppenderS, FileInfoAppenderA

For future information, you did correctly by putting additivity to false, because this means that the loggers will not inherit from root logger. As about the statement:

I want to have 2 loggers with different file appenders and restrict each to write into root logger

I do not understand it. If you want that your loggers write to these files while root logger having a console appender for example, just remove additivity and they will write to console and their own files. Also tested and it works very well.

I have read your comment. Now I add the code that I'm using and getting what you need:

class Program
{
static void Main(string[] args)
{
log4net.Config.XmlConfigurator.Configure();

ILog logger = LogManager.GetLogger("Async");
logger.Info("started async");
Console.WriteLine("Logger: {0}", (logger as log4net.Core.LogImpl).Logger.Name);
Console.WriteLine("Appenders: {0}", string.Join(", ", (logger as log4net.Core.LogImpl).Logger.Repository.GetAppenders().ToList().Select(appendr => appendr.Name)));

ILog logger2 = LogManager.GetLogger("Sync");
logger2.Info("started sync");
Console.WriteLine("Logger: {0}", (logger2 as log4net.Core.LogImpl).Logger.Name);
Console.WriteLine("Appenders: {0}", string.Join(", ", (logger2 as log4net.Core.LogImpl).Logger.Repository.GetAppenders().ToList().Select(appendr => appendr.Name)));

Console.ReadKey();
}
}

And the app.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<log4net>
<appender name="FileInfoAppenderA" type="log4net.Appender.RollingFileAppender">
<file value="C:\\temp\\AsyncTest.log"/>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="DEBUG"/>
<levelMax value="FATAL"/>
</filter>
<appendToFile value="true"/>
<rollingStyle value="Size"/>
<maxSizeRollBackups value="10"/>
<maximumFileSize value="100MB"/>
<staticLogFileName value="true"/>
<datePattern value="yyyyMMdd"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d - %m%n"/>
</layout>
</appender>

<appender name="FileInfoAppenderS" type="log4net.Appender.RollingFileAppender">
<file value="C:\\temp\\SyncTest.log"/>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="DEBUG"/>
<levelMax value="FATAL"/>
</filter>
<appendToFile value="true"/>
<rollingStyle value="Size"/>
<maxSizeRollBackups value="10"/>
<maximumFileSize value="100MB"/>
<staticLogFileName value="true"/>
<datePattern value="yyyyMMdd"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d - %m%n"/>
</layout>
</appender>

<root>
<level value="INFO"/>
</root>

<logger name="Sync" additivity="false">
<level value="INFO"/>
<appender-ref ref="FileInfoAppenderS"/>
</logger>

<logger name="Async" additivity="false">
<level value="INFO"/>
<appender-ref ref="FileInfoAppenderA"/>
</logger>
</log4net>
</configuration>

Log4net version: 1.2.13.0 with .NET 4.0
Tell me please if you get what you want.

How to get logger programmatically for two appenders in log4net?

The problem I had solved by setting Additivity to false, that was the reason why appenders were generating mixed output.



Related Topics



Leave a reply



Submit