Я пытаюсь найти простой способ добавления / записи в журнал, в то же время сохраняя его обрезанным до разумного размера. Я бы предпочел не просто добавлять файлы навсегда, а затем иметь сценарий очистки журнала. Я не могу понять, как я мог бы сделать это изящно, не используя какой-то второй файл в качестве временного держателя.
Вещи, на которые я смотрел для справки:
Я изучил расширенное руководство по написанию сценариев http://tldp.org/LDP/abs/html/io-redirection.html
Комбинированный вывод двух команд: Bash - Как вывести две команды в файл?
В идеале у меня было бы что-то вроде (я знаю, что это не работает)
(tail-n 1000 foo.log; ./foo.sh) > foo.log
который сохранит последние 1000 строк из моего текущего журнала, а затем добавит мой новый вывод для текущего запуска foo.sh.
Я не могу придумать способ использовать перенаправление добавления >> и ограничить исходный файл, не оборачивая вызов foo.sh в какой-то другой bar.sh
tail -n 1000 foo.log > tmp.log
mv tmp.log foo.log
./foo.sh >> foo.log
Это просто кажется глупым.
Возможно, мой ответ состоит в том, чтобы foo.sh не полагался на STDOUT как на место для отправки сообщений журнала, а скорее открывал файл напрямую.
ПОСЛЕДУЮЩИЕ ИЗМЕНЕНИЯ:
Преобладает мнение, что это не рекомендуется. Мнение, которое я ценю. Однако сервер, на котором это происходит, находится вне моего контроля и на самом деле не будет находиться под ... бдительным администратором. Это коробка, в которой множество разных групп владеют частями коробки, но никто не несет ответственности за общее состояние коробки. Я мог бы просто позволить журналу строиться вечно, и это, вероятно, не имело бы значения со временем, но я хотел бы сделать все, что в моих силах, чтобы управлять им, так как я знаю, что последний администратор ничего не сделает. Итак, использование crontab для запуска logrotate мне не подходит. Я просто ищу что-то, что я могу сделать с помощью ограниченного количества одной команды.
В общем, я бы рекомендовал вам не беспокоиться о размере журнала. Стандартный способ справиться с этим - использовать logrotate. Позвольте администраторам решить, насколько большим должен быть журнал. Я считаю, что часть хорошего * nix-приложения - это соблюдение общих стандартов: люди злятся, когда приложение делает что-то необычное.
Если ваша версия sed
имеет --in-place
переключатель, вы можете использовать это. Он по-прежнему cp
и mv
но это за кулисами, поэтому сценарий будет выглядеть немного менее беспорядочно в зависимости от того, как вы относитесь к sed
синтаксис. В противном случае ваш tail
(вы пропустили один в своем редактировании), и метод добавления - это то, как я подхожу к нему.
Другой подход с использованием sed
будет держать ваш журнал вверх ногами с самой новой записью в начале.
sed -ni '1s/^/New Entry\n/;100q;p' logfile
Это все, что нужно для этого (кроме того, чтобы «Новая запись» была тем, что вы хотите). Это сохранит файл журнала из 100 строк в обратном хронологическом порядке. Преимущество состоит в том, что вам не нужно знать, сколько строк в файле (tail
позаботится об этом за вас, но sed
не будет). Это в некоторой степени предполагает, что ваши записи журнала состоят из одной строки. Если вы регистрируете значительный объем вывода из сценария, я думаю, что вы вернулись к tail
и добавить.
Еще один вариант, который может хорошо работать для подробного многострочного вывода, - это позволить файловой системе делать всю работу за вас. Каждая запись в журнале будет отдельным файлом.
#!/bin/bash
# *** UNTESTED ***
logdir="/path/to/your/log/dir"
# run the script and log its output to a file named with the date and time
script > "$logdir/$(date "+%Y%m%dT%H%M%S").log"
# maintain the logs
# keep the last 100 days of daily logfiles
# (you could keep the last 100 hours of hourly logfiles, for example,
# by using -mmin and doing some math)
# the count is intended to keep old entries from being deleted
# leaving you with nothing if there are no new entries
count=$(find "$logdir" -maxdepth 1 -type f | wc -l)
if (( count > 100 ))
then
find "$logdir" -maxdepth 1 -type f -mtime +100 -delete
fi
Это не рекомендуется, потому что нет возможности удалить первую строку файла без перезаписи всего файла. В зависимости от размера файла это интенсивная операция. Даже если вы действительно хотели это сделать, два метода (на месте с поиском или отключением, воссозданием) приведут к тому, что tail не увидит новые строки или не подумает, что все строки новые.
Я должен согласиться с Кайлом по поводу logrotate et al. Но если вы твердо настроены контролировать его из своего приложения, попробуйте поискать в Google что-нибудь вроде: файл кругового журнала Linux
Вот похожий пост, который дал мне два хороших ответа: Превращение файла журнала в своего рода кольцевой буфер
У меня была такая же проблема, и я с удовольствием использую ulogbufd.
Вот небольшой сценарий, который я использовал для этой цели.
Это позволяет избежать проблемы чтения и записи одновременно, создав два файла, myfile
и myfile.old
.
#!/bin/bash
# Bail on error
set -e
# Constants
DEFAULT_NUM_LINES="1000"
# Usage message
usage()
{
echo "Usage: last-lines [-n num] filename" 1>&2
echo "Options:" 1>&2
echo " -n Specify number of lines file iteration (default ${DEFAULT_NUM_LINES})" 1>&2
echo " -h Show this help message" 1>&2
}
# Parse flags passed in on the command line
NUM_LINES="${DEFAULT_NUM_LINES}"
while [ ${#} -gt 0 ]; do
case "$1" in
-n)
shift
NUM_LINES="${1}"
shift
;;
-h|--help)
usage
exit
;;
--)
shift
break
;;
*)
break
;;
esac
done
case "${#}" in
1)
FILENAME="$1"
;;
*)
usage
exit 1
;;
esac
# Run
LINE_COUNT="0"
while read LINE; do
echo "${LINE}" >> "${FILENAME}"
LINE_COUNT=`expr "${LINE_COUNT}" + 1`
if [ "${LINE_COUNT}" -ge "${NUM_LINES}" ]; then
mv "${FILENAME}"{,.old}
LINE_COUNT="0"
fi
done