Условное ведение журнала с минимальной цикломатической сложностью

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

Значение: не более 10 «if», «else», «try», «catch» и других операторов ветвления рабочего процесса кода. Правильно. Как я объяснил в разделе «Вы тестируете частный метод?», такая политика имеет много хороших побочных эффектов.

Но: В начале нашего (200 человек - 7 лет) проекта мы с удовольствием регистрировали (и нет, мы не можем легко делегировать это какому-то 'Аспектно-ориентированное программирование 'для журналов).

myLogger.info("A String");
myLogger.fine("A more complicated String");
...

И когда первые версии нашей Системы были запущены, у нас возникла огромная проблема с памятью не из-за ведения журнала (которое в какой-то момент было отключено), а из-за параметров журнала (строк), которые всегда вычисляются, а затем передаются функциям info () или fine (), только для того, чтобы обнаружить, что уровень ведения журнала был «ВЫКЛЮЧЕН» и что регистрация не велась!

Поэтому QA вернулся и призвал наших программистов вести условный журнал. Всегда.

if(myLogger.isLoggable(Level.INFO) { myLogger.info("A String");
if(myLogger.isLoggable(Level.FINE) { myLogger.fine("A more complicated String");
...

Но теперь, с этим 10 цикломатическим уровнем сложности «нельзя перемещать» на ограничение функции, они утверждают, что различные журналы, которые они помещают в свою функцию, воспринимаются как бремя, потому что каждое «if (isLoggable ())» является засчитывается как +1 цикломатическая сложность!

Таким образом, если функция имеет 8 'if', 'else' и т. Д. В одном тесно связанном алгоритме с трудностями для совместного использования и 3 критических действиях журнала ... они превышают предел, даже если условные журналы не могут быть < em> действительно часть указанной сложности этой функции ...

Как бы вы справились с этой ситуацией?
Я видел несколько интересных эволюций кодирования (из-за этого «конфликта») в моем проекте, но я просто хочу сначала узнать ваши мысли.


Спасибо за все ответы.
Я должен настаивать на том, что проблема связана не с «форматированием», а с «оценкой аргументов» (оценка, которая может быть очень дорогостоящей, непосредственно перед вызовом метода, который ничего не сделает) < br> Итак, когда a написал выше «Строка», я на самом деле имел в виду aFunction (), где aFunction () возвращает String и является вызовом сложного метода, собирающего и вычисляющего все виды данных журнала, которые должны отображаться регистратором. .. или нет (отсюда проблема и обязательство использовать условное ведение журнала, отсюда и актуальная проблема искусственного увеличения «цикломатической сложности» ...)

Теперь у меня есть пункт «вариативная функция», выдвинутый некоторыми из вас (спасибо, Джон). < br> Примечание: быстрый тест на java6 показывает, что мои varargs функция оценивает свои аргументы перед вызовом, поэтому ее нельзя применять для вызова функции, но для 'объекта извлечения журнала' (или 'оболочки функции'), для которого toString () будет вызываться только в случае необходимости. . Понятно.

Я опубликовал свой опыт по этой теме.
Я оставлю его там до следующего вторника для голосования, а затем выберу один из ваших ответов.
Еще раз спасибо за все предложения :)


person Community    schedule 19.09.2008    source источник
comment
произвольные требования к цикломатической сложности + сложные (т. е. полезные) операторы журналирования = стимул писать дрянной код журналирования   -  person mob    schedule 21.10.2009
comment
Немного поздно, но все равно. В зависимости от языка вы можете скрыть оператор if внутри метода / макроса. Например, если это C / C ++, макросы, очевидно, помогут, хотя их использование может нарушить какую-либо другую политику обеспечения качества. Общие методы C # (то есть набор Info<T>, Info<T1,T2>, Info<T1,T2,T3> также позволят вам избежать этой проблемы, поскольку параметры будут передаваться в метод без упаковки / преобразования в строку, и они легко встраиваются, если они статичны (что относится к расширению методы тоже).   -  person Groo    schedule 12.08.2017
comment
@Groo Я согласен. В то время (19 сентября 2008 г., через 4 дня после официального публичного выпуска веб-сайта Stack Overflow) я спрашивал, имея в виду Java.   -  person VonC    schedule 12.08.2017
comment
@VonC: да, блин, я уже потом это понял. :) Я видел отредактированную метку времени с 2017 года, поэтому я не проверял исходную дату.   -  person Groo    schedule 12.08.2017


Ответы (12)


В Python вы передаете отформатированные значения в качестве параметров функции регистрации. Форматирование строки применяется только в том случае, если включено ведение журнала. По-прежнему существуют накладные расходы, связанные с вызовом функции, но они мизерные по сравнению с форматированием.

log.info ("a = %s, b = %s", a, b)

Вы можете сделать что-то подобное для любого языка с вариативными аргументами (C / C ++, C # / Java и т. Д.).


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


Поскольку в вопросе OP конкретно упоминается Java, вот как можно использовать приведенное выше:

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

Уловка состоит в том, чтобы иметь объекты, которые не будут выполнять дорогостоящие вычисления до тех пор, пока они не станут абсолютно необходимыми. Это легко сделать в таких языках, как Smalltalk или Python, которые поддерживают лямбда-выражения и замыкания, но все же это можно сделать в Java, проявив немного воображения.

Допустим, у вас есть функция get_everything(). Он будет извлекать каждый объект из вашей базы данных в список. Очевидно, вы не хотите называть это, если результат будет отброшен. Поэтому вместо прямого вызова этой функции вы определяете внутренний класс с именем LazyGetEverything:

public class MainClass {
    private class LazyGetEverything { 
        @Override
        public String toString() { 
            return getEverything().toString(); 
        }
    }

    private Object getEverything() {
        /* returns what you want to .toString() in the inner class */
    }

    public void logEverything() {
        log.info(new LazyGetEverything());
    }
}

В этом коде вызов getEverything() заключен в оболочку, так что на самом деле он не будет выполняться, пока он не понадобится. Функция регистрации выполнит toString() для своих параметров, только если включена отладка. Таким образом, ваш код будет нести только накладные расходы на вызов функции вместо полного вызова getEverything().

person John Millikin    schedule 19.09.2008
comment
Это работает. Здорово, что Python поддерживает это напрямую, но вы можете сделать это в оболочке, если ваш инструментарий для ведения журнала этого не делает. - person erickson; 20.09.2008
comment
Вау ... спасибо за уборку, Джон. 3000 очков репутации действительно помогают сделать так много отрицательных отзывов;) Я не уверен, что вы думаете о вашей точке зрения. Вы имеете в виду a и b, которые могут быть сложными функциями, возвращающими String, не оцениваются во время вызова функции журнала? - person VonC; 20.09.2008
comment
Нет, он имеет в виду, что вместо форматирования сообщения в вашем собственном коде передайте данные, которые вы бы использовали для этого, в регистратор, который выполнит форматирование за вас, но только если оператор включен. Для этого требуется средство форматирования, а не конкатенация строк. - person erickson; 20.09.2008
comment
Я все еще не понимаю, извините. передать данные ??? Это означает, что данные должны быть вычислены / оценены, чтобы быть переданными в любой механизм форматирования, верно? Если да, ... Я не понимаю, как он решает мою проблему (которая не связана с форматом). - person VonC; 20.09.2008
comment
@VonC: я думаю, вам следует уточнить, что имеется в виду, но из-за параметров журнала (строк), которые всегда вычисляются, ... вы имеете в виду такие вещи, как MySuperObject.ToString ()? - person pointernil; 20.09.2008
comment
@John Millikin благодарим вас за терпение и отзывы. См. Мой отредактированный вопрос, который я только что ответил. Я пойду завтра, спокойной ночи;) (этот бизнес на StackOverflow на самом деле является службой "следования за солнцем"! Вы, ребята, никогда не спите;)) - person VonC; 20.09.2008
comment
А? Не уверен, что вы имеете в виду под чисткой. Пользователи с высоким уровнем репутации также имеют ограниченное количество голосов против, и я не редактировал никаких сообщений в этом вопросе. ----- VonC: Я отредактирую свой пост, чтобы показать, как помогает ленивое форматирование. - person John Millikin; 20.09.2008
comment
Спасибо, Джон, теперь я понял. Для полноты и просто анекдота вы также можете использовать указатель функций или «ленивые вариативные функции!». Но мне известны только те, которые написаны на «языке D», как в digitalmars.com/d /1.0/function.html - person VonC; 20.09.2008

