Перейти к основному содержимому

Руководство по последовательному добавлению файлов

· 11 мин. чтения

1. Обзор

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

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

В этом руководстве мы узнаем, как настроить скользящие файловые приложения в некоторых из наиболее широко используемых библиотек ведения журналов: Log4j, Log4j2 и Slf4j.

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

2. Пример нашего приложения

Давайте начнем с примера приложения, которое регистрирует некоторые сообщения. Этот код основан на Log4j, но мы можем легко модифицировать его для работы с Log4j2 или Slf4j:

import org.apache.log4j.Logger;

public class Log4jRollingExample {

private static Logger logger = Logger.getLogger(Log4jRollingExample.class);

public static void main(String[] args) throws InterruptedException {
for(int i = 0; i < 2000; i++) {
logger.info("This is the " + i + " time I say 'Hello World'.");
Thread.sleep(100);
}
}
}

Приложение довольно наивное; он записывает некоторые сообщения в цикле с небольшой задержкой между итерациями. При выполнении 2000 циклов и паузе в 100 мс в каждом цикле выполнение приложения должно занять чуть более трех минут.

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

3. Прокручивающиеся файловые приложения в Log4j

3.1. Зависимости Maven

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

<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

Для дополнительных приложений, предоставляемых apache-log-extras , которые мы будем использовать в следующих примерах, мы добавим следующую зависимость, обязательно используя ту же версию, которую мы объявили для Log4j, чтобы обеспечить полную совместимость:

<dependency>
<groupId>log4j</groupId>
<artifactId>apache-log4j-extras</artifactId>
<version>1.2.17</version>
</dependency>

Мы можем найти последний выпуск Log4j и Apache Log4j Extras на Maven Central.

3.2. Роллинг в зависимости от размера файла

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

<appender name="roll-by-size" class="org.apache.log4j.RollingFileAppender">
<param name="file" value="target/log4j/roll-by-size/app.log" />
<param name="MaxFileSize" value="5KB" />
<param name="MaxBackupIndex" value="2" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %m%n" />
</layout>
</appender>

Здесь мы настроили Log4j для переноса файла журнала, когда его размер достигает 5 КБ, используя параметр MaxFileSize . Мы также указали Log4j хранить не более двух свернутых файлов журнала, используя параметр MaxBackupIndex .

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

27/11/2016  10:28    138 app.log
27/11/2016 10:28 5.281 app.log.1
27/11/2016 10:28 5.281 app.log.2

Так что же случилось? Log4j начал запись в файл app.log . Когда размер файла превышал ограничение в 5 КБ, Log4j перемещал app.log в app.log.1 , создавал новый пустой app.log и продолжал записывать новые сообщения журнала в app.log .

Затем, после того как новый файл app.log превысил ограничение в 5 КБ, этот процесс повторился. На этот раз app.log.1 был перемещен в app.log.2, освободив место для еще одного нового пустого app.log .

Этот процесс повторения повторялся несколько раз во время выполнения, но, поскольку мы настроили наше приложение для хранения не более двух свернутых файлов, файла с именем app.log.3 не существует .

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

Когда мы проверяем первую строку app.log.2 , она содержит сообщение, относящееся к 700-й итерации, что означает, что все предыдущие сообщения журнала были потеряны:

2016-11-27 10:28:34 INFO  This is the 700 time I say 'Hello World'.

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

Для этого мы будем использовать другие более мощные, гибкие и настраиваемые приложения Log4j, которые поставляются в специальном пакете под названием apache-log4j-extras .

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

3.3. Прокатка с автоматическим сжатием

Вернемся к примеру с Log4j и улучшим нашу настройку, добавив автоматическое сжатие свернутых файлов для экономии места:

<appender name="roll-by-size" class="org.apache.log4j.rolling.RollingFileAppender">
<rollingPolicy class="org.apache.log4j.rolling.FixedWindowRollingPolicy">
<param name="ActiveFileName" value="target/log4j/roll-by-size/app.log" />
<param name="FileNamePattern" value="target/log4j/roll-by-size/app.%i.log.gz" />
<param name="MinIndex" value="7" />
<param name="MaxIndex" value="17" />
</rollingPolicy>
<triggeringPolicy class="org.apache.log4j.rolling.SizeBasedTriggeringPolicy">
<param name="MaxFileSize" value="5120" />
</triggeringPolicy>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %m%n" />
</layout>
</appender>

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

