среда, 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);
}

42 комментария:

  1. спасибо хорошая статья для быстрого старта )

    ОтветитьУдалить
  2. Рада, что Вам она оказалось полезной

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

    ОтветитьУдалить
  4. Отлично написала, легонькие повествование, мне понравилось.

    Тебе нужно книжки писать!

    ОтветитьУдалить
  5. Ой, ссылка на документацию поломалась. Вот и пример, почему вредны ссылки типа "тут" и "здесь".

    2 pylnik:
    Леша, ты ли это? :) Я тебе развернутый ответ написала отдельным постом

    2 Paul D.
    Спасибо :) Может, когда и возьмусь

    ОтветитьУдалить
  6. Возмись) Я вот второй раз твоим блог пользуюсь) с первого раза не запомнил)

    ОтветитьУдалить
  7. Привет!
    Есть вопрос по теме. Я писал обычно на C++, поэтому многое в C# в новинку, особенно обилие пространств. Хочу использовать log4net в своём .NET 3.5-приложении, но не понимаю, как подключить эту библиотеку. И ещё не понял какие пространства нужны интерфейсы ILog. Подскажи, пожалуйста :)

    ОтветитьУдалить
  8. 2 andrej-b
    В любом проекте на .NET есть такая штука References (Видна в иерархии проекта). Туда заносятся ссылки на используемые в проекте сборки. Так вот, чтобы использовать log4net, нужно сначала подключить соответствующую библиотеку к проекту. Для этого нужно сделать Add Reference -> Browse и указать dll-ку с log4net. После этого станет можно подключать пространства имен из этой сборки и использовать классы.
    ILog находится в пространстве имен log4net. Кстати, using-и для пространств имен Студия умеет генерить сама.
    Я ответила на Ваш вопрос?

    ОтветитьУдалить
  9. darja, спасибо! В принципе это понимаю, просто думал, что log4net.dll это какая-то стандартная библиотека. Но насколько я понял, она не стандартная и её надо надыбать где-то.

    ОтветитьУдалить
  10. Извиняюсь за нозойливость :) но где найти описание тэгов конфигурационного файла log4net? Покопался, нашёл log4net SDK Reference, но не нашёл там ответ на свой вопрос. Не подскажешь?

    ОтветитьУдалить
  11. 2 andrej-b
    тут можно скачать log4net
    документация по конфигу
    Надо сказать, документация так себе, так что кое-что приходилось узнавать по блогам и исходникам самого log4net

    ОтветитьУдалить
  12. Спасибо, прикрутила себе.
    Единственный комментарий - инициализацию можно сделать прямо в статическом конструкторе класса Logger, а не в Global.asax

    ОтветитьУдалить
  13. Спасибо.
    Хорошая заметка.

    ОтветитьУдалить
  14. Отличная статья!!! Спасибо большое!

    ОтветитьУдалить
  15. Хорошая статья.
    Скажите, Дарья, а есть возможность писать лог каждый день в новый файл?
    Что бы частью имени файла была дата?

    При больших количествах сообщений найти что-то в одном файле за пару месяцев просто не реально.

    ОтветитьУдалить
  16. Хорошая статья. Русcкоязычных материалов по log4net не так уж и много...

    ОтветитьУдалить
  17. Всем спасибо за отзывы!

    2 Анонимный (который предпоследний)
    Наконец-то руки дошли Вам написать ответ. Вот он: http://megadarja.blogspot.com/2009/08/log4net.html

    ОтветитьУдалить
  18. Большое спасибо!
    А читать и парсить он может из своих файлов?

    ОтветитьУдалить
  19. 2 Roman:
    А вот это вряд ли. Разбирать текст - это уже не задача логгера. Для этого есть Perl.

    ОтветитьУдалить
  20. Добрый день, Дарья. Спасибо за статью. А как можно логгировать все исключения(даже те, которые не перехватываются)?

    ОтветитьУдалить
  21. Премного благодарен за такой труд.
    С вашего блога быстренько удалось разобраться с log4net. Детали смотрел в мануалах родных, но было уже значительно проще. Спасибо :)

    ОтветитьУдалить
  22. Спасибо большое за статью, все просто и понятно

    ОтветитьУдалить
  23. О спасибо вам, вы спасли мои нервы

    ОтветитьУдалить
  24. Дашуля - умничко! :)

    ОтветитьУдалить
  25. спасибо большое за заметку

    ОтветитьУдалить
  26. Замечательная и полезная статья!

    ОтветитьУдалить
  27. Спасибо, как раз появилась необходимость логи писать в очередной раз, решил вместо своих велосипедов что-то попробовать из имеющегося.

    ОтветитьУдалить
  28. добавлю про полезную (imho) возможность, вести отдельные секции логгирования (в добавок к root):
    ‹logger name="Какая-то-Секция"›
    ‹level value="WARNING" /›
    ‹appender-ref ref="Какой-то Appender /›
    ‹/logger›
    в коде, соответственно, создаем отдельный экземпляр
    private static readonly ILog log = LogManager.GetLogger("Какая-то-Секция");

    ОтветитьУдалить
  29. У меня вопрос: не подскажете, почему у меня с ошибками создается файл логов. Но не один, а несколько! причем изменяться могут все одновременно, могут по разному... в чем прикол?

    ОтветитьУдалить
  30. 2Vera.
    Думаю, что без текста ошибки говорить о чём-то конкретном сложно.
    Однажды мне пришлось долго повозиться с тем, что Lucene временами не могла создавать/удалять свои lock-файлы. Выяснилось, что файлы блокировал антивирус.

    ОтветитьУдалить
  31. Я неправильно расставила акценты в своем вопросе. Ошибки - да, без них никак. Они обычные.. То доступа в базу нет, то запись не найдена, то еще что-то. Не в этом суть.
    А в том, что сначала он создает один файл с именем errors_2010_10_12.log,
    затем в какой-то момент начинает писать в файл errors_2010_10_12.log.log.
    Потом пишеть одновременно и там, и там...

    я думала сначала, что идет ограничение на размер лога, но нет... один файл - около 1,5 мб, второй - перевалил за 20...

    ОтветитьУдалить
  32. Этот комментарий был удален автором.

    ОтветитьУдалить
  33. Этот комментарий был удален автором.

    ОтветитьУдалить
  34. сделал все как описано в статье, но почему-то у меня не получилось сделать так чтобы лог файл сохранялся по пути который указан в примере. Сделал так что пишет в файл в корневой папке.

    И еще вылезла следующая проблема - в лог файле вместо текста собщений -- "??"
    подскажите где искать ошибку..

    ОтветитьУдалить
  35. Добрый день. Мне очень помогла Ваша статья когда я начал создавать логгер для моей аппликации.
    Но у меня возникло ряд проблем.
    1. Не могу программно задать maximumFileSize, обшарил весь инет. Сделал по аналогии с установкой "LogName" но это не работает.
    XML


    C#
    log4net.GlobalContext.Properties["MSize"] = max_size;
    XmlConfigurator.Configure();

    2. Можно ли задать фильтр в котором вывод сообщений осуществляется свободным списком (например DEBUG, WARN и FATAL) а не через levelMin и levelMin?

    ОтветитьУдалить
  36. 1. Посмотрела в исходники -- вроде есть у RollingFileAppender-а такое свойство MaxFileSize. Другое дело, зачем его задавать программно.

    2. Можно свой написать, это несложно. У меня даже где-то статья про это есть.

    ОтветитьУдалить
  37. 1 Ну здесь довольно просто. Необходимо дать возможность менять размер и количество файлов конечному пользователю в зависимости от возможностей его компа (дисковое пространство например).
    2. Как сделать свободный список я разобрался но опять проблема. Как его менять динамически. Нет надобности забивать лог до того как произойдет сбой. А вот когда он произошел хорошо бы потом протестировать машину с полным логом включая нажатие всех кнопок на форме и всех других действий пользователя..

    Короче хотелось бы понять как можно программно менять любой из описных в статье параметров.

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

    можете, пожалуста помочь?

    ОтветитьУдалить
  39. если кому понадобится:
    в конфиге был прописан копипаст с нераспознаными символами перед началом тэгов, никакого странного поведения логгер не проявлял, просто писал в дефолтовом фомрате. проверив в другом текстовом редакторе (не в VS) заметил подвох, стёр символы, начало работать отлично.

    ОтветитьУдалить
  40. Спасибо за статью, все просто и понятно.
    У меня вопрос.
    Можно ли в conversionPattern задать ключ имени файла без полного пути? %F, %file - выводит полный путь.

    ОтветитьУдалить
  41. У меня тоже проблема с "??". Помогите, пожалуйста.

    Максим Т комментирует...

    И еще вылезла следующая проблема - в лог файле вместо текста собщений -- "??"
    подскажите где искать ошибку..

    ОтветитьУдалить

Пожалуйста, пишите содержательные комментарии и соблюдайте вежливость.