Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <fabien@symfony.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
 
12
namespace Symfony\Component\HttpKernel\DataCollector;
13
 
14
use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext;
15
use Symfony\Component\HttpFoundation\Request;
16
use Symfony\Component\HttpFoundation\RequestStack;
17
use Symfony\Component\HttpFoundation\Response;
18
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
19
 
20
/**
21
 * @author Fabien Potencier <fabien@symfony.com>
22
 *
23
 * @final
24
 */
25
class LoggerDataCollector extends DataCollector implements LateDataCollectorInterface
26
{
27
    private DebugLoggerInterface $logger;
28
    private ?string $containerPathPrefix;
29
    private ?Request $currentRequest = null;
30
    private ?RequestStack $requestStack;
31
    private ?array $processedLogs = null;
32
 
33
    public function __construct(object $logger = null, string $containerPathPrefix = null, RequestStack $requestStack = null)
34
    {
35
        if ($logger instanceof DebugLoggerInterface) {
36
            $this->logger = $logger;
37
        }
38
 
39
        $this->containerPathPrefix = $containerPathPrefix;
40
        $this->requestStack = $requestStack;
41
    }
42
 
43
    public function collect(Request $request, Response $response, \Throwable $exception = null)
44
    {
45
        $this->currentRequest = $this->requestStack && $this->requestStack->getMainRequest() !== $request ? $request : null;
46
    }
47
 
48
    public function reset()
49
    {
50
        if (isset($this->logger)) {
51
            $this->logger->clear();
52
        }
53
        $this->data = [];
54
    }
55
 
56
    public function lateCollect()
57
    {
58
        if (isset($this->logger)) {
59
            $containerDeprecationLogs = $this->getContainerDeprecationLogs();
60
            $this->data = $this->computeErrorsCount($containerDeprecationLogs);
61
            // get compiler logs later (only when they are needed) to improve performance
62
            $this->data['compiler_logs'] = [];
63
            $this->data['compiler_logs_filepath'] = $this->containerPathPrefix.'Compiler.log';
64
            $this->data['logs'] = $this->sanitizeLogs(array_merge($this->logger->getLogs($this->currentRequest), $containerDeprecationLogs));
65
            $this->data = $this->cloneVar($this->data);
66
        }
67
        $this->currentRequest = null;
68
    }
69
 
70
    public function getLogs()
71
    {
72
        return $this->data['logs'] ?? [];
73
    }
74
 
75
    public function getProcessedLogs()
76
    {
77
        if (null !== $this->processedLogs) {
78
            return $this->processedLogs;
79
        }
80
 
81
        $rawLogs = $this->getLogs();
82
        if ([] === $rawLogs) {
83
            return $this->processedLogs = $rawLogs;
84
        }
85
 
86
        $logs = [];
87
        foreach ($this->getLogs()->getValue() as $rawLog) {
88
            $rawLogData = $rawLog->getValue();
89
 
90
            if ($rawLogData['priority']->getValue() > 300) {
91
                $logType = 'error';
92
            } elseif (isset($rawLogData['scream']) && false === $rawLogData['scream']->getValue()) {
93
                $logType = 'deprecation';
94
            } elseif (isset($rawLogData['scream']) && true === $rawLogData['scream']->getValue()) {
95
                $logType = 'silenced';
96
            } else {
97
                $logType = 'regular';
98
            }
99
 
100
            $logs[] = [
101
                'type' => $logType,
102
                'errorCount' => $rawLog['errorCount'] ?? 1,
103
                'timestamp' => $rawLogData['timestamp_rfc3339']->getValue(),
104
                'priority' => $rawLogData['priority']->getValue(),
105
                'priorityName' => $rawLogData['priorityName']->getValue(),
106
                'channel' => $rawLogData['channel']->getValue(),
107
                'message' => $rawLogData['message'],
108
                'context' => $rawLogData['context'],
109
            ];
110
        }
111
 
112
        // sort logs from oldest to newest
113
        usort($logs, static function ($logA, $logB) {
114
            return $logA['timestamp'] <=> $logB['timestamp'];
115
        });
116
 
117
        return $this->processedLogs = $logs;
118
    }
119
 
120
    public function getFilters()
121
    {
122
        $filters = [
123
            'channel' => [],
124
            'priority' => [
125
                'Debug' => 100,
126
                'Info' => 200,
127
                'Notice' => 250,
128
                'Warning' => 300,
129
                'Error' => 400,
130
                'Critical' => 500,
131
                'Alert' => 550,
132
                'Emergency' => 600,
133
            ],
134
        ];
135
 
136
        $allChannels = [];
137
        foreach ($this->getProcessedLogs() as $log) {
138
            if ('' === trim($log['channel'] ?? '')) {
139
                continue;
140
            }
141
 
142
            $allChannels[] = $log['channel'];
143
        }
144
        $channels = array_unique($allChannels);
145
        sort($channels);
146
        $filters['channel'] = $channels;
147
 
148
        return $filters;
149
    }
150
 
151
    public function getPriorities()
152
    {
153
        return $this->data['priorities'] ?? [];
154
    }
155
 
156
    public function countErrors()
157
    {
158
        return $this->data['error_count'] ?? 0;
159
    }
160
 
161
    public function countDeprecations()
162
    {
163
        return $this->data['deprecation_count'] ?? 0;
164
    }
165
 
166
    public function countWarnings()
167
    {
168
        return $this->data['warning_count'] ?? 0;
169
    }
170
 
171
    public function countScreams()
172
    {
173
        return $this->data['scream_count'] ?? 0;
174
    }
175
 
176
    public function getCompilerLogs()
177
    {
178
        return $this->cloneVar($this->getContainerCompilerLogs($this->data['compiler_logs_filepath'] ?? null));
179
    }
180
 
181
    public function getName(): string
182
    {
183
        return 'logger';
184
    }
185
 
186
    private function getContainerDeprecationLogs(): array
187
    {
188
        if (null === $this->containerPathPrefix || !is_file($file = $this->containerPathPrefix.'Deprecations.log')) {
189
            return [];
190
        }
191
 
192
        if ('' === $logContent = trim(file_get_contents($file))) {
193
            return [];
194
        }
195
 
196
        $bootTime = filemtime($file);
197
        $logs = [];
198
        foreach (unserialize($logContent) as $log) {
199
            $log['context'] = ['exception' => new SilencedErrorContext($log['type'], $log['file'], $log['line'], $log['trace'], $log['count'])];
200
            $log['timestamp'] = $bootTime;
201
            $log['timestamp_rfc3339'] = (new \DateTimeImmutable())->setTimestamp($bootTime)->format(\DateTimeInterface::RFC3339_EXTENDED);
202
            $log['priority'] = 100;
203
            $log['priorityName'] = 'DEBUG';
204
            $log['channel'] = null;
205
            $log['scream'] = false;
206
            unset($log['type'], $log['file'], $log['line'], $log['trace'], $log['trace'], $log['count']);
207
            $logs[] = $log;
208
        }
209
 
210
        return $logs;
211
    }
212
 
213
    private function getContainerCompilerLogs(string $compilerLogsFilepath = null): array
214
    {
215
        if (!is_file($compilerLogsFilepath)) {
216
            return [];
217
        }
218
 
219
        $logs = [];
220
        foreach (file($compilerLogsFilepath, \FILE_IGNORE_NEW_LINES) as $log) {
221
            $log = explode(': ', $log, 2);
222
            if (!isset($log[1]) || !preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)++$/', $log[0])) {
223
                $log = ['Unknown Compiler Pass', implode(': ', $log)];
224
            }
225
 
226
            $logs[$log[0]][] = ['message' => $log[1]];
227
        }
228
 
229
        return $logs;
230
    }
