Куда помещать код логирования?
От: stomsky Россия  
Дата: 29.02.12 07:22
Оценка:
Доброго времени суток!
Пишу сейчас один сервис, который будет круглосуточно выполнять всякие полезные действия.
Надо вести подробный лог.
Сейчас кодирую и вижу как буквально на глазах "полезный" код засоряется кодом логирования.
Подскажите, плиз, как вы боритесь с этой проблемной?
Про аспекты я, конечно, слышал, но не хочется под это дело сторонние библиотеки привлекать.
И, кстати, какой вариант код по-вашему предпочтительнее (на осмысленность сообщений пожалуйста внимания не обращайте):

class MyClass
{
  ILogger _logger;
  ...
  void DoAnything()
  {
    _logger.Info("Начато выполнение действия...");

    _logger.Info("Выполняется DoAction1...");
    DoAction1();
    _logger.Info("DoAction1 выполнено успешно");

    _logger.Info("Выполняется DoAction2...");
    DoAction2();
    _logger.Info("DoAction2 выполнено успешно");

    _logger.Info("Выполняется DoAction3...");
    DoAction3();
    _logger.Info("DoAction3 выполнено успешно")

    _logger.Info("Действие выполнено успешно");
  }
  void DoAction1()
  { ... }
  void DoAction2()
  { ... }
  void DoAction3()
  { ... }
}


или такой:

class MyClass
{
  ILogger _logger;
  ...
  void DoAnything()
  {
    _logger.Info("Начато выполнение действия...");

    DoAction1();
    DoAction2();
    DoAction3();

    _logger.Info("Действие выполнено успешно");
  }
  void DoAction1()
  {
    _logger.Info("Выполняется DoAction2...");
    ...
    _logger.Info("DoAction1 выполнено успешно");
  }
  void DoAction2()
  {
    _logger.Info("Выполняется DoAction2...");
    ...
    _logger.Info("DoAction1 выполнено успешно");
  }
  void DoAction3()
  {
    _logger.Info("Выполняется DoAction3...");
    ...
    _logger.Info("DoAction3 выполнено успешно");
  }
}

В первом варианте, вроде бы логирование более централизованно, но код уж больно замусорен.
Во втором случае код логирования слишком размазан...

Или все-таки на аспекты смотреть? Кто-нибудь этот функционал из BlToolkit пробовал? Я его для доступа к данным использую...
Красота — наивысшая степень целесообразности. (c) И. Ефремов
Re: Куда помещать код логирования?
От: ksg71 Германия  
Дата: 29.02.12 07:46
Оценка: +1
Здравствуйте, stomsky, Вы писали:


S>В первом варианте, вроде бы логирование более централизованно, но код уж больно замусорен.

S>Во втором случае код логирования слишком размазан...

S>Или все-таки на аспекты смотреть? Кто-нибудь этот функционал из BlToolkit пробовал? Я его для доступа к данным использую...


что логируем, там и пишем,в данном случае первый вариант, аспекты нужны, но не для трассировки, а в примерах именно она (никаких "бизнес" задач не решается)
"замусоривание" трейсами — вполне нормальное явление, взгляните на код, ну например System.Net.SmtpClient
Das Reich der Freiheit beginnt da, wo die Arbeit aufhört. (c) Karl Marx
Re[2]: Куда помещать код логирования?
От: stomsky Россия  
Дата: 29.02.12 08:38
Оценка:
Здравствуйте, ksg71, Вы писали:

K>что логируем, там и пишем,в данном случае первый вариант, аспекты нужны, но не для трассировки, а в примерах именно она (никаких "бизнес" задач не решается)

Ну в случае сервиса, если надо не просто знать нормально или НЕ нормально отработало, но еще и иметь возможность выяснить "почему отработало так, а не иначе" (я в том числе и о бизнес-логике говорю), разница между трассировкой и логами ИМХО невелика. Логи шибко подробные должны быть...

K>"замусоривание" трейсами — вполне нормальное явление, взгляните на код, ну например System.Net.SmtpClient

Слабое утешение... Потом код читать сложно...
Красота — наивысшая степень целесообразности. (c) И. Ефремов
Re[3]: Куда помещать код логирования?
От: ksg71 Германия  
Дата: 29.02.12 08:47
Оценка:
Здравствуйте, stomsky, Вы писали:

S>Здравствуйте, ksg71, Вы писали:


K>>что логируем, там и пишем,в данном случае первый вариант, аспекты нужны, но не для трассировки, а в примерах именно она (никаких "бизнес" задач не решается)

