RotatingFileHandler.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  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. protected $filename;
  24. protected $maxFiles;
  25. protected $mustRotate;
  26. protected $nextRotation;
  27. protected $filenameFormat;
  28. protected $dateFormat;
  29. /**
  30. * @param string $filename
  31. * @param integer $maxFiles The maximal amount of files to keep (0 means unlimited)
  32. * @param integer $level The minimum logging level at which this handler will be triggered
  33. * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
  34. * @param int $filePermissions Optional file permissions (default (0644) are only for owner read/write)
  35. */
  36. public function __construct($filename, $maxFiles = 0, $level = Logger::DEBUG, $bubble = true, $filePermission = null)
  37. {
  38. $this->filename = $filename;
  39. $this->maxFiles = (int) $maxFiles;
  40. $this->nextRotation = new \DateTime('tomorrow');
  41. $this->filenameFormat = '{filename}-{date}';
  42. $this->dateFormat = 'Y-m-d';
  43. parent::__construct($this->getTimedFilename(), $level, $bubble, $filePermission);
  44. }
  45. /**
  46. * {@inheritdoc}
  47. */
  48. public function close()
  49. {
  50. parent::close();
  51. if (true === $this->mustRotate) {
  52. $this->rotate();
  53. }
  54. }
  55. public function setFilenameFormat($filenameFormat, $dateFormat)
  56. {
  57. $this->filenameFormat = $filenameFormat;
  58. $this->dateFormat = $dateFormat;
  59. $this->url = $this->getTimedFilename();
  60. $this->close();
  61. }
  62. /**
  63. * {@inheritdoc}
  64. */
  65. protected function write(array $record)
  66. {
  67. // on the first record written, if the log is new, we should rotate (once per day)
  68. if (null === $this->mustRotate) {
  69. $this->mustRotate = !file_exists($this->url);
  70. }
  71. if ($this->nextRotation < $record['datetime']) {
  72. $this->mustRotate = true;
  73. $this->close();
  74. }
  75. parent::write($record);
  76. }
  77. /**
  78. * Rotates the files.
  79. */
  80. protected function rotate()
  81. {
  82. // update filename
  83. $this->url = $this->getTimedFilename();
  84. $this->nextRotation = new \DateTime('tomorrow');
  85. // skip GC of old logs if files are unlimited
  86. if (0 === $this->maxFiles) {
  87. return;
  88. }
  89. $logFiles = glob($this->getGlobPattern());
  90. if ($this->maxFiles >= count($logFiles)) {
  91. // no files to remove
  92. return;
  93. }
  94. // Sorting the files by name to remove the older ones
  95. usort($logFiles, function ($a, $b) {
  96. return strcmp($b, $a);
  97. });
  98. foreach (array_slice($logFiles, $this->maxFiles) as $file) {
  99. if (is_writable($file)) {
  100. unlink($file);
  101. }
  102. }
  103. }
  104. protected function getTimedFilename()
  105. {
  106. $fileInfo = pathinfo($this->filename);
  107. $timedFilename = str_replace(
  108. array('{filename}', '{date}'),
  109. array($fileInfo['filename'], date($this->dateFormat)),
  110. $fileInfo['dirname'] . '/' . $this->filenameFormat
  111. );
  112. if (!empty($fileInfo['extension'])) {
  113. $timedFilename .= '.'.$fileInfo['extension'];
  114. }
  115. return $timedFilename;
  116. }
  117. protected function getGlobPattern()
  118. {
  119. $fileInfo = pathinfo($this->filename);
  120. $glob = str_replace(
  121. array('{filename}', '{date}'),
  122. array($fileInfo['filename'], '*'),
  123. $fileInfo['dirname'] . '/' . $this->filenameFormat
  124. );
  125. if (!empty($fileInfo['extension'])) {
  126. $glob .= '.'.$fileInfo['extension'];
  127. }
  128. return $glob;
  129. }
  130. }