С текущими фреймворками логирования вопрос спорный

Текущие платформы ведения журналов, такие как slf4j или log4j 2, в большинстве случаев не требуют операторов защиты. Они используют параметризованный оператор журнала, так что событие может регистрироваться безоговорочно, но форматирование сообщения происходит только в том случае, если событие включено. Построение сообщения выполняется регистратором по мере необходимости, а не приложением упреждающе.

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

Действительно ли охранные заявления добавляют сложности?

Рассмотрите возможность исключения заявлений охранников лесозаготовок из расчета цикломатической сложности.

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

Негибкие метрики могут испортить хорошего программиста. Будь осторожен!

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

Необходимость условного ведения журнала

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

private static final Logger log = Logger.getLogger(MyClass.class);

Connection connect(Widget w, Dongle d, Dongle alt) 
  throws ConnectionException
{
  log.debug("Attempting connection of dongle " + d + " to widget " + w);
  Connection c;
  try {
    c = w.connect(d);
  } catch(ConnectionException ex) {
    log.warn("Connection failed; attempting alternate dongle " + d, ex);
    c = w.connect(alt);
  }
  log.debug("Connection succeeded: " + c);
  return c;
}

В Java каждый из операторов журнала создает новый StringBuilder и вызывает метод toString() для каждого объекта, присоединенного к строке. Эти toString() методы, в свою очередь, могут создавать StringBuilder собственных экземпляров и вызывать toString() методы своих членов и т. Д. В потенциально большом графе объектов. (До Java 5 это было еще дороже, поскольку использовалось StringBuffer, и все его операции синхронизированы.)

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

