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


16

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ジョブのセットアップ、ルールの指定、プロセスの一時停止などを必要としない、よりシンプルなものを探しています。


「ギビバイト」とは何ですか?
ピーターモーテンセン14

@PeterMortensen Wikipedia:
jw013 14

回答:


6

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

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

その後、からpidを取得できます/tmp/spewpipe.pid

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

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

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

テキスト出力用であることに注意してください。nullバイトの可能性がある場合-zは、ゼロを他の何かに置き換える-を使用する必要があります。これは、実装を簡素化するためのトレードオフでした。


ありがとう。私は楽しみにしていますpipelog-0.3;-)。また、metacpan.org/release/File-Write-Rotateに出会いました。cronジョブは、ファイルサイズに基づいてローテーションを行うのにあまり役立ちません。
ステファンシャゼル14

サイズに基づいて回転!?!出力をフラッシュし続けるので、間隔を空けてファイルを統計できます
...-goldilocks

(私の質問の要件のように)サイズを20M未満に確実に維持することはできませんでした。
ステファンシャゼラス14

もう1つは、テキストのみであるということです(これについては最後の段落を追加しました)。
goldilocks 14

4

ダン・バーンスタインのマルチログは、これを行うことができます-またはおそらくそのほとんどは、ファイル記述子を介して!プロセッサーにアウトレットを提供して、あなたが好きなように違いを補います-20M / 1Gサイズの仕様は、16Mログごとの制限外。以下は、大部分で、コピー+は、上記のリンクから選択して貼り付け、あるリンクはまた、行ごとにタイムスタンプなどの他のオプションについて詳しく説明しても、[]他のファイル[S]が唯一の最も最近のラインマッチング含む保守パターンを、より。

インターフェース

 multilog script

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

行を選択する

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

-pattern

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

+pattern

パターンが行に一致する場合、行を選択します。

... パターンは、星と星以外の文字列です。これは、同じ順序ですべての星と非星に一致する文字列の連結に一致します。非星はそれ自身と一致します。パターンの終わりの前の星は、パターン内の次の文字を含まない文字列と一致します。パターンの最後にある星は、任意の文字列に一致します。

自動ローテーションログ

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

 dir

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

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

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

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

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

アクション...

 ssize

...後続のdirアクションの最大ファイルサイズを設定します。multilogことを決定する電流があれば十分に大きいです、現在は持っているサイズをバイト。multilog最大ファイルサイズの2000バイト以内に改行がある場合、currentは十分に大きいと判断します。ログファイルを行境界で終了させようとします。)サイズは4096〜16777215の間である必要がありますデフォルトの最大ファイルサイズは99999です。

バージョン0.75以降:ALRM信号multilogを受信すると、電流が十分に大きいとすぐに判断します。、現在は空です。

(注:私は疑います zsh scheduleALRM必要に応じて、指定された間隔で送信するようビルトインを簡単に説得できるとます。)

アクション...

 nnum

...後続のdirアクションのログファイルの数を設定します。名前を変更した後、現在の場合は、multilog見ているNUM以上の古いログファイルを、それが最小のタイムスタンプを持つ古いログファイルを削除します。numは少なくとも2でなければなりません。ログファイルのデフォルト数は10です。

アクション...

 !processor

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


2

膨大なコードの作成を伴わない近似としては、この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の大きなファイルに分割して回転します。


多分... ループマウント? btrfsでマウントすることもできますcompress-force=zlib
mikeserv 14

2

以下は、あなたが要求しているようなことをするためのハッキングされた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)

1
またはハッシュバンexecを使用する代わりに、Pythonを最初に使用するシェルスクリプトとして使用する理由はありますか?pythonenv python
ペテルフ14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.