Denis Gladkikh
Russian   |  English

Конфигурирование log4net runtime & Получаем больше информации для тестов

Для логгирования приложений, написанных под .NET большинство разработчиков используют log4net (а другие разработчики используют log4 под другие платформы). Но так, к сожалению, бывает не везде. Так, например, в нашем проекте до сегодняшнего дня мы использовали свою самописную библиотеку для логгирования. Была она, видимо, написана давно нашим team leader, но так как нас сейчас покидает текущий team leader, мы решили так же распрощаться с некоторым legacy кодом, который был дорог нашему лидеру, но который не особо то мы хотим поддерживать. Сразу уточню, что код написан отлично, и наш team leader – отличный кодер, но log4net имеет больше преимуществ, а текущий логгер нам приходится частенько дописывать функционалом, который уже давно есть в log4net.

Большинство конфигурируют log4net при помощи config файла (попросту обычный xml файл), и скорее всего у фирм такой файл уже написан давно, и его просто копируют из проекта в проект, изменяя названия файлов или подправляя формат сообщений. А чтобы написать хороший конфиг рекомендую посмотреть на примеры, который предоставляет сама библиотека log4net. Самый используемый Appender – это, наверное, RollingFileAppender. Для этого сайта у меня, к примеру, такой упрощенный config:

<log4net>
    <!-- contexts:
    -->
    <!-- Setup the root category, add the appenders and set the default priority 
            Off, Fatal, Error, Warn, Info, Debug, All.-->
    <root>
        <level value="Error" />
        <appender-ref ref="RollingFileAppender" />
        <!--<appender-ref ref="TraceAppender" />-->
    </root>
            
    <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
        <param name="File" value="App_Data\\Logs\\personalweb.log" />
        <param name="AppendToFile" value="true" />
        <param name="RollingStyle" value="Size" />
        <param name="MaxSizeRollBackups" value="10" />
        <param name="MaximumFileSize" value="1MB" />
        <param name="StaticLogFileName" value="true" />
        <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
        <layout type="log4net.Layout.PatternLayout">
            <header value="[Application Starts]&#13;&#10;" />
            <footer value="[Application Stops]&#13;&#10;" />
            <param name="ConversionPattern" value="%date{yyyy-dd-MM HH:mm:ss} [%thread] %-5level %logger{3} - %message%newline" />
        </layout>
    </appender>
    
    <appender name="TraceAppender" type="log4net.Appender.TraceAppender">
        <layout type="log4net.Layout.PatternLayout">
            <param name="ConversionPattern" value="%date{yyyy-dd-MM HH:mm:ss} [%thread] %-5level %logger{3} - %message%newline" />
        </layout>
    </appender>
 
</log4net>

Логгер по большому счету должен спасать тогда, когда на сайте или в приложении валятся ошибки уже на live инсталяции, а понять о сути их происхождения сложно, тогда грамотно расставленные записи в лог помогут справиться нам с этой задачей. Но можно получить от него и больше пользы еще до этого, для этого мы заставим наш код, который выполняется в тестах, писать в Output окно Visual Studio. Я делаю это потому, что я тот программист, который запускает проект в режиме отладки только в крайнем случае, сначала я пишу код/тесты, проверяю их, потом строю приложение – смотрю, что все работает и с легкостью комичу код. И только в случае непонятных мне ошибок, странного поведения или memory-leak я начинаю отлаживать приложение. Такой подход очень сильно мне экономит время.

Так вот, тесты в моем процессе разработки играют большую роль, и, конечно же, я не хочу запускать тесты в режиме отладке, но хочу видеть как можно больше информации в output окне, потому мой конфигуратор для log4net выглядит следующим образом

public static class Log4NetUtils
{
    private static bool _isConfigurated;
    private static readonly object _locker = new object();
 
    /// <summary>
    ///     Load configuration from config file
    /// </summary>
    public static void InitLogger()
    {
        InitLogger(false);
    }
 