Это приводит к введению охранных заявлений в форме:

  if (log.isDebugEnabled())
    log.debug("Attempting connection of dongle " + d + " to widget " + w);

С помощью этой защиты оценка аргументов d и w и конкатенация строк выполняется только при необходимости.

Решение для простой и эффективной регистрации

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

public final class FormatLogger
{

  private final Logger log;

  public FormatLogger(Logger log)
  {
    this.log = log;
  }

  public void debug(String formatter, Object... args)
  {
    log(Level.DEBUG, formatter, args);
  }

  … &c. for info, warn; also add overloads to log an exception …

  public void log(Level level, String formatter, Object... args)
  {
    if (log.isEnabled(level)) {
      /* 
       * Only now is the message constructed, and each "arg"
       * evaluated by having its toString() method invoked.
       */
      log.log(level, String.format(formatter, args));
    }
  }

}

class MyClass 
{

  private static final FormatLogger log = 
     new FormatLogger(Logger.getLogger(MyClass.class));

  Connection connect(Widget w, Dongle d, Dongle alt) 
    throws ConnectionException
  {
    log.debug("Attempting connection of dongle %s to widget %s.", d, w);
    Connection c;
    try {
      c = w.connect(d);
    } catch(ConnectionException ex) {
      log.warn("Connection failed; attempting alternate dongle %s.", d);
      c = w.connect(alt);
    }
    log.debug("Connection succeeded: %s", c);
    return c;
  }

}

