Logger::getLogRotateSize()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
namespace WebStream\Log;
3
4
use WebStream\IO\File;
5
use WebStream\IO\Reader\FileReader;
6
use WebStream\IO\Writer\SimpleFileWriter;
7
use WebStream\Container\Container;
8
use WebStream\Exception\Extend\IOException;
9
use WebStream\Exception\Extend\LoggerException;
10
11
/**
12
 * Loggerクラス
13
 * @author Ryuichi Tanaka
14
 * @since 2012/01/16
15
 * @version 0.7
16
 */
17
class Logger
18
{
19
    use LoggerUtils;
20
21
    /**
22
     * @var Logger ロガー
23
     */
24
    private static $logger;
25
26
    /**
27
     * @var LoggerFormatter ロガーフォーマッタ
28
     */
29
    private static $formatter;
30
31
    /**
32
     * @var Container ログ設定コンテナ
33
     */
34
    private static $config;
35
36
    /**
37
     * @var Container ログ設定コンテナ
38
     */
39
    private $logConfig;
40
41
    /**
42
     * @var array<IOutputter> Outputterリスト
43
     */
44
    private $outputters;
45
46
    /**
47
     * @var Container IOコンテナ
48
     */
49
    private $ioContainer;
50
51
    /**
52
     * @var File ログファイル
53
     */
54
    private $logFile;
55
56
    /**
57
     * @var File ステータスファイル
58
     */
59
    private $statusFile;
60
61
    /**
62
     * コンストラクタ
63
     * @param Container ログ設定コンテナ
0 ignored issues
show
Documentation Bug introduced by
The doc comment ログ設定コンテナ at position 0 could not be parsed: Unknown type name 'ログ設定コンテナ' at position 0 in ログ設定コンテナ.
Loading history...
64
     */
65
    private function __construct(Container $logConfig)
66
    {
67
        $this->logConfig = $logConfig;
68
        $this->outputters = [];
69
70
        $logFile = new File($logConfig->logPath);
0 ignored issues
show
Bug Best Practice introduced by
The property logPath does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug introduced by
It seems like $logConfig->logPath can also be of type null; however, parameter $filePath of WebStream\IO\File::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

70
        $logFile = new File(/** @scrutinizer ignore-type */ $logConfig->logPath);
Loading history...
71
        $statusFile = new File($logConfig->statusPath);
0 ignored issues
show
Bug Best Practice introduced by
The property statusPath does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
72
73
        $this->ioContainer = new Container();
74
75
        $this->ioContainer->statusReader = function () use ($statusFile) {
0 ignored issues
show
Bug Best Practice introduced by
The property statusReader does not exist on WebStream\Container\Container. Since you implemented __set, consider adding a @property annotation.
Loading history...
76
            return new FileReader($statusFile);
77
        };
78
        $this->ioContainer->statusWriter = function () use ($statusFile) {
0 ignored issues
show
Bug Best Practice introduced by
The property statusWriter does not exist on WebStream\Container\Container. Since you implemented __set, consider adding a @property annotation.
Loading history...
79
            return new SimpleFileWriter($statusFile->getFilePath());
80
        };
81
        $this->ioContainer->logWriter = function () use ($logFile) {
0 ignored issues
show
Bug Best Practice introduced by
The property logWriter does not exist on WebStream\Container\Container. Since you implemented __set, consider adding a @property annotation.
Loading history...
82
            return new SimpleFileWriter($logFile->getFilePath());
83
        };
84
85
        $this->logFile = $logFile;
86
        $this->statusFile = $statusFile;
87
    }
88
89
    /**
90
     * デストラクタ
91
     */
92
    public function __destruct()
93
    {
94
        $this->directWrite();
95
    }
96
97
    /**
98
     * ログ設定を返却する
99
     * @return Container ログ設定
100
     */
101
    public function getConfig()
102
    {
103
        return $this->logConfig;
104
    }
105
106
    /**
107
     * 遅延書き出しを有効にする
108
     */
109
    public static function enableLazyWrite()
110
    {
111
        self::$logger->lazyWrite();
112
    }
113
114
    /**
115
     * 即時書き出しを有効にする
116
     */
117
    public static function enableDirectWrite()
118
    {
119
        self::$logger->directWrite();
120
    }
121
122
    /**
123
     * インスタンスを返却する
124
     * @return WebStream\Module\Logger ロガーインスタンス
0 ignored issues
show
Bug introduced by
The type WebStream\Log\WebStream\Module\Logger was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
125
     */
126
    public static function getInstance()
127
    {
128
        return self::$logger;
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::logger returns the type WebStream\Log\Logger which is incompatible with the documented return type WebStream\Log\WebStream\Module\Logger.
Loading history...
129
    }
130
131
    /**
132
     * ファイナライザ
133
     */
134
    public static function finalize()
135
    {
136
        self::$config = null;
137
        self::$logger = null;
138
        self::$formatter = null;
139
    }
140
141
    /**
142
     * Loggerを初期化する
143
     * @param Container ログ設定コンテナ
0 ignored issues
show
Documentation Bug introduced by
The doc comment ログ設定コンテナ at position 0 could not be parsed: Unknown type name 'ログ設定コンテナ' at position 0 in ログ設定コンテナ.
Loading history...
144
     */
145
    public static function init(Container $config)
146
    {
147
        self::$config = $config;
148
        self::$logger = new Logger($config);
149
        self::$formatter = new LoggerFormatter($config);
150
    }
151
152
    /**
153
     * Loggerが初期化済みかどうかチェックする
154
     * @param bool 初期化済みならtrue
0 ignored issues
show
Documentation Bug introduced by
The doc comment 初期化済みならtrue at position 0 could not be parsed: Unknown type name '初期化済みならtrue' at position 0 in 初期化済みならtrue.
Loading history...
155
     */
156
    public static function isInitialized()
157
    {
158
        return self::$logger !== null;
159
    }
160
161
    /**
162
     * Loggerメソッドの呼び出しを受ける
163
     * @param string メソッド名(ログレベル文字列)
0 ignored issues
show
Documentation Bug introduced by
The doc comment メソッド名(ログレベル文字列) at position 0 could not be parsed: Unknown type name 'メソッド名' at position 0 in メソッド名(ログレベル文字列).
Loading history...
164
     * @param array 引数
165
     */
166
    public static function __callStatic($level, $arguments)
167
    {
168
        if (self::$logger === null || self::$formatter === null) {
169
            if (self::$config !== null) {
170
                self::init(self::$config);
171
            } else {
172
                throw new LoggerException("Logger is not initialized.");
173
            }
174
        }
175
176
        call_user_func_array([self::$logger, "write"], array_merge([$level], $arguments));
177
    }
178
179
    /**
180
     * Outputterを設定する
181
     * @param array<IOutputter> $outputters Outputterリスト
182
     */
183
    public function setOutputter(array $outputters)
184
    {
185
        foreach ($outputters as $outputter) {
186
            if (!$outputter instanceof \WebStream\Log\Outputter\IOutputter) {
187
                throw new LoggerException("Log outputter must implement WebStream\Log\Outputter\IOutputter.");
188
            }
189
        }
190
        $this->outputters = $outputters;
191
    }
192
193
    /**
194
     * タイムスタンプを取得する
195
     * @return string タイムスタンプ
196
     */
197
    private function getTimeStamp()
0 ignored issues
show
Unused Code introduced by
The method getTimeStamp() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
198
    {
199
        date_default_timezone_set('Asia/Tokyo');
200
        $msec = sprintf("%2d", floatval(microtime()) * 100);
201
202
        return strftime("%Y-%m-%d %H:%M:%S") . "," . $msec;
203
    }
204
205
    /**
206
     * ログを書き出す
207
     * @param string ログレベル文字列
0 ignored issues
show
Documentation Bug introduced by
The doc comment ログレベル文字列 at position 0 could not be parsed: Unknown type name 'ログレベル文字列' at position 0 in ログレベル文字列.
Loading history...
208
     * @param string 出力文字列
209
     * @param array<mixed> 埋め込み値リスト
210
     */
211
    public function write($level, $msg, $context = null)
212
    {
213
        if ($this->logConfig->logLevel > $this->toLogLevelValue($level)) {
0 ignored issues
show
Bug Best Practice introduced by
The property logLevel does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
214
            return;
215
        }
216
217
        if (is_array($context) && count($context) > 0) {
218
            // sprintfと同様の展開
219
            // [a-zA-Z0-9_-\.] 以外もキーには指定可能だが仕様としてこれ以外は不可とする
220
            preg_match_all('/\{\s*([a-zA-Z0-9._-]+)\s*?\}/', $msg, $matches);
221
            foreach ($matches[1] as $index => $value) {
222
                if (array_key_exists($value, $context)) {
223
                    $matches[1][$index] = $context[$value];
224
                } else {
225
                    unset($matches[0][$index]);
226
                }
227
            }
228
            $msg = str_replace($matches[0], $matches[1], $msg);
229
        }
230
231
        // ログローテート処理
232
        $this->rotate();
233
234
        try {
235
            if (count($this->outputters) > 0) {
236
                foreach ($this->outputters as $outputter) {
237
                    $outputter->write(self::$formatter->getFormattedMessage($msg, $level));
238
                }
239
            } else {
240
                $this->ioContainer->logWriter->write(self::$formatter->getFormattedMessage($msg, $level) . PHP_EOL);
0 ignored issues
show
Bug Best Practice introduced by
The property logWriter does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug introduced by
The method write() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

240
                $this->ioContainer->logWriter->/** @scrutinizer ignore-call */ 
241
                                               write(self::$formatter->getFormattedMessage($msg, $level) . PHP_EOL);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
241
            }
242
        } catch (IOException $e) {
243
            throw new LoggerException($e);
244
        }
245
    }
246
247
    /**
248
     * ログファイルをアーカイブする
249
     * stream.log -> stream.(作成日時)-(現在日時).log
250
     * @param string ログファイルパス
0 ignored issues
show
Documentation Bug introduced by
The doc comment ログファイルパス at position 0 could not be parsed: Unknown type name 'ログファイルパス' at position 0 in ログファイルパス.
Loading history...
251
     */
252
    private function rotate()
253
    {
254
        // ログファイルがない場合はローテートしない
255
        if (!$this->logFile->exists()) {
256
            return;
257
        }
258
259
        // ログローテート実行
260
        if ($this->logConfig->rotateCycle !== null) {
0 ignored issues
show
Bug Best Practice introduced by
The property rotateCycle does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
261
            $this->rotateByCycle();
262
        } elseif ($this->logConfig->rotateSize !== null) {
0 ignored issues
show
Bug Best Practice introduced by
The property rotateSize does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
263
            $this->rotateBySize();
264
        }
265
    }
266
267
    /**
268
     * ログステータスファイルに書きこむ
269
     * @throws IOException
270
     */
271
    private function writeStatus()
272
    {
273
        $this->ioContainer->statusWriter->write(intval(preg_replace('/^.*\s/', '', microtime())));
0 ignored issues
show
Bug Best Practice introduced by
The property statusWriter does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug introduced by
The method write() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

273
        $this->ioContainer->statusWriter->/** @scrutinizer ignore-call */ 
274
                                          write(intval(preg_replace('/^.*\s/', '', microtime())));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
274
    }
275
276
    /**
277
     * ログステータスファイルを読み込む
278
     * @return int UnixTime
279
     * @throws LoggerException
280
     */
281
    private function readStatus()
282
    {
283
        $content = $this->ioContainer->statusReader->read();
0 ignored issues
show
Bug introduced by
The method read() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

283
        /** @scrutinizer ignore-call */ 
284
        $content = $this->ioContainer->statusReader->read();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug Best Practice introduced by
The property statusReader does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
284
        if (!preg_match('/^\d{10}$/', $content)) {
285
            throw new LoggerException("Invalid log state file contents: " . $content);
286
        }
287
288
        return intval($content);
289
    }
290
291
    /**
292
     * ステータスファイルを作成する
293
     */
294
    private function createStatusFile()
295
    {
296
        // ステータスファイルがない場合は書きだす
297
        if (!$this->statusFile->exists()) {
298
            $this->writeStatus();
299
        }
300
    }
301
302
    /**
303
     * ローテートを実行する
304
     * @param integer 作成日時のUnixTime
0 ignored issues
show
Documentation Bug introduced by
The doc comment 作成日時のUnixTime at position 0 could not be parsed: Unknown type name '作成日時のUnixTime' at position 0 in 作成日時のUnixTime.
Loading history...
305
     * @param integer 現在日時のUnixTime
306
     */
307
    private function runRotate($from, $to)
308
    {
309
        $fromDate = date("YmdHis", $from);
310
        $toDate = date("YmdHis", $to);
311
        $archivePath = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $archivePath is dead and can be removed.
Loading history...
312
        if (preg_match('/(.*)\.(.+)/', $this->logConfig->logPath, $matches)) {
0 ignored issues
show
Bug Best Practice introduced by
The property logPath does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
313
            $archivePath = "$matches[1].${fromDate}-${toDate}.$matches[2]";
314
            // mvを実行
315
            $this->logFile->renameTo($archivePath);
316
            // ステータスファイルを削除
317
            $this->statusFile->delete();
318
        }
319
    }
320
321
    /**
322
     * 時間単位でローテートする
323
     * stream.log -> stream.(作成日時)-(現在日時).log
324
     */
325
    private function rotateByCycle()
326
    {
327
        $this->createStatusFile();
328
        $now = intval(preg_replace('/^.*\s/', '', microtime()));
329
        $createdAt = $this->readStatus();
330
331
        $hour = intval(floor(($now - $createdAt) / 3600));
332
        if ($hour >= $this->logConfig->rotateCycle) {
0 ignored issues
show
Bug Best Practice introduced by
The property rotateCycle does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
333
            $this->runRotate($createdAt, $now);
334
        }
335
    }
336
337
    /**
338
     * サイズ単位でローテートする
339
     * stream.log -> stream.(作成日時)-(現在日時).log
340
     */
341
    private function rotateBySize()
342
    {
343
        $this->createStatusFile();
344
        $now = intval(preg_replace('/^.*\s/', '', microtime()));
345
        $createdAt = $this->readStatus();
346
347
        $sizeKb = (int) floor($this->logFile->size() / 1024);
0 ignored issues
show
Bug introduced by
The method size() does not exist on WebStream\IO\File. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

347
        $sizeKb = (int) floor($this->logFile->/** @scrutinizer ignore-call */ size() / 1024);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
348
        // 指定したサイズより大きければローテート
349
        if ($sizeKb >= $this->logConfig->rotateSize) {
0 ignored issues
show
Bug Best Practice introduced by
The property rotateSize does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
350
            $this->runRotate($createdAt, $now);
351
        }
352
    }
353
354
    /**
355
     * ログ出力パスを返却する
356
     * @return string ログ出力パス
357
     */
358
    public function getLogPath()
359
    {
360
        return $this->logConfig->logPath;
0 ignored issues
show
Bug Best Practice introduced by
The property logPath does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
361
    }
362
363
    /**
364
     * ログローテートサイクルを返却する
365
     * @return string ログ出力パス
366
     */
367
    public function getLogRotateCycle()
368
    {
369
        return $this->logConfig->rotateCycle;
0 ignored issues
show
Bug Best Practice introduced by
The property rotateCycle does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
370
    }
371
372
    /**
373
     * ログローテートサイズを返却する
374
     * @return string ログ出力パス
375
     */
376
    public function getLogRotateSize()
377
    {
378
        return $this->logConfig->rotateSize;
0 ignored issues
show
Bug Best Practice introduced by
The property rotateSize does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
379
    }
380
381
    /**
382
     * 遅延書き出しを有効にする
383
     */
384
    public function lazyWrite()
385
    {
386
        foreach ($this->outputters as $outputter) {
387
            if ($outputter instanceof \WebStream\Log\Outputter\ILazyWriter) {
388
                $outputter->enableLazyWrite();
389
            }
390
        }
391
    }
392
393
    /**
394
     * 即時書き出しを有効にする
395
     */
396
    public function directWrite()
397
    {
398
        foreach ($this->outputters as $outputter) {
399
            if ($outputter instanceof \WebStream\Log\Outputter\ILazyWriter) {
400
                $outputter->enableDirectWrite();
401
            }
402
        }
403
    }
404
}
405