ログを追跡し、ログにテキストが表示されたときにコマンドを実行する最良の方法


54

サーバーが稼働しているときに、ログファイルに特定のテキスト行を出力するサーバーログがあります。サーバーが起動したらコマンドを実行したいので、次のようなことをします。

tail -f /path/to/serverLog | grep "server is up" ...(now, e.g., wget on server)?

これを行う最良の方法は何ですか?


3
必ず使用してtail -F回転をログに処理するために-つまりはmy.logいっぱいになり、に移動my.log.1し、あなたのプロセスが新しい作成my.log
SG

回答:


34

簡単な方法はawkです。

tail -f /path/to/serverLog | awk '
                    /Printer is on fire!/ { system("shutdown -h now") }
                    /new USB high speed/  { system("echo \"New USB\" | mail admin") }'

はい、両方ともカーネルログからの実際のメッセージです。Perlは、このために使用する方が少しエレガントかもしれませんし、tailの必要性を置き換えることもできます。perlを使用している場合、次のようになります。

open(my $fd, "<", "/path/to/serverLog") or die "Can't open log";
while(1) {
    if(eof $fd) {
        sleep 1;
        $fd->clearerr;
        next;
    }
    my $line = <$fd>;
    chomp($line);
    if($line =~ /Printer is on fire!/) {
        system("shutdown -h now");
    } elsif($line =~ /new USB high speed/) {
        system("echo \"New USB\" | mail admin");
    }
}

awk短くて簡単にオンザフライで1行で実行できるソリューションが気に入っています。しかし、実行したいコマンドに引用符がある場合、これは少し面倒になる可能性があります、代替手段があります、パイプラインと複合コマンドを使用して、簡単なワンライナーソリューションも可能ですが、結果のコマンドを渡す必要はありません文字列として?
ジョンデリー

1
@jon awkをスクリプトとして書くことができます。スクリプトの最初の行として「#!/ usr / bin awk -f」を使用します。これにより、この例の外側の単一引用符が不要になり、system()コマンド内で使用できるように解放されます。
ペンギン359

@ penguin359、True。ただし、コマンドラインから実行するのも面白いでしょう。私の場合、予見できないことも含めて、やりたいことがいろいろあります。そのため、サーバーを起動してすべてを1行で実行できると便利です。
ジョンデリー

私は代替品を見つけましたが、それがどれほど堅実なのかはわかりません:tail -f /path/to/serverLog | grep "server is up" | head -1 && do_some_command
-jonderry

@jonヘッドをそのように使用すると、少し壊れやすいようです。さらに重要なことは、私の例のように再現性がないことです。「server is up」がログの最後の10行にある場合、コマンドを起動してすぐに終了します。再起動すると、「server is」を含まない10行がログに追加されない限り、起動して終了する可能性が高くなります。動作が改善される可能性のある変更はtail -n 0 -f /path/to/serverLog 、ファイルの最後の0行を読み取り、さらに行が印刷されるのを待つことです。
ペンギン359

16

1つの可能性だけを探していて、awkまたはを使用するのではなく、ほとんどシェルに滞在したい場合は、perl次のようなことができます。

tail -F /path/to/serverLog | 
grep --line-buffered 'server is up' | 
while read ; do my_command ; done

... my_commandserver is up」がログファイルに表示れるたびに実行されます。複数の可能性のために、あなたは多分落とすことができgrep、代わりに使用case範囲内while

大文字-Ftail、ログファイルがローテーションされるのを監視するように指示しています。つまり、現在のファイルの名前が変更され、同じ名前の別のファイルが代わりに使用tailされた場合、新しいファイルに切り替わります。

この--line-bufferedオプションはgrep、各行の後にバッファーをフラッシュするように指示します。そうしmy_commandないと、タイムリーに到達できない場合があります(ログのサイズが適切なサイズであると想定)。


2
私はこの答えが本当に好きですが、最初はうまくいきませんでした。に--line-bufferedオプションを追加する必要があると思うかgrep、そうでない場合は行間で出力をフラッシュする必要my_commandがあります。必要に応じackて、--flushフラグがあります。希望する場合はag、でラップしてみてくださいstdbufstackoverflow.com/questions/28982518/…–
文書化