Теперь ни один из каскадных toString() вызовов с выделением их буфера не произойдет, если в этом нет необходимости! Это эффективно устраняет снижение производительности, которое привело к заявлениям о защите. Одним небольшим штрафом в Java будет автоматическая упаковка любых аргументов примитивного типа, которые вы передаете регистратору.

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

Дальнейшие улучшения

Также обратите внимание, что в Java объект MessageFormat может использоваться вместо «формата» String, что дает вам дополнительные возможности, такие как формат выбора для более аккуратной обработки количественных чисел. Другой альтернативой может быть реализация вашей собственной возможности форматирования, которая вызывает некоторый интерфейс, который вы определяете для «оценки», а не базовый метод toString().

person erickson    schedule 19.09.2008
comment
+1 для FormatLogger. Сделав нечто подобное, я могу поручиться за полезность этого подхода. В некоторых проведенных нами тестах производительность практически не снижается при отключении соответствующего уровня ведения журнала. - person javashlook; 25.03.2009
comment
log4j версии 1.2.17 больше не имеет Logger.isEnabled (Level), вместо этого используйте Logger.isEnabledFor (Level). - person CodeMonkeyKing; 31.03.2015
comment
оболочка имеет побочный эффект, номер строки, сообщаемый файлом log4j, будет не вашим классом, а этой строкой все время - person Kalpesh Soni; 10.07.2015
comment
@KalpeshSoni Использование регистрации номеров строк в log4j не рекомендуется из-за большого снижения производительности. К счастью, log4j имеет открытый исходный код, поэтому, если вы настаиваете на ведении журнала номеров строк, вы можете изменить номера строк. Или вы можете просто использовать текущую версию log4j или slf4j; этому ответу 7 лет, и сейчас он менее актуален. - person erickson; 10.07.2015
comment
ответ появляется, когда вы включаете ведение журнала google log4j и т. д. ключевые слова :) - извините, я не уверен, но какие решения сегодня более актуальны? Чем отличается новый log4j или slf4j, если вы используете обертки - person Kalpesh Soni; 10.07.2015
comment
@KalpeshSoni log4j 2, slf4j и другие имеют встроенные параметризованные сообщения, поэтому вам не нужно создавать функциональность самостоятельно. - person erickson; 10.07.2015
comment
Я добавил к своему ответу новый заголовок, объясняющий это. Спасибо за ваш вклад. - person erickson; 10.07.2015
comment
Однако новый способ имеет интересное значение для производительности. Интерфейс нового формата использует varargs, если число параметров ›= 3, что создает ссылку на объект массива. Это может привести к появлению большого количества ненужного мусора и увеличению накладных расходов на сборщик мусора, что может оказаться недопустимым для приложений, чувствительных к производительности. - person Hari Menon; 25.08.2016
comment
Пример log4j2 устарел. log4j2 поддерживает поставщиков и, следовательно, позволяет использовать лямбда-выражения. - person Marcono1234; 23.03.2018
comment
@ Marcono1234 У меня нет примера log4j2, но я предполагаю, что вы имеете в виду первый абзац. Я бы сказал, что поддержка лямбда не устаревает и не устаревает не лямбда-подходы. Я вижу так много ужасного кода, написанного в бездумных объятиях новой блестящей вещи. Но приятно иметь в этом API новую опцию, которую можно использовать там, где это необходимо. - person erickson; 23.03.2018
comment
@erickson, верно. Я исходил из вашего ответа stackoverflow.com/a/963681/4288506 и не читал ваш ответ здесь достаточно внимательно, а только увидел isDebugEnabled() и предположил, что он основан на log4j. Можете ли вы вспомнить ситуации, когда имеет смысл проверять уровень журнала вместо использования Suppliers? - person Marcono1234; 25.03.2018
comment
@ Marcono1234 Понятно. Нет, я не могу придумать ничего из головы. - person erickson; 26.03.2018

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

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

