Как добавить префикс категории в сообщение log4net? - PullRequest
7 голосов
/ 15 декабря 2010

Мне нравится добавлять префикс категории ко всем сообщениям в существующих сообщениях регистрации. Однако добавление этого префикса ко всем существующим сообщениям регистрации по одному утомительно. Есть ли способ, которым я могу просто добавить атрибут на уровень класса, тогда все сообщения в этом классе будут записываться в журнал для определенной категории?

Вместо того, как сейчас, как показано ниже,

Log.Info("[Ref] Level 1 Starts ...");

Я действительно хочу что-то подобное или другой способ определить log4net.ILog.

[LoggingCategory("Ref")]
public class MyClass 
{
   public void MyMethod()
   {
        Log.Info("Level 1 Starts ...");
   }
}

Ответы [ 2 ]

9 голосов
/ 16 декабря 2010

Вы спрашиваете, как сделать это через Атрибут.Предложение @ Jonathan выглядит так, как будто оно будет работать нормально, но вы можете достичь достаточно хорошего результата, используя встроенные возможности log4net.

Если вы хотите сгруппировать классы в «категории», то вы можете получитьрегистратор, основанный на имени категории, а не на имени класса.Когда вы устанавливаете выходной формат, вы можете использовать токен форматирования регистратора, чтобы указать log4net записывать имя регистратора в выводе.

Обычно можно получить регистратор на основе имени класса, например:

public class Typical
{
  private static readonly ILog logger = 
       LogManager.GetLogger
          (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

  public void F()
  {
    logger.Info("this message will be tagged with the classname");
  }
}

Вполне допустимо извлекать регистраторы на основе произвольного имени, подобного этому:

public class A
{
  private static readonly ILog logger = LogManager.GetLogger("REF");

  public void F()
  {
    logger.Info("this message will be tagged with REF");
  }
}

public class B
{
  private static readonly ILog logger = LogManager.GetLogger("REF");

  public void F()
  {
    logger.Info("this message will be tagged with REF");
  }
}

public class C
{
  private static readonly ILog logger = LogManager.GetLogger("UMP");

  public void F()
  {
    logger.Info("this message will be tagged with UMP");
  }
}

В предыдущем примере классы A и B считаются одной и той же "категорией", поэтомуони нашли регистратор с тем же именем.Класс C находится в другой категории, поэтому он получил регистратор с другим именем.

Вы можете настроить свои регистраторы (в файле конфигурации) с вашей собственной иерархией "категории":

App
App.DataAccess
App.DataAccess.Create
App.DataAccess.Read
App.DataAccess.Update
App.DataAccess.Delete
App.UI
App.UI.Login
App.UI.Query
App.UI.Options

Вы также можете настроить выходной формат регистратора так, чтобы он регистрировал только часть полного имени регистратора.Примерно так:

%logger:2

Чтобы получить последние 2 части полностью определенного имени.Например, если полное имя вашего класса:

NameSpaceA.NameSpaceB.NameSpaceC.Class

, то в приведенном выше формате это имя будет выводиться как имя регистратора:

NameSpaceC.Class

Я не уверен на 100% в синтаксисепотому что я не использовал его и не могу найти хороший пример прямо сейчас.

Один из недостатков этого подхода заключается в том, что вы должны определить и запомнить, какие у вас категории, и вы должны решить, какая категория подходит.для каждого класса (у вас также есть эта проблема, если вы хотите украсить каждый класс атрибутом, содержащим его категорию).Кроме того, если у вас есть несколько классов в одной категории, вы не можете включить или выключить ведение журнала или изменить уровень ведения журнала для подмножества этих классов.

Может быть, было бы полезно зарегистрировать одно пространство имен изнутрииерархия пространства имен:

Может быть, вы можете "классифицировать" ваши классы на основе их пространства имен.Таким образом, вы можете записать непосредственное родительское пространство имен класса в качестве своей категории.

Итак, для полного имени класса, указанного выше, вы можете зарегистрировать «NameSpaceC» как «категорию» или имя регистратора.

Я не уверен, что вы можете сделать этоиз коробки с log4net, но вы можете легко написать PatternLayoutConverter, чтобы получить имя регистратора и удалить имя класса и любые пространства имен «более высокого уровня».

Вот ссылка на пример пользовательского PatternLayoutConverter.Принимает параметр, который в моем случае я хотел использовать для поиска значения в словаре.В этом случае параметр может представлять смещение от END полностью определенного имени регистратора (то же самое, что и параметр для встроенного в log4net объекта компоновки имени регистратора), но можно добавить дополнительный код, чтобы регистрировать ТОЛЬКО единственное пространство имен при этомindex.

Настраиваемое свойство log4net PatternLayoutConverter (с индексом)

Опять же, учитывая это полное имя класса:

NameSpaceA.NameSpaceB.NameSpaceC.Class

Вы могли бы рассмотретьнепосредственное родительское пространство имен, чтобы быть "категорией".Если вы определили пользовательский PatternLayoutConverter, category, и он принял параметр, то ваша конфигурация может выглядеть следующим образом:

%category

По умолчанию она возвращает подстроку между последней и следующей за последней '.' персонажи.Если задан параметр, он может вернуть любое дискретное пространство имен вверх по цепочке.

PatternLayoutConverter может выглядеть примерно так (не проверено):

