vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php line 26

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Monolog package.
  4.  *
  5.  * (c) Jordi Boggiano <j.boggiano@seld.be>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Monolog\Handler;
  11. use Monolog\Logger;
  12. use Monolog\Utils;
  13. /**
  14.  * Stores logs to files that are rotated every day and a limited number of files are kept.
  15.  *
  16.  * This rotation is only intended to be used as a workaround. Using logrotate to
  17.  * handle the rotation is strongly encouraged when you can use it.
  18.  *
  19.  * @author Christophe Coevoet <stof@notk.org>
  20.  * @author Jordi Boggiano <j.boggiano@seld.be>
  21.  */
  22. class RotatingFileHandler extends StreamHandler
  23. {
  24.     const FILE_PER_DAY 'Y-m-d';
  25.     const FILE_PER_MONTH 'Y-m';
  26.     const FILE_PER_YEAR 'Y';
  27.     protected $filename;
  28.     protected $maxFiles;
  29.     protected $mustRotate;
  30.     protected $nextRotation;
  31.     protected $filenameFormat;
  32.     protected $dateFormat;
  33.     /**
  34.      * @param string   $filename
  35.      * @param int      $maxFiles       The maximal amount of files to keep (0 means unlimited)
  36.      * @param int      $level          The minimum logging level at which this handler will be triggered
  37.      * @param bool     $bubble         Whether the messages that are handled can bubble up the stack or not
  38.      * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write)
  39.      * @param bool     $useLocking     Try to lock log file before doing any writes
  40.      */
  41.     public function __construct($filename$maxFiles 0$level Logger::DEBUG$bubble true$filePermission null$useLocking false)
  42.     {
  43.         $this->filename Utils::canonicalizePath($filename);
  44.         $this->maxFiles = (int) $maxFiles;
  45.         $this->nextRotation = new \DateTime('tomorrow');
  46.         $this->filenameFormat '{filename}-{date}';
  47.         $this->dateFormat 'Y-m-d';
  48.         parent::__construct($this->getTimedFilename(), $level$bubble$filePermission$useLocking);
  49.     }
  50.     /**
  51.      * {@inheritdoc}
  52.      */
  53.     public function close()
  54.     {
  55.         parent::close();
  56.         if (true === $this->mustRotate) {
  57.             $this->rotate();
  58.         }
  59.     }
  60.     /**
  61.      * {@inheritdoc}
  62.      */
  63.     public function reset()
  64.     {
  65.         parent::reset();
  66.         if (true === $this->mustRotate) {
  67.             $this->rotate();
  68.         }
  69.     }
  70.     public function setFilenameFormat($filenameFormat$dateFormat)
  71.     {
  72.         if (!preg_match('{^Y(([/_.-]?m)([/_.-]?d)?)?$}'$dateFormat)) {
  73.             trigger_error(
  74.                 'Invalid date format - format must be one of '.
  75.                 'RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), RotatingFileHandler::FILE_PER_MONTH ("Y-m") '.
  76.                 'or RotatingFileHandler::FILE_PER_YEAR ("Y"), or you can set one of the '.
  77.                 'date formats using slashes, underscores and/or dots instead of dashes.',
  78.                 E_USER_DEPRECATED
  79.             );
  80.         }
  81.         if (substr_count($filenameFormat'{date}') === 0) {
  82.             trigger_error(
  83.                 'Invalid filename format - format should contain at least `{date}`, because otherwise rotating is impossible.',
  84.                 E_USER_DEPRECATED
  85.             );
  86.         }
  87.         $this->filenameFormat $filenameFormat;
  88.         $this->dateFormat $dateFormat;
  89.         $this->url $this->getTimedFilename();
  90.         $this->close();
  91.     }
  92.     /**
  93.      * {@inheritdoc}
  94.      */
  95.     protected function write(array $record)
  96.     {
  97.         // on the first record written, if the log is new, we should rotate (once per day)
  98.         if (null === $this->mustRotate) {
  99.             $this->mustRotate = !file_exists($this->url);
  100.         }
  101.         if ($this->nextRotation $record['datetime']) {
  102.             $this->mustRotate true;
  103.             $this->close();
  104.         }
  105.         parent::write($record);
  106.     }
  107.     /**
  108.      * Rotates the files.
  109.      */
  110.     protected function rotate()
  111.     {
  112.         // update filename
  113.         $this->url $this->getTimedFilename();
  114.         $this->nextRotation = new \DateTime('tomorrow');
  115.         // skip GC of old logs if files are unlimited
  116.         if (=== $this->maxFiles) {
  117.             return;
  118.         }
  119.         $logFiles glob($this->getGlobPattern());
  120.         if ($this->maxFiles >= count($logFiles)) {
  121.             // no files to remove
  122.             return;
  123.         }
  124.         // Sorting the files by name to remove the older ones
  125.         usort($logFiles, function ($a$b) {
  126.             return strcmp($b$a);
  127.         });
  128.         foreach (array_slice($logFiles$this->maxFiles) as $file) {
  129.             if (is_writable($file)) {
  130.                 // suppress errors here as unlink() might fail if two processes
  131.                 // are cleaning up/rotating at the same time
  132.                 set_error_handler(function ($errno$errstr$errfile$errline) {});
  133.                 unlink($file);
  134.                 restore_error_handler();
  135.             }
  136.         }
  137.         $this->mustRotate false;
  138.     }
  139.     protected function getTimedFilename()
  140.     {
  141.         $fileInfo pathinfo($this->filename);
  142.         $timedFilename str_replace(
  143.             array('{filename}''{date}'),
  144.             array($fileInfo['filename'], date($this->dateFormat)),
  145.             $fileInfo['dirname'] . '/' $this->filenameFormat
  146.         );
  147.         if (!empty($fileInfo['extension'])) {
  148.             $timedFilename .= '.'.$fileInfo['extension'];
  149.         }
  150.         return $timedFilename;
  151.     }
  152.     protected function getGlobPattern()
  153.     {
  154.         $fileInfo pathinfo($this->filename);
  155.         $glob str_replace(
  156.             array('{filename}''{date}'),
  157.             array($fileInfo['filename'], '[0-9][0-9][0-9][0-9]*'),
  158.             $fileInfo['dirname'] . '/' $this->filenameFormat
  159.         );
  160.         if (!empty($fileInfo['extension'])) {
  161.             $glob .= '.'.$fileInfo['extension'];
  162.         }
  163.         return $glob;
  164.     }
  165. }