Регистрация без кода Bloat

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

В настоящее время я использую C # и NLog, однако я думаю, что любой совет здесь был бы агностиком языка и инструмента.

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

void Foo()
{ 
    Bar bar = dbContext().Bars.First();
    bool someCondition = bar.DoSomething();
    if (someContition)
    {
        dbContext().FooBars.Add(new FooBar());
    }
}

В то, что выглядит так:

void Foo()
{ 
    _logger.Info("Getting first bar from database...");

    Bar bar = dbContext().Bars.First();

    _logger.Info("First bar returned from database, id = {0}", bar.Id);

    _logger.Info("Doing something on bar with id = {0}", bar.Id);
    bool someCondition = bar.DoSomething();

    _logger.Info("Something done on bar with id = {0}, response = {1}", bar.Id, someCondition);

    if (someContition)
    {
        _logger.Warn("Adding new FooBar to database");
        dbContext().FooBars.Add(new FooBar());
        _logger.Warn("Added new FooBar to database successfully");
    }
}

Здесь мы теперь имеем больше строк журнала, чем строки кода.

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

Есть ли способ обойти эту уродливую ручную регистрацию, или я застрял с ней?

38 голосов | спросил Nick Williams 24 MarpmMon, 24 Mar 2014 13:48:12 +04002014-03-24T13:48:12+04:0001 2014, 13:48:12

7 ответов


30

Этот вид ведения журнала трассировки может быть разрешен с помощью Аспект .

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

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

Я не знаю достаточно о C #, чтобы рекомендовать конкретную ориентированную на аспект структуру, или соответствующий синтаксис для вас. К сожалению.

ответил Bill Michell 24 MarpmMon, 24 Mar 2014 14:00:04 +04002014-03-24T14:00:04+04:0002 2014, 14:00:04
20

Глядя на ваш код, я считаю, что ваша проблема заключается не только в том, что строки протоколирования уродливы, но они представляют собой риск в том, чтобы стоить вам больше, чем помогать вам:

  • Как и в случае с комментариями, обслуживание строки журнала часто забывается и может нарушить ваш код при внесении изменений или, что еще хуже, запутать вас при попытке устранить неполадку, прочитав файлы журнала (подумайте о ситуации, когда вы меняете Bar bar = dbContext().Bars.First();, Bar bar = dbContext().Bars.Last();, но не меняя файлы журнала - журнал скажет вам, что первая строка возвращается X, и теперь вы будете царапать голову, как это произошло?)
  • Журналы вашего кода, если они вообще нужны, определенно не для уровня Info, а не для обсуждения уровня Warn - это Debug code>. Заполнение ваших файлов журналами отладки сделает ваши файлы журналов огромными и непригодными для использования.
    • Журналы Warn должны регистрироваться только , когда происходит что-то из ожидаемого поведения, так что устранение неполадок будет сфокусировано на этом.
    • Журналы Info должны быть краткими, пропорционально количеству транзакций и передавать ценную информацию.
    • Журналы Debug обычно добавляются ad-hoc, когда вы пытаетесь выследить неуловимую ошибку и нуждаетесь в дополнительной информации внутри определенного кода. В сценарии производства эти журналы должны быть отключены off .

Из вашего сообщения вытекает, что вы получили это требование от команды operations . Мне сложно понять, почему команда операций даст вам требование регистрировать поведение внутреннего кода ... Возможно, вам потребуется иметь журналы specific на уровне функций, но с мелкозернистым протоколированием требование кажется противоречащим интуиции, а не в сфере ответственности.

В любом случае, чтобы решить первую пулю (и сделать ваш код чище), лучшая стратегия AFAIK использует Аспектно-ориентированное программирование , как рекомендовал @BillMichell.

ответил Uri Agassi 24 MarpmMon, 24 Mar 2014 16:22:28 +04002014-03-24T16:22:28+04:0004 2014, 16:22:28
9

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

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

void Foo()
{
    Bar bar = GetFirstBar();
    bool someCondition = bar.DoSomething();
    if (someContition)
    {
        AddFooBar();
    }
}

Bar GetFirstBar()
{
    _logger.Info("Getting first bar from database...");
    return dbContext().Bars.First();
    _logger.Info("First bar returned from database, id = {0}", bar.Id);
}

void AddFooBar()
{
    _logger.Warn("Adding new FooBar to database");
    dbContext().FooBars.Add(new FooBar());
    _logger.Warn("Added new FooBar to database successfully");
}

bool DoSomething(this Bar bar)
{
    _logger.Info("Doing something on bar with id = {0}", bar.Id);
    // Implementation goes here
    _logger.Info("Something done on bar with id = {0}, response = {1}", bar.
    Id, someCondition);
}

Итак, отвечая на более общий вопрос: «Как добавить журнал, сохраняя читаемый код?», я реорганизую свои методы на более мелкие методы, пока каждый метод не станет настолько читабельным, как должен быть.

ответил Justin 27 MaramThu, 27 Mar 2014 04:28:40 +04002014-03-27T04:28:40+04:0004 2014, 04:28:40
6

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

