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

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