Но как всегда: если сомневаетесь, протестируйте его и измерьте влияние на загрузку процессора и память.

person pointernil    schedule 19.09.2008
comment
Да, хороший теоретический ответ ... хотя я все еще кодирую на простой старой java 1.4.2 !!! и не имеют доступа к такому аккуратному угощению. Тем не менее, +1 за предложение, поскольку вопрос действительно зависит от языка. - person VonC; 20.09.2008

Спасибо за все ваши ответы! Ребята, молодцы :)

Теперь мой отзыв не такой прямолинейный, как ваш:

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

  • выделенные объекты 'Log Retriever', которые могут быть переданы в оболочку Logger, необходим только вызов toString ()
  • используется вместе с вариативной функцией (или обычным массивом Object []!)

Вот и все, как объяснили @John Millikin и @erickson.

Однако эта проблема заставила нас немного задуматься о том, «Почему мы вообще вели лог?»
Наш проект на самом деле состоит из 30 различных проектов (от 5 до 10 человек каждый), развернутых на различных производственных платформах, с потребностями в асинхронной связи. и архитектура центральной шины.
Простое ведение журнала, описанное в вопросе, подходило для каждого проекта в начале (5 лет назад), но с тех пор мы должны сделать шаг вперед. Введите KPI.

Вместо того, чтобы просить регистратор регистрировать что-либо, мы просим автоматически созданный объект (называемый KPI) зарегистрировать событие. Это простой вызов (myKPI.I_am_signaling_myself_to_you ()), который не обязательно должен быть условным (что решает проблему «искусственного увеличения цикломатической сложности»).

Этот объект KPI знает, кто его вызывает, и, поскольку он запускается с самого начала приложения, он может извлекать множество данных, которые мы ранее вычисляли на месте, когда мы регистрировали.
Плюс этот объект KPI можно отслеживать независимо и вычислять / публиковать по запросу свою информацию на единой и отдельной шине публикации.
Таким образом, каждый клиент может запросить информацию, которую он действительно хочет (например, «начался ли мой процесс, и если да, с каких это пор?»), вместо того, чтобы искать правильный файл журнала и искать загадочную строку ...

В самом деле, вопрос: «Почему мы вообще врубались?» заставили нас понять, что мы ведем журналы не только для программиста и его модульных или интеграционных тестов, но для гораздо более широкого сообщества, включая самих конечных клиентов. Наш механизм «отчетности» должен был быть централизованным, асинхронным, 24/7.

Специфика этого механизма KPI выходит за рамки этого вопроса. Достаточно сказать, что его правильная калибровка, безусловно, является единственной наиболее сложной нефункциональной проблемой, с которой мы сталкиваемся. Он по-прежнему время от времени ставит систему на колени! Однако, правильно откалиброванный, он спасает жизнь.

Еще раз спасибо за все предложения. Мы рассмотрим их для некоторых частей нашей системы, когда еще существует простое ведение журнала.
Но другой момент в этом вопросе заключался в том, чтобы проиллюстрировать вам конкретную проблему в гораздо более широком и более сложном контексте.
Надеюсь, вы понравилось. Я мог бы задать вопрос по KPI (который, верьте или нет, пока не обсуждается в SOF!) Позже на следующей неделе.

Я оставлю этот ответ на голосование до следующего вторника, затем выберу ответ (явно не этот;))

person VonC    schedule 20.09.2008
comment
@Ram Спустя 7 с половиной лет KPI меняются от проекта к проекту. Некоторые из них перечислены на странице en.wikipedia.org/wiki/Performance_indicator#IT_Operations. - person VonC; 01.03.2016
comment
Не заметил времени разговора! В любом случае спасибо за ссылку1 - person Ram; 01.03.2016

Может быть, это слишком просто, но как насчет использования рефакторинга "метода извлечения" вокруг предложения защиты? Ваш пример кода этого:

