PluginProbe
Media Cloud Sync / 1.4.2
Media Cloud Sync v1.4.2
1.4.2 1.4.1 1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 All 36 releases
media-cloud-sync / includes / sdk / google / monolog / monolog / src / Monolog / Handler / RotatingFileHandler.php

RotatingFileHandler.php in Media Cloud Sync 1.4.2, at includes/sdk/google/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php

188 lines 7.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare (strict_types=1);
4 /*
5 * This file is part of the Monolog package.
6 *
7 * (c) Jordi Boggiano <[email protected]>
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12 namespace Dudlewebs\WPMCS\GCP\Monolog\Handler;
13
14 use DateTimeZone;
15 use InvalidArgumentException;
16 use Dudlewebs\WPMCS\GCP\Monolog\Level;
17 use Dudlewebs\WPMCS\GCP\Monolog\Utils;
18 use Dudlewebs\WPMCS\GCP\Monolog\LogRecord;
19 /**
20 * Stores logs to files that are rotated every day and a limited number of files are kept.
21 *
22 * This rotation is only intended to be used as a workaround. Using logrotate to
23 * handle the rotation is strongly encouraged when you can use it.
24 *
25 * @author Christophe Coevoet <[email protected]>
26 * @author Jordi Boggiano <[email protected]>
27 */
28 class RotatingFileHandler extends StreamHandler
29 {
30 public const FILE_PER_DAY = 'Y-m-d';
31 public const FILE_PER_MONTH = 'Y-m';
32 public const FILE_PER_YEAR = 'Y';
33 protected string $filename;
34 protected int $maxFiles;
35 protected bool|null $mustRotate = null;
36 protected \DateTimeImmutable $nextRotation;
37 protected string $filenameFormat;
38 protected string $dateFormat;
39 protected DateTimeZone|null $timezone = null;
40 /**
41 * @param int $maxFiles The maximal amount of files to keep (0 means unlimited)
42 * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write)
43 * @param bool $useLocking Try to lock log file before doing any writes
44 */
45 public function __construct(string $filename, int $maxFiles = 0, int|string|Level $level = Level::Debug, bool $bubble = \true, ?int $filePermission = null, bool $useLocking = \false, string $dateFormat = self::FILE_PER_DAY, string $filenameFormat = '{filename}-{date}', DateTimeZone|null $timezone = null)
46 {
47 $this->filename = Utils::canonicalizePath($filename);
48 $this->maxFiles = $maxFiles;
49 $this->setFilenameFormat($filenameFormat, $dateFormat);
50 $this->nextRotation = $this->getNextRotation();
51 $this->timezone = $timezone;
52 parent::__construct($this->getTimedFilename(), $level, $bubble, $filePermission, $useLocking);
53 }
54 /**
55 * @inheritDoc
56 */
57 public function close() : void
58 {
59 parent::close();
60 if (\true === $this->mustRotate) {
61 $this->rotate();
62 }
63 }
64 /**
65 * @inheritDoc
66 */
67 public function reset() : void
68 {
69 parent::reset();
70 }
71 /**
72 * @return $this
73 */
74 public function setFilenameFormat(string $filenameFormat, string $dateFormat) : self
75 {
76 $this->setDateFormat($dateFormat);
77 if (\substr_count($filenameFormat, '{date}') === 0) {
78 throw new InvalidArgumentException('Invalid filename format - format must contain at least `{date}`, because otherwise rotating is impossible.');
79 }
80 $this->filenameFormat = $filenameFormat;
81 $this->url = $this->getTimedFilename();
82 $this->close();
83 return $this;
84 }
85 /**
86 * @inheritDoc
87 */
88 protected function write(LogRecord $record) : void
89 {
90 // on the first record written, if the log is new, we rotate (once per day) after the log has been written so that the new file exists
91 if (null === $this->mustRotate) {
92 $this->mustRotate = null === $this->url || !\file_exists($this->url);
93 }
94 // if the next rotation is expired, then we rotate immediately
95 if ($this->nextRotation <= $record->datetime) {
96 $this->mustRotate = \true;
97 $this->close();
98 // triggers rotation
99 }
100 parent::write($record);
101 if (\true === $this->mustRotate) {
102 $this->close();
103 // triggers rotation
104 }
105 }
106 /**
107 * Rotates the files.
108 */
109 protected function rotate() : void
110 {
111 // update filename
112 $this->url = $this->getTimedFilename();
113 $this->nextRotation = $this->getNextRotation();
114 $this->mustRotate = \false;
115 // skip GC of old logs if files are unlimited
116 if (0 === $this->maxFiles) {
117 return;
118 }
119 $logFiles = \glob($this->getGlobPattern());
120 if (\false === $logFiles) {
121 // failed to glob
122 return;
123 }
124 if ($this->maxFiles >= \count($logFiles)) {
125 // no files to remove
126 return;
127 }
128 // Sorting the files by name to remove the older ones
129 \usort($logFiles, function ($a, $b) {
130 return \strcmp($b, $a);
131 });
132 $basePath = \dirname($this->filename);
133 foreach (\array_slice($logFiles, $this->maxFiles) as $file) {
134 if (\is_writable($file)) {
135 // suppress errors here as unlink() might fail if two processes
136 // are cleaning up/rotating at the same time
137 \set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline) : bool {
138 return \true;
139 });
140 \unlink($file);
141 $dir = \dirname($file);
142 while ($dir !== $basePath) {
143 $entries = \scandir($dir);
144 if ($entries === \false || \count(\array_diff($entries, ['.', '..'])) > 0) {
145 break;
146 }
147 \rmdir($dir);
148 $dir = \dirname($dir);
149 }
150 \restore_error_handler();
151 }
152 }
153 }
154 protected function getTimedFilename() : string
155 {
156 $fileInfo = \pathinfo($this->filename);
157 $timedFilename = \str_replace(['{filename}', '{date}'], [$fileInfo['filename'], (new \DateTimeImmutable(timezone: $this->timezone))->format($this->dateFormat)], ($fileInfo['dirname'] ?? '') . '/' . $this->filenameFormat);
158 if (isset($fileInfo['extension'])) {
159 $timedFilename .= '.' . $fileInfo['extension'];
160 }
161 return $timedFilename;
162 }
163 protected function getGlobPattern() : string
164 {
165 $fileInfo = \pathinfo($this->filename);
166 $glob = \str_replace(['{filename}', '{date}'], [$fileInfo['filename'], \str_replace(['Y', 'y', 'm', 'd'], ['[0-9][0-9][0-9][0-9]', '[0-9][0-9]', '[0-9][0-9]', '[0-9][0-9]'], $this->dateFormat)], ($fileInfo['dirname'] ?? '') . '/' . $this->filenameFormat);
167 if (isset($fileInfo['extension'])) {
168 $glob .= '.' . $fileInfo['extension'];
169 }
170 return $glob;
171 }
172 protected function setDateFormat(string $dateFormat) : void
173 {
174 if (0 === \preg_match('{^[Yy](([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) {
175 throw new InvalidArgumentException('Invalid date format - format must be one of ' . 'RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), RotatingFileHandler::FILE_PER_MONTH ("Y-m") ' . 'or RotatingFileHandler::FILE_PER_YEAR ("Y"), or you can set one of the ' . 'date formats using slashes, underscores and/or dots instead of dashes.');
176 }
177 $this->dateFormat = $dateFormat;
178 }
179 protected function getNextRotation() : \DateTimeImmutable
180 {
181 return match (\str_replace(['/', '_', '.'], '-', $this->dateFormat)) {
182 self::FILE_PER_MONTH => (new \DateTimeImmutable('first day of next month'))->setTime(0, 0, 0),
183 self::FILE_PER_YEAR => (new \DateTimeImmutable('first day of January next year'))->setTime(0, 0, 0),
184 default => (new \DateTimeImmutable('tomorrow'))->setTime(0, 0, 0),
185 };
186 }
187 }
188