S>Ну в случае сервиса, если надо не просто знать нормально или НЕ нормально отработало, но еще и иметь возможность выяснить "почему отработало так, а не иначе" (я в том числе и о бизнес-логике говорю), разница между трассировкой и логами ИМХО невелика. Логи шибко подробные должны быть...

K>>"замусоривание" трейсами — вполне нормальное явление, взгляните на код, ну например System.Net.SmtpClient

S>Слабое утешение... Потом код читать сложно...

ну почему так отработало, а не иначе, надо не логами выяснять, а тестами (я о бизнес логике)
а для диагностики — трейсы, и AOP тут, кмк, ни к чему
Das Reich der Freiheit beginnt da, wo die Arbeit aufhört. (c) Karl Marx
Re[4]: Куда помещать код логирования?
От: ksg71 Германия  
Дата: 29.02.12 08:59
Оценка:
Здравствуйте, ksg71, Вы писали:

K>Здравствуйте, stomsky, Вы писали:


S>>Здравствуйте, ksg71, Вы писали:


K>>>что логируем, там и пишем,в данном случае первый вариант, аспекты нужны, но не для трассировки, а в примерах именно она (никаких "бизнес" задач не решается)

S>>Ну в случае сервиса, если надо не просто знать нормально или НЕ нормально отработало, но еще и иметь возможность выяснить "почему отработало так, а не иначе" (я в том числе и о бизнес-логике говорю), разница между трассировкой и логами ИМХО невелика. Логи шибко подробные должны быть...

K>>>"замусоривание" трейсами — вполне нормальное явление, взгляните на код, ну например System.Net.SmtpClient

S>>Слабое утешение... Потом код читать сложно...

K>ну почему так отработало, а не иначе, надо не логами выяснять, а тестами (я о бизнес логике)

K>а для диагностики — трейсы, и AOP тут, кмк, ни к чему

хотя, конечно, согласен — всех ситуаций не отловить,
но и аспектами можно код усложнить и замусорить
Das Reich der Freiheit beginnt da, wo die Arbeit aufhört. (c) Karl Marx
Re[3]: Куда помещать код логирования?
От: Nuseraro Россия  
Дата: 29.02.12 09:27
Оценка: 7 (2)
Здравствуйте, stomsky, Вы писали:

S>Слабое утешение... Потом код читать сложно...


Я вот честно говоря не нашел в литературе подтверждение своих наработок, но давно считаю, что трассировка начала и конца функции — это плохая практика. Такое трассирование само по себе мусор, поэтому и код замусоривается. Я ниже написал какую-то конкретику. Мб она для вас тоже не подойдет. Но ключевая идея — не трассируйте тогда, когда практически очевидно, что все 3 трейса например или пройдут одновременно или не пройдут, кроме случая очевидной описки. Каждый трейс должен быть индивидуально полезен, особенно если вы его пишете вручную, а не юзаете модные библиотеки.


Наиболее полезные трэйсы/логи:
1) утверждения на предусловия
        public bool IsNeedToPayBonus(Context context)
        {
            if (context == null)
            {
                Warning("Я имел в виду что тут контекст всегда задан");
                return;
            }

            if (context.CurrentOrder == null)
            {
                Warning("Я имел в виду что тут у контекста есть ордер");
                return;
            }

            return context.CurrentOrder.Price > 1000;
        }

2) условная логика
        public bool IsNeedToPayBonus(Context context)
        {
            //...
            if (context.CurrentOrder.IsPremium)
            {
                Trace("Используется алгоритм премиального рассчета");
                return context.CurrentOrder.Price > 100;
            }
            else
            {
                Trace("Используется алгоритм обычного рассчета");
                return context.CurrentOrder.Price > 1000;
            }            
        }

3) Логирование ошибки
        public bool IsNeedToPayBonus(Context context)
        {
            try
            {
                //...
            }
            catch (Exception e)
            {
                Error(e.Message + e.StackTrace);
                throw;
            }
        }

4) Трассировка конкретных значений, например, при взаимодействии с источниками данных. Короче тех значений, которые потом можно как-то где-то посмотреть
        public bool IsNeedToPayBonus(Context context)
        {    
            //...
            Save(context.CurrentOrder);
            Trace("Сохранил в базу ордер {0}. Цена - {1}", context.CurrentOrder.Id, context.CurrentOrder.Price);
            //...
        }
Homo Guglens
Re[4]: Куда помещать код логирования?
От: stomsky Россия  
Дата: 29.02.12 09:57
Оценка:
Здравствуйте, ksg71, Вы писали:

K>ну почему так отработало, а не иначе, надо не логами выяснять, а тестами (я о бизнес логике)

