logfmt いじりまだやってるのかよという感じだが、まだやっている。
grep -o
はマッチした部分文字列をそれぞれ一行ずつ出力することを利用して、
❯ cat logfmt.log at=info method=GET path=/ host=mutelight.org fwd="124.133.52.161" dyno=web.2 connect=4ms service=8ms status=200 bytes=1653 level=info tag=stopping_fetchers id=ConsumerFetcherManager-1382721708341 module=kafka.consumer.ConsumerFetcherManager level=info msg="Stopping all fetchers" tag=stopping_fetchers id=ConsumerFetcherManager-1382721708341 module=kafka.consumer.ConsumerFetcherManager
❯ egrep -o '[^ ]*=([^ ]*|"[^"]*")' logfmt.log at=info method=GET path=/ host=mutelight.org fwd="124.133.52.161" dyno=web.2 connect=4ms service=8ms status=200 bytes=1653 level=info tag=stopping_fetchers id=ConsumerFetcherManager-1382721708341 module=kafka.consumer.ConsumerFetcherManager level=info msg="Stopping all fetchers" tag=stopping_fetchers id=ConsumerFetcherManager-1382721708341 module=kafka.consumer.ConsumerFetcherManager
これが最低限だが、どこまでが同一行だったか分かりづらい。egrep -n
で行番号をつけると多少マシになる。
❯ egrep -n -o '[^ ]*=([^ ]*|"[^"]*")' logfmt.log 1:at=info 1:method=GET 1:path=/ 1:host=mutelight.org 1:fwd="124.133.52.161" 1:dyno=web.2 1:connect=4ms 1:service=8ms 1:status=200 1:bytes=1653 2:level=info 2:tag=stopping_fetchers 2:id=ConsumerFetcherManager-1382721708341 2:module=kafka.consumer.ConsumerFetcherManager 3:level=info 3:msg="Stopping all fetchers" 3:tag=stopping_fetchers 3:id=ConsumerFetcherManager-1382721708341 3:module=kafka.consumer.ConsumerFetcherManager
空行で区切られているとグループ分けを視覚的に把握しやすそう。これは awk
で各行の行頭にある行番号を直前行と比較して違ってたら空行を挟む、というのでごちゃごちゃやることもできるが、
❯ egrep -n -o '[^ ]*=([^ ]*|"[^"]*")' logfmt.log | awk '{cur=$0; if(substr(prev,1,1)!=substr(cur,1,1)){print ""}; print; prev=cur }' 1:at=info 1:method=GET 1:path=/ 1:host=mutelight.org 1:fwd="124.133.52.161" 1:dyno=web.2 1:connect=4ms 1:service=8ms 1:status=200 1:bytes=1653 2:level=info 2:tag=stopping_fetchers 2:id=ConsumerFetcherManager-1382721708341 2:module=kafka.consumer.ConsumerFetcherManager 3:level=info 3:msg="Stopping all fetchers" 3:tag=stopping_fetchers 3:id=ConsumerFetcherManager-1382721708341 3:module=kafka.consumer.ConsumerFetcherManager
(グループ分け後は行番号は不要なので消したい、という場合は egrep -n -o '[^ ]*=([^ ]*|"[^"]*")' logfmt.log | awk '{cur=$0; if(substr(prev,1,1)!=substr(cur,1,1)){print ""}; print substr($0, 3); prev=cur }'
と substr したものを print するか、パイプで | cut -c 3-
と繋ぐ)
linux - split a file by a line prefix - Super User にスマートな回答があった。
❯ egrep -n -o '[^ ]*=([^ ]*|"[^"]*")' logfmt.log | awk -F: '{print>$1}'
一時ファイルをたくさん作ることになるが、スクリプトはとても簡潔で書きやすい。そしてシェルっぽいやり方だとも感じる。
❯ cat 1 1:at=info 1:method=GET 1:path=/ 1:host=mutelight.org 1:fwd="124.133.52.161" 1:dyno=web.2 1:connect=4ms 1:service=8ms 1:status=200 1:bytes=1653 ❯ cat 2 2:level=info 2:tag=stopping_fetchers 2:id=ConsumerFetcherManager-1382721708341 2:module=kafka.consumer.ConsumerFetcherManager ❯ cat 3 3:level=info 3:msg="Stopping all fetchers" 3:tag=stopping_fetchers 3:id=ConsumerFetcherManager-1382721708341 3:module=kafka.consumer.ConsumerFetcherManager
後始末しやすいように一時的な作業ディレクトリを作って移動しておくか、スクリプトをちょっと変えて出力ファイル名に prefix をつけてもいい。
❯ egrep -n -o '[^ ]*=([^ ]*|"[^"]*")' logfmt.log | awk -F: '{fname="hoge_"$1; print>fname}' ❯ ls hoge_* hoge_1 hoge_2 hoge_3
中身を見るのにいちいちファイルを開かないといけないのは手間に思えるが、こういうテキスト処理のコマンドの結果は作業中割と頻繁に見返したくなるもので、その都度重たいコマンドを再実行するのは結果的に時間の無駄に繋がったりしがち。なので最初から一時ファイルに書いておくのが正解なのかもしれない。
grep/egrep
は rg
で置き換え可能。grep 自体や正規表現の方言を避けるためにも rg
が使えるなら使うのがベター。