В производственном коде вы ожидаете, что все будет хорошо. Фактически, в идеале вы хотите только знать, когда что-то пойдет не так: вы получаете исключение из своего кода, отказ в вызове базы данных или критическая часть вашего кода возвращает что-то неожиданное. В этих случаях вы хотите зарегистрировать то, что пошло не так, и, если возможно, зарегистрируйте переменные, с которыми вы работаете, чтобы помочь воспроизвести ошибку.

В вышеприведенном случае вы не хотите регистрироваться каждый раз, когда вы получаете Bar, или что у вас есть Bar с ID X, или что вы делаете что-то в баре, или что вы что-то сделали на панели , Вы ожидаете, что они будут работать должным образом, поэтому вы хотите только регистрироваться, когда что-то пошло не так, и затем запишите переменные бара, которые вы используете в doSomething() и идентификаторе панели. Если someCondition является истинным, вы хотите зарегистрировать, что вы что-то добавили, но вы не хотите, чтобы он регистрировал его, только если он поступил не так.

Если операции говорят, что им действительно нужны эти протоколирующие заявления, они видят это неправильно. Им не нужны эти протоколирующие заявления, они хотят ИНФОРМАЦИЮ, сгенерированные этими инструкциями ведения журнала. Вероятно, они хотят получать статистику на основе этих протоколирующих операторов или что-то, что связано с операторами протоколирования. Обсудите с операциями, почему они нуждаются в этих заявлениях и предоставляют им то, что им нужно.

ответил Nzall 24 MarpmMon, 24 Mar 2014 19:05:40 +04002014-03-24T19:05:40+04:0007 2014, 19:05:40
2

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

Один практический подход к протоколированию состоит в том, чтобы поместить строку журнала в большинство ветвей key - некоторые ветви выполняют мелкие вещи и не требуют протоколирования. Наиболее важной ветвью для журнала является исключение журнала catch(Exception x) - всегда

Что касается обслуживания журналов, то я имею несколько уникальный подход: я создал addins для своих любимых сред разработки (VS, Eclipse), которые после нажатия горячей клавиши вставляют короткую уникальную кодированную последовательность символов заданной длины (10 буквы, все колпачки), и я использую эти + данные во время выполнения вместо человекочитаемых слов.

Я называю эти «коды дебрикса», и у них есть несколько преимуществ, некоторые из которых:

  • нет грязной сложной формулировки
  • вы всегда можете найти место в коде, который создал конкретный журнал
  • вы можете легко подсчитать экземпляры определенного сообщения или другой статистики: у меня есть сценарии LinqPad, которые берут имя файла журнала и дебрикс код для создания моментальных снимков окрестностей каждого события.
  • Используя только данные времени выполнения, вы можете быть уверены, что ваше сообщение всегда актуально: если объект реорганизован, вы вынуждены рефакторинг сообщения журнала также.
  • уникальные коды дебрикса на всей сложной системе с несколькими платформами по-прежнему ведут вас к одному месту, которое создало журнал сообщение, независимо от того, является ли это javascript, серверной, клиентской стороной (OSX Прожектор очень удобен)
  • код может быть включен с отображаемыми клиентом сообщениями об ошибках для поддержки клиентов. Наряду с меткой времени, все равно гарантируется уникальная идентификация события.

Вот как я бы использовал этот подход для регистрации вашего примера кода:

void Foo()
{ 
  try{
    log.I("[FFASREQEKT]"); 
    Bar bar = dbContext().Bars.First();

    bool someCondition = bar.DoSomething();

    if (someContition)
    {
        log.I("[FFGFAJNNBGB]"); 
        dbContext().FooBars.Add(new FooBar());
    }
  } catch (Exception x){
    // This catch is only for illustrative purpose. In real code it may be outside this method
    log.E("[FFGADJNANQ]", x);  
  }
}

Это позволяет вам иметь полное представление о том, какой путь кода был сделан с наименьшим количеством сообщений.

ответил Sten Petrov 25 MaramTue, 25 Mar 2014 00:58:29 +04002014-03-25T00:58:29+04:0012 2014, 00:58:29
1

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

  • Общий INFO и ad-hoc DEBUG уровень регистрации, который позволяет отслеживать ошибку в определенной части приложения и предоставлять метаданные, такие как id и аналогичной информации.
  • И во-вторых, протоколирование всех (пользовательских) действий, чтобы можно было увидеть, что именно сделал пользователь (как для исправления ошибок, так и при наличии у клиента претензии, что он сделал что-то два месяца назад, что, предположительно, не сработало).

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

ответил David Mulder 24 MarpmMon, 24 Mar 2014 18:14:32 +04002014-03-24T18:14:32+04:0006 2014, 18:14:32
1

Возможно, для вашего конкретного примера может быть полезно получить подкласс dbContext (скажем, loggedDBContext), который обертывает его методы кодом, который регистрирует все, что вам нужно, а затем выполняет метод «истина». Добавление /удаление журнала затем будет состоять в выборе использования dbContext vs loggedDBContext в конструкторе (или на заводе), но ничего не изменит.

В некоторых языках существуют способы непосредственного ввода таких аспектов в функции - изменение их поведения, не требуя, чтобы вызывающий код даже знал, что функции были изменены, а теперь также регистрируются; но я не уверен, что это можно сделать на C # без явного подкласса.

ответил Peteris 24 MarpmMon, 24 Mar 2014 21:32:45 +04002014-03-24T21:32:45+04:0009 2014, 21:32:45

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132