PHP
Bash
cron

cronとshellとphpと標準出力とエラー出力とアラートとログローテート

More than 1 year has passed since last update.

バッチあるある

・出力全部捨ててる!
・ログがディスク圧迫!
・アラートが飛んでこないとか、 エラーでもないのにメールが来る!
・メール送信処理、ログファイル処理、ログローテート、、、などの本質的じゃないところの仕様どうしようとか、バグとか。

php

test.php
<?php
// これらは標準出力
echo "this is php debug message by echo()\n";
fputs(STDOUT, "this is php debug message by fputs(STDOUT).\n");
file_put_contents('php://stdout', "this is php debug message by file_put_contents(php://stdout).\n");

// こいつらはエラー出力
fputs(STDERR, "this is php error message by fputs(STDERR).\n");
file_put_contents('php://stderr', "this is php error message by file_put_contents(php://stderr).\n");
// これもエラー出力
trigger_error("trigger_error E_USER_NOTICE", E_USER_NOTICE);
trigger_error("trigger_error E_USER_WARNING", E_USER_WARNING);
trigger_error("trigger_error E_USER_ERROR", E_USER_ERROR);

exit(0); // 正常終了
exit(1); // 異常終了exit(1-254);

・標準出力、エラー出力を分けよう
・正常終了、異常終了を分けよう
・echo, fputs,などを色んな所に書くのではなく、ラッパー関数を作ってそれを呼ぶといいはず。logDebug($msg); logError($msg);とか。

shell

test.bash
#!/bin/bash

function create_pid {
    echo $$ > $pid_file
}

function delete_pid {
    rm $pid_file
}

function check_pid {
    if [ -f $pid_file ]; then
        pid=`cat $pid_file`
        if (ps -e | awk '{print $1}' | grep $pid >/dev/null); then
            echo "${0} is runnning, exit"
            exit 1
        fi
    fi
}

function main {
    echo "this is bash debug message." # 標準出力
    echo "this is bash error message. 1>&2" 1>&2 # エラー出力

    check_pid  # 多重起動なら終了
    create_pid # 多重起動チェック用ファイルを作成

    # 外部プログラム実行して終了ステータスを判定
    $batch_cmd
    if [ $? -gt 0 ]; then
        delete_pid # 多重起動チェック用ファイルを削除
        exit 1 # 異常終了1-255
    fi

    delete_pid # 多重起動チェック用ファイルを削除
    exit 0 # 正常終了
}

pid_file="/tmp/mazda-test-script-pid.`hostname`"
batch_cmd="/usr/bin/php /path/to/mazda/test.php"
main

・標準出力、エラー出力を分けよう
・外部プログラムの終了ステータスを判定しよう
・正常終了、異常終了を分けよう
・多重起動防止しておこう

cron

crontab
# メールを送らない設定。コメントには日本語も使えます
MAILTO=""

# MAILTOを上書きできます
MAILTO="yuki.mazda+foo@bar.com"
* * * * * cd /path/to/mazda; /bin/bash /path/to/mazda/test.bash >> /path/to/mazda/logs/`date +\%Y\%m\%d`.log

# MAILTOには複数のアドレスが設定出来ます
# 複数アドレスの時はダブルクォートなしで、,のあとのスペースもなしじゃなきゃダメかも?
MAILTO="yuki.mazda+hoge@hoge.com, yuki.mazda+hoge@foo.com"

# 1ヶ月前のログをfindして削除する
0 10 * * * find /path/to/mazda/logs/ -type f -name "`date +\%Y\%m\%d --date '1 month ago'`.log" -exec rm -f {} \;

# この例ではもともとメールを送らない設定だったのでもとに戻しておこう
MAILTO=""

・cronには出力をメール送信する機能があります。送信先はMAILTOで変えられます。MAILTO=""ならメール送信しません
・cron行ごとにMAILTOを設定できます。上書きできます
・標準出力をログファイルにリダイレクトしよう
・ログファイルは20130620.logみたいな形式にしておこう
・古いログファイルは適当に削除するcronもセットする
・プロジェクトルートにcdしておいたほうが楽できるよ
・権限気にしなくて済むので/bin/bash /path/to/scriptが楽

こうすると

・デバッグログは必要な分だけ保存され続けるし、不要なメールは飛んで来ないし、エラーがあった時だけアラートが来る
・すでにあるlinuxアーキテクチャでつくるので、メール送信処理、ログファイル処理とか、むだに仕様決めなくて済む
・ちゃんと正常終了、異常終了のステータスを分けておくと/var/log/syslogにそれっぽく出てくれます。

そうは言っても

・fluentdでログったりもありえるし、個別にアラートメールしたりもする。ログファイルを自前で処理したり。アプリケーションによってはこれらは必要です。好きにすればいいよ
・フレームワークがあるならそれを使えば良いとおもいます。