    /// <summary>
    ///     Load configuration from config file when <paramref name = "useBasicConfiguration" /> = false 
    ///     or set basic configuration with trace appender if <paramref name = "useBasicConfiguration" /> = true. 
    ///     Method is thread safe only for x86 and x64 architectures and Microsoft .NET platform.
    /// </summary>
    /// <param name = "useBasicConfiguration"></param>
    public static void InitLogger(bool useBasicConfiguration)
    {
        if (!_isConfigurated)
        {
            lock (_locker)
            {
                if (!_isConfigurated)
                {
                    if (useBasicConfiguration)
                        ConfigureRunTimeFromBasic();
                    else
                        ConfigureFromFile();
                    _isConfigurated = true;
                }
            }
        }
    }
 
    private static void ConfigureRunTimeFromBasic()
    {
        TraceAppender traceAppender = new TraceAppender();
 
        PatternLayout patternLayout = new PatternLayout
                                          {
                                              ConversionPattern =
                                                  "%date{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{2} - %message%newline"
                                          };
        patternLayout.ActivateOptions();
 
        traceAppender.Layout = patternLayout;
 
        BasicConfigurator.Configure(traceAppender);
    }
 
    private static void ConfigureFromFile()
    {
        string fileConfiguration = ConfigurationManager.AppSettings.Get("Log4NetConfigurationFile");
        if (string.IsNullOrWhiteSpace(fileConfiguration))
            fileConfiguration = "log4net.config";
        XmlConfigurator.ConfigureAndWatch(new FileInfo(AppDomain.CurrentDomain.BaseDirectory + fileConfiguration));
    }
}

Соответственно для самого приложения вызывается метод Log4NetUtils.InitLogger() в Global.asax для веб приложения или в Main функции Windows-приложения, а вот в SetUp() для тестов я вызываю Log4NetUtils.InitLogger(true), который и включает мне логгирование всего в Output окно VS для тестов:

[TestFixture]
class ParserSpec
{
    // ReSharper disable InconsistentNaming
    [SetUp]
    public void SetUp()
    {
        Log4NetUtils.InitLogger(false);
    }
 
    [Test]
    public void parse_string_to_int()
    {
        int i = "1".To<int>();
        Assert.AreEqual(i, 1);
    }
    
    //......
 
    // ReSharper enable InconsistentNaming
}

На самом деле у меня есть еще файл BaseSpec, в котором уже существует SetUp метод и делает он не только регистрацию log4net. А в целом получается очень удобно:

VS Output

P.S. Америку я тут не открывал, просто сохранил для потомков (будущих проектов) :)


Вас также может заинтересовать

rss twitter

Комментарии (16)

