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
← All changes | includes/sdk/google/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php +84 -60 1.2.11 → 1.4.2 View file →
@@ -8,13 +8,15 @@
8 8 *
9 9 * For the full copyright and license information, please view the LICENSE
10 10 * file that was distributed with this source code.
11 11 */
12 -namespace Dudlewebs\WPMCS\Monolog\Handler;
12 +namespace Dudlewebs\WPMCS\GCP\Monolog\Handler;
13 13
14 +use DateTimeZone;
14 15 use InvalidArgumentException;
15 -use Dudlewebs\WPMCS\Monolog\Logger;
16 -use Dudlewebs\WPMCS\Monolog\Utils;
16 +use Dudlewebs\WPMCS\GCP\Monolog\Level;
17 +use Dudlewebs\WPMCS\GCP\Monolog\Utils;
18 +use Dudlewebs\WPMCS\GCP\Monolog\LogRecord;
17 19 /**
18 20 * Stores logs to files that are rotated every day and a limited number of files are kept.
19 21 *
20 22 * This rotation is only intended to be used as a workaround. Using logrotate to
@@ -27,39 +29,33 @@
27 29 {
28 30 public const FILE_PER_DAY = 'Y-m-d';
29 31 public const FILE_PER_MONTH = 'Y-m';
30 32 public const FILE_PER_YEAR = 'Y';
31 - /** @var string */
32 - protected $filename;
33 - /** @var int */
34 - protected $maxFiles;
35 - /** @var bool */
36 - protected $mustRotate;
37 - /** @var \DateTimeImmutable */
38 - protected $nextRotation;
39 - /** @var string */
40 - protected $filenameFormat;
41 - /** @var string */
42 - protected $dateFormat;
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;
43 40 /**
44 - * @param string $filename
45 - * @param int $maxFiles The maximal amount of files to keep (0 means unlimited)
46 - * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write)
47 - * @param bool $useLocking Try to lock log file before doing any writes
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
48 44 */
49 - public function __construct(string $filename, int $maxFiles = 0, $level = Logger::DEBUG, bool $bubble = \true, ?int $filePermission = null, bool $useLocking = \false)
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)
50 46 {
51 47 $this->filename = Utils::canonicalizePath($filename);
52 48 $this->maxFiles = $maxFiles;
53 - $this->nextRotation = new \DateTimeImmutable('tomorrow');
54 - $this->filenameFormat = '{filename}-{date}';
55 - $this->dateFormat = static::FILE_PER_DAY;
49 + $this->setFilenameFormat($filenameFormat, $dateFormat);
50 + $this->nextRotation = $this->getNextRotation();
51 + $this->timezone = $timezone;
56 52 parent::__construct($this->getTimedFilename(), $level, $bubble, $filePermission, $useLocking);
57 53 }
58 54 /**
59 - * {@inheritDoc}
55 + * @inheritDoc
60 56 */
61 - public function close(): void
57 + public function close() : void
62 58 {
63 59 parent::close();
64 60 if (\true === $this->mustRotate) {
65 61 $this->rotate();
@@ -65,99 +61,127 @@
65 61 $this->rotate();
66 62 }
67 63 }
68 64 /**
69 - * {@inheritDoc}
65 + * @inheritDoc
70 66 */
71 - public function reset()
67 + public function reset() : void
72 68 {
73 69 parent::reset();
74 - if (\true === $this->mustRotate) {
75 - $this->rotate();
76 - }
77 70 }
78 - public function setFilenameFormat(string $filenameFormat, string $dateFormat): self
71 + /**
72 + * @return $this
73 + */
74 + public function setFilenameFormat(string $filenameFormat, string $dateFormat) : self
79 75 {
80 - if (!preg_match('{^[Yy](([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) {
81 - 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.');
82 - }
83 - if (substr_count($filenameFormat, '{date}') === 0) {
76 + $this->setDateFormat($dateFormat);
77 + if (\substr_count($filenameFormat, '{date}') === 0) {
84 78 throw new InvalidArgumentException('Invalid filename format - format must contain at least `{date}`, because otherwise rotating is impossible.');
85 79 }
86 80 $this->filenameFormat = $filenameFormat;
87 - $this->dateFormat = $dateFormat;
88 81 $this->url = $this->getTimedFilename();
89 82 $this->close();
90 83 return $this;
91 84 }
92 85 /**
93 - * {@inheritDoc}
86 + * @inheritDoc
94 87 */
95 - protected function write(array $record): void
88 + protected function write(LogRecord $record) : void
96 89 {
97 - // on the first record written, if the log is new, we should rotate (once per day)
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
98 91 if (null === $this->mustRotate) {
99 - $this->mustRotate = null === $this->url || !file_exists($this->url);
92 + $this->mustRotate = null === $this->url || !\file_exists($this->url);
100 93 }
101 - if ($this->nextRotation <= $record['datetime']) {
94 + // if the next rotation is expired, then we rotate immediately
95 + if ($this->nextRotation <= $record->datetime) {
102 96 $this->mustRotate = \true;
103 97 $this->close();
98 + // triggers rotation
104 99 }
105 100 parent::write($record);
101 + if (\true === $this->mustRotate) {
102 + $this->close();
103 + // triggers rotation
104 + }
106 105 }
107 106 /**
108 107 * Rotates the files.
109 108 */
110 - protected function rotate(): void
109 + protected function rotate() : void
111 110 {
112 111 // update filename
113 112 $this->url = $this->getTimedFilename();
114 - $this->nextRotation = new \DateTimeImmutable('tomorrow');
113 + $this->nextRotation = $this->getNextRotation();
114 + $this->mustRotate = \false;
115 115 // skip GC of old logs if files are unlimited
116 116 if (0 === $this->maxFiles) {
117 117 return;
118 118 }
119 - $logFiles = glob($this->getGlobPattern());
119 + $logFiles = \glob($this->getGlobPattern());
120 120 if (\false === $logFiles) {
121 121 // failed to glob
122 122 return;
123 123 }
124 - if ($this->maxFiles >= count($logFiles)) {
124 + if ($this->maxFiles >= \count($logFiles)) {
125 125 // no files to remove
126 126 return;
127 127 }
128 128 // Sorting the files by name to remove the older ones
129 - usort($logFiles, function ($a, $b) {
130 - return strcmp($b, $a);
129 + \usort($logFiles, function ($a, $b) {
130 + return \strcmp($b, $a);
131 131 });
132 - foreach (array_slice($logFiles, $this->maxFiles) as $file) {
133 - if (is_writable($file)) {
132 + $basePath = \dirname($this->filename);
133 + foreach (\array_slice($logFiles, $this->maxFiles) as $file) {
134 + if (\is_writable($file)) {
134 135 // suppress errors here as unlink() might fail if two processes
135 136 // are cleaning up/rotating at the same time
136 - set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline): bool {
137 - return \false;
137 + \set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline) : bool {
138 + return \true;
138 139 });
139 - unlink($file);
140 - restore_error_handler();
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();
141 151 }
142 152 }
143 - $this->mustRotate = \false;
144 153 }
145 - protected function getTimedFilename(): string
154 + protected function getTimedFilename() : string
146 155 {
147 - $fileInfo = pathinfo($this->filename);
148 - $timedFilename = str_replace(['{filename}', '{date}'], [$fileInfo['filename'], date($this->dateFormat)], $fileInfo['dirname'] . '/' . $this->filenameFormat);
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);
149 158 if (isset($fileInfo['extension'])) {
150 159 $timedFilename .= '.' . $fileInfo['extension'];
151 160 }
152 161 return $timedFilename;
153 162 }
154 - protected function getGlobPattern(): string
163 + protected function getGlobPattern() : string
155 164 {
156 - $fileInfo = pathinfo($this->filename);
157 - $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);
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);
158 167 if (isset($fileInfo['extension'])) {
159 168 $glob .= '.' . $fileInfo['extension'];
160 169 }
161 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 + };
162 186 }
163 187 }