Рубрики
Без рубрики

Флоггер Для Ведения журнала

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

Автор оригинала: baeldung.

1. Обзор

В этом уроке мы поговорим о фреймворке Flogger , свободно используемом API ведения журнала для Java, разработанном Google.

2. Зачем использовать Флоггер?

Со всеми фреймворками ведения журнала, которые в настоящее время представлены на рынке, такими как Log4j и Logback, зачем нам нужен еще один фреймворк ведения журнала?

Оказывается, у Флоггера есть несколько преимуществ перед другими фреймворками – давайте посмотрим.

2.1. Удобочитаемость

Свободный характер API Loggers во многом способствует тому, чтобы сделать его более читаемым.

Давайте рассмотрим пример, в котором мы хотим регистрировать сообщение каждые десять итераций.

С традиционной структурой ведения журнала мы увидим что-то вроде:

int i = 0;

// ...

if (i % 10 == 0) {
    logger.info("This log shows every 10 iterations");
    i++;
}

Но теперь, с помощью Флоггера, все вышесказанное можно упростить до:

logger.atInfo().every(10).log("This log shows every 10 iterations");

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

2.2. Производительность

Объекты ведения журнала оптимизируются до тех пор, пока мы избегаем вызова toString для зарегистрированных объектов:

User user = new User();
logger.atInfo().log("The user is: %s", user);

Если мы регистрируемся, как показано выше, бэкэнд имеет возможность оптимизировать ведение журнала. С другой стороны, если мы вызываем toString напрямую или объединяем строки, то эта возможность теряется:

logger.atInfo().log("Ths user is: %s", user.toString());
logger.atInfo().log("Ths user is: %s" + user);

2.3. Расширяемость

Фреймворк Flogger уже охватывает большую часть основных функций, которые мы ожидаем от фреймворка ведения журнала.

Однако есть случаи, когда нам нужно будет добавить к функциональности. В этих случаях можно расширить API.

В настоящее время для этого требуется отдельный вспомогательный класс. Мы могли бы, например, расширить API Flogger, написав класс User Logger :

logger.at(INFO).forUserId(id).withUsername(username).log("Message: %s", param);

Это может быть полезно в тех случаях, когда мы хотим последовательно форматировать сообщение. Затем регистратор User/| предоставит реализацию пользовательских методов для идентификатора пользователя(String id) и с именем пользователя(String username).

Для этого класс User Logger должен будет расширить класс AbstractLogger и предоставить реализацию для API . Если мы посмотрим на FluentLogger , это просто регистратор без дополнительных методов, поэтому мы можем начать с копирования этого класса как есть, а затем построить из этого фундамента, добавив в него методы.

2.4. Эффективность

Традиционные фреймворки широко используют varargs. Эти методы требуют, чтобы перед вызовом метода был выделен и заполнен новый объект [] . Кроме того, все передаваемые основные типы должны быть автоматически упакованы.

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

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

Типичная структура ведения журнала будет иметь эти методы:

level(String, Object)
level(String, Object...)

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

log(Level, Object...)

В дополнение к этому, обычно существуют варианты методов, которые принимают причину (экземпляр Throwable ), связанную с оператором log:

level(Throwable, String, Object)
level(Throwable, String, Object...)

Ясно, что API связывает три проблемы в один вызов метода:

  1. Он пытается указать уровень журнала (выбор метода)
  2. Попытка прикрепить метаданные к оператору журнала (Throwable cause)
  3. А также указание сообщения журнала и аргументов.

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

Теперь мы можем понять, почему важно иметь два метода в цепочке:

logger.atInfo().withCause(e).log("Message: %s", arg);

Давайте теперь посмотрим, как мы можем использовать его в нашей кодовой базе.

3. Зависимости

Настроить Флоггер довольно просто. Нам просто нужно добавить flogger и flogger-system-backend в наш pom:


    
        com.google.flogger
        flogger
        0.4
    
    
        com.google.flogger
        flogger-system-backend
        0.4
        runtime
    

Установив эти зависимости, мы теперь можем перейти к изучению API, который находится в вашем распоряжении.

4. Изучение API Fluent

Во-первых, давайте объявим статический экземпляр для нашего регистратора:

private static final FluentLogger logger = FluentLogger.forEnclosingClass();

А теперь мы можем начать вести журнал. Начнем с чего-нибудь простого:

int result = 45 / 3;
logger.atInfo().log("The result is %d", result);

Сообщения журнала могут использовать любой из спецификаторов формата Java printf , таких как %s, %d или %016x .

4.1. Уклонение от работы на лесосеках

Создатели флоггера рекомендуют нам избегать работы на сайте журнала.

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

public static String collectSummaries() {
    longRunningProcess();
    int items = 110;
    int s = 30;
    return String.format("%d seconds elapsed so far. %d items pending processing", s, items);
}

Заманчиво позвонить собрать резюме прямо в нашем отчете журнала:

logger.atFine().log("stats=%s", collectSummaries());

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

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

Вместо этого мы должны использовать метод Lazy Args.lazy :

logger.atFine().log("stats=%s", LazyArgs.lazy(() -> collectSummaries()));

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

Хотя разрешено охранять операторы журнала с помощью IsEnabled :

if (logger.atFine().isEnabled()) {
    logger.atFine().log("summaries=%s", collectSummaries());
}

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

4.2. Работа С Исключениями

Как насчет исключений, как мы с ними справляемся?