Dmitry ( ) #
avatar
Была похожая ситуация с проектом, но остановился на NLog, послушавшись статьи с Хабра (http://habrahabr.ru/blogs/net/98638/), так что сравнений показать особо не могу, а интересно бы..Что касается логгера - действительно штука на порядок удобнее самописного, легко переносится с проекта на проект и очень удобна на стадии сдачи промежуточных версии клиенту(когда везде напихано блоков обработки/поимки исключений и все это сохраняется туда, куда сконфигурирован сохранять объект логгера)
Andrey ( ) #
avatar
Ранее использовал NLog, в текущем проекте log4net но думаю заменить его на тот же NLog. Основной аргумент: отсутствует развитие и поддержка. На днях делали оценку перевода проекта под .NET Framework 4 c поддержкой Client Profile для клиентской части. Оказалось что с этим в log4net большие проблемы :(
Тем более что log4net уже выкинули в NHibernate 3, а NLog сейчас снова активно развивается и проблем с 4-м фреймворком не имеет.
Denis Gladkikh ( ) #
avatar
Andrey, Dmitry, про NLog я тоже слышал, но я пока особых проблем с log4net не видел, и решил пока остановится все таки на нем, но то, что он не развивается, конечно печально.

Вообще, вроде исходники log4net же есть в сети, почему бы кому-нибудь не взять их и не стать развивать дальше. :) Где они, добрые люди?
osmirnov ( ) #
avatar
В нашем текущем проекте логи нужны на каждый день. И если вам пригодится, то вот решение от лучших собаководов:
osmirnov ( ) #
avatar
Извиняюсь, парсер съел всю разметку. Попытка №2 (параметр в .config):
datePattern value="yyyy-MM-dd'.log'"
Сергей Попов ( ) #
avatar
Вот мне потребовалось как-то изменить алгоритм именования файлов для rolling file appender..

Посмотрел я на кишки log4net, и пришел к выводу, что объектная модель у него прямо скажем, не очень. Для решения этой задачи мне пришлось полностью скопировать код appender'а из поставки, т.к. самый важный метод в базовом appender был не виртуальный (можно, конечно, было просто добавить virtual для этого метода, но я стараюсь не менять ничего в кодах сторонних библиотек, если без этого можно обойтись, чтобы не усложнять себе жизнь при upgrade).

В общем, есть у этого самого log4net свои issues, и их немало.
Denis Gladkikh ( ) #
avatar
osmirnov, ага, про datePattern знаю. полезная вещь тоже.

Сергей Попов, багов то там не мало это тоже верно, все они уже давно висят в JIRA для log4net. А что потребовалось изменить, то как он называет старые файлы?
Сергей Попов ( ) #
avatar
Denis Gladkikh
Вот именно, что давно висят ;-)

А потребовалось изменить я уже не точно помню, по-моему в той версии rolling file appender дату всегда добавлял конец имени файла, а мне нужно было, чтобы имя заканчивалось на «.log».
Demigor ( ) #
avatar
NLog написан .Net профессионалами с нуля, log4net - банальный порт Java кода на C# с последующим исправлением ошибок.

Без глубокого знания архитектуры .Net framework, невозможно написать что-то более-менее сложное без серьезных косяков. Поэтому log4net, мы даже не рассматривали как логгер, достойный внедрения.
Denis Gladkikh ( ) #
avatar
Demigor, насчет того, что профессионалы трудились над NLog - сказать мне что-то сложно, всякое бывает. А вот удачных портов с Java на C# предостаточно, тот же NHibernate чего стоит. log4net ни разу не подвел, отличное решение.
Чак Норрис ( ) #
avatar
@Demigor

> NLog написан .Net профессионалами с нуля, log4net - банальный порт Java кода на C# с последующим исправлением ошибок.

На чём оснавано сие утверждение? Информация из Тонкого Мира? А вы вкурсе, что NLog пишет по большей части один Jaroslaw Kowalski? И что log4net находится под покровительством Apache со всеми вытекающими (5 активных коммитеров, около десятва сторонних контрибьютеров)?
Denis Gladkikh ( ) #
avatar
c Чаком не поспоришь. :)
skorpk ( ) #
avatar
Использую enterprise library v4 в текущем проекте. Доволен.
Mark Belling ( ) #
avatar
http://dotnetlog.theobjectguy.com
Denis Gladkikh ( ) #
avatar
Mark Belling, 5$ за логгер? шутите? зачем? Это только для некомерческого использования. А для комерческого 49$, просто смешно и нет смысла, когда есть очень хорошие конкуренты.
Bats Ihor ( ) #
avatar
у нас на проекте недавно заметил один глюк с log4net если пробовать записать что-то в лог с другого процесса под х64 ОС, то ничего не пишется, а на 32 все ок. Через это пришлось практически прикинуться гадалкой... Баг с трудом но пофиксался.
Добавить комментарий

Если вы хотите получать уведомления о новых комментариях к данному топику, укажите, пожалуйста, email и отметьте соответствующий пункт в форме. Если вы хотите добавить код в тексте комментария, то заключите его внутри тега [code]...[/code], более того можно уточнить язык, на котором написан данный код при помощи [code cs]...[/code], где вместо cs могут быть cs, html, xml, java, js, php, sql, cpp, css.

 

busy