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

# バッチあるある

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


# php
```php:test.php
<?php
if(!defined('STDIN'))  define('STDIN',  fopen('php://stdin',  'rb'));
if(!defined('STDOUT')) define('STDOUT', fopen('php://stdout', 'wb'));
if(!defined('STDERR')) define('STDERR', fopen('php://stderr', 'wb'));

// これらは標準出力
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
```bash: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/adachi-test-script-pid.`hostname`"
batch_cmd="/usr/bin/php /path/to/adachi/test.php"
main
```

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


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

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

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

# 1ヶ月前のログをfindして削除する
0 10 * * * find /path/to/adachi/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でログったりもありえるし、個別にアラートメールしたりもする。ログファイルを自前で処理したり。アプリケーションによってはこれらは必要です。好きにすればいいよ
- フレームワークがあるならそれを使えば良いとおもいます。