Help us understand the problem. What is going on with this article?

シェルスクリプトのロギングを楽にするtips

More than 3 years have passed since last update.

背景

cronに仕込んだバッチは、echo文でログ・・・ よくある話です。
ちょっとした関数を用意しておけばログは楽に取れるので、私の手法を記録しておきます。
使っている変数のせいで、bash限定。

log関数をつくる

コード

たった5行のコード。
使う前に どこかでLOGFILE変数を設定する必要がある。
ログレベルみたいな便利なシロモノはない。

#!/bin/bash -
# You must set 'LOGFILE'
readonly PROCNAME=${0##*/}
function log() {
  local fname=${BASH_SOURCE[1]##*/}
  echo -e "$(date '+%Y-%m-%dT%H:%M:%S') ${PROCNAME} (${fname}:${BASH_LINENO[0]}:${FUNCNAME[1]}) $@" | tee -a ${LOGFILE}
}

出力フォーマットはただのecho文なので好きに直してください。-eオプションも不要かな・・・
PROCNAME変数は重複する可能性があるので、必要に応じて変えてください。

ログのフォーマット

上記実装だとこのように出力される。

2015-08-27T18:58:14 libtest.sh (libtest.sh:5:main) message

途中の()の中身は、コロン区切りでこんな感じ。

  • log関数を呼び出した スクリプト名
  • log関数を呼び出したスクリプトの 行番号
  • log関数を呼び出した 関数名

ホスト名は付けていないので、必要なら${HOSTNAME}とかを入れると良いんじゃないかと。
大元のプロセス名部分は不要だというなら、${PROCNAME}を削ってください。

使い方

echoと一緒。

log "message"

単一スクリプトでの実装例

先ほどのlog関数を実装したスクリプトを書いてみる。

実装

LOGFILE変数は最初に宣言している。
mainの途中に関数実装が挟まる素敵な例。

bash_log.sh
#!/bin/bash -
readonly LOGFILE="/tmp/${0##*/}.log"

readonly PROCNAME=${0##*/}
function log() {
  local fname=${BASH_SOURCE[1]##*/}
  echo -e "$(date '+%Y-%m-%dT%H:%M:%S') ${PROCNAME} (${fname}:${BASH_LINENO[0]}:${FUNCNAME[1]}) $@" | tee -a ${LOGFILE}
}


#
# MAIN
#

log "This is test message 1"
log "Test 2"
log "Test 3"

function logtest() {
  log "test from logtest function"
}

logtest

実行

$ cd /tmp
$ ./bash_log.sh
2015-08-27T15:56:23 bash_log.sh (bash_log.sh:13:main) This is test message 1
2015-08-27T15:56:23 bash_log.sh (bash_log.sh:14:main) Test 2
2015-08-27T15:56:23 bash_log.sh (bash_log.sh:15:main) Test 3
2015-08-27T15:56:23 bash_log.sh (bash_log.sh:18:logtest) test from logtest function

$ cat bash_log.sh.log
2015-08-27T15:56:23 bash_log.sh (bash_log.sh:13:main) This is test message 1
2015-08-27T15:56:23 bash_log.sh (bash_log.sh:14:main) Test 2
2015-08-27T15:56:23 bash_log.sh (bash_log.sh:15:main) Test 3
2015-08-27T15:56:23 bash_log.sh (bash_log.sh:18:logtest) test from logtest function

コンソールにも出力し、ログにも記録する。
プロセス名とかスクリプト名が冗長かな・・・

ライブラリ風の実装例

今度はライブラリのように、loglib.shを作成してみる。

実装(ライブラリ)

LOGFILE変数は、スクリプト毎に定義できるようにここでは定義していない。

loglib.sh
#!/bin/bash -
# You must set 'LOGFILE'
readonly PROCNAME=${0##*/}
function log() {
  local fname=${BASH_SOURCE[1]##*/}
  echo -e "$(date '+%Y-%m-%dT%H:%M:%S') ${PROCNAME} (${fname}:${BASH_LINENO[0]}:${FUNCNAME[1]}) $@" | tee -a ${LOGFILE}
}

実装(スクリプト本体)

作ったloglib.shを使うlibtest.shを書く。
LOGFILE変数は、ライブラリを使う 本体スクリプト側で定義する 例としている。

libtest.sh
#!/bin/bash -
LOGFILE="/tmp/test.log"
. /tmp/loglib.sh

log "test message from main"

function logtest() {
  log "test message from func"
}

logtest

. /tmp/libtest_sub.sh
sub_command

最後の2行で呼び出しているlib_test.shおよびsub_commmandの実装はこちら。

libtest_sub.sh
#!/bin/bash -

function sub_command() {
  log "This is sub script"
}

うーん。試験用とはいえ、我ながらとんでもない実装だ。

実行

$ ./libtest.sh
2015-08-27T18:58:14 libtest.sh (libtest.sh:5:main) test message from main
2015-08-27T18:58:14 libtest.sh (libtest.sh:8:logtest) test message from func
2015-08-27T18:58:14 libtest.sh (libtest_sub.sh:4:sub_command) This is sub script

$ cat /tmp/test.log
2015-08-27T18:58:14 libtest.sh (libtest.sh:5:main) test message from main
2015-08-27T18:58:14 libtest.sh (libtest.sh:8:logtest) test message from func
2015-08-27T18:58:14 libtest.sh (libtest_sub.sh:4:sub_command) This is sub script

よしよし。

どのスクリプトファイルのどの位置から呼ばれたかわかるので、複数人でメンテしているようなバッチに向いている出力になったと思う。

Ets
インフラエンジニア。スクリプト系の言語を中心に勉強してます。
livesense
求人情報サイト「マッハバイト」「転職ナビ」「転職会議」「就活会議」「転職ドラフト」および不動産情報サイト「IESHIL」などを開発・運営しています。
https://www.livesense.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした