Как ограничить размер файла журнала, используя >>

24

Как я могу ограничить размер файла журнала, записанного с >>200 МБ?

$ run_program >> myprogram.log
Дэвид
источник
1
Вы хотите, чтобы программа была убита после 200 МБ? Или вы хотите последние 200 МБ со всем старым в ведре битов?
Аарон Д. Мараско
Нет, процесс не может быть остановлен, пока не будет убит вручную
Дэвид
решил пойти на logrotate, спасибо всем за ценный вклад
Дэвид

Ответы:

9

Если ваше приложение (т.е. run_program) не поддерживает ограничение размера файла журнала, вы можете периодически проверять размер файла в цикле с помощью внешнего приложения или сценария.

Вы также можете использовать, logrotate(8)чтобы вращать ваши журналы, у него есть sizeпараметр, который вы можете использовать для своих целей:

При этом файл журнала поворачивается при достижении указанного размера. Размер может быть указан в байтах (по умолчанию), килобайтах (sizek) или мегабайтах (sizem).

Эмре Язычи
источник
1
+1 Лог-файлы с повторяющейся информацией часто могут сжиматься на порядки.
пользователь неизвестен
Есть ли logrotate усекать файлы, или просто копировать или перемещать их? Потому что это будет работать, только если файл, связанный с fd, будет усечен. Если это mv'd, файл будет продолжать расти, если он unlink'd, он будет просто оставаться открытым и продолжать расти, пока не завершится процесс ... IIRC logrotate копирует, отменяет связь и создает новый файл, именно поэтому часто приходится отправлять SIGHUP демонам, когда их журналы чередуются.
Майкл Трауш
Обратите внимание , что даже если она усекается, есть проблема , если файл не был открыт в режиме добавления (лог - файлы должны быть всегда открыты в режиме добавления, но в моем опыте, часто нет)
Random832
Logrotate может вращаться, как только файл достигнет определенного размера, ежедневно, еженедельно и т. Д. Он также может сжиматься, и вы можете использовать postscriptопцию для отправки конфигурации logrotate SIGHUPв программу.
laebshade
12

Если вашей программе не нужно записывать какие-либо ДРУГИЕ файлы, которые были бы больше этого предела, вы можете сообщить ядру об этом пределе, используя ulimit. Перед тем, как выполнить команду, запустите ее, чтобы установить ограничение размера файла 200 МБ для всех процессов, запущенных в текущем сеансе оболочки:

ulimit -f $((200*1024))

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

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

Вы можете создать новый образ файловой системы, смонтировать его с помощью устройства loop и поместить файл журнала в эту файловую систему:

dd if=/dev/zero of=./200mb.img bs=1024 count=200000 # create new empty 200MB file
mkfs.ext2 200mb.img # or ext3, or whatever fits your needs
mkdir logs
sudo mount -t ext2 -o loop 200mb.img logs # only root can do '-o loop' by default
run_program >>logs/myprogram.log

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

Алекс
источник
Креативная идея ... которая оставляет на усмотрение программы то, что делать, когда она заканчивается.
Аарон Д. Мараско
6

Вы можете усечь вывод с помощью head:

size=$((200*1024*1024-$(stat -c %s myprogram.log)))
run_program | head -c ${size} >> myprogram.log
tuxce
источник
Очень креативно. Просто отметим, что это будет работать только для ограничения НОВЫХ данных, записываемых в файл, не будет приниматься во внимание, насколько большой или маленький файл уже был.
Калеб
2
Обратите внимание, что вполне вероятно, что это убьет программу (с SIGPIPE), как только она достигнет предела размера, а не отбросит данные.
Random832
1
Я думал похожим образом с некоторой ddмагией, но да @ Random832 прав, вы получите SIGPIPEas head/ dd/ whateverотбрасываете его.
Аарон Д. Мараско
Как насчет игнорирования его с trap '' SIGPIPE?
августа
Или труба вместо { head -c "$size" >> log; cat > /dev/null; }.
Стефан Шазелас
5

В пакете apache2-utilsприсутствует утилита под названием rotatelogs, она может быть вам полезна.

Сводка:

rotatelogs [-l] [-L linkname ] [-p программа ] [-f] [-t] [-v] [-e] [-c] [-n количество файлов ] время ротации файла журнала | размер файла (B | K | M | G) [ смещение ]

Пример:

your_program | rotatelogs -n 5 /var/log/logfile 1M

Полное руководство вы можете прочитать по этой ссылке .

PRIHLOP
источник
Ссылка не существует.
Александр Гончий
1

Я уверен, что оригинальный плакат нашел решение. Вот еще один для других, которые могут читать эту тему ...

Curtail ограничивает размер вывода программы и сохраняет последние 200 МБ вывода с помощью следующей команды:

$ run_program | curtail -s 200M myprogram.log

Ссылки

ПРИМЕЧАНИЕ: я поддерживаю вышеупомянутое РЕПО. Просто делюсь решением ...

Дейв Волавер
источник
Мне нравится идея свертывания. Я не так знаком с C, так что есть ли шанс предоставить бинарный файл для него? или хотя бы подробные инструкции по его установке?
Фелипе
0

Поскольку это текст, я бы написал сценарий на вашем любимом языке и направил его на это. Сделайте так, чтобы он обрабатывал файловый ввод / вывод (или сохранял все это в памяти, а затем сбрасывал его SIGHUPили тому подобное). Для этого вместо 200 МБ я бы подумал о «разумном» количестве строк для отслеживания.

Аарон Д. Мараско
источник
Хранение 200 МБ данных журнала в памяти только для того, чтобы урезать его, не очень хорошо использует системные ресурсы. Также не учитывается количество строк в большом лог-файле. Я бы порекомендовал использовать инструменты, созданные для этого, как syslogи logrotate.
Калеб
0

Следующий скрипт должен сделать эту работу.

LOG_SIZE=500000
NUM_SEGM=2
while getopts "s:n:" opt; do
  case "$opt" in
    s)
      LOG_SIZE=$OPTARG
      ;;
    n)
      NUM_SEGM=$OPTARG
      ;;
  esac
done
shift $((OPTIND-1))
if [ $# == 0 -o -z "$1" ]; then
    echo "missing output file argument"
    exit 1
fi
OUT_FILE=$1
shift
NUM=1
while :; do
    dd bs=10 count=$(($LOG_SIZE/10)) >> $OUT_FILE 2>/dev/null
    SZ=`stat -c%s $OUT_FILE`
    if [ $SZ -eq 0 ]; then
        rm $OUT_FILE
        break
    fi
    echo -e "\nLog portion finished" >> $OUT_FILE
    mv $OUT_FILE $OUT_FILE.n$NUM
    NUM=$(($NUM + 1))
    [ $NUM -gt $NUM_SEGM ] && NUM=1
done

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

Обратите внимание на небольшую ошибку, если вы используете ее с демоном, который разветвляется на задний план. Использование канала предотвратит переход демона в фоновый режим. В этом случае есть (вероятно, специфичный для bash) синтаксис, чтобы избежать проблемы:

my_daemon | ( logger.sh /var/log/my_log.log <&0 & )

Обратите внимание, что <&0, несмотря на внешнюю избыточность, без этого не получится.

STSP
источник