Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
91.30% |
42 / 46 |
|
71.43% |
5 / 7 |
CRAP | |
0.00% |
0 / 1 |
FileLogger | |
91.30% |
42 / 46 |
|
71.43% |
5 / 7 |
12.09 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
1 | |||
toSafeFileNameHeader | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
toHeaderDate | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
cleanupCore | |
57.14% |
4 / 7 |
|
0.00% |
0 / 1 |
3.71 | |||
cleanup | |
91.67% |
11 / 12 |
|
0.00% |
0 / 1 |
3.01 | |||
getLogFilePath | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
logImpl | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace PeServer\Core\Log; |
6 | |
7 | use PeServer\Core\Code; |
8 | use PeServer\Core\Collection\Arr; |
9 | use PeServer\Core\IO\Directory; |
10 | use PeServer\Core\IO\File; |
11 | use PeServer\Core\IO\IOUtility; |
12 | use PeServer\Core\IO\Path; |
13 | use PeServer\Core\Log\LoggerBase; |
14 | use PeServer\Core\Log\Logging; |
15 | use PeServer\Core\Log\LogOptions; |
16 | use PeServer\Core\Text; |
17 | use PeServer\Core\Throws\Throws; |
18 | |
19 | /** |
20 | * ファイルロガー。 |
21 | */ |
22 | class FileLogger extends LoggerBase |
23 | { |
24 | #region variable |
25 | |
26 | /** |
27 | * 出力ディレクトリパス。 |
28 | */ |
29 | private readonly string $directoryPath; |
30 | /** |
31 | * ファイル書式名 |
32 | * |
33 | * @phpstan-var literal-string |
34 | */ |
35 | private readonly string $baseFileName; |
36 | |
37 | /** |
38 | * 破棄済みファイルパターン。 |
39 | * |
40 | * @var string[] |
41 | */ |
42 | private static array $cleanupFilePatterns = []; |
43 | |
44 | #endregion |
45 | |
46 | /** |
47 | * 生成。 |
48 | * |
49 | * @param LogOptions $options |
50 | */ |
51 | public function __construct(Logging $logging, LogOptions $options) |
52 | { |
53 | parent::__construct($logging, $options); |
54 | |
55 | $directoryPath = $this->options->configuration['directory'] ?? Text::EMPTY; |
56 | Throws::throwIfNullOrWhiteSpace($directoryPath); |
57 | $this->directoryPath = $directoryPath; |
58 | |
59 | $baseFileName = Code::toLiteralString($this->options->configuration['name'] ?? Text::EMPTY); |
60 | Throws::throwIfNullOrWhiteSpace($baseFileName); |
61 | $this->baseFileName = $baseFileName; |
62 | |
63 | /** @phpstan-var non-negative-int */ |
64 | $count = $this->options->configuration['count'] ?? 0; |
65 | Throws::throwIf(0 <= $count); //@phpstan-ignore-line [DOCTYPE] |
66 | $this->cleanup($count); |
67 | } |
68 | |
69 | #region function |
70 | |
71 | private function toSafeFileNameHeader(): string |
72 | { |
73 | $trimHeader = Text::trim($this->options->header, '/\\'); |
74 | return Text::replace($trimHeader, ['/', '\\', '*', '|', '<', '>', '?', ':'], '_'); |
75 | } |
76 | |
77 | protected function toHeaderDate(bool $isCleanup): string |
78 | { |
79 | return $isCleanup |
80 | ? '*' |
81 | : date('Ymd'); |
82 | } |
83 | |
84 | /** |
85 | * 破棄処理内部実装。 |
86 | * |
87 | * @param int $maxCount |
88 | * @phpstan-param non-negative-int $maxCount |
89 | * @param string $filePattern |
90 | */ |
91 | private function cleanupCore(int $maxCount, string $filePattern): void |
92 | { |
93 | $logFiles = Directory::find($this->directoryPath, $filePattern); |
94 | $logCount = Arr::getCount($logFiles); |
95 | if ($logCount <= $maxCount) { |
96 | return; |
97 | } |
98 | |
99 | $length = $logCount - $maxCount; |
100 | for ($i = 0; $i < $length; $i++) { |
101 | File::removeFile($logFiles[$i]); |
102 | } |
103 | } |
104 | |
105 | /** |
106 | * 破棄処理。 |
107 | * |
108 | * @param int $maxCount |
109 | * @phpstan-param non-negative-int $maxCount |
110 | */ |
111 | private function cleanup(int $maxCount): void |
112 | { |
113 | if (!$maxCount) { |
114 | return; |
115 | } |
116 | |
117 | $filePattern = Text::replaceMap( |
118 | $this->baseFileName, |
119 | [ |
120 | 'HEADER' => $this->toSafeFileNameHeader(), |
121 | 'DATE' => $this->toHeaderDate(true), |
122 | ] |
123 | ); |
124 | if (!Arr::containsValue(self::$cleanupFilePatterns, $filePattern)) { |
125 | self::$cleanupFilePatterns[] = $filePattern; |
126 | $this->cleanupCore($maxCount, $filePattern); |
127 | } |
128 | } |
129 | |
130 | private function getLogFilePath(): string |
131 | { |
132 | $fileName = Text::replaceMap( |
133 | $this->baseFileName, |
134 | [ |
135 | 'HEADER' => $this->toSafeFileNameHeader(), |
136 | 'DATE' => $this->toHeaderDate(false), |
137 | ] |
138 | ); |
139 | |
140 | return Path::combine($this->directoryPath, $fileName); |
141 | } |
142 | |
143 | #endregion |
144 | |
145 | #region LoggerBase |
146 | |
147 | protected function logImpl(int $level, int $traceIndex, $message, ...$parameters): void |
148 | { |
149 | Directory::createDirectoryIfNotExists($this->directoryPath); |
150 | |
151 | $logMessage = $this->format($level, $traceIndex + 1, $message, ...$parameters); |
152 | |
153 | $filePath = $this->getLogFilePath(); |
154 | //error_logが制限されている場合はこっちを使用する→: file_put_contents($filePath, $logMessage . PHP_EOL, FILE_APPEND | LOCK_EX); |
155 | error_log($logMessage . PHP_EOL, 3, $filePath); |
156 | } |
157 | |
158 | #endregion |
159 | } |