Выполнение защитных вызовов groovy @Log annotation внутри замыканий

#groovy

#groovy

Вопрос:

РЕДАКТИРОВАТЬ: GROOVY-6932 был зарегистрирован и теперь закрыт — проблема отмечена как исправленная в Groovy v2.4.8.

Я использую аннотацию @Slf4j, чтобы добавить ведение журнала в мои классы groovy.

Мне это нравится из-за преобразования AST, которое обертывает мои вызовы журнала внутри проверки «включения», описанной здесь

Однако я обнаружил, что предложение guard не работает, если метод log вызывается из замыкания.

Запущенный на Groovy 2.2.0, этот код регистрирует только одно сообщение, но дважды выводит «вызывается».

 @Grapes([
  @Grab(group='org.slf4j', module='slf4j-api', version='1.7 '),
  @Grab(group='ch.qos.logback', module='logback-classic', version='1. ')])
import groovy.util.logging.Slf4j

new TestCode().doSomethingThatLogs()

@Slf4j
class TestCode {
  void doSomethingThatLogs(){
    log.info createLogString(1)
    log.trace createLogString(2)
    Closure c = { log.trace createLogString(3) }
    c()
  }

  String createLogString(int p){
    println "called with $p"
    return "blah: $p"
  }
}
 

Я попытался добавить спецификаторы «this», «owner» и «delegate» в инструкцию log, но результат тот же.

Контекст, в котором я нашел это, был, когда я пытался зарегистрировать некоторые узлы с помощью XmlUtil.serialize во время синтаксического анализа XML с помощью XMLSlurper.

Я работаю над решением проблемы, оборачивая объект NodeChild в облегченный объект с помощью метода toString(), который вызывает класс XmlUtil. Это будет работать нормально, и о накладных расходах на дополнительные классы-оболочки не стоит беспокоиться. Меня больше интересует поиск простого способа решения этой проблемы, чтобы я мог вернуться к тому, чтобы не думать о стоимости создания сообщений журнала, когда они все равно не регистрируются.

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

Комментарии:

1. Попробуйте добавить делегат в инструкцию log: delegate.log

2. Я попробовал ваш пример, это указывает на тестовый код класса, я также проверил источник аннотации Slf4j. На мой взгляд, это должно сработать, поэтому я думаю, что вам следует сообщить о проблеме Groovy. Мне жаль, что я не смог помочь.

3. у вас такая же проблема, если вы используете @Log4j ?

4. Вы пробовали просматривать сгенерированный код в декомпиляторе (в IntelliJ он уже встроен, или используйте что-то вроде Jad) — это может помочь увидеть, что генерируется преобразованиями AST.

Ответ №1:

Просто быстрая мысль. Как вы узнаете, что log.traceEnabled будет при выполнении закрытия? Смотрите измененный пример …

 @Grapes([
        @Grab(group='org.slf4j', module='slf4j-api', version='1.7 '),
        @Grab(group='ch.qos.logback', module='logback-classic', version='1. ')])

import groovy.util.logging.Slf4j
import jline.internal.Log
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;

new TestCode().doSomethingThatLogs()

@Slf4j
class TestCode {
    void doSomethingThatLogs(){

        log.info createLogString(1)
        log.trace createLogString(2)
        Closure c = { log.trace createLogString(3) }
        Logger root = (Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
        root.setLevel(Level.TRACE);
        c()
    }

    String createLogString(int p){
        println "called with $p"
        return "blah: $p"
    }
}
 

Теперь во время создания замыкания log.traceEnabled значение false, но при выполнении замыкания это изменилось.

Вывод:

 called with 1
16:53:15.408 [main] INFO  TestCode - blah: 1
called with 3
16:53:15.414 [main] TRACE TestCode - blah: 3
 

Обратите внимание, что второе TRACE все еще не напечатано 🙂

Комментарии:

1. На самом деле я не знаю, но я предположил, что Groovy генерирует тот же код, который вы написали бы вручную, то есть окружает операторы журнала сгенерированным байт-кодом для «if log.isDebugEnabled()». Вы хотите сказать, что, по вашему мнению, Groovy удаляет байт-код во время компиляции / загрузки?

2. Извините… Я не был в теме. Я углубился в детали, но не смог понять всю логику за кулисами (Groovy!?) Однако некоторые замечания: 1. Поскольку Groovy является динамическим, это не весь «байт-код», а метаклассы и некоторая скрытая магия. 2. Если вы посмотрите на код @Slf4j , вы можете увидеть, что «перенос» выполняется с помощью TernaryExpression / isTraceEnabled? log… : NULL / который вызывается динамически 3. Однако, когда это происходит в закрытии, этот вызов сохраняется, но он выполняется после оценки аргумента и …

3. … таким образом, вызывается ваш метод. Однако вы правы … похоже на ОШИБКУ!