Тестирование не прокатит. Попробую изложить подробности вкратце...
Мой сервис занимается перекачкой данных из одной базы данных (древнючей-древнючей), назовем ее "Источник", в другую (MS SQL), назовем "Приемник".
Если в "Источнике" появилась новая запись, то она через некоторое время T должна оказаться в "Приемнике".
И в "Источнике" и в "Приемнике" постоянно работают пользователи.
В идеале, после копирования строки в "Приемник", пользователи не должны вносить изменения в "Источнике".
Но технически реализовать такое невозможно.
Единственный разумный выход — пометить измененную строку в "Приемнике", а GUI приложения, работающего с БД "Приемника", просигнализирует пользователю о недопустимой ситуации, чтобы дальше включился "генератор докладных записок" (ну т.е. пусть пользователи, работающие с "Приемником" предъявляют претензии пользователям, работающим с "Источником").
Так вот, в логе хотелось бы отразить обнаружение факта изменения скопированной строки.
И таких "мелочей", которых быть не должно, но они есть, потому что недопустить их возникновения НЕ возможно, много.
Поэтому лог должен быть подробный.
Красота — наивысшая степень целесообразности. (c) И. Ефремов
Re[5]: Куда помещать код логирования?
От: ksg71 Германия  
Дата: 29.02.12 10:21
Оценка:
Здравствуйте, stomsky, Вы писали:

S>Здравствуйте, ksg71, Вы писали:


K>>ну почему так отработало, а не иначе, надо не логами выяснять, а тестами (я о бизнес логике)

S>Тестирование не прокатит. Попробую изложить подробности вкратце...
S>Мой сервис занимается перекачкой данных из одной базы данных (древнючей-древнючей), назовем ее "Источник", в другую (MS SQL), назовем "Приемник".
S>Если в "Источнике" появилась новая запись, то она через некоторое время T должна оказаться в "Приемнике".
S>И в "Источнике" и в "Приемнике" постоянно работают пользователи.
S>В идеале, после копирования строки в "Приемник", пользователи не должны вносить изменения в "Источнике".
S>Но технически реализовать такое невозможно.
S>Единственный разумный выход — пометить измененную строку в "Приемнике", а GUI приложения, работающего с БД "Приемника", просигнализирует пользователю о недопустимой ситуации, чтобы дальше включился "генератор докладных записок" (ну т.е. пусть пользователи, работающие с "Приемником" предъявляют претензии пользователям, работающим с "Источником").
S>Так вот, в логе хотелось бы отразить обнаружение факта изменения скопированной строки.
S>И таких "мелочей", которых быть не должно, но они есть, потому что недопустить их возникновения НЕ возможно, много.
S>Поэтому лог должен быть подробный.

почему не прокатит?

сервис получает строку из источника, проверяет есть ли версия в приемнике, если есть — помечает и бросает исключение, нет — сохраняет в приемнике,
вся эта логика легко юнит-тестируется (при должном дизайне сервиса), а логирование сводится к минимуму
имхо и с остальными "мелочами" можно также поступить
Das Reich der Freiheit beginnt da, wo die Arbeit aufhört. (c) Karl Marx
Re[6]: Куда помещать код логирования?
От: stomsky Россия  
Дата: 29.02.12 10:29
Оценка:
Здравствуйте, ksg71, Вы писали:

K>почему не прокатит?


K>сервис получает строку из источника, проверяет есть ли версия в приемнике, если есть — помечает и бросает исключение, нет — сохраняет в приемнике,

K>вся эта логика легко юнит-тестируется (при должном дизайне сервиса), а логирование сводится к минимуму
K>имхо и с остальными "мелочами" можно также поступить
Да я не об этом!
Просто в "Приемнике" есть битовый флаг "Запись изменена в 'Источнике'".
Но что именно изменилось? Какое именно поле?
Вот в лог я и напишу, что у платежного поручения изменилась, например, сумма.
В моем случае лог ведется не только и не столько для отладки и технической диагностики, но и с целью выявления неких обстоятельств в сфере бизнес-логики.
Красота — наивысшая степень целесообразности. (c) И. Ефремов
Re[7]: Куда помещать код логирования?
От: ksg71 Германия  
Дата: 29.02.12 10:42
Оценка:
Здравствуйте, stomsky, Вы писали:

S>Здравствуйте, ksg71, Вы писали:


K>>почему не прокатит?


K>>сервис получает строку из источника, проверяет есть ли версия в приемнике, если есть — помечает и бросает исключение, нет — сохраняет в приемнике,

