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とかに纏めるんだろうなぁ。