Applications: Logrotate и BASH.
Задача: организация высыпающихся из web-сервера и контейнеризатора "Tomcat (Catalina)" журнальных файлов, не затрагивая при этом конфигурацию такового.
По моим наблюдениям разработчики и сопровожденцы web-приложений на "Java", созданных для запуска в контейнерах "Tomcat", весьма беззаботны и настройку параметров нарезки и ротации файлов журналов событий не практикуют вообще. В результате, через полгода-год после начала эксплуатации сервиса в директории журналов скапливается мешанина из тысяч файлов от несущего сервера "Tomcat", менеджера компонентов, контейнеризатора, а также от каждого контейнера по отдельности по состоянию и событиям доступа пользователей. Упорядочим их, разделив работу на два этапа.
Настраиваем нарезку журнала запуска "Tomcat".
Чаще всего подсистему запуска "Tomcat"-а настраивают так, что сообщения этого этапа выводятся в определяемый переменной окружения "$CATALINA_OUT" специальный журнальный файл, называемый как-то вроде "console.log" или "catalina.out". В него много чего вываливается и он разрастается бесконтрольно, в отличии от файлов журналов событий инициируемых уже внутри самого "Tomcat"-а. Для начала наладим его ротацию по аналогии с соседними журналами, для чего натравим на него системное средство "Logrotate".
Есть две методики нарезания журналов на куски: по временным диапазонам и предельному размеру файла. Лично я стараюсь использовать второй способ, из соображений более просто просчитываемого расхода файлового пространства. Однако почти во всех встречающихся мне инсталляциях "Tomcat" журналы уже нарезаются на суточные сегменты в соответствии с настройками "по умолчанию", так что выбирать не приходится - тоже будем усекать файлы ежедневно.
Создаем конфигурационный файл ротации журнала событий запуска "Tomcat":
# mkdir -p /etc/logrotate.d
# vi /etc/logrotate.d/tomcat
# vi /etc/logrotate.d/tomcat
# Перечень подлежащих нарезке и ротации журналов.
/var/log/tomcat/catalina.out /var/log/tomcat/console.log {
# Указываем производить ротацию журнала ежедневно.
daily
# Количество хранимых страниц журнала (один месяц, по файлу на день).
rotate 30
# Велим не считать ошибкой отсутствие файла.
missingok
# Ничего не делаем, если файл журнала пуст.
notifempty
# "Tomcat" логи не сжимает, ну и мы не будем делать этого.
nocompress
# Добавляем к имени файла архива журнала дату ("по умолчанию" формат "-YYYYMMDD").
dateext
# Корректируем формат подставляемой даты, приводя её к принятому в "Tomcat".
dateformat .%Y-%m-%d
# Указываем копировать данные журнала в архивный и зачищать действующий.
# (это не требует перезапуска приложения для перехода на новый файл)
copytruncate
# Указываем пользователя, от имени которого мы манипулируем файлами журналов.
su tomcat tomcat
}
/var/log/tomcat/catalina.out /var/log/tomcat/console.log {
# Указываем производить ротацию журнала ежедневно.
daily
# Количество хранимых страниц журнала (один месяц, по файлу на день).
rotate 30
# Велим не считать ошибкой отсутствие файла.
missingok
# Ничего не делаем, если файл журнала пуст.
notifempty
# "Tomcat" логи не сжимает, ну и мы не будем делать этого.
nocompress
# Добавляем к имени файла архива журнала дату ("по умолчанию" формат "-YYYYMMDD").
dateext
# Корректируем формат подставляемой даты, приводя её к принятому в "Tomcat".
dateformat .%Y-%m-%d
# Указываем копировать данные журнала в архивный и зачищать действующий.
# (это не требует перезапуска приложения для перехода на новый файл)
copytruncate
# Указываем пользователя, от имени которого мы манипулируем файлами журналов.
su tomcat tomcat
}
Проверяем корректность конфигурационного файла:
# logrotate -d /etc/logrotate.d/tomcat
Здесь мы сделали малое из задуманного - привели к единообразному для всех "Tomcat"-овских журналов формату имени один или два выбивающихся из ряда вон.
Налаживаем ротацию журналов "Tomcat".
Вторая и основная часть задачи заключается в аккуратном удалении старых журналов событий и попутном архивировании оставляемых на хранение.
Разумеется, проще всего было бы опираться на "метки" времени создания или изменения файла, задаваемые операционной системой, но они могут быть изменены уже после последней записи приложением в журнал - например при перемещении набора журналов в новое место файловой системы или даже профилактического изменения прав доступа, не говоря уже о случайной корректировке в процессе изучения содержимого. Потому, учитывая некоторую нестабильность свойства метки времени самого файла, я предпочитаю использовать дату указанную непосредственно в имени файла журнала, задаваемом приложением при его создании (именно потому ранее при настройке утилиты "Logrotate" мы подгоняли вид указываемой даты под принятый в "Apache Tomcat").
Мне привычнее перебирать файлы посредством BASH-скрипта:
# mkdir -p /usr/local/bin/
# vi /usr/local/bin/tomcat-logs-thinning.sh
# chmod ugo+x /usr/local/bin/tomcat-logs-thinning.sh
# vi /usr/local/bin/tomcat-logs-thinning.sh
# chmod ugo+x /usr/local/bin/tomcat-logs-thinning.sh
#!/bin/bash
# # Прореживаем журналы, удаляя всё старее заданной даты, архивируя остальное и незадействованное. # #
# # Опираемся на имена файлов, содержащие даты в формате "YYYY-MM-DD", а не на время создания таковых! # #
# Проверяем корректность вводных.
if [ "$(ls -d ${1})" == "" -o "$(echo ${2} | grep -E '^[0-9]+$')" == "" ]; then
echo "This is a self-made script thinning and archiving event logs like Apache Tomcat server."
echo "Usage: $(basename $0) \"/PATH/TO/LOGS\" \"DAY AGE\""
echo "(sample: $(basename $0) /var/lib/tomcat/logs 180)"
exit 1
fi
# Определяем набор переменных.
DPATH=${1}
AGE=${2}
LOG="${DPATH}/thinning.log"
# Фиксируем время запуска процедуры прореживания файлов журналов.
echo >> ${LOG}; echo "Thinning start at $(date +%Y-%m-%d/%H:%M)" | tee -a ${LOG}
# Перебираем все файлы внутри заданной директории журналов:
cd "${DPATH}"
for I in *
do
# Вычленяем дату из имени файла.
EXDATE=$(echo ${I} | awk 'match($0, /[0-9]{4}-[0-9]{2}-[0-9]{2}/) {print substr($0, RSTART, RLENGTH);}')
# Пропускаем файлы, в именах которых не обнаружено строки даты заданного формата.
[ ${?} -ne 0 -o "${EXDATE}" == "" ] && { continue; }
# Фильтруем файлы, отбирая те, в именах которых даты ближе заданного к текущей и точно не меньше одного месяца (перестраховываемся), а остальные подавая на удаление.
if [ $(date +%s -d "${EXDATE}") -gt $(date +%s -d "${AGE} day ago") -o $(date +%s -d "${EXDATE}") -gt $(date +%s -d "1 month ago") ]
then
# Если файл не пустой, ещё не архивирован и никем не используется, то GZIP-уем его.
if [ -s "${DPATH}/${I}" ] && [ "$(echo ${I} | grep -E '*\.gz$')" == "" ] && [ "$(lsof -t "${DPATH}/${I}" 2>/dev/null)" == "" ]; then
echo "zipping: ${DPATH}/${I}" | tee -a ${LOG}
gzip -f -9 "${DPATH}/${I}"
else
continue
fi
# (ветвь удаления старых журналов)
else
# Удаляем устаревшие файлы
echo "removing: ${DPATH}/${I}" | tee -a ${LOG}
rm --force "${DPATH}/${I}"
fi
done
exit 0
# # Прореживаем журналы, удаляя всё старее заданной даты, архивируя остальное и незадействованное. # #
# # Опираемся на имена файлов, содержащие даты в формате "YYYY-MM-DD", а не на время создания таковых! # #
# Проверяем корректность вводных.
if [ "$(ls -d ${1})" == "" -o "$(echo ${2} | grep -E '^[0-9]+$')" == "" ]; then
echo "This is a self-made script thinning and archiving event logs like Apache Tomcat server."
echo "Usage: $(basename $0) \"/PATH/TO/LOGS\" \"DAY AGE\""
echo "(sample: $(basename $0) /var/lib/tomcat/logs 180)"
exit 1
fi
# Определяем набор переменных.
DPATH=${1}
AGE=${2}
LOG="${DPATH}/thinning.log"
# Фиксируем время запуска процедуры прореживания файлов журналов.
echo >> ${LOG}; echo "Thinning start at $(date +%Y-%m-%d/%H:%M)" | tee -a ${LOG}
# Перебираем все файлы внутри заданной директории журналов:
cd "${DPATH}"
for I in *
do
# Вычленяем дату из имени файла.
EXDATE=$(echo ${I} | awk 'match($0, /[0-9]{4}-[0-9]{2}-[0-9]{2}/) {print substr($0, RSTART, RLENGTH);}')
# Пропускаем файлы, в именах которых не обнаружено строки даты заданного формата.
[ ${?} -ne 0 -o "${EXDATE}" == "" ] && { continue; }
# Фильтруем файлы, отбирая те, в именах которых даты ближе заданного к текущей и точно не меньше одного месяца (перестраховываемся), а остальные подавая на удаление.
if [ $(date +%s -d "${EXDATE}") -gt $(date +%s -d "${AGE} day ago") -o $(date +%s -d "${EXDATE}") -gt $(date +%s -d "1 month ago") ]
then
# Если файл не пустой, ещё не архивирован и никем не используется, то GZIP-уем его.
if [ -s "${DPATH}/${I}" ] && [ "$(echo ${I} | grep -E '*\.gz$')" == "" ] && [ "$(lsof -t "${DPATH}/${I}" 2>/dev/null)" == "" ]; then
echo "zipping: ${DPATH}/${I}" | tee -a ${LOG}
gzip -f -9 "${DPATH}/${I}"
else
continue
fi
# (ветвь удаления старых журналов)
else
# Удаляем устаревшие файлы
echo "removing: ${DPATH}/${I}" | tee -a ${LOG}
rm --force "${DPATH}/${I}"
fi
done
exit 0
Запускать архивацию и прореживание журналов можно с разной периодичностью - лично я делаю это раз в три-четыре дня, ближе к утру воскресенья и среды:
# vi /etc/crontab
....
# Tomcat`s like logs zipping and thinning (safe 180 days)
0 4 * * 0,3 root /usr/local/bin/tomcat-logs-thinning.sh /var/log/tomcat 180 &
# Tomcat`s like logs zipping and thinning (safe 180 days)
0 4 * * 0,3 root /usr/local/bin/tomcat-logs-thinning.sh /var/log/tomcat 180 &
Итак, в два простых этапа мы привели именование файлов журналов событий "Tomcat" к более единообразному виду и ограничили их разрастание определённым временным лимитом.
Обращаю внимание на то, что всего этого можно было достигнуть путём корректировки конфигурационных файлов компонентов "Tomcat", а выбранный путь решения задачи обусловлен отсутствием возможности изменять настройки приложений в чужой зоне ответственности.
P.S. Для сведения - мне сказали, что обе задачи можно реализовать через функционал системной утилиты "logrotate", но наверное я тупой, потому что за пару дней чтения инструкции и перебора вариантов настроек у меня ровным счётом ничего не получилось.