K>>вся эта логика легко юнит-тестируется (при должном дизайне сервиса), а логирование сводится к минимуму
K>>имхо и с остальными "мелочами" можно также поступить
S>Да я не об этом!
S>Просто в "Приемнике" есть битовый флаг "Запись изменена в 'Источнике'".
S>Но что именно изменилось? Какое именно поле?
S>Вот в лог я и напишу, что у платежного поручения изменилась, например, сумма.
S>В моем случае лог ведется не только и не столько для отладки и технической диагностики, но и с целью выявления неких обстоятельств в сфере бизнес-логики.


ну и какие после этого вопросы, куда помещать код логирования?
Das Reich der Freiheit beginnt da, wo die Arbeit aufhört. (c) Karl Marx
Re[4]: Куда помещать код логирования?
От: stomsky Россия  
Дата: 29.02.12 10:45
Оценка:
Здравствуйте, Nuseraro, Вы писали:

N>Я вот честно говоря не нашел в литературе подтверждение своих наработок, но давно считаю, что трассировка начала и конца функции — это плохая практика. Такое трассирование само по себе мусор, поэтому и код замусоривается.

Гм... Об этом я, честно говоря, не задумывался... Надо посмотреть на проблему с этой стороны. Уже спасибо, за свежую мысль!

N>
N>        public bool IsNeedToPayBonus(Context context)
N>        {
N>            if (context == null)
N>            {
N>                Warning("Я имел в виду что тут контекст всегда задан");
N>                return;
N>            }

N>            if (context.CurrentOrder == null)
N>            {
N>                Warning("Я имел в виду что тут у контекста есть ордер");
N>                return;
N>            }

N>            return context.CurrentOrder.Price > 1000;
N>        }
N>

А я в таких случаях ArgumentNullException'ы выбрасываю. Потому как "context == null" — в данном случае ИМХО откровенный косяк.

А здесь:
N>2) условная логика
N>
N>        public bool IsNeedToPayBonus(Context context)
N>        {
N>            //...
N>            if (context.CurrentOrder.IsPremium)
N>            {
N>                Trace("Используется алгоритм премиального рассчета");
N>                return context.CurrentOrder.Price > 100;
N>            }
N>            else
N>            {
N>                Trace("Используется алгоритм обычного рассчета");
N>                return context.CurrentOrder.Price > 1000;
N>            }            
N>        }
N>

и здесь:
N>4) Трассировка конкретных значений, например, при взаимодействии с источниками данных. Короче тех значений, которые потом можно как-то где-то посмотреть
N>
N>        public bool IsNeedToPayBonus(Context context)
N>        {    
N>            //...
N>            Save(context.CurrentOrder);
N>            Trace("Сохранил в базу ордер {0}. Цена - {1}", context.CurrentOrder.Id, context.CurrentOrder.Price);
N>            //...
N>        }
N>


как раз и есть тот мусор от которого хотелось бы избавиться!!! (это я про "Trace")
Это как лишние комментарии: из кода и так понятно что там делается! Только код читать мешают.
Но в лог-то вывести надо...
Красота — наивысшая степень целесообразности. (c) И. Ефремов
Re: Куда помещать код логирования?
От: adontz Грузия http://adontz.wordpress.com/
Дата: 29.02.12 10:46
Оценка:
Здравствуйте, stomsky, Вы писали:

Первый вариант не позволит настраивать журналирование по классам.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[8]: Куда помещать код логирования?
От: stomsky Россия  
Дата: 29.02.12 10:52
Оценка:
Здравствуйте, ksg71, Вы писали:

K>ну и какие после этого вопросы, куда помещать код логирования?


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

Ну ладно... уговорили: я, как обычно, сам не знаю чего хочу!
Красота — наивысшая степень целесообразности. (c) И. Ефремов
Re[2]: Куда помещать код логирования?
От: stomsky Россия  
Дата: 29.02.12 10:54
Оценка:
Здравствуйте, adontz, Вы писали:

A>Первый вариант не позволит настраивать журналирование по классам.

Не понял, что значит словосочетание "журналирование по классам"... По классам чего? Или по классам каким?
Красота — наивысшая степень целесообразности. (c) И. Ефремов
Re[3]: Куда помещать код логирования?
От: adontz Грузия http://adontz.wordpress.com/
Дата: 29.02.12 10:56
Оценка: -1
Здравствуйте, stomsky, Вы писали:

S>Здравствуйте, adontz, Вы писали:


A>>Первый вариант не позволит настраивать журналирование по классам.

S>Не понял, что значит словосочетание "журналирование по классам"... По классам чего? Или по классам каким?

