Completed
Pull Request — master (#1)
by Ryuichi
02:26
created

Logger::getConfig()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 2
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 ログ設定コンテナ
64
     */
65 141
    private function __construct(Container $logConfig)
66
    {
67 141
        $this->logConfig = $logConfig;
68 141
        $this->outputters = [];
69
70 141
        $logFile = new File($logConfig->logPath);
71 141
        $statusFile = new File($logConfig->statusPath);
72
73 141
        $this->ioContainer = new Container();
74
75
        $this->ioContainer->statusReader = function () use ($statusFile) {
76 14
            return new FileReader($statusFile);
77
        };
78
        $this->ioContainer->statusWriter = function () use ($statusFile) {
79
            return new SimpleFileWriter($statusFile->getFilePath());
80
        };
81
        $this->ioContainer->logWriter = function () use ($logFile) {
82
            return new SimpleFileWriter($logFile->getFilePath());
83
        };
84
85 141
        $this->logFile = $logFile;
86 141
        $this->statusFile = $statusFile;
87 141
    }
88
89
    /**
90
     * デストラクタ
91
     */
92 1
    public function __destruct()
93
    {
94 1
        $this->directWrite();
95 1
    }
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 ロガーインスタンス
125
     */
126 141
    public static function getInstance()
127
    {
128 141
        return self::$logger;
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 ログ設定コンテナ
144
     */
145 141
    public static function init(Container $config)
146
    {
147 141
        self::$config = $config;
148 141
        self::$logger = new Logger($config);
149 141
        self::$formatter = new LoggerFormatter($config);
150 141
    }
151
152
    /**
153
     * Loggerが初期化済みかどうかチェックする
154
     * @param bool 初期化済みならtrue
155
     */
156
    public static function isInitialized()
157
    {
158
        return self::$logger !== null;
159
    }
160
161
    /**
162
     * Loggerメソッドの呼び出しを受ける
163
     * @param string メソッド名(ログレベル文字列)
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 141
    public function setOutputter(array $outputters)
184
    {
185 141
        foreach ($outputters as $outputter) {
186 141
            if (!$outputter instanceof \WebStream\Log\Outputter\IOutputter) {
187
                throw new LoggerException("Log outputter must implement WebStream\Log\Outputter\IOutputter.");
188
            }
189
        }
190 141
        $this->outputters = $outputters;
191 141
    }
192
193
    /**
194
     * タイムスタンプを取得する
195
     * @return string タイムスタンプ
196
     */
197
    private function getTimeStamp()
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
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 ログレベル文字列
208
     * @param string 出力文字列
209
     * @param array<mixed> 埋め込み値リスト
210
     */
211 141
    public function write($level, $msg, $context = null)
212
    {
213 141
        if ($this->logConfig->logLevel > $this->toLogLevelValue($level)) {
214 44
            return;
215
        }
216
217 97
        if (is_array($context) && count($context) > 0) {
218
            // sprintfと同様の展開
219
            // [a-zA-Z0-9_-\.] 以外もキーには指定可能だが仕様としてこれ以外は不可とする
220 4
            preg_match_all('/\{\s*([a-zA-Z0-9._-]+)\s*?\}/', $msg, $matches);
221 4
            foreach ($matches[1] as $index => $value) {
222 4
                if (array_key_exists($value, $context)) {
223 4
                    $matches[1][$index] = $context[$value];
224
                } else {
225
                    unset($matches[0][$index]);
226
                }
227
            }
228 4
            $msg = str_replace($matches[0], $matches[1], $msg);
229
        }
230
231
        // ログローテート処理
232 97
        $this->rotate();
233
234
        try {
235 97
            if (count($this->outputters) > 0) {
236 97
                foreach ($this->outputters as $outputter) {
237 97
                    $outputter->write(self::$formatter->getFormattedMessage($msg, $level));
238
                }
239
            } else {
240
                $this->ioContainer->logWriter->write(self::$formatter->getFormattedMessage($msg, $level) . PHP_EOL);
241
            }
242
        } catch (IOException $e) {
0 ignored issues
show
Bug introduced by
The class WebStream\Exception\Extend\IOException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
243
            throw new LoggerException($e);
244
        }
245 97
    }
246
247
    /**
248
     * ログファイルをアーカイブする
249
     * stream.log -> stream.(作成日時)-(現在日時).log
250
     * @param string ログファイルパス
251
     */
252 97
    private function rotate()
253
    {
254
        // ログファイルがない場合はローテートしない
255 97
        if (!$this->logFile->exists()) {
256
            return;
257
        }
258
259
        // ログローテート実行
260 97
        if ($this->logConfig->rotateCycle !== null) {
261 14
            $this->rotateByCycle();
262 83
        } elseif ($this->logConfig->rotateSize !== null) {
263
            $this->rotateBySize();
264
        }
265 97
    }
266
267
    /**
268
     * ログステータスファイルに書きこむ
269
     * @throws IOException
270
     */
271
    private function writeStatus()
272
    {
273
        $this->ioContainer->statusWriter->write(intval(preg_replace('/^.*\s/', '', microtime())));
274
    }
275
276
    /**
277
     * ログステータスファイルを読み込む
278
     * @return int UnixTime
279
     * @throws LoggerException
280
     */
281 14
    private function readStatus()
282
    {
283 14
        $content = $this->ioContainer->statusReader->read();
284 14
        if (!preg_match('/^\d{10}$/', $content)) {
285
            throw new LoggerException("Invalid log state file contents: " . $content);
286
        }
287
288 14
        return intval($content);
289
    }
290
291
    /**
292
     * ステータスファイルを作成する
293
     */
294 14
    private function createStatusFile()
295
    {
296
        // ステータスファイルがない場合は書きだす
297 14
        if (!$this->statusFile->exists()) {
298
            $this->writeStatus();
299
        }
300 14
    }
301
302
    /**
303
     * ローテートを実行する
304
     * @param integer 作成日時のUnixTime
305
     * @param integer 現在日時のUnixTime
306
     */
307 8
    private function runRotate($from, $to)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $to. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
308
    {
309 8
        $fromDate = date("YmdHis", $from);
310 8
        $toDate = date("YmdHis", $to);
311 8
        $archivePath = null;
0 ignored issues
show
Unused Code introduced by
$archivePath is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
312 8
        if (preg_match('/(.*)\.(.+)/', $this->logConfig->logPath, $matches)) {
313 8
            $archivePath = "$matches[1].${fromDate}-${toDate}.$matches[2]";
314
            // mvを実行
315
            $this->logFile->renameTo($archivePath);
316
            // ステータスファイルを削除
317
            $this->statusFile->delete();
318
        }
319 8
    }
320
321
    /**
322
     * 時間単位でローテートする
323
     * stream.log -> stream.(作成日時)-(現在日時).log
324
     */
325
    private function rotateByCycle()
326
    {
327 14
        $this->createStatusFile();
328 14
        $now = intval(preg_replace('/^.*\s/', '', microtime()));
329 14
        $createdAt = $this->readStatus();
330
331 14
        $hour = intval(floor(($now - $createdAt) / 3600));
332 14
        if ($hour >= $this->logConfig->rotateCycle) {
333 8
            $this->runRotate($createdAt, $now);
334
        }
335 14
    }
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);
348
        // 指定したサイズより大きければローテート
349
        if ($sizeKb >= $this->logConfig->rotateSize) {
350
            $this->runRotate($createdAt, $now);
351
        }
352
    }
353
354
    /**
355
     * ログ出力パスを返却する
356
     * @return string ログ出力パス
357
     */
358
    public function getLogPath()
359
    {
360
        return $this->logConfig->logPath;
361
    }
362
363
    /**
364
     * ログローテートサイクルを返却する
365
     * @return string ログ出力パス
366
     */
367
    public function getLogRotateCycle()
368
    {
369
        return $this->logConfig->rotateCycle;
370
    }
371
372
    /**
373
     * ログローテートサイズを返却する
374
     * @return string ログ出力パス
375
     */
376
    public function getLogRotateSize()
377
    {
378
        return $this->logConfig->rotateSize;
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 2
        foreach ($this->outputters as $outputter) {
399 2
            if ($outputter instanceof \WebStream\Log\Outputter\ILazyWriter) {
400 2
                $outputter->enableDirectWrite();
401
            }
402
        }
403 2
    }
404
}
405