Показаны сообщения с ярлыком логгирование. Показать все сообщения
Показаны сообщения с ярлыком логгирование. Показать все сообщения

воскресенье, 9 августа 2009 г.

Ежедневные логи на log4net

Недавно поступил вопрос:

A есть возможность писать лог каждый день в новый файл? Чтобы частью имени файла была дата?

Сделать такое можно. Нужно только в конфигурации appender-а написать следующее:

<appender name="DebugFileAppender" type="log4net.Appender.RollingFileAppender">
     <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
     <file value="debug" />
     <appendToFile value="true" />     
  <rollingStyle value="Date" />   <datePattern value="yyyyMMdd" />   <staticLogFileName value="false" />
  <maxSizeRollBackups value="10" />   <layout type="log4net.Layout.PatternLayout">   <conversionPattern value="%-5p [%d] [%C.%M] %m%n" />   </layout> </appender>

Что же мы тут сделали? У класса RollingFileAppender, который пишет лог в файл, есть такое свойство RollingStyle, которое определяет, когда логгер будет начинать новый файл лога. Допустимы следующие значения:

  • Size — новый файл создается, как только лог достигает определенного размера (этот размер задается в свойстве maximumFileSize).
  • Date — новый файл создается каждый день, и к названию файла, определенному в свойстве file, добавляется дата в формате, заданном в свойстве datePattern.
  • Composite — комбинирует свойства Date и Size, т.е. учитываются и размер файла, и дата. Кроме того, данное значение является дефолтным для свойства RollingStyle.
  • Once — лог создается один раз при запуске программы.

Итак, в нашем примере мы задали RollingStyle = Date (можно было бы и Composite) и указали формат даты. Но, пока staticLogFileName = true, все это будет игнорироваться, так что надо задать false (эта часть мне несколько непонятна, в документации для staticLogFileName написано другое, но на деле получается именно так). И теперь к имени файла с логом добавляется дата.

NB

Если в datePattern мы напишем что-нибудь вроде yyyyMMdd.log, то файлы, которые будут создаваться, будут называться debug20090808.loA.D.. Это происходит из-за того, что g также является возможным форматом даты. Логгер добросовестно применил этот формат и сообщил, что дата относится к нашей эре (кстати, тоже интересный вопрос: в MSDN написано, что формат эры — gg, а g — это general). Чтобы избежать таких ситуаций, символы, которые являются корректными форматами для дат, надо квотить:

     <datePattern value="yyyyMMdd.lo\g" />

Кстати, txt это тоже касается.

воскресенье, 21 декабря 2008 г.

Еще про log4net

В комментариях к прошлой статье про log4net задал мне товарищ следующий вопрос:

Вопрос: ты знаешь, как заставить log4net создавать лог-файлы для каждого экземпляра интерфейса ILog?
Т.е. есть у меня
static log4net.ILog log = log4net.LogManager.GetLogger("My_mega_logger");
Теперь я хочу, чтобы лог-файл назывался "log_My_mega_logger.log".

Я подумала-подумала, да и придумала решение. Заодно, кажется, разгадала секрет, зачем в конфиге нужен элемент logger. Итак, вот что получилось.

Для примера я создаю консольное приложение, и цепляю в References библиотеку log4net.

App.config

Добавляем в проект конфигурационный файл и пишем в нем, например, такое

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>
  <log4net debug="true">
    <appender name="SomeAppender" type="log4net.Appender.RollingFileAppender">
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
      <file value="Logs\some.log" />
      ...
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%-5p [%d] [%M] %m%n" />
      </layout>
    </appender>
    <appender name="OtherAppender" type="log4net.Appender.RollingFileAppender">
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
      <file value="Logs\other.log" />
      ...
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%-5p [%d] [%M] %m%n" />
      </layout>
    </appender>
    <root>
      <level value="ALL" />
    </root>
    <logger name="SomeLogger">
      <level value="ALL" />
      <appender-ref ref="SomeAppender" />
    </logger>
    <logger name="OtherLogger">
      <level value="ALL" />
      <appender-ref ref="OtherAppender" />
    </logger>
  </log4net>
  <appSettings>
    <add key="log4net.Internal.Quiet" value="true" />
  </appSettings>
</configuration>

Т.е. мы определили два Appender-а, которые завязаны на разные файлы, и два Logger-а, которые по-разному называются и завязаны на соответствующие Appender-ы.

В приложении

Класс нашего консольного приложения может выглядеть так:

class Program
{
  private static readonly ILog someLog = LogManager.GetLogger("SomeLogger");
  private static readonly ILog otherLog = LogManager.GetLogger("OtherLogger");

  static void Main(string[] args)
  {
    XmlConfigurator.Configure();

    someLog.Debug("Some message");
    otherLog.Debug("Other message");
  }
}

