bash(Linux)でコマンドの実行結果をログで残したいと思い、どんなシェルスクリプトでも使いまわせるログ出力関数を作成してみました。
「直感的でシンプル」が今回のコンセプト。
まずはどんなログが出力されるかをご紹介。
下記の例では、docker-compose upコマンドを実行しています。(docker-composを起動させるコマンド)
2021/02/28 23:09:24 処理開始
2021/02/28 23:09:25 docker-compose up -d --build 実行
2021/02/28 23:09:26 Creating network "dockerdjango_default" with the default driver
Building python
Creating db ... done
Creating python ... done
Creating nginx ... done
2021/02/28 23:09:43 docker-compose up -d --build 正常終了
2021/02/28 23:09:44 処理終了
2021/02/28 23:11:36 処理開始
2021/02/28 23:11:36 docker-compose up -d --build 実行
2021/02/28 23:11:39 docker-compose up -d --build 失敗
Creating network "dockerdjango_default" with
the default driver Building python Creating
dockerdjango_db_1 ...
[後略]
次はスクリプトの中身をご紹介。
最初に定義しているlog関数が今回のテーマとなる部分です。
この関数はそのままコピペするだけで他スクリプトで利用できます。
※LOG_PATH(ログ出力先)とLOG_NAME(ログファイル名)の値は、
用途に合わせて書き換えてください。
#!/bin/bash
# -----ログ出力関数関連処理(そのままコピペするだけで他スクリプトで利用できる) ----------------
# ログ出力先を設定。現在はカレントにログを吐く設定になっています。絶対パスに書き換え推奨
LOG_PATH="$(pwd)"
# ログファイル名を設定。現在の設定ではスクリプト名に.logを付けた名前になる
LOG_NAME="$(basename $0 | sed -e 's/.sh//g').log"
# ログ関数
function log () {
# ログ出力先とログ名を関数に設定。
LOG=$LOG_PATH/$LOG_NAME
# ログ出力日時のフォーマットを yyyy/mm/dd hh:mm:ss に設定する
time=$(date '+%Y/%m/%d %T')
# 最初の引数の文字列をログ出力する
# 画面表示不要の場合 tee -a を >> に書き換え)
echo -e "$time" "$1" | tee -a $LOG
# 2番目の引数があればそれもログに出力する(エラーログで使用)
# 画面表示不要の場合 tee -a を >> に書き換え)
if [[ $2 != "" ]]; then
echo -e "$2" | tee -a $LOG
fi
}
# ----------------------------------------------------------------------------------------------------------
# 処理開始logを出力
log "処理開始"
# コマンド開始ログ
log "docker-compose up -d --build 実行"
# コマンド実行
result="$(docker-compose up -d --build 2>&1 > /dev/null)"
# Docker-compose実行結果判定
if [[ $(echo $?) -ne 0 ]]; then
#コマンド失敗時のログ
log "docker-compose up -d --build 失敗" "$result"
exit 1
fi
# コマンド終了ログ
log "$result"
log "docker-compose up -d --build 正常終了"
# 処理終了logを出力
log "処理終了"
exit 0
log関数の使い方
正常系
まずは先に紹介したコピペ部分をスクリプトの先頭に貼り付けます。
貼り付けた後は、基本的に、log関数の第一引数に出力したいメッセージを記述するだけです。
下記例では、第一引数に"処理開始"を設定しています。
log "処理開始"
こちらを実行するとログファイルに、次のようなメッセージが出力されます。
2021/05/24 23:11:36 処理開始
ログファイルの出力先は、デフォルトではログ関数の定義されているスクリプトと同じディレクトリとなります。(ログファイルが無い場合は処理実行時に作成される)
また、ログファイル名は、スクリプト名に「.log」が付与されたものとなります。
ログ出力先を変更する方法については後述します。
異常系
使い方は正常系とほぼ同じですが、異常系では第二引数にもメッセージを設定します。
log "cp コマンドの実行に失敗" "ファイルコピー時にエラーが発生しました"
上記のように設定を行うと、次のようにログが出力されます。
2021/02/28 23:11:39 cp コマンドの実行に失敗
ファイルコピー時にエラーが発生しました
第二引数に指定したメッセージは、第一引数で設定したメッセージの下に出力されます。
コマンドのエラーメッセージをそのまま利用したい場合は、次のようにコマンドのエラーメッセージを変数に格納し、それを第二引数へ渡します。
result="$(実行したいコマンド 2>&1 > /dev/null)"
それをlog関数の第二引数へ渡します。
log "コマンド 失敗" "$result"
すると次のようなイメージでログ出力が行われます。
2021/05/24 23:11:39 コマンド 失敗
Creating network "hogehoge" failed
it's the end. Let's give up...
コードの解説
ログ出力先とログの名前
# ログ出力先とログ名を取得
LOG_PATH="$(pwd)"
LOG_NAME="$(basename $0 | sed -e 's/.sh//g').log"
# ログ関数
function log () {
# ログ出力先とログ名を関数に設定。
LOG=$LOG_PATH/$LOG_NAME
# -- 以下略 --
LOG_PATH
変数には今自分のいるディレクトリにログを出力するように設定しています。
別の場所にログを出力したい場合、変数に設定する値を書き換えてください。
今の状態でcronなどを実行すると意図しないディレクトリにログファイルができたりするので、絶対パスでの指定を推奨。
LOG_NAME
変数にはログファイル名を設定しています。
任意のログ名を設定したい場合、変数に設定する値を書き換えてください。
「$(basename $0 | sed -e 's/.sh//g').log」は自分のスクリプト名を取得して、「.sh」を「.log」に書き換えています。
$0
は自分のスクリプト名を取得しますが、実行した場所のパスまで取得するので、basename
でスクリプト名だけ抽出しています。
ログ出力日時のフォーマット設定
function log () {
# -- 中略 -
# ログ出力日時のフォーマットを yyyy/mm/dd hh:mm:ss に設定する
time=$(date '+%Y/%m/%d %T')
# -- 以下略 --
dateコマンドを使用してコマンドが実行された時間を取得している部分です。
引数に「 '+%Y/%m/%d'」 と設定すると年月日が取得できます。
さらに「%T」を指定すると「hh:mm:ss」の形式で時間も取得できます。
ログ出力部分
function log () {
# -- 中略 -
# 最初の引数の文字列をログ出力する
echo -e "$time" "$1" >> $LOG
# 2番目の引数があればそれもログに出力する(エラーログで使用)
if [[ $2 != "" ]]; then
echo -e "$2" >> $LOG
fi
# -- 以下略 --
「echo -e "$time" "$1" >> $LOG」は関数の第一引数に指定した文字列をログに出力する処理です。
「-e」はechoのオプションで、改行のある文字列も改行を考慮して出力するオプションです。
「$time」で先ほど設定したコマンド実行時間を取得し、
「$1」部分で第一引数の文字列を取得しています。
「>>」でログの内容を上書きではなく、追記するように設定しています。
以下は関数の使用例です。
# スクリプトに次の処理を記載
log "処理開始"
# 実行後のログ内容
2021/02/28 23:11:36 処理開始
ログ出力部分(エラーメッセージ部分)
# 2番目の引数があればそれもログに出力する(エラーログで使用)
if [[ $2 != "" ]]; then
echo -e "$2" >> $LOG
fi
コマンドでエラーが発生した時に、追加でメッセージを出力する処理です。
log関数の第二引数に文字列を記述することで使用できます。
この処理は「if [[ $2 != "" ]]; then」の部分で第二引数が存在するかを判定しています。
存在しなければ何もしません。
存在する場合は第一引数で設定したメッセージを出力、改行した後に、追加でメッセージを出力します。
以下は関数の使用例です。
# スクリプトに次の処理を記載
log "zipコマンド 失敗" "zipコマンドを実行しましたが原因不明のエラーが発生しました"
# 実行後のログ内容
2021/02/28 23:11:36 zipコマンド 失敗
zipコマンドを実行しましたが原因不明のエラーが発生しました