По классам которые "class". И log4net, и NLog при правильном использовании позволяют фильтровать сообщения по классам.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re: Куда помещать код логирования?
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.02.12 10:57
Оценка: 9 (2)
Здравствуйте, stomsky, Вы писали:

S>Доброго времени суток!

S>Пишу сейчас один сервис, который будет круглосуточно выполнять всякие полезные действия.
S>Надо вести подробный лог.
S>Сейчас кодирую и вижу как буквально на глазах "полезный" код засоряется кодом логирования.
S>Подскажите, плиз, как вы боритесь с этой проблемной?
Имхо, достаточно буквально трёхстрочной "библиотеки", чтобы превратить вашу лапшу в:
void DoAnything()
  { 
    Trace(()=>{
      Trace(()=>DoAction1());
      Trace(()=>DoAction2());
      Trace(()=>DoAction3());
    });
  }

Ещё десять строчек — и можно добавить в трейс значения интересных вам переменных. DRY.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: Куда помещать код логирования?
От: stomsky Россия  
Дата: 29.02.12 11:19
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>
S>void DoAnything()
S>  { 
S>    Trace(()=>{
S>      Trace(()=>DoAction1());
S>      Trace(()=>DoAction2());
S>      Trace(()=>DoAction3());
S>    });
S>  }
S>

УАУ!!! ВОТ ЭТО КРУТО!!!
Я серьезно: каждый раз убеждаюсь, что этот форум — кладезь оригинальных и красивых решений!
Спасибо большое!

Тока скажите честно, ВЫ САМИ такое стали бы использовать???? Нет, это ПРАВДА красиво на мой взгляд! Но, по-моему, какой-то огород на ровном месте... Хотя, я, конечно, сам просил чего-нибудь эдакого в рамках стандартного фреймворка...
Красота — наивысшая степень целесообразности. (c) И. Ефремов
Re: Куда помещать код логирования?
От: AlexNek  
Дата: 29.02.12 18:32
Оценка:
Здравствуйте, stomsky, Вы писали:

s> Доброго времени суток!

s> Пишу сейчас один сервис, который будет круглосуточно выполнять всякие полезные действия.
s> Надо вести подробный лог.
s> Сейчас кодирую и вижу как буквально на глазах "полезный" код засоряется кодом логирования.
...
s> Или все-таки на аспекты смотреть? Кто-нибудь этот функционал из BlToolkit пробовал? Я его для доступа к данным использую...
Вообще то вначале нужен ответ на вопрос для чего это мне нужно?
Если нужно просто разобраться с какой то проблемой в реальной работе, то лучше делать индивидуальные логи в тексте которые можно отключать через препроцессор, если важно время. У нас в каждом проекте есть файл с описанием отладочных директив. Можно например трассировать передачу данных и/или прием и/или конвертирование. Спец. трассировка "спрятана" в регионах. Аспектами было бы сложно, так как в данном случае почти каждый метод имеет индивидуальных подход логирования наиболее удобный для поставленной задачи.
Если же нужен просто глобальный обзор одинаковый для многих, то постшапр с аспектами довольно хорошее решение. Буквально недавно только пробовал. Достаточно одной строки в ассебли инфо (ну и аспект), чтобы получить в логе вход, выход, параметры и возможное исключение для всех классов и методов в библиотеке. Мусора конечно полно, но зато бывают видны "интересные вещи".
avalon 1.0rc3 rev 419, zlib 1.2.3
Re[4]: Куда помещать код логирования?
От: abibok  
Дата: 29.02.12 19:47
Оценка:
N>3) Логирование ошибки
N>
N>        public bool IsNeedToPayBonus(Context context)
N>        {
N>            try
N>            {
N>                //...
N>            }
N>            catch (Exception e)
N>            {
N>                Error(e.Message + e.StackTrace);
N>                throw;
N>            }
N>        }
N>


Не делайте так. Происходит потеря информации о месте возникновения исходного исключения.
Re[5]: Куда помещать код логирования?
От: AlexNek  
Дата: 29.02.12 19:56
Оценка:
Здравствуйте, abibok, Вы писали:

a> N>3) Логирование ошибки

a> N>
a> N>        public bool IsNeedToPayBonus(Context context)
a> N>        {
a> N>            try
a> N>            {
a> N>                //...
a> N>            }
a> N>            catch (Exception e)
a> N>            {
a> N>                Error(e.Message + e.StackTrace);
a> N>                throw;
a> N>            }
a> N>        }
a> N>


a> Не делайте так. Происходит потеря информации о месте возникновения исходного исключения.

А можно пояснить, что то мысль про место не совсем понятна. И как нужно правильно?
avalon 1.0rc3 rev 419, zlib 1.2.3
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.