を使用して、これを試しましたdo exit ;。それはうまくいくように見えましたが、尻尾は終わらず、スクリプトは次の行に移動しませんでした。doセクションに尻尾を止める方法はありますか?
Machtyn

14

この質問はすでに答えられているように見えますが、より良い解決策があると思います。

むしろ、tail | whateverあなたが本当に欲しいものはあると思いますswatch。Swatchは、ユーザーが求めていることを実行し、ログファイルを監視し、ログ行に基づいてアクションを実行するために明示的に設計されたプログラムです。使用tail|fooするには、これを行うために端末をアクティブに実行する必要があります。一方、スウォッチはデーモンとして実行され、常にログを監視しています。SwatchはすべてのLinuxディストリビューションで利用可能です。

ぜひお試しください。ドライバーの裏側で釘を打ち込むことはできますが、そうすべきではありません。

私が見つけたスウォッチに関する最高の30秒のチュートリアルはこちらです:http : //www.campin.net/newlogcheck.html


1
そのチュートリアルは現在404です:/
ここから

1
WebArchiveはあなたの友達です:web.archive.org/web/20140922041805/http://campin.net/...

10

multitailすぐにこの機能を備えたユーティリティについて誰も言及していないのは奇妙です。使用例の1つ:

pingコマンドの出力を表示し、タイムアウトが表示される場合は、現在ログインしているすべてのユーザーにメッセージを送信します

multitail -ex timeout "echo timeout | wall" -l "ping 192.168.0.1"

multitail使用もご覧ください。


+1、私は「マルチテールにそのような忍者のスキルが隠れていた」という考えがありませんでした。それを指摘してくれてありがとう。
カレブ

8

は自分で仕事をすることができます

それがどれほどシンプルで読みやすいかを見てみましょう:

mylog() {
    echo >>/path/to/myscriptLog "$@"
}

while read line;do
    case "$line" in
        *"Printer on fire"* )
            mylog Halting immediately
            shutdown -h now
            ;;
        *DHCPREQUEST* )
            [[ "$line" =~ DHCPREQUEST\ for\ ([^\ ]*)\  ]]
            mylog Incomming or refresh for ${BASH_REMATCH[1]}
            $HOME/SomethingWithNewClient ${BASH_REMATCH[1]}
            ;;
        * )
            mylog "untrapped entry: $line"
            ;;
    esac
  done < <(tail -f /path/to/logfile)

bashを使用しない場合regex、これは非常に高速になります!

しかし、 + は非常に効率的で興味深いタンデムです

しかし、高負荷サーバーの場合、およびsed非常に高速で非常にスケーラブルであるため、私はこれをよく使用します。

while read event target lost ; do
    case $event in
        NEW )
            ip2int $target intTarget
            ((count[intTarget]++))
        ...

    esac
done < <(tail -f /path/logfile | sed -une '
  s/^.*New incom.*from ip \([0-9.]\+\) .*$/NEW \1/p;
  s/^.*Auth.*ip \([0-9.]\+\) failed./FAIL \1/p;
  ...
')

6

それは私がこれを始めた方法ですが、それではるかに洗練されました。考慮すべきいくつかのこと:

  1. ログの末尾に「server is up」が既に含まれている場合。
  2. 見つかったテールプロセスを自動的に終了します。

私はこれに沿って何かを使用します:

RELEASE=/tmp/${RANDOM}$$
(
  trap 'false' 1
  trap "rm -f ${RELEASE}" 0
  while ! [ -s ${RELEASE} ]; do sleep 3; done
  # You can put code here if you want to do something
  # once the grep succeeds.
) & wait_pid=$!
tail --pid=${wait_pid} -F /path/to/serverLog \
| sed "1,10d" \
| grep "server is up" > ${RELEASE}

ファイルにデータが含まtailれるまで開いたままにすることで機能し${RELEASE}ます。

一度grepそれを成功します。

  1. 出力を書き込み${RELEASE}ます
  2. ${wait_pid}プロセスを終了する
  3. 出る tail

注:起動時に生成されるsed行数を実際に決定し、その数をtail削除するために、より洗練されたものにすることができます。しかし、一般的には10です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.