public void Example()
{
  if(myLogger.isLoggable(Level.INFO))
      myLogger.info("A String");
  if(myLogger.isLoggable(Level.FINE))
      myLogger.fine("A more complicated String");
  // +1 for each test and log message
}

Становится это:

public void Example()
{
   _LogInfo();
   _LogFine();
   // +0 for each test and log message
}

private void _LogInfo()
{
   if(!myLogger.isLoggable(Level.INFO))
      return;

   // Do your complex argument calculations/evaluations only when needed.
}

private void _LogFine(){ /* Ditto ... */ }
person flipdoubt    schedule 28.09.2008
comment
Ваши методы _LogInfo () и _LogFine () не принимают никаких параметров для сообщения журнала - и если бы они это сделали, то затраты на объединение параметров все равно были бы понесены. - person matt b; 07.10.2008
comment
Эти методы не принимают параметры, потому что они вычисляют параметры только после проверки того, что параметры должны быть рассчитаны. Исходная точка зрения автора заключалась в том, что на приложение отрицательно влияло вычисление ненужных параметров. Я хочу вернуть себе репутацию! - person flipdoubt; 07.10.2008
comment
См. Комментарий: // Выполняйте вычисления / оценки сложных аргументов только при необходимости. - person flipdoubt; 07.10.2008
comment
Вы только что выбрали дерьмовый пример. - person wowest; 25.02.2009
comment
Видимо, но я использовал пример VonC в исходном вопросе. - person flipdoubt; 25.02.2009

В C или C ++ я бы использовал препроцессор вместо операторов if для условного ведения журнала.

person Tom Ritter    schedule 19.09.2008
comment
Это не упрощает включение отладки (регистрации) в производственном коде. - person Jonathan Leffler; 16.09.2009
comment
@JonathanLeffler Действительно, потому что все функции ведения журнала фактически удаляются перед компиляцией. Исполняемый файл выпуска не имеет функции журнала. - person MechMK1; 11.02.2015
comment
На самом деле Том прав. Макрос позволяет скрыть оператор ветвления (что снижает цикломатическую сложность), в то время как параметры макроса просто заменяются без оценки. Это не значит, что if нужно жестко запрограммировать. - person Groo; 12.08.2017

Передайте уровень журнала в средство ведения журнала и позвольте ему решить, следует ли записывать оператор журнала:

//if(myLogger.isLoggable(Level.INFO) {myLogger.info("A String");
myLogger.info(Level.INFO,"A String");

ОБНОВЛЕНИЕ: Ах, я вижу, что вы хотите условно создать строку журнала без условного оператора. Предположительно во время выполнения, а не во время компиляции.

Я просто скажу, что способ, которым мы это решили, - это поместить код форматирования в класс регистратора, чтобы форматирование происходило только в том случае, если уровень прошел. Очень похоже на встроенный sprintf. Например:

myLogger.info(Level.INFO,"A String %d",some_number);   

Это должно соответствовать вашим критериям.

person Community    schedule 19.09.2008
comment
Вот что он делает ... перед тем, как приступить к работе по форматированию сообщения, что может оказаться дорогостоящей тратой, если сообщение не будет зарегистрировано. - person erickson; 20.09.2008
comment
Это не решает проблему оценки строк и использования памяти, несмотря на то, что ведение журнала отключено. - person Tom Ritter; 20.09.2008
comment
В порядке. Тот же ответ, что и у @John Millikin. Можете ли вы подтвердить мне, что some_number (который на самом деле может быть сложной функцией, возвращающей строку) не оценивается во время выполнения? - person VonC; 20.09.2008

Условное ведение журнала - зло. Это добавляет ненужный беспорядок в ваш код.

Вы всегда должны отправлять в регистратор объекты, которые у вас есть:

Logger logger = ...
logger.log(Level.DEBUG,"The foo is {0} and the bar is {1}",new Object[]{foo, bar});

