Good morning guys.
I wrote a single istance C# 2.0 app (call it myapp).
Myapp is called many times, and at every call generates a sort of "task" that will be executed in a separated thread.
If you call myapp several times in a short time, task are executed in parallel.
Generally I use log4net for logging purposes; I configure it loading an xml file by XmlConfigurator.Configure(<config>.xml)
at startup, then I use static LogManager.GetLogger(name)
in every class I need a logger, quite simple.
This scenario is challenging, instead. I need to do is this: based on one of the args received on every call (call it arg), I need to get a different RollingFileAppender that logs in a different file, e. g. .log.
Just to make an example:
1st call: myapp.exe -arg:01
- myapp creates thread1
- set a new RollingFileAppender to 01.log file, if not exists
- objects used in this thread must log in 01.log file
2nd call: myapp.exe -arg:02
- create thread2
- set a new RollingFileAppender to 02.log file, if not exists
- objects used in this thread must log in 02.log file, but not in log.01
3rd call: myapp.exe -arg:01
- create thread03
- get the RollingFileAppender to 01.log file (it already exists!)
- objects used in this thread must log in 01.log file, but not in log.02
And so on.
I don't need to leave the configuration of RollingAppender in xml file, I can create it programmatically; my idea is to use a static wrapper class, call it LogHelper, that creates appenders if they do not exist based on arg, and that dispatch right ILog istances when needed by objects (in classes I would use something like ILog log = LogHelper.GetLogger(name, arg
) to get a logger to use instead o default log4net method LogManager.GetLogger(name)
).
So if I have 2 istances of the same class in 2 different threads, when I log messages goes one per file, depending or arg (I will inject arg in every object, if needed).
I browse many threads here in StackOverflow, but I can't find a solution.
Can someone point me to the right direction?
Thanks in advance.
You can configure the log4net. config file to create log files. The file is located in the webroot\App_data directory of the installation.
Log4net is a logging utility for . NET applications. It's based on log4j, which is for Java applications. Log4net is highly configurable, so you can use it in many scenarios.
I ended up with a slightly different solution.
I created a LogMaster
static class (sorry for poor name) that work similar to the default log4net LogManager
class.
The difference is that you can get different ILog
istances based on arg
: LogMaster
will create a new ILoggerRepository
for each different arg
you will use.
Here the code:
#region Usings
using System;
using System.IO;
using log4net;
using log4net.Appender;
using log4net.Config;
using log4net.Core;
using log4net.Filter;
using log4net.Layout;
using log4net.Repository;
using log4net.Repository.Hierarchy;
#endregion
namespace Common.Voyager
{
/// <summary>
/// A static class that emulates defualt log4net LogManager static class.
/// The difference is that you can get various loggers istances based from an args.
/// LogMaster will create a different logger repository for each new arg it will receive.
/// </summary>
public static class LogMaster
{
#region Const
private const string RollingFileAppenderNameDefault = "Rolling";
private const string MemoryAppenderNameDefault = "Memory";
#endregion
#region Constructors
static LogMaster()
{
}
#endregion
#region Public Methods
public static ILog GetLogger(string arg, string name)
{
//It will create a repository for each different arg it will receive
var repositoryName = arg;
ILoggerRepository repository = null;
var repositories = LogManager.GetAllRepositories();
foreach (var loggerRepository in repositories)
{
if (loggerRepository.Name.Equals(repositoryName))
{
repository = loggerRepository;
break;
}
}
Hierarchy hierarchy = null;
if (repository == null)
{
//Create a new repository
repository = LogManager.CreateRepository(repositoryName);
hierarchy = (Hierarchy)repository;
hierarchy.Root.Additivity = false;
//Add appenders you need: here I need a rolling file and a memoryappender
var rollingAppender = GetRollingAppender(repositoryName);
hierarchy.Root.AddAppender(rollingAppender);
var memoryAppender = GetMemoryAppender(repositoryName);
hierarchy.Root.AddAppender(memoryAppender);
BasicConfigurator.Configure(repository);
}
//Returns a logger from a particular repository;
//Logger with same name but different repository will log using different appenders
return LogManager.GetLogger(repositoryName, name);
}
#endregion
#region Private Methods
private static IAppender GetRollingAppender(string arg)
{
var level = Level.All;
var rollingFileAppenderLayout = new PatternLayout("%date{HH:mm:ss,fff}|T%2thread|%25.25logger|%5.5level| %message%newline");
rollingFileAppenderLayout.ActivateOptions();
var rollingFileAppenderName = string.Format("{0}{1}", RollingFileAppenderNameDefault, arg);
var rollingFileAppender = new RollingFileAppender();
rollingFileAppender.Name = rollingFileAppenderName;
rollingFileAppender.Threshold = level;
rollingFileAppender.CountDirection = 0;
rollingFileAppender.AppendToFile = true;
rollingFileAppender.LockingModel = new FileAppender.MinimalLock();
rollingFileAppender.StaticLogFileName = true;
rollingFileAppender.RollingStyle = RollingFileAppender.RollingMode.Date;
rollingFileAppender.DatePattern = ".yyyy-MM-dd'.log'";
rollingFileAppender.Layout = rollingFileAppenderLayout;
rollingFileAppender.File = string.Format("{0}.{1}", "log", arg);
rollingFileAppender.ActivateOptions();
return rollingFileAppender;
}
private static IAppender GetMemoryAppender(string station)
{
//MemoryAppender
var memoryAppenderLayout = new PatternLayout("%date{HH:MM:ss} | %message%newline");
memoryAppenderLayout.ActivateOptions();
var memoryAppenderWithEventsName = string.Format("{0}{1}", MemoryAppenderNameDefault, station);
var levelRangeFilter = new LevelRangeFilter();
levelRangeFilter.LevelMax = Level.Fatal;
levelRangeFilter.LevelMin = Level.Info;
var memoryAppenderWithEvents = new MemoryAppenderWithEvents();
memoryAppenderWithEvents.Name = memoryAppenderWithEventsName;
memoryAppenderWithEvents.AddFilter(levelRangeFilter);
memoryAppenderWithEvents.Layout = memoryAppenderLayout;
memoryAppenderWithEvents.ActivateOptions();
return memoryAppenderWithEvents;
}
#endregion
}
}
Usage:
var arg = "myArg";
var loggerName = "MyLogger";
var log = LogMaster.GetLogger(arg, loggerName);
Using this solution you can benefit from default LogManager behavior retrieving ILog
loggers: if a logger with the same name already exists in a repository, you will get back that istance (recycling behavior).
Thank you @making3 for your suggestions!
I've done something similar, where I needed a different log for every instance of a class. You can create logs dynamically with a few steps.
It looks like a default Logger (Line 97) is already defined, but it's internal to their assembly, so it will need to be inherited (as far as I know).
public sealed class DynamicLogger : Logger
{
internal DynamicLogger(string name) : base(name)
{
base.Hierarchy = (log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository();
}
}
Sample method to retrieve an ILog:
public static ILog GetSample(string arg)
{
var logger = new DynamicLogger(arg);
logger.Level = Level.All;
var consoleAppender = new ConsoleAppender();
consoleAppender.Name = arg;
consoleAppender.Layout = new PatternLayout(arg + ": %m%newline");
logger.AddAppender(consoleAppender);
var newLog = new LogImpl(logger);
if (_logs.Any(log => log.Logger.Name == newLog.Logger.Name) == false)
_logs.Add(newLog);
return newLog;
}
Basic usage:
var foo = DynamicLog.GetSample("foo");
var bar = DynamicLog.GetSample("bar");
foo.Error("Test");
bar.Error("Test");
For your scenario, look at creating a RollingFileAppender
, and look at the available properties on the object, since this was just an example.
EDIT: Added the below for storing ILog's, and modified the original GetSample
method above.
Add an array of ILog's and a GetLogger method:
private static List<ILog> _logs = new List<ILog>();
public static ILog GetLogger(string name)
{
return _logs.SingleOrDefault(a => a.Logger.Name == name);
}
Sample usage:
DynamicLog.GetSample("foo");
var foo = DynamicLog.GetLogger("foo");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With