web-dev-qa-db-ja.com

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

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

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

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

56
jonderry

簡単な方法は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はこれに使用するのに少しエレガントで、テールの必要性を置き換えることもできます。 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");
    }
}
36
penguin359

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

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

...ログファイルに "server is up"が表示されるたびにmy_commandが実行されます。複数の可能性がある場合は、grepをドロップして、代わりにcase内でwhileを使用できます。

大文字の-Fは、ログファイルのローテーションを監視するようにtailに指示します。 i.e。現在のファイルの名前が変更され、同じ名前の別のファイルが存在する場合、tailは新しいファイルに切り替えます。

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

20
Jander

この質問はすでに回答されているようですが、もっと良い解決策があると思います。

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

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

私が見つけたスウォッチに関する最高の30秒のチュートリアルは here です。

14
bahamat

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

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

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

multitailの使用法 別の例 も参照してください。

11
php-coder

bash 一人で仕事ができる

それがどれほど簡単で読みやすいか見てみましょう:

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は使用していませんが、これは非常に高速なままです。

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

しかし、高負荷のサーバーの場合、そして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;
  ...
')
8
F. Hauri

それが私もこれを始めた方法ですが、それではるかに洗練されてきました。気にする必要のある事柄:

  1. ログの末尾に「server 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}

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

grepが成功すると:

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

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

6
nix