CakePHP2 ログローテーションの重複実行の回避
2020/09/11
事情によりCakePHP2を使っているのですが、アクセスが増えた時に、レスポンス遅延が発生しました。 どうも警告が大量に出力されたり、そもそもログ出力が多かったりして、ログローテーションが重複実行されて サーバが不安定になったことがあり、CakePHP2のファイルログのソースコード(lib/Cake/Log/Engine/FileLog.php)を見たところ、 ログ出力メソッド内で、ファイルサイズをチェックして、renameしたりunlinkしていくという、かなり愚直な実装になっていました。 そして、重複実行をブロックするような処理はありませんでした。 CakePHP2を使ってPVが多いサイトを運用してきた人達もいっぱいいると思うんですが、大丈夫なんですかね?問題なかったんだろうか。
CakePHP2のFileLogを拡張して、ログローテーションにロック機能を付けてみました。
app/Log/Engine/AppFileLog.php
Cakeのソースである FileLog.php の _rotateFile メソッドをオーバーライドすることでログローテーションにロック機能を 付けてみました。
<?php App::uses('FileLog', 'Log/Engine'); class AppFileLog extends FileLog {/** * ローテイトする必要があればローテイトを行なうメソッド * * ローテイトにロック機能を付与して、重複してローテイト処理が行なわれるのを回避します。 * ログディレクトリに、ロックファイル(rotate.lockディレクトリ)を作成することでロックします。 * 10分以上前に作成されたロックファイルは、期限切れとして削除します。 * * @param string $filename Log file name * @return mixed True if rotated successfully or false in case of error, otherwise null. * Void if file doesn't need to be rotated. */protected function _rotateFile($filename) {$filepath = $this->_path . $filename;if (version_compare(PHP_VERSION, '5.3.0') >= 0) {clearstatcache(true, $filepath);} else {clearstatcache();} if (!file_exists($filepath) ||filesize($filepath) < $this->_size ) {return null;} //ローテイトロックの確認$rotate_lock = sprintf('%s/rotate.lock', $this->_path);$rotating = file_exists($rotate_lock);if ($rotating) {$expire = strtotime('10 minutes ago');$created = filemtime($rotate_lock);if ($created < $expire){rmdir($rotate_lock); //10分前のロックファイルは既に期限切れとして削除する。$rotating = false;//ロック解除する。}}if ($rotating) {//ローテイト中なら終了する。重複して処理しない。return;}//ロックファイル作成mkdir($rotate_lock); if ($this->_config['rotate'] === 0) {$result = unlink($filepath);} else {$result = rename($filepath, $filepath . '.' . time());} $files = glob($filepath . '.*');if ($files) {$filesToDelete = count($files) - $this->_config['rotate'];while ($filesToDelete > 0) {unlink(array_shift($files));$filesToDelete--;}} rmdir($rotate_lock); //ロックファイルを削除する。return $result;}} |
app/Config/bootstrap.php
bootstrap.php で、engine に AppFile を指定することで、追加した AppFileLog.php を使うようにしています。
<?php CakeLog::config('debug', array('engine' => 'AppFile','types' => array('notice', 'info', 'debug'),'file' => 'debug' . '_' . date('Ymd'),)); CakeLog::config('error', array('engine' => 'AppFile','types' => array('warning', 'error', 'critical', 'alert', 'emergency'),'file' => 'error' . '_' . date('Ymd'),)); |
新しいシステムなら、ログはfluentdとかに纏めるんだろうなぁ。