Немного по-индийски, но, в общем, работает. Когда программа отработает, одно сообщение запишется в один файл, другое - в другой.

понедельник, 2 июня 2008 г.

Отключение internal log в log4net

log4net имеет обыкновение выводить в консоль еще и свой собственный вывод. Выглядит это примерно так:
log4net: XmlHierarchyConfigurator: Configuration update mode [Merge].
log4net: XmlHierarchyConfigurator: Logger [root] Level string is [ALL].
log4net: XmlHierarchyConfigurator: Logger [root] level set to [name="ALL",value=-2147483648].
...
Называется это internal log. Когда мы пишем web-приложение, мы его не видим и не знаем. Но стоит начать писать консольное приложение, использующие log4net, как он тут же вылезает и начинает мешать. Для того, чтобы отключить, достаточно в секции appSettings конфигурационного файла приложения написать:
<add key="log4net.Internal.Quiet" value="true" />

пятница, 25 апреля 2008 г.

Написание своего фильтра для log4net

Например, я использую NHibernate. Он использует log4net чтобы выводить свои сообщения, а я использую log4net для своих целей. И куча хибернейтовских сообщений мне мешают. Очевидное решение данной проблемы - написать собственный фильтр. Фильтр должен наследоваться от абстрактного класса FilterSkeleton. Вот, что получилось у меня:

using System;
using log4net.Filter;

namespace Common
{
  class SourceFilter : FilterSkeleton
  {
    private string source;
    private bool acceptOnMatch;

    public string Source
    {
      get { return source; }
      set { source = value; }
    }

    public bool AcceptOnMatch
    {
      get { return acceptOnMatch; }
      set { acceptOnMatch = value; }
    }

    public override FilterDecision Decide(log4net.Core.LoggingEvent loggingEvent)
    {
      if (loggingEvent.LocationInformation.ClassName.Contains(source) == acceptOnMatch)
        return FilterDecision.Accept;
      else
        return FilterDecision.Deny;
    }
  }
}
После этого можно в Web.config добавить в appender следующее:
<filter type="Orpo.Common.SourceFilter">
  <Source value="NHibernate"/>
  <AcceptOnMatch value="false"/>
</filter>
Все, фильтр готов, сообщения из источника NHibernate никуда не выводятся.

среда, 9 апреля 2008 г.

Использование log4net

log4net - логгер для .net-приложений, позволяющий выводить любые сообщения в файл, несколько файлов, в БД или еще куда-нибудь. Кроме самого пользовательского сообщения, может вывести подробную информацию (время, класс, метод, и т.д., где был вызван вывод данного сообщения).

Подключение

Web Application

  1. Добавить log4net.dll в References приложения
  2. Добавить в AssemblyInfo.cs строку
    [assembly: log4net.Config.XmlConfigurator(Watch = true)]
    

Web Site

Просто переписать log4net.dll в каталог Bin

Настройка

Логгер настраивается в web.Config. Вначале в раздел configSections следует добавить строчку:

<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net">

Затем, нужно добавить раздел:

<log4net debug="true">
...
</log4net>

В этом разделе идет основная настройка логгера. В разделе могут быть потомки типа appender, logger и root. Назначение элемента logger пока что осталось для меня загадкой, так что рассмотрим остальные.

Appender

Для того, чтобы заставить log4net что-то куда-то писать, нужно определить Appender. Подходящее русское обозначение мне придумать не удалось, но суть в том, что он хранит настройки одного логгера. Типов appender-ов существует много (см. документацию), но мне довелось работать только с RollingFileAppender, который определяет логгер, пишущий в файл. Другие типы appender-ов позволяют писать в базу данных(AdoNetAppender), Windows EventLog (EventLogAppender) и т.д. Рассмотрим пример:

<appender name="ERRORFileAppender" type="log4net.Appender.RollingFileAppender">
  <lockingmodel type="log4net.Appender.FileAppender+MinimalLock"/>
  <file value="\Logs\errors.log" />
  <appendtofile value="true" />
  <rollingstyle value="Size" />
  <maxsizerollbackups value="10" />
  <maximumfilesize value="200MB" />
  <staticlogfilename value="true" />
  <filter> ... </filter>
  <layout> ... </layout>
</appender>

Назначение перечисленных Appender-а, в принципе, понятно. Следует отметить параметр lockingModel и его значение MinimalLock. Дело в том, что log4net во время работы приложения жестко лочит файл с логом, его нельзя ни открыть (разве что редактором Far-а), ни удалить. Поэтому, если очень хочется удалить лог web-приложения, нужно перезапускать IIS, что не есть хорошо. Данная настройка "разлочивает" лог, его свободно можно удалять и открывать хоть для записи.

Ключевыми настройками Appender-а являются Filter и Layout.

Filter

