問題
以下のログファイルから JSON 形式の行を jq コマンドで処理したい。
sample.log
hoge
{"time":"2024-01-01T00:00:00.000000+09:00","level":"INFO","request_id":"aaa","message":"👼"}
fuga
{"time":"2024-01-01T23:59:59.999999+09:00","level":"ERROR","request_id":"bbb","message":"👿"}
{"time":"2024-01-31T00:00:00.000000+09:00","level":"ERROR","request_id":"ccc","message":"😈"}
foo
例えば "level":"ERROR"
の行のみを取得したい。しかし JSON 形式ではない行のせいでパースエラーが発生してしまう 😢
$ cat sample.log | jq -c 'select(.level == "ERROR")'
jq: parse error: Invalid numeric literal at line 2, column 0
例えば grep コマンドでJSON 形式ではない行を取り除くことでエラーを防ぐことができる。
$ cat sample.log | grep '^{.*}$' | jq -c 'select(.level == "ERROR")'
{"time":"2024-01-01T23:59:59.999999+09:00","level":"ERROR","request_id":"bbb","message":"👿"}
{"time":"2024-01-31T00:00:00.000000+09:00","level":"ERROR","request_id":"ccc","message":"😈"}
もっといい方法、具体的には jq コマンドだけでエラーを防ぐ方法はないだろうか? 🤔
解決方法
jq コマンドの -R
オプションと fromjson?
を利用する。
$ cat sample.log | jq -cR 'fromjson? | select(.level == "ERROR")'
{"time":"2024-01-01T23:59:59.999999+09:00","level":"ERROR","request_id":"bbb","message":"👿"}
{"time":"2024-01-31T00:00:00.000000+09:00","level":"ERROR","request_id":"ccc","message":"😈"}
-
-R
- 入力を生の文字列として読み込む
- 通常、jq は入力を JSON として解析するが、
-R
オプションを使用すると、入力をそのまま文字列として扱う
-
fromjson?
- 各行を JSON として解析する
-
?
は解析に失敗した場合、エラーを無視して null を返すことを表す