Используйте тройник (или эквивалентный), но ограничьте максимальный размер файла или поверните к новому файлу - PullRequest
18 голосов
/ 15 июля 2011

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

Я видел logrotate, но он не работает в режиме реального времени. Как я понимаю, это работа по "очистке", которая выполняется параллельно.

Каково правильное решение? Я предполагаю, что напишу крошечный скрипт, чтобы сделать это, но я надеялся, что есть простой способ с существующими текстовыми инструментами.

Представьте себе:

my_program | tee --max-bytes 100000 log/my_program_log

дал бы ... Всегда записывать последний файл журнала как: Журнал / my_program_log

Затем по мере заполнения ... переименовывается в log / my_program_log000001 и запускает новый журнал / my_program_log.

Ответы [ 6 ]

22 голосов
/ 03 сентября 2011

использовать сплит:

my_program | tee >(split -d -b 100000 -)

Или, если вы не хотите видеть вывод, вы можете напрямую передать разделение:

my_program | split -d -b 100000 -

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

while ((1)); do ln -fns target_log_name $(ls -t | head -1); sleep 1; done
5 голосов
/ 26 января 2012

или используя awk

program | awk 'BEGIN{max=100} {n+=length($0); print $0 > "log."int(n/max)}'

Он сохраняет линии вместе, поэтому максимальное значение не является точным, но это может быть полезно, особенно для целей регистрации. Вы можете использовать awk's sprintf для форматирования имени файла.

Вот сценарий, который можно использовать с помощью awk

#!/bin/bash
maxb=$((1024*1024))    # default 1MiB
out="log"              # output file name
width=3                # width: log.001, log.002
while getopts "b:o:w:" opt; do
  case $opt in
    b ) maxb=$OPTARG;;
    o ) out="$OPTARG";;
    w ) width=$OPTARG;;
    * ) echo "Unimplented option."; exit 1
  esac
done
shift $(($OPTIND-1))

IFS='\n'              # keep leading whitespaces
if [ $# -ge 1 ]; then # read from file
  cat $1
else                  # read from pipe
  while read arg; do
    echo $arg
  done
fi | awk -v b=$maxb -v o="$out" -v w=$width '{
    n+=length($0); print $0 > sprintf("%s.%0.*d",o,w,n/b)}'

сохраните это в файл с именем 'bee', запустите 'chmod +x bee', и вы можете использовать его как

program | bee

или разделить существующий файл как

bee -b1000 -o proglog -w8 file
2 голосов
/ 01 июля 2017

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

Сводка:

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

Пример:

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

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

2 голосов
/ 07 мая 2012

Чтобы ограничить размер до 100 байт, вы можете просто использовать dd:

my_program | dd bs=1 count=100 > log

Когда записано 100 байт, dd закроет канал и my_program получит EPIPE.

1 голос
/ 15 июля 2011

Самый простой способ решить эту проблему, возможно, использовать python и модуль ведения журнала , который был разработан для этой цели.Создайте сценарий, который читает из stdin и записывает в stdout и реализует ротацию журналов, описанную ниже.

Модуль «протоколирования» предоставляет

class logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0,
              backupCount=0, encoding=None, delay=0)

, который в точности соответствует тому, о чем вы спрашиваете.

Вы можете использовать значения maxBytes и backupCount, чтобы разрешить ролловеру файла заданный размер.

From docs.python.org

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

import glob
import logging
import logging.handlers

LOG_FILENAME = 'logging_rotatingfile_example.out'

# Set up a specific logger with our desired output level
my_logger = logging.getLogger('MyLogger')
my_logger.setLevel(logging.DEBUG)

# Add the log message handler to the logger
handler = logging.handlers.RotatingFileHandler(
              LOG_FILENAME, maxBytes=20, backupCount=5)

my_logger.addHandler(handler)

# Log some messages
for i in range(20):
    my_logger.debug('i = %d' % i)

# See what files are created
logfiles = glob.glob('%s*' % LOG_FILENAME)

for filename in logfiles:
    print(filename)

В результате должно быть 6 отдельных файлов, каждый с частью истории журнала для приложения:

logging_rotatingfile_example.out
logging_rotatingfile_example.out.1
logging_rotatingfile_example.out.2
logging_rotatingfile_example.out.3
logging_rotatingfile_example.out.4
logging_rotatingfile_example.out.5