Данная настройка позволяет отфильтровывать сообщения по некоторому признаку. Для фильтра обязательно указывается тип. Существует несколько типов фильтров, можно также писать свои. Наиболее часто используемые фильтры - по типу сообщения (LevelRangeFilter и LevelMatchFilter) и по тексту сообщения (StringMatchFilter). Для начала, остановимся на типах (уровнях) сообщений. Сообщения в log4net делятся на несколько уровней:

  • ALL
  • DEBUG
  • INFO
  • WARN
  • ERROR
  • FATAL
  • OFF

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

Фильтр, выбирающий только ошибочные и фатальные сообщения:

<filter type="log4net.Filter.LevelRangeFilter">
  <levelMin value="ERROR"/>
  <levelMax value="FATAL"/>
</filter>

Фильтр, выбирающий только отладочные сообщения

<filter type="log4net.Filter.LevelMatchFilter">
  <levelToMatch value="DEBUG"/>
  <acceptOnMatch value="true"/>
</filter>

Layout

В разделе Layout определяется формат вывода сообщений в файл. Как ранее было сказано, log4net, помимо самого сообщения, может выводить кучу полезной информации. Layout может выглядеть, например, таким образом:

<layout type="log4net.Layout.PatternLayout">
  <conversionPattern value="%-5p [%d] [%C.%M] %m%n"/>
</layout>

В conversionPattern указано, какие значения и в каком формате будут выводиться в лог. Выходной лог с таким Layout-ом получится примерно таким:

DEBUG [2008-04-08 17:45:14,662] [TableTemplates.GetTemplate] Is visible: [visible]
DEBUG [2008-04-08 17:45:14,662] [TableTemplates.GetTemplate] Report Footer (rows:1)
DEBUG [2008-04-08 17:45:14,662] [TableTemplates.GetTemplate] Number of cells in row [2]
INFO  [2008-04-08 17:45:14,663] [ClientDataObjects.get_ClientObjects] Client object found in session

Значения ключей conversionPattern:

  • %p — Тип события лога (DEBUG, ERROR и т.д.)
  • %d — Дата и время, когда логгирующая функция была вызвана. Можно задавать различные форматы вывода даты и времени. К примеру, %d{HH:mm:ss,SSS} отображает только время
  • %C — Имя класса, в котором была вызвана какая-либо функция лога
  • %M — Имя метода
  • %m — Собственно, сообщение в лог
  • %F — Имя файла
  • %l — Номер строки
  • %r — Количество миллисекунд, прошедших с начала работы программы
  • %t — Имя потока
  • %n — Переход на новую строку
  • %% — Символ "%"

Root

Написать Appender-ы недостаточно для того, чтобы log4net начал писать логи. Нужно еще зарегистрировать их в корне лога, вот таким вот образом:

<root>
  <level value="WARN" />
  <appender-ref ref="ERRORFileAppender" />
  <appender-ref ref="DEBUGFileAppender" />
</root>

level - уровень, сообщения ниже которого не выводятся ни в один лог. Приведенная запись значит, что в логи не будут выводиться сообщения типа DEBUG и INFO.

Использование

Я делаю так. Пишу класс:

using log4net;
using log4net.Config;
using System;

///
/// Logger - shell for log4net logger
///
public static class Logger
{
  private static readonly ILog log = LogManager.GetLogger(typeof(Logger));

  public static ILog Log
  {
    get { return log; }
  }

  public static void InitLogger()
  {
    XmlConfigurator.Configure();
  }
}

Потом в Global.asax в метод Application_Start добавляю строчку:

Logger.InitLogger();

И подключение лога в код готово. После этого в любом месте кода можно написать что-то вроде:

Logger.Log.Debug("Some message");
Интерфейс ILog представляет различные методы для вывода сообщений различных типов. Рассмотрим некоторые методы:
Функции Описание Пример
Info(String message)

Debug(String message)

Warn(String message)

Error(String message)

Fatal(String message)
Вывод в лог сообщения message
Logger.Log.Info("Sample log message");
InfoFormat(String format, params object[] args)

DebugFormat(String format, params object[] args)

WarnFormat(String format, params object[] args)

ErrorFormat(String format, params object[] args)

FatalFormat(String format, params object[] args)
Вывод в лог сообщения c параметрами. Аналогично выводу сообщения String.Format(format, args).
Logger.Log.DebugFormat("Key={0}, Value={1}", key, value);
Info(String message, Exception e)

Debug(String message, Exception e)

Warn(String message, Exception e)

Error(String message, Exception e)

Fatal(String message, Exception e)
Вывод в лог сообщения с исключением. В лог помещается целиком текст исключения и stack trace.
try
{
  a = b / c;
}
catch (Exception e)
{
  Logger.Log.Error("Exception catched while calculation.", e);
}