1つのログメッセージ自体に改行が含まれているログファイルをgrepすると、ログメッセージの一部しか表示されません。各ログエントリーの先頭にフォーマット化されたテキストがあり、そのテキストでgrepしたときにはログメッセージ全体を見たいものです。それをなんとかするPerlスクリプトです。(なぜPerlなんか使うのかという疑問に対しては、この記事の最後に言い訳を書いておきました)
ちょっと意味が伝わりづらいと思うので、例を示します。
こんなようなログファイルがあったとします。
2023-08-09T11:27:15.418 [INFO][Thread-1]: log message message aaa
2023-08-09T11:27:15.421 [INFO][Thread-2]: log message message bbb
2023-08-09T11:27:15.422 [DEBUG][Thread-1]: SELECT
id,
name,
...
これを Thread-1 でgrepすると、こうなってしまいます。
2023-08-09T11:27:15.418 [INFO][Thread-1]: log message message aaa
2023-08-09T11:27:15.422 [DEBUG][Thread-1]: SELECT
SQLが1行目しか見れません。
ログメッセージごとにタイムスタンプやそのほかのメタ情報が先頭に付与され、そのあとにログメッセージ本体が続くフォーマットですが、ログメッセージ自体に改行が含まれると、このようにとても残念なことになります。
ログファイルを以下のように変換できれば、grepしても情報が失われずに済みます。grepする前にメタ情報を各行に追加してしまうのです。
2023-08-09T11:27:15.418 [INFO][Thread-1]: log message message aaa
2023-08-09T11:27:15.421 [INFO][Thread-2]: log message message bbb
2023-08-09T11:27:15.422 [DEBUG][Thread-1]: SELECT
2023-08-09T11:27:15.422 [DEBUG][Thread-1]| id,
2023-08-09T11:27:15.422 [DEBUG][Thread-1]| name,
2023-08-09T11:27:15.422 [DEBUG][Thread-1]| ...
このログファイル変換をパイプで処理するPerlスクリプトを書きました。
Perlスクリプト
forgrep.pl
などのファイル名で以下のスクリプトを作ります。
use strict;
use warnings;
use utf8;
my $currHead = "";
sub modifyLine {
my ($line) = @_;
chomp($line);
if ($line =~ /^([-:\.0-9]{10}T[-:\.0-9]{12}\s+)(.*)$/) {
$currHead = $1;
$line = $2;
while () {
if ($line =~ /^(\[.+?\])(.*)$/) {
$currHead .= $1;
$line = $2;
} else {
last;
}
}
if ($line =~ /^(:\s+)(.*)$/) {
$line = $2;
}
return $currHead . ": " . $line;
} else {
return $currHead . "| " . $line;
}
}
while(<STDIN>) {
print modifyLine($_) . "\n";
}
使い方
$ cat log.txt | perl ./forgrep.pl | grep 'Thread-1' | less -S
less
コマンドの-S
オプションは、長い行があったときにターミナル上で折り返さずに、右にスクロールできるようにするものです。
なぜPerl?
Perlは多くの環境で使えるスクリプトです。明示的にインストールしなくても使えることが多いです。しかし、プロジェクトでメインの言語となっているケースは少ないです。プロジェクトでPerl自体のバージョン管理やパッケージ管理がされていることがないため、勝手に使ってもチームで管理しているコードと衝突することがほぼありません。なので、私はちょっとしたテキスト処理に使うスクリプトにPerlを使うことが多いです。今月私が一番書いたプログラミング言語はPerlです。