Ну, Флоггер поставляется с с трассировкой стека методом, который мы можем использовать для регистрации Выбрасываемый экземпляр:

try {
    int result = 45 / 0;
} catch (RuntimeException re) {
    logger.atInfo().withStackTrace(StackSize.FULL).withCause(re).log("Message");
}

Где withStackTrace принимает в качестве аргумента Размер стека перечисление с постоянными значениями МАЛЫЙ, СРЕДНИЙ, БОЛЬШОЙ или ПОЛНЫЙ . Трассировка стека, сгенерированная с помощью трассировки стека () , будет отображаться как Трассировка стека сайта блога исключение по умолчанию java.util.logging backend. Однако другие бэкенды могут решить справиться с этим по-другому.

4.3. Конфигурация и уровни ведения журнала

До сих пор мы использовали logger.at Информация в большинстве наших примеров, но Flogger поддерживает многие другие уровни. Мы рассмотрим их, но сначала давайте представим, как настроить параметры ведения журнала.

Для настройки ведения журнала мы используем класс LoggerConfig .

Например, когда мы хотим установить уровень ведения журнала на FINE :

LoggerConfig.of(logger).setLevel(Level.FINE);

И Flogger поддерживает различные уровни ведения журнала:

logger.atInfo().log("Info Message");
logger.atWarning().log("Warning Message");
logger.atSevere().log("Severe Message");
logger.atFine().log("Fine Message");
logger.atFiner().log("Finer Message");
logger.atFinest().log("Finest Message");
logger.atConfig().log("Config Message");

4.4. Ограничение скорости

Как насчет вопроса об ограничении ставок? Как мы справляемся с ситуацией, когда мы не хотим регистрировать каждую итерацию?

Флоггер приходит нам на помощь с помощью каждого(int n) метода :

IntStream.range(0, 100).forEach(value -> {
    logger.atInfo().every(40).log("This log shows every 40 iterations => %d", value);
});

При выполнении приведенного выше кода мы получаем следующий вывод:

Sep 18, 2019 5:04:02 PM com.baeldung.flogger.FloggerUnitTest lambda$givenAnInterval_shouldLogAfterEveryTInterval$0
INFO: This log shows every 40 iterations => 0 [CONTEXT ratelimit_count=40 ]
Sep 18, 2019 5:04:02 PM com.baeldung.flogger.FloggerUnitTest lambda$givenAnInterval_shouldLogAfterEveryTInterval$0
INFO: This log shows every 40 iterations => 40 [CONTEXT ratelimit_count=40 ]
Sep 18, 2019 5:04:02 PM com.baeldung.flogger.FloggerUnitTest lambda$givenAnInterval_shouldLogAfterEveryTInterval$0
INFO: This log shows every 40 iterations => 80 [CONTEXT ratelimit_count=40 ]

Что, если мы захотим регистрировать данные каждые 10 секунд? Тогда мы можем использовать не более чем каждый(int n, единица измерения времени) :

IntStream.range(0, 1_000_0000).forEach(value -> {
    logger.atInfo().atMostEvery(10, TimeUnit.SECONDS).log("This log shows [every 10 seconds] => %d", value);
});

С этим результатом теперь становится:

Sep 18, 2019 5:08:06 PM com.baeldung.flogger.FloggerUnitTest lambda$givenATimeInterval_shouldLogAfterEveryTimeInterval$1
INFO: This log shows [every 10 seconds] => 0 [CONTEXT ratelimit_period="10 SECONDS" ]
Sep 18, 2019 5:08:16 PM com.baeldung.flogger.FloggerUnitTest lambda$givenATimeInterval_shouldLogAfterEveryTimeInterval$1
INFO: This log shows [every 10 seconds] => 3545373 [CONTEXT ratelimit_period="10 SECONDS [skipped: 3545372]" ]
Sep 18, 2019 5:08:26 PM com.baeldung.flogger.FloggerUnitTest lambda$givenATimeInterval_shouldLogAfterEveryTimeInterval$1
INFO: This log shows [every 10 seconds] => 7236301 [CONTEXT ratelimit_period="10 SECONDS [skipped: 3690927]" ]

5. Использование Флоггера С Другими Бэкендами

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

5.1 Флоггер С Slf4j

Настроить серверную часть Slf4j очень просто. Во-первых, нам нужно добавить зависимость flogger-slf4j-backend в наш pom :


    com.google.flogger
    flogger-slf4j-backend
    0.4

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

System.setProperty(
  "flogger.backend_factory", "com.google.common.flogger.backend.slf4j.Slf4jBackendFactory#getInstance");

И теперь наше приложение будет использовать существующую конфигурацию.

5.1 Флоггер С Log4j

Мы выполняем аналогичные шаги для настройки серверной части Log4j. Давайте добавим зависимость logger-log4j-backend в наш pom :


    com.google.flogger
    flogger-log4j-backend
    0.4
    
        
            com.sun.jmx
            jmxri
        
        
            com.sun.jdmk
            jmxtools
        
        
            javax.jms
            jms
        
    



    log4j
    log4j
    1.2.17


    log4j
    apache-log4j-extras
    1.2.17

Нам также необходимо зарегистрировать серверную фабрику Флоггера для Log4j:

System.setProperty(
  "flogger.backend_factory", "com.google.common.flogger.backend.log4j.Log4jBackendFactory#getInstance");

И все, наше приложение теперь настроено на использование существующих конфигураций Log4j!

6. Заключение

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

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

Как обычно, исходный код этого учебника доступен на GitHub .