231
 
232
    private function sanitizeLogs(array $logs)
233
    {
234
        $sanitizedLogs = [];
235
        $silencedLogs = [];
236
 
237
        foreach ($logs as $log) {
238
            if (!$this->isSilencedOrDeprecationErrorLog($log)) {
239
                $sanitizedLogs[] = $log;
240
 
241
                continue;
242
            }
243
 
244
            $message = '_'.$log['message'];
245
            $exception = $log['context']['exception'];
246
 
247
            if ($exception instanceof SilencedErrorContext) {
248
                if (isset($silencedLogs[$h = spl_object_hash($exception)])) {
249
                    continue;
250
                }
251
                $silencedLogs[$h] = true;
252
 
253
                if (!isset($sanitizedLogs[$message])) {
254
                    $sanitizedLogs[$message] = $log + [
255
                        'errorCount' => 0,
256
                        'scream' => true,
257
                    ];
258
                }
259
                $sanitizedLogs[$message]['errorCount'] += $exception->count;
260
 
261
                continue;
262
            }
263
 
264
            $errorId = md5("{$exception->getSeverity()}/{$exception->getLine()}/{$exception->getFile()}\0{$message}", true);
265
 
266
            if (isset($sanitizedLogs[$errorId])) {
267
                ++$sanitizedLogs[$errorId]['errorCount'];
268
            } else {
269
                $log += [
270
                    'errorCount' => 1,
271
                    'scream' => false,
272
                ];
273
 
274
                $sanitizedLogs[$errorId] = $log;
275
            }
276
        }
277
 
278
        return array_values($sanitizedLogs);
279
    }
