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 theLogManager.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
How to Fix "Referenced Assembly Does Not Have a Strong Name" Error
ASP.NET MVC Ambiguous Action Methods
C# - Approach for Saving User Settings in a Wpf Application
Implementing Zero or One to Zero or One Relationship in Ef Code First by Fluent API
How Does Inheritance Work for Attributes
Embedding One Dll Inside Another as an Embedded Resource and Then Calling It from My Code
How to Automatically Scroll to the Bottom of a Multiline Text Box
Convert JSON String to JSON Object C#
Read Post Data Submitted to ASP.NET Form
Can a C# Class Inherit Attributes from Its Interface
Dictionary Returning a Default Value If the Key Does Not Exist
Understanding Async/Await in C#
MVC Razor View Nested Foreach's Model