2011-07-15 14 views
12

UNIXプロセスからの出力をキャプチャしたいが、最大ファイルサイズを制限したい、あるいは新しいファイルにローテーションしたい。最大ファイルサイズを制限するか、新しいファイルにローテーションする

私はlogrotateを見ましたが、リアルタイムでは動作しません。私が理解するように、それは並行して実行される「クリーンアップ」ジョブです。

正しい解決策は何ですか?私はそれを行うための小さなスクリプトを書くだろうと思うが、私は、既存のテキストツールで簡単な方法があることを望んでいた。

は想像:

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

は、常に最新のログファイルへの書き込み... を与えるだろう:それは満たされるにつれて、その後 ログ/ my_program_log

を.../my_program_log000001を記録し、新しいログを開始するには、名前を変更します/ my_program_log。

答えて

14

使用スプリット:

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 
+0

Bah ... Bash(および他のいくつかのシェル)の>()演算子は忘れました。私はそれをあまり使用しません。あなたのものは最も簡潔な答えです。 – kevinarpe

1

これを解決する最も簡単な方法は、おそらくPythonとこの目的のために設計されたlogging moduleを使用することです。 stdinから読み取ったスクリプトを作成し、stdoutに書き込み、以下に説明するlog-rotationを実装します。あなたは求めているまさにん

"ログ" モジュールprovides

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

maxBytesとbackupCountの値を使用して、ファイルを所定のサイズでロールオーバーできます。 docs.python.org

から

時には、ログファイルが新しいファイルを開いて、それにログインした後、一定の大きさに成長させたいです。特定の数のこれらのファイルを保存し、多くのファイルが作成されたら、ファイルの数とファイルのサイズの両方が制限されたままになるようにファイルを回転します。この使用パターンの場合は、loggingパッケージは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

私は混乱しています。私のプログラムはPythonではありません。これはどうやって私を助けますか?私は標準のGNU coreutils:awk/tee/split/etcを使用したいと思います。 – kevinarpe

5

やawkの

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

を使用してそれが一緒にラインを保持し、その最大値が正確ではありませんが、これは特にためにいいかもしれませんロギング目的。 awkの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)}' 

、「蜂」という名前のファイルにこれを保存し、実行「chmod +x bee」とあなたが

program | bee 

または既存を分割するとしてそれを使用することができますを使用して、pipableスクリプトですファイルとして

bee -b1000 -o proglog -w8 file 
+0

私はあなたのコメントに同意します: "それはラインをまとめているので、最大値は正確ではありませんが、これは特にロギング目的には良いかもしれません。 – kevinarpe

2

サイズを100バイトに制限するには、dd:

my_program | dd bs=1 count=100 > log 

100バイトが書き込まれると、ddはパイプを閉じ、my_programはEPIPEを受け取ります。

0

もう1つの解決方法は、Apache rotatelogsユーティリティを使用することです。

またはスクリプト次のパッケージapache2-utils

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

rotatelogsと呼ばれる存在のユーティリティである、それは完全にあなたの要件に適合しています。

概要:

rotatelogsの[-l] [-L リンク名] [-p プログラム] [-f] [-t] [-V] [-e] [-c] [ -n ファイル数]ログファイルローテーション時間 | ファイルサイズ(B | K | M | G)オフセット]

例:

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

もしthis linkに読み取ることができる完全なマニュアル。

関連する問題