280
 
281
    private function isSilencedOrDeprecationErrorLog(array $log): bool
282
    {
283
        if (!isset($log['context']['exception'])) {
284
            return false;
285
        }
286
 
287
        $exception = $log['context']['exception'];
288
 
289
        if ($exception instanceof SilencedErrorContext) {
290
            return true;
291
        }
292
 
293
        if ($exception instanceof \ErrorException && \in_array($exception->getSeverity(), [\E_DEPRECATED, \E_USER_DEPRECATED], true)) {
294
            return true;
295
        }
296
 
297
        return false;
298
    }
299
 
300
    private function computeErrorsCount(array $containerDeprecationLogs): array
301
    {
302
        $silencedLogs = [];
303
        $count = [
304
            'error_count' => $this->logger->countErrors($this->currentRequest),
305
            'deprecation_count' => 0,
306
            'warning_count' => 0,
307
            'scream_count' => 0,
308
            'priorities' => [],
309
        ];
310
 
311
        foreach ($this->logger->getLogs($this->currentRequest) as $log) {
312
            if (isset($count['priorities'][$log['priority']])) {
313
                ++$count['priorities'][$log['priority']]['count'];
314
            } else {
315
                $count['priorities'][$log['priority']] = [
316
                    'count' => 1,
317
                    'name' => $log['priorityName'],
318
                ];
319
            }
320
            if ('WARNING' === $log['priorityName']) {
321
                ++$count['warning_count'];
322
            }
323
 
324
            if ($this->isSilencedOrDeprecationErrorLog($log)) {
325
                $exception = $log['context']['exception'];
326
                if ($exception instanceof SilencedErrorContext) {
327
                    if (isset($silencedLogs[$h = spl_object_hash($exception)])) {
328
                        continue;
329
                    }
330
                    $silencedLogs[$h] = true;
331
                    $count['scream_count'] += $exception->count;
332
                } else {
333
                    ++$count['deprecation_count'];
334
                }
335
            }
336
        }
337
 
338
        foreach ($containerDeprecationLogs as $deprecationLog) {
339
            $count['deprecation_count'] += $deprecationLog['context']['exception']->count;
340
        }
341
 
342
        ksort($count['priorities']);
343
 
344
        return $count;
345
    }
346
}