サーバーが稼働しているときに特定の行のテキストをログファイルに出力するサーバーログがあります。サーバーが起動したらコマンドを実行したいので、次のようにします。
tail -f /path/to/serverLog | grep "server is up" ...(now, e.g., wget on server)?
これを行う最良の方法は何ですか?
簡単な方法は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");
}
}
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
にタイムリーに到達できない可能性があります(ログに適切なサイズの行があると想定)。
この質問はすでに回答されているようですが、もっと良い解決策があると思います。
tail | whatever
ではなく、swatch
が本当に欲しいと思います。 Swatchは、ユーザーが求めていることを実行し、ログファイルを監視し、ログ行に基づいてアクションを実行するために明示的に設計されたプログラムです。 tail|foo
を使用するには、これを行うために端末をアクティブに実行している必要があります。一方、Swatchはデーモンとして実行され、常にログを監視します。 SwatchはすべてのLinuxディストリビューションで利用できます。
ぜひお試しください。ドライバーの裏側で釘を打ち込むことはできますが、そうする必要はありません。
私が見つけたスウォッチに関する最高の30秒のチュートリアルは here です。
この機能をすぐに使えるmultitail
ユーティリティについて誰も言及していないのは奇妙です。使用例の1つ:
Pingコマンドの出力を表示し、タイムアウトが表示された場合は、現在ログインしているすべてのユーザーにメッセージを送信します
multitail -ex timeout "echo timeout | wall" -l "ping 192.168.0.1"
multitail
の使用法 別の例 も参照してください。
それがどれほど簡単で読みやすいか見てみましょう:
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;
...
')
それが私もこれを始めた方法ですが、それではるかに洗練されてきました。気にする必要のある事柄:
私はこれに沿って何かを使用します:
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
が成功すると:
${RELEASE}
に書き込みます。${wait_pid}
プロセスを終了してtail
を終了します注:sed
は、起動時にtail
が生成する行数を実際に決定し、その数を削除するために、より洗練されている場合があります。しかし、一般的には10です。