  class CategoryLookupPatternConverter : PatternLayoutConverter
  {
    protected override void Convert(System.IO.TextWriter writer, LoggingEvent loggingEvent)
    {
      //Assumes logger name is fully qualified classname.  Need smarter code to handle
      //arbitrary logger names.
      string [] names = loggingEvent.LoggerName.Split('.');
      string cat = names[names.Length - 1];
      writer.Write(setting);
    }
  }

Или, используя свойство Option, чтобы получить Nthимя пространства имен (относительно конца):

  class CategoryLookupPatternConverter : PatternLayoutConverter
  {
    protected override void Convert(System.IO.TextWriter writer, LoggingEvent loggingEvent)
    {
      //Assumes logger name is fully qualified classname.  Need smarter code to handle
      //arbitrary logger names.
      string [] names = loggingEvent.LoggerName.Split('.');
      string cat;
      if (Option > 0 && Option < names.Length)
      {
        cat = names[names.Length - Option];
      }
      else
      {
        string cat = names[names.Length - 1];
      }
      writer.Write(setting);
    }

  }

Идея @Jonathan довольно крутая, но она добавляет некоторую дополнительную кодировку с вашей стороны, чтобы определить и поддерживать новую оболочку логгера (но многие люди так делают и не считают это особенно обременительным бременем). ). Конечно, мои собственные идеи PatternLayoutConverter также требуют пользовательского кода с вашей стороны.

Еще одним недостатком является то, что GetCategory выглядит довольно дорого для вызова при каждом входе в систему.

5 голосов
/ 16 декабря 2010

Интересная проблема, грубая попытка ...

Log4NetLogger - каротажный адаптер

public class Log4NetLogger
{
    private readonly ILog _logger;
    private readonly string _category;

    public Log4NetLogger(Type type)
    {
        _logger = LogManager.GetLogger(type);
        _category = GetCategory();
    }

    private string GetCategory()
    {
        var attributes = new StackFrame(2).GetMethod().DeclaringType.GetCustomAttributes(typeof(LoggingCategoryAttribute), false);
        if (attributes.Length == 1)
        {
            var attr = (LoggingCategoryAttribute)attributes[0];
            return attr.Category;
        }
        return string.Empty;
    }

    public void Debug(string message)
    {
        if(_logger.IsDebugEnabled) _logger.Debug(string.Format("[{0}] {1}", _category, message));
    }
}

LoggingCategoryAttribute - применимо к классам

[AttributeUsage(AttributeTargets.Class)]
public class LoggingCategoryAttribute : Attribute
{
    private readonly string _category;

    public LoggingCategoryAttribute(string category)
    {
        _category = category;
    }

    public string Category { get { return _category; } }
}

LogTester - тестовая реализация

[LoggingCategory("LT")]
public class LogTester
{
    private static readonly Log4NetLogger Logger = new Log4NetLogger(typeof(LogTester));

    public void Test()
    {
        Logger.Debug("This log message should have a prepended category");
    }
}
...