2014-01-09 5 views
6

Доброе утро, ребята.log4net: разные журналы в разных файловых приложениях во время выполнения

Я написал одно приложение istance C# 2.0 (назовите его myapp).
Myapp вызывается много раз, и при каждом вызове создается своего рода «задача», которая будет выполняться в отдельном потоке.
Если вы вызываете myapp несколько раз за короткое время, задача выполняется параллельно.

Обычно я использую log4net для ведения журнала; Я настраиваю его на загрузку xml-файла на XmlConfigurator.Configure(<config>.xml) при запуске, тогда я использую статический LogManager.GetLogger(name) в каждом классе, мне нужен регистратор, довольно простой.

Этот сценарий является сложным, вместо этого. Мне нужно сделать следующее: на основе одного из аргументов, полученных при каждом вызове (назовите его arg), мне нужно получить другой RollingFileAppender, который регистрируется в другом файле, например. г. .журнал.

Просто сделать пример:

первого вызова: myapp.exe -arg:01
- MyApp создает thread1
- установить новый RollingFileAppender в 01.log файл, если не существует
- объекты, используемые в этой теме необходимо войти в 01.log файле

второй вызов: myapp.exe -arg:02
- создать thread2
- установить новый RollingFileAppender в 02.log файл, если не существует
- объекты, используемые в этой теме необходимо войти 02.log файл, но не в log.01

третий вызов: myapp.exe -arg:01
- создать thread03
- получить RollingFileAppender в 01.log файл (он уже существует!)
- объекты, используемые в этой теме необходимо войти 01.log файл, но не в log.02

И так далее. Мне не нужно оставлять конфигурацию RollingAppender в XML-файле, я могу создать его программно; моя идея состоит в том, чтобы использовать статический класс-оболочку, называть его LogHelper, который создает приложения, если они не существуют на основе arg, и что отправка права ILOG при необходимости требует объектов (в классах я бы использовал что-то вроде ILog log = LogHelper.GetLogger(name, arg), чтобы получить регистратор для использования вместо этого по умолчанию для метода log4net LogManager.GetLogger(name)). Итак, если у меня есть 2 значения одного и того же класса в 2 разных потоках, когда я регистрирую сообщения, идет один на файл, в зависимости или arg (я буду вводить arg в каждый объект, если необходимо).

Я просматриваю много потоков здесь, в StackOverflow, но я не могу найти решение.

Может ли кто-нибудь указать мне в правильном направлении?

Заранее спасибо.

ответ

7

Я закончил с немного другим решением.
Я создал статический класс LogMaster (извините за плохое имя), которые работают аналогично классу по умолчанию log4net LogManager.
Разница в том, что вы можете получить разные ILog istances на основе 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); 

Используя это решение, которое вы можете воспользоваться по умолчанию поведения LogManager ВОССТАНОВЛЕНИЯ ILog регистраторов: если регистратор с тем же именем уже существует в хранилище , вы вернетесь к этому состоянию (поведение по переработке).

Спасибо @ making3 за ваши предложения!

+0

Hi Ferdinando Santacroce, First och all, great research, congrats .. Если вы используете код из консольного приложения, вывод журнала также отправляется на консоль, знаете ли вы это? – oakman

+0

Спасибо, надеюсь, вы сочли это полезным. Да, я это знаю, спасибо :) –

2

Я сделал что-то подобное, где мне нужен другой журнал для каждого экземпляра класса. Динамики можно создавать с помощью нескольких шагов.

Похоже, что default Logger (Line 97) уже определен, но он является внутренним для их сборки, поэтому он должен быть унаследован (насколько я знаю).

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

метод проб для извлечения Илог:

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, и посмотреть на доступных свойств объекта , поскольку это был всего лишь пример.

EDIT: Добавлен ниже для хранения ILog и изменен исходный метод GetSample выше.

Добавить массив Илога-х и метод 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"); 
+0

Большое спасибо за ваше предложение. Я пробовал это с успехом в простом консольном приложении с RollingFileAppender. Теперь я попробую с параллельными потоками, я пересекаю пальцы :) –

+0

Есть способ получить программный программный журнал, как только вы его построите с помощью DynamicLog.GetSample()? Я не использую регистраторы репозитория; если я вызываю BasicConfigurator.Configure(), log4net создает репозиторий по умолчанию с помощью ConsoleAppender. –

+0

Вы всегда можете создать свой собственный метод LogManager.GetLogger. Все, что должно быть необходимо, это создать список ILog, сохранить журнал, созданный 'GetSample', а затем использовать другой метод, например собственный' GetLogger', который будет искать все ILog для определенного имени, используя 'log.Logger.Name '. Дайте мне знать, если вам нужен пример. – matth

Смежные вопросы