やりたかったこと
最近仕事で、AWSのリソースを自動で管理するためのBashスクリプトを書く機会がありました。その中で、Bashでもまともなプログラミング言語(PythonとかGo、Javaとか)と同じようにログ出力を行いたくなりました。
最初僕は、ログを吐き出したい箇所で毎回、文字列をecho
してそれをログファイルにリダイレクト(>>
)していたのですが、ログに実行日時や行数も出力をするようにしたり、ログレベルの概念も導入しようとしたりすると、それではだいぶコードがモッサリしてきて格好悪くなってきます。そこで、自分なりに考えたBashプログラムでのロギングの方法を、ここに記します。
実装方法
前提条件
まず、以下に紹介するスクリプトは、環境変数LOG_LEVEL
とLOG_FILE
の設定がなされていることを前提に動きます。
LOG_LEVEL
は、どのレベル以上のログを出力するかを定義します。ここではログレベルは、DEBUG
、INFO
、ERROR
の三種類のみとします。もっと増やしたりしたい場合は、下のコードにちょっと手を加えるだけで実現できると思います。LOG_FILE
は、その名の通り、ログを出力する先のファイルのことを指します。絶対パスでも、相対パスでも行けると思います。
こんな感じで設定します。
export LOG_LEVEL=INFO
export LOG_FILE=/home/me/log.txt
ロギング用の関数
以下の関数で、ログを吐きます。ログには、実行時間と行数とログメッセージの二種類の情報を吐きます。この関数の引数は、以下の通りです。
- ログのレベル - 文字列
- ログの文言 - 文字列
コードはこちら。
print_log(){# ERRORレベルのログは、どんな時でも出すif["$1"=="ERROR"];then
echo"$(date) line $BASH_LINENO: $2">>$LOG_FILE# INFOレベルのログは、ログレベルがDEBUGもしくはINFOに設定されている時のみ出すelif["$1"=="INFO"];then
if["$LOG_LEVEL"=="DEBUG"||"$LOG_LEVEL"=="INFO"];then
echo"$(date) line $BASH_LINENO: $2">>$LOG_FILE# DEBUGレベルのログは、ログレベルがDEBUGに設定されている時のみ出すelif["$1"=="DEBUG"];then
if["$LOG_LEVEL"=="DEBUG"];then
echo"$(date) line $BASH_LINENO: $2">>$LOG_FILEelse
echo"$(date) line $BASH_LINENO: INVALID LOG_LEVEL">>$LOG_FILEfi}
引数のログレベルに変な値を入れると、INVALID LOG_LEVEL
という怒りのメッセージをログに吐き出すようにしています。
関数の呼び出し
呼び出し元のサンプルコードは、こちら。特に大したことはしておらず、ただ単に上に記した関数にログレベルとログの文言の引数を渡しているだけです。
# 処理の始めにログを出す
print_log DEBUG start-some-job
# 何かしらの処理sed-i-e"s/something/xxxxx/g"sed-i-e"s/otherthing/yyyyy/g"# 処理の終わりにログを出す
print_log DEBUG some-job-finished
こうすることで、Bashのシェルスクリプトでも割とすっきりとロギングを行うことができます。
余談
調べてみると、Bashスクリプト用のロガーみたいなものも、公開されているみたいです。ただし、ここまで立派なものが必要ではなく、単純にログを吐き出せればいいという場合は、僕が上に書いた関数を実装するほうが楽だと思います。