а затем иметь java.util.logging.Formatter, который использует MessageFormat для сглаживания foo и bar в строку для вывода. Он будет вызываться только в том случае, если регистратор и обработчик будут регистрироваться на этом уровне.

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

person Community    schedule 02.07.2011

alt text
(источник: scala-lang.org)

Scala имеет аннотацию @ elidable (), который позволяет удалять методы с флагом компилятора.

С помощью scala REPL:

C:> scala

Добро пожаловать в Scala версии 2.8.0.final (64-разрядная серверная виртуальная машина Java HotSpot ™, Java 1. 6.0_16). Введите выражения, чтобы они оценивались. Введите: help для получения дополнительной информации.

scala> импорт scala.annotation.elidable импорт scala.annotation.elidable

scala> импорт scala.annotation.elidable._ импорт scala.annotation.elidable._

scala> @elidable (FINE) def logDebug (аргумент: строка) = println (аргумент)

logDebug: (arg: String) Единица

scala> logDebug («тестирование»)

scala>

С elide-beloset

C:> scala -Xelide-ниже 0

Добро пожаловать в Scala версии 2.8.0.final (64-разрядная серверная виртуальная машина Java HotSpot ™, Java 1. 6.0_16). Введите выражения, чтобы они оценивались. Введите: help для получения дополнительной информации.

scala> импорт scala.annotation.elidable импорт scala.annotation.elidable

scala> импорт scala.annotation.elidable._ импорт scala.annotation.elidable._

scala> @elidable (FINE) def logDebug (аргумент: строка) = println (аргумент)

logDebug: (arg: String) Единица

scala> logDebug («тестирование»)

тестирование

scala>

См. Также определение утверждения Scala

person Community    schedule 20.07.2010

Как бы я ни ненавидел макросы в C / C ++, на работе у нас есть #defines для части if, которая, если false игнорирует (не оценивает) следующие выражения, но если true возвращает поток, в который материал может быть передан по конвейеру с помощью ' ‹< 'оператор. Нравится:

LOGGER(LEVEL_INFO) << "A String";

Я предполагаю, что это устранит дополнительную «сложность», которую видит ваш инструмент, а также устранит любое вычисление строки или любых выражений, которые нужно регистрировать, если уровень не был достигнут.

person quamrana    schedule 19.09.2008
comment
Эй, если это позволяет избежать оценки A String во время выполнения, это правильный ответ (я просто немного заржавел на своем уровне C / C ++) - person VonC; 20.09.2008
comment
Что произойдет, если отключить ведение журнала? Полученному объекту по-прежнему потребуется operator<<, и поэтому цепочка операторов вывода все равно будет выполняться. Не думаю, что этот ответ кому-то поможет. - person John Millikin; 20.09.2008
comment
Вопрос включает что-то вроде: if (myLogger.isLoggable (Level.INFO) myLogger.info (A String); Теперь, если мы просто добавим #define LOGGER (LEVEL) if (myLogger.isLoggable (LEVEL)) myLogger (), тогда предполагая ' myLogger () 'возвращает поток, который вы выигрываете всеми способами. (за исключением того, что вы используете макросы) - person quamrana; 21.09.2008

Вот элегантное решение с использованием тернарного выражения

logger.info (logger.isInfoEnabled ()? "Здесь идет инструкция журнала ...": null);

person Community    schedule 16.09.2009
comment
Использование тернарного оператора по-прежнему увеличивает цикломатическую сложность. - person mob; 20.10.2009

Рассмотрим функцию утилиты ведения журнала ...

void debugUtil(String s, Object… args) {
   if (LOG.isDebugEnabled())
       LOG.debug(s, args);
   }
);

Затем сделайте звонок с «закрытием» вокруг дорогостоящей оценки, которой вы хотите избежать.

debugUtil(“We got a %s”, new Object() {
       @Override String toString() { 
       // only evaluated if the debug statement is executed
           return expensiveCallToGetSomeValue().toString;
       }
    }
);
person Community    schedule 22.11.2011