CLI での構造化ロギングのすすめ
e.g. aqua で logrus を使った構造化ロギング
$ aqua i
INFO[0001] create a symbolic link aqua_version=1.3.0 link_file=/home/foo/.local/share/aquaproj-aqua/bin/gh new=aqua-proxy program=aqua
INFO[0001] download and unarchive the package aqua_version=1.3.0 package_name=cli/cli package_version=v2.2.0 program=aqua registry=standard
構造化ロギングは既に当たり前のように広く使われている技術なので今更その話をするのかと思われるかもしれませんが、
CLI ではあまり使われてない印象があるので CLI でも使うと便利であるという話をしたいと思います。
構造化ロギングの概要とかは割愛します。
自分は Go が好きなので Go を前提に話します。
構造化ロギングのライブラリとしては sirupsen/logrus や uber-go/zap を使っています。
Web アプリケーションなどの構造化ロギングでは JSON で出力するとプログラムで処理しやすくて便利ですが、
CLI では人間が読みやすいフォーマットで出力するのが良いでしょう。
ログをプラグラムで処理しないような場合でも構造化ロギングは以下のような点で有用です。
- ログを
%v
などを使って組み立てなくてよい - ログでコードを検索しやすい
- コンテキストを継承しやすい
- ログの情報量が多く、トラブルシューティングに役立つ
- ログレベルを設定できる(構造化ロギングとは直接関係ないけど)
%v
などを使って組み立てなくてよい
ログを 言うまでもなく %v
などで文字列を組み立てるのは大変です。
パラメータの数が増えればなお大変ですし、どう表現すればいいのか考える必要もあります。
ログでコードを検索しやすい
%v
などで文字列を組み立てていると、コードを grep で検索しても引っかからないので不便です。
コンテキストを継承しやすい
関数呼び出しの際に構造化されたコンテキストを引数として渡すことで、
親のコンテキストを引き継いでログに出力できます。
ログの情報量が多く、トラブルシューティングに役立つ
上述の通りデータをコンテキストに簡単に追加できるため、多くの情報をログに含めることが出来、トラブルシューティングに役立ちます。
パラメータの値に問題があった際に気づきやすいです。
バグ報告などの問い合わせの際にログを貼ってもらうことで、相手から必要な情報を引き出すために色々質問する手間が省けるという側面もあります。
aqua のログを見ると、 program
と aqua_version
というフィールドがついています。
program
によってこれが aqua のログであるということがわかりますし、 aqua_version
によって使っているバージョンがなにか、古いバージョンを使ってないか聞かなくてもわかります。
$ aqua i
INFO[0001] create a symbolic link aqua_version=1.3.0 link_file=/home/foo/.local/share/aquaproj-aqua/bin/gh new=aqua-proxy program=aqua
INFO[0001] download and unarchive the package aqua_version=1.3.0 package_name=cli/cli package_version=v2.2.0 program=aqua registry=standard
ログレベルを設定できる(構造化ロギングとは直接関係ないけど)
- debug log を通常は非表示にして問題があったときだけ出力できる
- info log や warn log, error log を区別できる
error に構造化データを付与するライブラリ
logrus や zap を使う際に補助的に使える小さなライブラリを紹介します。
Go では fmt.Errorf
を使って error にコンテキストを付与するのが一般的ですが、
fmt.Errorf ではテキストしか付与できないので、構造化ロギングのために構造化されたデータを付与することが出来ません。これらのライブラリを使うと、 error に構造化データを付与し、 error から構造化データを取り出してロギングすることが出来ます。 zap や logrus のロガーをラップするようなことはせず、補助的な小さなライブラリとして実装しているので、提供している API は最小限で簡単に使えます。
Discussion