cronで日次バッチのログを日付ごとのファイルに分ける

  • cron
  • Shell
  • Logging

この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。

cronで日次バッチを回すとき、ログを1本のファイルにひたすら追記していく構成だとどこからどこまでが今日の実行分か分かりにくくなる。なので実行日ごとにファイルを分けたい。

やり方はシンプルで、リダイレクト先のファイル名にdateコマンドの出力を埋め込むだけ。

0 0 * * * /bin/sh /scripts/batch.sh > /var/log/scripts/update_`date +\%Y\%m\%d`.log

毎日0時にbatch.shを実行して、update_20131209.logのように実行日の日付が入ったファイルに出力される。日をまたげば別ファイルになるので、原因調査のときに該当日のログだけ見ればいい状態になる。

ファイル名の日付は実行時点で決まる

バッククォートによるコマンド置換(`date +\%Y\%m\%d`)は、cronがこの行をsh -c経由で実行するタイミングで評価される。つまり設定を書いた日付ではなく、実際にバッチが動いた日の日付がファイル名に入る。日付ごとに分けたいという目的にはこの挙動で合っている。

%はそのままだと壊れる

crontab内では%は改行として特殊解釈される。date +%Y%m%dとそのまま書くと、cronはその%のところで行を分割してしまい、dateに渡すはずだった引数が欠けて壊れる。日付ごとにファイルを分ける、という目的の根幹の部分なので、\%とエスケープするのを忘れると機能しない。

システム全体のcrontabか個人のcrontabかで書式が変わる

上の例は個人のcrontab -e向けの書き方。もし/etc/crontabや/etc/cron.d/*のようなシステム全体のcrontabに置く場合は、実行ユーザーを指定するuserフィールドが間に必要になる。

0 0 * * * user /bin/sh /scripts/batch.sh > /var/log/scripts/update_`date +\%Y\%m\%d`.log

個人のcrontabにこの形式のまま書くとuserの部分がコマンドとして解釈されてエラーになるので、設置場所に応じて書式を合わせる必要がある。

ファイル分割だけだとエラーは拾えていない

>は標準出力だけをリダイレクトする。ファイルは日付ごとに分かれても、batch.shがエラーを吐いた場合はそのエラーはログファイルに入らず、cronのデフォルト挙動(メール送信、もしくは破棄)に流れてしまう。エラーも含めて日付ごとに追いたいなら

... > /var/log/scripts/update_`date +\%Y\%m\%d`.log 2>&1

としておく方が、分割した意味がある。

運用上ついてまわる話

  • dateはシステムのTZ設定に依存するので、サーバーのタイムゾーンが想定(UTCかJSTか)と合っているかは確認しておく
  • 日付ごとにファイルが増え続ける構成なので、logrotateなどで古いログを削除・圧縮する仕組みは別途必要になる。分割自体はファイルを溜め続ける方向にしか働かない