Самый последний файл всегда logging_rotatingfile_example.out, и каждый раз, когда он достигает предела размера, он переименовывается с суффиксом .1.Каждый из существующих файлов резервных копий переименовывается для увеличения суффикса (.1 становится .2 и т. Д.), А файл .6 стирается.

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

0 голосов
/ 01 июня 2016

Другим решением будет использование утилиты Apache rotatelogs.

Или следующий скрипт:

#!/bin/ksh
#rotatelogs.sh -n numberOfFiles pathToLog fileSize[B|K|M|G]
numberOfFiles=10
while getopts "n:fltvecp:L:" opt; do
    case $opt in
  n) numberOfFiles="$OPTARG"
    if ! printf '%s\n' "$numberOfFiles" | grep '^[0-9][0-9]*$' >/dev/null;     then
      printf 'Numeric numberOfFiles required %s. rotatelogs.sh -n numberOfFiles pathToLog fileSize[B|K|M|G]\n' "$numberOfFiles" 1>&2
      exit 1
    elif [ $numberOfFiles -lt 3 ]; then
      printf 'numberOfFiles < 3 %s. rotatelogs.sh -n numberOfFiles pathToLog fileSize[B|K|M|G]\n' "$numberOfFiles" 1>&2
    fi
  ;;
  *) printf '-%s ignored. rotatelogs.sh -n numberOfFiles pathToLog fileSize[B|K|M|G]\n' "$opt" 1>&2
  ;;
  esac
done
shift $(( $OPTIND - 1 ))
pathToLog="$1"
fileSize="$2"
if ! printf '%s\n' "$fileSize" | grep '^[0-9][0-9]*[BKMG]$' >/dev/null; then
  printf 'Numeric fileSize followed by B|K|M|G required %s. rotatelogs.sh -n numberOfFiles pathToLog fileSize[B|K|M|G]\n' "$fileSize" 1>&2
  exit 1
fi
sizeQualifier=`printf "%s\n" "$fileSize" | sed "s%^[0-9][0-9]*\([BKMG]\)$%\1%"`
multip=1
case $sizeQualifier in
B) multip=1 ;;
K) multip=1024 ;;
M) multip=1048576 ;;
G) multip=1073741824 ;;
esac
fileSize=`printf "%s\n" "$fileSize" | sed "s%^\([0-9][0-9]*\)[BKMG]$%\1%"`
fileSize=$(( $fileSize * $multip ))
fileSize=$(( $fileSize / 1024 ))
if [ $fileSize -le 10 ]; then
  printf 'fileSize %sKB < 10KB. rotatelogs.sh -n numberOfFiles pathToLog fileSize[B|K|M|G]\n' "$fileSize" 1>&2
  exit 1
fi
if ! touch "$pathToLog"; then
  printf 'Could not write to log file %s. rotatelogs.sh -n numberOfFiles pathToLog fileSize[B|K|M|G]\n' "$pathToLog" 1>&2
  exit 1
fi
lineCnt=0
while read line
do
  printf "%s\n" "$line" >>"$pathToLog"
  lineCnt=$(( $lineCnt + 1 ))
  if [ $lineCnt -gt 200 ]; then
    lineCnt=0
    curFileSize=`du -k "$pathToLog" | sed -e 's/^[  ][  ]*//' -e 's%[   ][  ]*$%%' -e 's/[  ][  ]*/[    ]/g' | cut -f1 -d" "`
    if [ $curFileSize -gt $fileSize ]; then
      DATE=`date +%Y%m%d_%H%M%S`
      cat "$pathToLog" | gzip -c >"${pathToLog}.${DATE}".gz && cat /dev/null >"$pathToLog"
      curNumberOfFiles=`ls "$pathToLog".[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_[0-9][0-9][0-9][0-9][0-9][0-9].gz | wc -l | sed -e 's/^[   ][  ]*//' -e 's%[   ][  ]*$%%' -e 's/[  ][  ]*/[    ]/g'`
      while [ $curNumberOfFiles -ge $numberOfFiles ]; do
        fileToRemove=`ls "$pathToLog".[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_[0-9][0-9][0-9][0-9][0-9][0-9].gz | head -1`
        if [ -f "$fileToRemove" ]; then
          rm -f "$fileToRemove"
          curNumberOfFiles=`ls "$pathToLog".[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_[0-9][0-9][0-9][0-9][0-9][0-9].gz | wc -l | sed -e 's/^[   ][  ]*//' -e 's%[   ][  ]*$%%' -e 's/[  ][  ]*/[    ]/g'`
        else
          break
        fi
      done
    fi
  fi
done
...