web-dev-qa-db-ja.com

コマンドの出力をリングバッファに保存します

Stdoutに多くの出力を生成する長時間実行コマンドがあります。たとえば、最後の3日間または最後のギビバイト(途中で線が切れるのを避けます)だけを保持し、可能であれば20 MiB以下のファイルチャンクに保持したいです。各ファイルチャンクには、数値のサフィックスまたはタイムスタンプが付けられます。

何かのようなもの:

my-cmd | magic-command --output-file-template=my-cmd-%t \
                       --keep-bytes=1G \
                       --keep-time=3d \
                       --max-chunk-size=20M \
                       --compress=xz

書くでしょう:

my-cmd-2014-09-05T10:04:23Z

20Mに達すると、圧縮して新しいファイルを開く、などのようになり、しばらくすると最も古いファイルの削除を開始します。

そのようなコマンドは存在しますか?

私はlogrotateと他のアプリケーションによって書き込まれたファイルを管理するその機能を知っていますが、cronジョブの設定、ルールの指定、プロセスの中断を必要としない、より簡単なものを探しています。等.

16

pipelog を使用して、必要なものの一部を取得できます。これにより、「実行中のプロセスのログを、外部信号に応答する中間体を介してパイプすることで、ログを回転またはクリアできます」。例:

spewstuff | pipelog spew.log -p /tmp/spewpipe.pid -x "gzip spew.log.1"

次に、/tmp/spewpipe.pidからpidを取得できます。

kill -s USR1 $(</tmp/spewpipe.pid)

しかし、cronか何かで設定する必要があるでしょう。ただし、これには1つの問題があります。 I gzip spew.log.1に注意してください-これは、ログがローテーションされた後に-xコマンドが実行されるためです。したがって、gzipを実行して後でファイルを移動する短いスクリプトを作成し、それをspew.log.1.gzコマンドとして使用しない限り、毎回-xを上書きするというさらなる問題があります。

完全な開示:私はこれを書いたので、もちろんそれは機能します完全に機能します。 ;)バージョン0.2の場合、圧縮オプション、またはそれをより容易にするものを念頭に置きます(-xの使用目的は多少異なりますが、上記のように機能します)。また、自動化されたロールオーバーは良い考えです...最初のバージョンは、必要のない機能を追加する誘惑に抵抗したため、意図的に最小限に抑えられています(結局、cronジョブを設定するのはそれほど難しくありません)。

これはtextの出力を目的としています。 nullバイトの可能性がある場合は、-zを使用する必要があります。これにより、ゼロが別の値に置き換えられます。これは、実装を簡素化するためのトレードオフでした。

6
goldilocks

Dan Bernsteinの multilog は明らかにこれを行うことができます-またはおそらくそのほとんどは、!processorにファイル記述子を介してアウトレットを提供します20M/1Gサイズの仕様は、16Mがログあたりの外側の制限であると思われるため、多少の調整が必要になる場合がありますが、お好みで違いを補ってください。ほとんどの場合、上記のリンクからのコピーと貼り付けの選択が続きますが、リンクには、行ごとのタイムスタンプなど、他のオプションの詳細も表示され、最新の行の一致のみを含む[an] other file [s]が維持されますpatternなど。

インターフェース

 multilog script

...scriptは、任意の数の引数で構成されます。各引数は1つのアクションを指定します。アクションは、入力の各行に対して順番に実行されます。

ラインの選択

各行が最初に選択されます。アクション...

-pattern

...パターンがラインと一致する場合、ラインの選択を解除します。アクション...

+pattern

patternが行に一致する場合、行を選択します。

...patternは星と非星の文字列です。同じ順序ですべての星と非星が一致する文字列の連結に一致します。非スターはそれ自体と一致します。パターンの最後の前のスターは、パターンの次の文字を含まないすべての文字列と一致します。パターンの最後の星は、任意の文字列と一致します。

