Подтвердить что ты не робот

Используйте тройник (или эквивалент), но ограничьте максимальный размер файла или повернуть в новый файл

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

Он объединяет линии, поэтому max не является точным, но это может быть приятно, особенно для ведения журналов. Вы можете использовать awk sprintf для форматирования имени файла.

Здесь можно использовать script, используя 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

Ответ 3

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

my_program | dd bs=1 count=100 > log

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

Ответ 4

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

Модуль "logging" предоставляет

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

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

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

Из 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 на соответствующее значение.

Ответ 5

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

Описание:

rotatelogs [-l] [-L linkname] [-p program] [-f] [-t] [-v] [-e] [-c] [-n number-of-files] время работы журнала | filesize (B | K | M | G) [смещение]

Пример:

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

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

Ответ 6

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

Или после script:

#!/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