log4net:运行时不同文件appender上的不同日志

时间:2014-01-09 14:07:29

标签: c# multithreading logging log4net

早上好。

我写了一个单一的C#2.0应用程序(称之为 myapp ) Myapp被多次调用,并且每次调用都会产生一种“任务”,它将在一个单独的线程中执行 如果您在短时间内多次调用myapp,则会同时执行任务。

通常我使用log4net进行日志记录;我配置它在启动时由XmlConfigurator.Configure(<config>.xml)加载xml文件,然后我在每个需要记录器的类中使用静态LogManager.GetLogger(name),非常简单。

相反,这种情况具有挑战性。 我需要做的是:根据每次调用收到的一个args(称之为 arg ),我需要获得一个不同的 RollingFileAppender 来登录不同的文件,e。 G。日志。

举个例子:

第一个电话:myapp.exe -arg:01
  - myapp创建thread1
  - 将新的RollingFileAppender设置为01.log文件(如果不存在)   - 此线程中使用的对象必须登录01.log文件

第二个电话:myapp.exe -arg:02
  - 创建thread2
  - 将新的RollingFileAppender设置为02.log文件(如果不存在)   - 此线程中使用的对象必须登录02.log文件,但不能登录log.01

第3个电话:myapp.exe -arg:01
  - 创建thread03
  - 将RollingFileAppender转换为01.log文件(它已经存在!
  - 此线程中使用的对象必须登录01.log文件,但不能登录log.02

等等。 我不需要将RollingAppender的配置保留在xml文件中,我可以通过编程方式创建它;我的想法是使用一个静态包装类,称之为LogHelper,如果基于 arg 它们不存在,则创建appender,并且当对象需要时调度正确的ILog istances(在类中我会使用某些东西)比如ILog log = LogHelper.GetLogger(name, arg)来使用记录器而不是默认的log4net方法LogManager.GetLogger(name))。 因此,如果我在2个不同的线程中有2个相同类的istances,当我记录每个文件的消息时,依赖于 arg (我会在每个对象中注入 arg ,如果需要的话。)

我在StackOverflow中浏览了很多线程,但我找不到解决方案。

有人能指出我正确的方向吗?

提前致谢。

2 个答案:

答案 0 :(得分:9)

我的解决方案略有不同 我创建了一个LogMaster静态类(对于可怜的名字而言),它类似于默认的log4net LogManager类。
不同之处在于,您可以根据ILog获得不同的arg金额:LogMaster会为您将使用的每个不同的ILoggerRepository创建新的arg

这里是代码:

#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
    }
}

用法:

var arg = "myArg";
var loggerName = "MyLogger";
var log = LogMaster.GetLogger(arg, loggerName);

使用此解决方案,您可以从检索ILog记录器的默认LogManager行为中受益:如果存储库中已存在具有相同名称的记录器,您将获得该记录(回收行为)。

感谢@ making3的建议!

答案 1 :(得分:2)

我做了类似的事情,我需要为每个类的实例提供不同的日志。您可以通过几个步骤动态创建日志。

看起来default Logger (Line 97)已经定义了,但它是程序集内部的,所以它需要继承(据我所知)。

public sealed class DynamicLogger : Logger 
{ 
    internal DynamicLogger(string name) : base(name) 
    { 
        base.Hierarchy = (log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository();
    } 
}

检索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;
}

基本用法:

var foo = DynamicLog.GetSample("foo");
var bar = DynamicLog.GetSample("bar");
foo.Error("Test");
bar.Error("Test");

对于您的场景,请查看创建RollingFileAppender,并查看对象上的可用属性,因为这只是一个示例。

编辑:添加以下内容以存储ILog,并修改了上述原始GetSample方法。

添加ILog和GetLogger方法的数组:

private static List<ILog> _logs = new List<ILog>();

public static ILog GetLogger(string name)
{
    return _logs.SingleOrDefault(a => a.Logger.Name == name);
}

样本用法:

DynamicLog.GetSample("foo");
var foo = DynamicLog.GetLogger("foo");