自動的にローテーションされるログ

dirがドットまたはスラッシュで始まる場合、アクション...

 dir

...選択した各行をdirという名前のログに追加します。 dirが存在しない場合は、multilogが作成します。

ログの形式は次のとおりです。

  1. dirは、いくつかの古いログファイルを含むディレクトリで、current、およびmultilogのその他のファイル。そのアクションを追跡します。

  2. 各古いログファイルには、@で始まり、ファイルがいつ終了したかを示す正確なタイムスタンプが続き、次のコードのいずれかで終わる名前があります。

    • 。s:このファイルは完全に処理され、安全にディスクに書き込まれます。
    • 。u:このファイルは、停止時に作成されていました。切り捨てられている可能性があります。処理されていません。

アクション...

 ssize

...後続のdirアクションの最大ファイルサイズを設定します。 multilogは、currentcurrentsizeバイトです。 multilogはまた、最大ファイルサイズの2000バイト以内の改行を検出した場合、currentが十分に大きいと判断します。行の境界でログファイルを終了しようとします。) は4096から16777215の間でなければなりません。デフォルトの最大ファイルサイズは99999です。

バージョン0.75以降:multilog[〜#〜] alrm [〜#〜]信号を受信すると、すぐにcurrentは、currentが空でない場合、十分な大きさです。

(注:zshschedule組み込み関数は、必要に応じて、指定された間隔でALRMを送信するように簡単に説得できると思います。)

アクション...

 nnum

...後続のdirアクションのログファイルの数を設定します。 currentの名前を変更した後、multilognumまたは古いログファイルが多いほど、タイムスタンプが最小の古いログファイルが削除されます。 numは少なくとも2でなければなりません。ログファイルのデフォルト数は10です。

アクション...

 !processor

...後続のdirアクションのプロセッサを設定します。 multilogcurrentからprocessorをフィードし、 currentではなく、古いログファイルとして出力します。 multilogは、プロセッサが記述子5に書き込む出力も保存し、次のログファイルでプロセッサを実行するときに記述子4でその出力を読み取り可能にします。信頼性のために、processorは、出力の作成に問題がある場合、ゼロ以外で終了する必要があります。 multilogはそれを再度実行します。 processorを実行すると、multilogへの入力を供給するプログラムがブロックされる場合があることに注意してください。

4
mikeserv

これは、あなたが要求しているようなことをするためのハッキングされたpythonスクリプトです:

#!/bin/sh
''':'
exec python "$0" "$@"
'''

KEEP = 10
MAX_SIZE = 1024 # bytes
LOG_BASE_NAME = 'log'

from sys import stdin
from subprocess import call

log_num = 0
log_size = 0
log_name = LOG_BASE_NAME + '.' + str(log_num)
log_fh = open(log_name, 'w', 1)

while True:
        line = stdin.readline()
        if len(line) == 0:
                log_fh.close()
                call(['gzip', '-f', log_name])
                break
        log_fh.write(line)
        log_size += len(line)
        if log_size >= MAX_SIZE:
                log_fh.close()
                call(['gzip', '-f', log_name])
                if log_num < KEEP:
                        log_num += 1
                else:
                        log_num = 0
                log_size = 0
                log_name = LOG_BASE_NAME + '.' + str(log_num)
                log_fh = open(log_name, 'w', 1)
2
Mark Wagner

膨大なコードの記述を含まない概算として私がこれまでに見つけた中で最高のものは、次のzshコードです。

autoload zmv
mycmd |
  while head -c20M > mycmd.log && [ -s mycmd.log ]; do
    zmv -f '(mycmd.log)(|.(<->))(|.gz)(#qnOn)' '$1.$(($3+1))$4'
    {rm -f mycmd.log.1 mycmd.log.50.gz; (gzip&) > mycmd.log.1.gz} < mycmd.log.1
  done

ここでは最大51の20MiBの大きなファイルに分割して回転しています。

2