В теге скользящей политики параметр ActiveFileName указывает путь к основным файлам журнала, содержащим последние сообщения, а параметр FileNamePattern указывает шаблон, описывающий, каким должен быть путь к свернутым файлам. Отметим, что это действительно шаблон, потому что специальный заполнитель %i будет заменен индексом свернутого файла.

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

Теперь, когда мы запускаем приложение, мы получаем другой набор файлов журнала:

03/12/2016 19:24 88 app.1.log.gz
...
03/12/2016 19:26 88 app.2.log.gz
03/12/2016 19:26 88 app.3.log.gz
03/12/2016 19:27 70 app.current.log

Файл app.current.log — это место, где происходили последние журналы. Предыдущие журналы скручиваются и сжимаются, когда их размер достигает установленного предела.

3.4. Роллинг на основе даты и времени

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

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

<appender name="roll-by-time"
class="org.apache.log4j.rolling.RollingFileAppender">
<rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
<param name="FileNamePattern" value="target/log4j/roll-by-time/app.%d{HH-mm}.log.gz" />
</rollingPolicy>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p - %m%n" />
</layout>
</appender>

3.5. Роллинг в зависимости от размера и времени

Объединив SizeBasedTriggeringPolicy и TimeBasedRollingPolicy, мы можем получить приложение, которое прокручивается на основе даты/времени, а когда размер файла достигает установленного предела, оно также прокручивается на основе размера:

<appender name="roll-by-time-and-size" class="org.apache.log4j.rolling.RollingFileAppender">
<rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
<param name="ActiveFileName" value="log4j/roll-by-time-and-size/app.log" />
<param name="FileNamePattern" value="log4j/roll-by-time-and-size/app.%d{HH-mm}.%i.log.gz" />
</rollingPolicy>
<triggeringPolicy
class="org.apache.log4j.rolling.SizeBasedTriggeringPolicy">
<param name="MaxFileSize" value="100" />
</triggeringPolicy>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p - %m%n" />
</layout>
</appender>

Когда мы запускаем наше приложение с этой настройкой, мы получаем следующие файлы журнала:

03/12/2016 19:25 234 app.19-25.1481393432120.log.gz
03/12/2016 19:25 234 app.19-25.1481393438939.log.gz
03/12/2016 19:26 244 app.19-26.1481393441940.log.gz
03/12/2016 19:26 240 app.19-26.1481393449152.log.gz
03/12/2016 19:26 3.528 app.19-26.1481393470902.log

В файле app.19-26.1481393470902.log происходит текущее ведение журнала. Как мы видим, все логи в промежутке между 19:25 и 19:26 хранятся в нескольких сжатых лог-файлах с именами, начинающимися с « app.19-25». Заполнитель « %i» заменяется постоянно растущим числом.

4. Прокручивающиеся файловые приложения в Log4j2

4.1. Зависимости Maven

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

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.7</version>
</dependency>

Как обычно, последнюю версию мы можем найти на Maven Central.

4.2. Роллинг в зависимости от размера файла

Давайте изменим наше примерное приложение, чтобы использовать библиотеки журналирования Log4j2. Мы настроим прокрутку файлов в зависимости от размера файла журнала в файле конфигурации log4j2.xml :

<RollingFile 
name="roll-by-size"
fileName="target/log4j2/roll-by-size/app.log"
filePattern="target/log4j2/roll-by-size/app.%i.log.gz"
ignoreExceptions="false">
<PatternLayout>
<Pattern>%d{yyyy-MM-dd HH:mm:ss} %p %m%n</Pattern>
</PatternLayout>
<Policies>
<OnStartupTriggeringPolicy />
<SizeBasedTriggeringPolicy size="5 KB" />
</Policies>
</RollingFile>

В теге Policies мы указали все триггерные политики, которые хотим применить. OnStartupTriggeringPolicy инициирует проверку при каждом запуске приложения, что может быть полезно для автономных приложений. Мы также указали SizeBasedTriggeringPolicy, указав, что прокрутка должна происходить всякий раз, когда файл журнала достигает 5 КБ.

4.3. Роллинг на основе даты и времени

Используя политики, предлагаемые Log4j2, мы настроим приложение для свертывания и сжатия файла журнала в зависимости от времени:

<RollingFile name="roll-by-time" 
fileName="target/log4j2/roll-by-time/app.log"
filePattern="target/log4j2/roll-by-time/app.%d{MM-dd-yyyy-HH-mm}.log.gz"
ignoreExceptions="false">
<PatternLayout>
<Pattern>%d{yyyy-MM-dd HH:mm:ss} %p %m%n</Pattern>
</PatternLayout>
<TimeBasedTriggeringPolicy />
</RollingFile>

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

4.4. Роллинг в зависимости от размера и времени

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

<RollingFile name="roll-by-time-and-size" 
fileName="target/log4j2/roll-by-time-and-size/app.log"
filePattern="target/log4j2/roll-by-time-and-size/app.%d{MM-dd-yyyy-HH-mm}.%i.log.gz"
ignoreExceptions="false">
<PatternLayout>
<Pattern>%d{yyyy-MM-dd HH:mm:ss} %p %m%n</Pattern>
</PatternLayout>
<Policies>
<OnStartupTriggeringPolicy />
<SizeBasedTriggeringPolicy size="5 KB" />
<TimeBasedTriggeringPolicy />
</Policies>
<DefaultRolloverStrategy>
<Delete basePath="${baseDir}" maxDepth="2">
<IfFileName glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" />
<IfLastModified age="20d" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>

В этой конфигурации мы заявили, что рулон должен происходить в зависимости от времени и размера. Приложение может понять, на какой временной интервал мы ссылаемся, благодаря шаблону, используемому для имени файла, « app.%d{MM-dd-yyyy-HH-mm}.%i.log.gz» , который неявно устанавливает свертывание каждую минуту и сжатие свернутого файла.

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

4.5. Зависимости Maven

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

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.7</version>
</dependency>

Как обычно, последнюю версию мы можем найти на Maven Central.

5. Прокручивающиеся файловые приложения в Slf4j

5.1. Зависимости Maven

Когда мы хотим использовать Slf4j2 с бэкэндом Logback в качестве нашей библиотеки ведения журнала, мы добавим эту зависимость в наш pom.xml :

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.6</version>
</dependency>

Как обычно, последнюю версию мы можем найти на Maven Central.

5.2. Роллинг в зависимости от размера файла

Теперь давайте посмотрим, как использовать Slf4j с его внутренним Logback по умолчанию . Мы настроим прокрутку файлов в файле конфигурации logback.xml , который находится в пути к классам приложения:

<appender name="roll-by-size" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>target/slf4j/roll-by-size/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>target/slf4j/roll-by-size/app.%i.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
<totalSizeCap>1MB</totalSizeCap>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5KB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>

Снова мы сталкиваемся с концепцией скользящей политики. Основной механизм такой же, как и в Log4j и Log4j2. FixedWindowRollingPolicy позволяет нам использовать заполнитель индекса в шаблоне имени свернутого файла.

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

5.3. Роллинг в зависимости от времени

В Slf4j мы можем свернуть файл журнала на основе времени, используя предоставленную TimeBasedRollingPolicy . Эта политика позволяет нам указать имя шаблона скользящего файла, используя заполнители, связанные со временем и датой:

<appender name="roll-by-time" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>target/slf4j/roll-by-time/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>target/slf4j/roll-by-time/app.%d{yyyy-MM-dd-HH-mm}.log.zip
</fileNamePattern>
<maxHistory>20</maxHistory>
<totalSizeCap>1MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %p %m%n</pattern>
</encoder>
</appender>

5.4. Роллинг в зависимости от размера и времени

Если нам нужно свернуть файл на основе времени и размера, мы можем использовать предоставленную SizeAndTimeBasedRollingPolicy . При использовании этой политики мы должны указать как заполнитель, связанный со временем, так и заполнитель индекса.

Каждый раз, когда размер файла журнала для определенного временного интервала превышает настроенный предел размера, создается другой файл журнала с тем же значением временного заполнителя, но с увеличенным индексом:

<appender name="roll-by-time-and-size"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>target/slf4j/roll-by-time-and-size/app.log</file>
<rollingPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>
target/slf4j/roll-by-time-and-size/app.%d{yyyy-MM-dd-mm}.%i.log.zip
</fileNamePattern>
<maxFileSize>5KB</maxFileSize>
<maxHistory>20</maxHistory>
<totalSizeCap>1MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %p %m%n</pattern>
</encoder>
</appender>

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

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

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