У меня есть такой код:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Test {
  private static final Logger logger = LogManager.getLogger();
  public static void main (String[] args) {
    logger.info("Text: {}", getText());
  }
  static String getText() {
    // Expensive action using IO
    return "";
  }
}

В моем log4j2.json регистратор установлен на ERROR.

Я хочу, чтобы, когда он не нужен, getText() вообще не вызывался. Для этого я использовал API сообщений и вместо этого написал следующее:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.Message;
public class Test {
  private static final Logger logger = LogManager.getLogger();
  public static void main (String[] args) {
    logger.info(new TextMessage());
  }
  static String getText() {
    // Expensive action using IO
    return "";
  }
  static class TextMessage implements Message {
    @Override public String getFormat() { return null; }
    @Override public String getFormattedMessage() {
        return String.format("text: %s", getText());
    }
    @Override public Object[] getParameters() { return null; }
    @Override public Throwable getThrowable() { return null; }
  }
}

У меня две проблемы с этим кодом.

  1. Я не могу использовать {}, обычно используемый для регистрации.
  2. Это очень многословно.

Я проверил javadoc на наличие Message, которые я мог бы использовать в лямбда-выражениях Java 8 (то есть только один абстрактный метод), но его нет.

Я думал о создании интерфейса ToStringable, который будет использоваться следующим образом.

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Test {
  private static final Logger logger = LogManager.getLogger();
  public static void main (String[] args) {
    logger.info("Text: {}", new ToStringable() { public String toString() { return getText();}});
  }
  static String getText() {
    // Expensive action using IO
    return "";
  }
}
interface ToStringable { @Override String toString(); }

Это выполняет свою работу, но его трудно читать, и я не могу использовать лямбда Java 8 для этого (см. Ниже, обратите внимание, что этот код не компилируется).

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Test {
  private static Logger logger = LogManager.getLogger();
  public static void main (String[] args) {
    logger.info("Text: {}", () -> getText());
  }
  static String getText() {
    // Expensive action using IO
    return "";
  }
}
interface ToStringable {@Override String toString(); }

Наконец, только старый добрый if (logger.isXxxxEnabled()) { logger.xxxx(...) } надежно решил проблему, но какой смысл в использовании современного ленивого регистратора?

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Test {
  private static final Logger logger = LogManager.getLogger();
  public static void main (String[] args) {
    if (logger.isInfoEnabled()) {
      logger.info("Text: {}", getText());
    }
  }
  static String getText() {
    // Expensive action using IO
    return "";
  }
}

Сталкивался ли кто-нибудь с этой проблемой, и если да, то как она была решена?

Заключительные слова: первые три фрагмента кода - это короткие, автономные, правильные примеры. Это означает, что они здесь только для того, чтобы показать проблему. Не думайте, пожалуйста, за пределами проблемы (имеется в виду: не говорите мне быть прагматичным и просто отпустите).

2
Olivier Grégoire 11 Фев 2015 в 16:00

1 ответ

ОБНОВЛЕНИЕ (5 августа 2015 г.)

Рассмотрите возможность голосования за этот билет LOG4J2-599, чтобы обеспечить поддержку лямбда-выражений в Log4j 2. .


ОБНОВЛЕНИЕ (10 августа 2015 г.)

Следующая версия Log4J, 2.4, будет поддерживать лямбда-выражения. Пример использования:

// log message is not constructed if DEBUG level is not enabled
logger.debug("Result of some expensive operation is {}", () -> someLongRunningOperation());

На вопрос уже был дан ответ, это просто дополнительный совет на случай, если вы планируете использовать это с асинхронными регистраторами или асинхронным приложением:

  • Вы упомянули, что создание результата сообщения - дорогостоящая операция. Я предполагаю, что вы хотите использовать асинхронные регистраторы / приложения для повышения пропускной способности и / или времени отклика вашего приложения. Имеет смысл. Имейте в виду, что если создание результата сообщения действительно дорого, тогда (в зависимости от того, сколько вы регистрируете) ваша очередь может заполниться; как только это произойдет, вы снова войдете в синхронный журнал. Вы можете настроить размер очереди, чтобы справиться с пакетами, но если постоянная скорость записи в журнал очень высока, вы можете столкнуться с этой проблемой. Log4j2 включает JMX MBeans (1, 2) можно использовать, чтобы узнать, насколько заполнена очередь.
  • Результат журнала вывода будет отражать значение сообщения в момент, когда фоновый поток ввода-вывода вызвал Message.getFormattedMessage, а не значение сообщения в момент, когда поток вашего приложения вызвал Logger.info. Если регистрируется много ленивых сообщений, временной интервал между этими двумя событиями может увеличиваться. Также имейте в виду, что ваш объект сообщения должен быть потокобезопасным. Вы, наверное, уже знаете это, но я подумал, что стоит указать на это.

Надеюсь, это будет полезно.

1
Remko Popma 15 Авг 2015 в 13:50