1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Rap2hpoutre\LaravelLogViewer; |
4
|
|
|
|
5
|
|
|
use Rap2hpoutre\LaravelLogViewer\Level; |
6
|
|
|
use Rap2hpoutre\LaravelLogViewer\Pattern; |
7
|
|
|
use Illuminate\Support\Arr; |
8
|
|
|
use Illuminate\Support\Str; |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* Class LaravelLogViewer |
12
|
|
|
* @package Rap2hpoutre\LaravelLogViewer |
13
|
|
|
*/ |
14
|
|
|
class LaravelLogViewer |
15
|
|
|
{ |
16
|
|
|
/** |
17
|
|
|
* @var string file |
18
|
|
|
*/ |
19
|
|
|
private $file; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* @var string folder |
23
|
|
|
*/ |
24
|
|
|
private $folder; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @var string storage_path |
28
|
|
|
*/ |
29
|
|
|
private $storage_path; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Why? Uh... Sorry |
33
|
|
|
*/ |
34
|
|
|
const MAX_FILE_SIZE = 52428800; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var Level level |
38
|
|
|
*/ |
39
|
|
|
private $level; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @var Pattern pattern |
43
|
|
|
*/ |
44
|
|
|
private $pattern; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* LaravelLogViewer constructor. |
48
|
|
|
*/ |
49
|
|
|
public function __construct() |
50
|
|
|
{ |
51
|
|
|
$this->level = new Level(); |
52
|
|
|
$this->pattern = new Pattern(); |
53
|
|
|
$this->storage_path = function_exists('config') ? config('logviewer.storage_path', storage_path('logs')) : storage_path('logs'); |
54
|
|
|
|
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @param string $folder |
59
|
|
|
*/ |
60
|
|
|
public function setFolder($folder) |
61
|
|
|
{ |
62
|
|
|
if (app('files')->exists($folder)) { |
63
|
|
|
$this->folder = $folder; |
64
|
|
|
} |
65
|
|
|
if(is_array($this->storage_path)) { |
66
|
|
View Code Duplication |
foreach ($this->storage_path as $value) { |
|
|
|
|
67
|
|
|
$logsPath = $value . '/' . $folder; |
68
|
|
|
if (app('files')->exists($logsPath)) { |
69
|
|
|
$this->folder = $folder; |
70
|
|
|
break; |
71
|
|
|
} |
72
|
|
|
} |
73
|
|
|
} else { |
74
|
|
|
if ($this->storage_path) { |
75
|
|
|
$logsPath = $this->storage_path . '/' . $folder; |
76
|
|
|
if (app('files')->exists($logsPath)) { |
77
|
|
|
$this->folder = $folder; |
78
|
|
|
} |
79
|
|
|
} |
80
|
|
|
} |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @param string $file |
85
|
|
|
* @throws \Exception |
86
|
|
|
*/ |
87
|
|
|
public function setFile($file) |
88
|
|
|
{ |
89
|
|
|
$file = $this->pathToLogFile($file); |
90
|
|
|
|
91
|
|
|
if (app('files')->exists($file)) { |
92
|
|
|
$this->file = $file; |
93
|
|
|
} |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* @param string $file |
98
|
|
|
* @return string |
99
|
|
|
* @throws \Exception |
100
|
|
|
*/ |
101
|
|
|
public function pathToLogFile($file) |
102
|
|
|
{ |
103
|
|
|
|
104
|
|
|
if (app('files')->exists($file)) { // try the absolute path |
105
|
|
|
return $file; |
106
|
|
|
} |
107
|
|
|
if (is_array($this->storage_path)) { |
108
|
|
View Code Duplication |
foreach ($this->storage_path as $folder) { |
|
|
|
|
109
|
|
|
if (app('files')->exists($folder . '/' . $file)) { // try the absolute path |
110
|
|
|
$file = $folder . '/' . $file; |
111
|
|
|
break; |
112
|
|
|
} |
113
|
|
|
} |
114
|
|
|
return $file; |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
$logsPath = $this->storage_path; |
118
|
|
|
$logsPath .= ($this->folder) ? '/' . $this->folder : ''; |
119
|
|
|
$file = $logsPath . '/' . $file; |
120
|
|
|
// check if requested file is really in the logs directory |
121
|
|
|
if (dirname($file) !== $logsPath) { |
122
|
|
|
throw new \Exception('No such log file'); |
123
|
|
|
} |
124
|
|
|
return $file; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* @return string |
129
|
|
|
*/ |
130
|
|
|
public function getFolderName() |
131
|
|
|
{ |
132
|
|
|
return $this->folder; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* @return string |
137
|
|
|
*/ |
138
|
|
|
public function getFileName() |
139
|
|
|
{ |
140
|
|
|
return basename($this->file); |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* @return array |
145
|
|
|
*/ |
146
|
|
|
public function all($parseStack = false) |
147
|
|
|
{ |
148
|
|
|
$log = array(); |
|
|
|
|
149
|
|
|
|
150
|
|
|
if (!$this->file) { |
151
|
|
|
$log_file = (!$this->folder) ? $this->getFiles() : $this->getFolderFiles(); |
152
|
|
|
if (!count($log_file)) { |
153
|
|
|
return []; |
154
|
|
|
} |
155
|
|
|
$this->file = $log_file[0]; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
$max_file_size = function_exists('config') ? config('logviewer.max_file_size', LaravelLogViewer::MAX_FILE_SIZE) : LaravelLogViewer::MAX_FILE_SIZE; |
159
|
|
|
if (app('files')->size($this->file) > $max_file_size) { |
160
|
|
|
return null; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
$file = app('files')->get($this->file); |
164
|
|
|
|
165
|
|
|
|
166
|
|
|
return $this->convertStringExceptionLogToArray($file, $parseStack); |
167
|
|
|
|
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* Convert the given exception to an array. |
172
|
|
|
* |
173
|
|
|
* @param \Exception $e |
174
|
|
|
* @return array |
175
|
|
|
*/ |
176
|
|
|
//https://laravel.com/api/[x.x]/Illuminate/Foundation/Exceptions/Handler.html#method_convertExceptionToArray |
177
|
|
|
public function convertExceptionToArray(\Exception $e) |
178
|
|
|
{ |
179
|
|
|
return |
180
|
|
|
// config('app.debug') ? |
181
|
|
|
[ |
182
|
|
|
'message' => $e->getMessage(), |
183
|
|
|
'exception' => get_class($e), |
184
|
|
|
'file' => $e->getFile(), |
185
|
|
|
'line' => $e->getLine(), |
186
|
|
|
'trace' => collect($e->getTrace())->map(function ($trace) { |
187
|
|
|
return Arr::except($trace, ['args']); |
188
|
|
|
})->all(), |
189
|
|
|
// ] : [ |
190
|
|
|
// 'message' => $this->isHttpException($e) ? $e->getMessage() : 'Server Error', |
191
|
|
|
]; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* |
196
|
|
|
* @param String $e |
197
|
|
|
* @return array |
198
|
|
|
*/ |
199
|
|
|
public function convertStringExceptionLogToArray(String $e, $parseStack = false) |
200
|
|
|
{ |
201
|
|
|
$log = array(); |
202
|
|
|
|
203
|
|
|
preg_match_all($this->pattern->getPattern('logs'), $e, $headings); |
204
|
|
|
|
205
|
|
|
if (!is_array($headings)) { |
206
|
|
|
return $log; |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
$log_data = preg_split($this->pattern->getPattern('logs'), $e); |
210
|
|
|
|
211
|
|
|
if ($log_data[0] < 1) { |
212
|
|
|
array_shift($log_data); |
213
|
|
|
} |
214
|
|
|
foreach ($headings as $h) { |
215
|
|
|
for ($i = 0, $j = count($h); $i < $j; $i++) { |
216
|
|
|
foreach ($this->level->all() as $level) { |
217
|
|
|
if (strpos(strtolower($h[$i]), '.' . $level) || strpos(strtolower($h[$i]), $level . ':')) { |
218
|
|
|
// $h[$i] = str_replace('\\\\', '\\', $h[$i]); |
219
|
|
|
$current = []; |
220
|
|
|
preg_match($this->pattern->getPattern('current_log', 0) . $level . $this->pattern->getPattern('current_log', 1), $h[$i], $current); |
221
|
|
|
if (!isset($current[4])) { |
222
|
|
|
preg_match($this->pattern->getPattern('current_log', 0) . $level . $this->pattern->getPattern('current_log', 2), $h[$i], $current); |
223
|
|
|
if (!$current) { |
|
|
|
|
224
|
|
|
continue; |
225
|
|
|
} |
226
|
|
|
$log[] = array( |
227
|
|
|
'context' => $current[3], |
228
|
|
|
'level' => $level, |
229
|
|
|
'folder' => $this->folder, |
230
|
|
|
'level_class' => $this->level->cssClass($level), |
231
|
|
|
'level_img' => $this->level->img($level), |
232
|
|
|
'date' => $current[1], |
233
|
|
|
'message' => $current[4]." ".(isset($current[5]) ? $current[5] : ""). " ", |
234
|
|
|
// 'exception' => null, |
235
|
|
|
// 'code' => null, |
236
|
|
|
// 'file' => null, |
237
|
|
|
// 'line' => null, |
238
|
|
|
// 'trace' => null, |
239
|
|
|
); |
240
|
|
|
} else { |
241
|
|
|
// $log_data[$i] = str_replace('\\\\', '\\', $log_data[$i]); |
242
|
|
|
$trace = $log_data[$i]; |
243
|
|
|
if ($parseStack) { |
244
|
|
|
$trace = $this->convertStringExceptionStackToArray($log_data[$i]); |
245
|
|
|
} |
246
|
|
|
$log[] = array( |
247
|
|
|
'context' => $current[3], |
248
|
|
|
'level' => $level, |
249
|
|
|
'folder' => $this->folder, |
250
|
|
|
'level_class' => $this->level->cssClass($level), |
251
|
|
|
'level_img' => $this->level->img($level), |
252
|
|
|
'date' => $current[1], |
253
|
|
|
'message' => $current[6], |
254
|
|
|
'exception' => $current[4], |
255
|
|
|
// 'code' => $current[5], |
256
|
|
|
'file' => isset($current[7]) ? $current[7] : null, |
257
|
|
|
'line' => isset($current[8]) ? intval($current[8]) : null, |
258
|
|
|
'trace' => $trace, |
259
|
|
|
); |
260
|
|
|
} |
261
|
|
|
} |
262
|
|
|
} |
263
|
|
|
} |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
return array_reverse($log); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* |
271
|
|
|
* @param String $e |
272
|
|
|
* @return array |
273
|
|
|
*/ |
274
|
|
|
public function convertStringExceptionToArray(String $e) |
275
|
|
|
{ |
276
|
|
|
$log = array(); |
277
|
|
|
|
278
|
|
|
$eArray = preg_split("/\n/", $e); |
279
|
|
|
preg_match($this->pattern->getPattern('current_log_string'), $eArray[0], $current); |
280
|
|
|
|
281
|
|
|
$log['message'] = $current[2]; |
282
|
|
|
$log['exception'] = $current[1]; |
283
|
|
|
$log['file'] = isset($current[3]) ? $current[3] : null; |
284
|
|
|
$log['line'] = isset($current[4]) ? intval($current[4]) : null; |
285
|
|
|
$log['trace'] = $this->convertArrayExceptionStackStringToArray(array_slice($eArray, 2)); |
286
|
|
|
|
287
|
|
|
return $log; |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
/** |
291
|
|
|
* |
292
|
|
|
* @param String $eStack |
293
|
|
|
* @return array |
294
|
|
|
*/ |
295
|
|
|
public function convertStringExceptionStackToArray(String $eStack) |
296
|
|
|
{ |
297
|
|
|
$eStack = preg_replace($this->pattern->getPattern('stack_init_section'), '', $eStack); |
298
|
|
|
$eStackArray = preg_split("/\\n *\#/", $eStack); |
299
|
|
|
return $this->convertArrayExceptionStackStringToArray($eStackArray); |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* |
304
|
|
|
* @param array $eStackArray |
305
|
|
|
* @return array |
306
|
|
|
*/ |
307
|
|
|
public function convertArrayExceptionStackStringToArray(array $eStackArray) |
308
|
|
|
{ |
309
|
|
|
$eStackArrayReturn = []; |
310
|
|
|
|
311
|
|
|
foreach ($eStackArray as $s) { |
312
|
|
|
$s = preg_replace($this->pattern->getPattern('stack_startWith'), '', $s); |
313
|
|
|
if (Str::startsWith($s, "{main}")) { |
314
|
|
|
break; |
315
|
|
|
} |
316
|
|
|
if (Str::startsWith($s, "[internal function]: ")) { |
317
|
|
|
$s = str_replace("[internal function]: ", "", $s); |
318
|
|
|
$call = new \StdClass(); |
319
|
|
|
preg_match($this->pattern->getPattern('stack', 0), $s, $matchs); |
320
|
|
|
if ($matchs) { |
|
|
|
|
321
|
|
|
$call->function = $matchs[3]; |
322
|
|
|
$call->class = $matchs[1]; |
323
|
|
|
$call->type = $matchs[2]; |
324
|
|
|
// $call->args = $matchs[4]; |
325
|
|
|
$eStackArrayReturn[] = $call; |
326
|
|
View Code Duplication |
} else { |
|
|
|
|
327
|
|
|
preg_match($this->pattern->getPattern('stack', 1), $s, $matchs); |
328
|
|
|
$call->function = $matchs[1]; |
329
|
|
|
// $call->args = $matchs[2]; |
330
|
|
|
$eStackArrayReturn[] = $call; |
331
|
|
|
} |
332
|
|
|
} else { |
333
|
|
|
preg_match($this->pattern->getPattern('stack', 2), $s, $matchs); |
334
|
|
|
if ($matchs) { |
|
|
|
|
335
|
|
|
$call = new \StdClass(); |
336
|
|
|
$call->file = $matchs[1]; |
337
|
|
|
$call->line = intval($matchs[2]); |
338
|
|
|
$tmp = $matchs[3]; |
339
|
|
|
preg_match($this->pattern->getPattern('stack', 0), $tmp, $matchs); |
340
|
|
|
if ($matchs) { |
|
|
|
|
341
|
|
|
$call->function = $matchs[3]; |
342
|
|
|
$call->class = $matchs[1]; |
343
|
|
|
$call->type = $matchs[2]; |
344
|
|
|
if (isset($matchs[4])) { |
345
|
|
|
// $call->args = $matchs[4]; |
346
|
|
|
} |
347
|
|
View Code Duplication |
} else { |
|
|
|
|
348
|
|
|
preg_match($this->pattern->getPattern('stack', 1), $tmp, $matchs); |
349
|
|
|
if ($matchs) { |
|
|
|
|
350
|
|
|
$call->function = $matchs[1]; |
351
|
|
|
} |
352
|
|
|
if (isset($matchs[2])) { |
353
|
|
|
// $call->args = $matchs[2]; |
354
|
|
|
} |
355
|
|
|
} |
356
|
|
|
$eStackArrayReturn[] = $call; |
357
|
|
|
} else { |
358
|
|
|
$eStackArrayReturn[] = $s; |
359
|
|
|
} |
360
|
|
|
} |
361
|
|
|
} |
362
|
|
|
return $eStackArrayReturn; |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
/** |
366
|
|
|
* |
367
|
|
|
* @param array $stackArray |
368
|
|
|
* @return String |
369
|
|
|
*/ |
370
|
|
|
public static function convertArrayExceptionStacArrayToString(array $stackArray) |
371
|
|
|
{ |
372
|
|
|
$stack = ""; |
373
|
|
|
$stack .= "[stacktrace]"; |
374
|
|
|
$stack .= "\n"; |
375
|
|
|
foreach ($stackArray as $index=>$data) { |
376
|
|
|
$dataArray = (array)$data; |
377
|
|
|
$stack .= "#$index "; |
378
|
|
|
if (isset($dataArray['file'])) { |
379
|
|
|
$stack .= "{$dataArray['file']} ({$dataArray['line']}): "; |
380
|
|
|
} |
381
|
|
|
$strClass = ""; |
382
|
|
|
if (isset($dataArray['class'])) { |
383
|
|
|
$strClass = $dataArray['class']; |
384
|
|
|
} |
385
|
|
|
$strType = ""; |
386
|
|
|
if (isset($dataArray['type'])) { |
387
|
|
|
$strType = $dataArray['type']; |
388
|
|
|
} |
389
|
|
|
$strFunction = ""; |
390
|
|
|
if (isset($dataArray['function'])) { |
391
|
|
|
$strFunction = $dataArray['function']; |
392
|
|
|
} |
393
|
|
|
$strArgs = ""; |
394
|
|
|
if (isset($dataArray['args'])) { |
395
|
|
|
$strArgs = $dataArray['args']; |
396
|
|
|
} |
397
|
|
|
$stack .= "{$strClass}{$strType}{$strFunction}({$strArgs})"; |
398
|
|
|
$stack .= "\n"; |
399
|
|
|
} |
400
|
|
|
return $stack; |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
/** |
404
|
|
|
* @return array |
405
|
|
|
*/ |
406
|
|
|
public function getFolders() |
407
|
|
|
{ |
408
|
|
|
$folders = glob($this->storage_path . '/*', GLOB_ONLYDIR); |
409
|
|
|
if (is_array($this->storage_path)) { |
410
|
|
|
foreach ($this->storage_path as $value) { |
411
|
|
|
$folders = array_merge( |
412
|
|
|
$folders, |
413
|
|
|
glob($value . '/*', GLOB_ONLYDIR) |
414
|
|
|
); |
415
|
|
|
} |
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
if (is_array($folders)) { |
419
|
|
|
foreach ($folders as $k => $folder) { |
420
|
|
|
$folders[$k] = basename($folder); |
421
|
|
|
} |
422
|
|
|
} |
423
|
|
|
return array_values($folders); |
424
|
|
|
} |
425
|
|
|
|
426
|
|
|
/** |
427
|
|
|
* @param bool $basename |
428
|
|
|
* @return array |
429
|
|
|
*/ |
430
|
|
|
public function getFolderFiles($basename = false) |
431
|
|
|
{ |
432
|
|
|
return $this->getFiles($basename, $this->folder); |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
/** |
436
|
|
|
* @param bool $basename |
437
|
|
|
* @param string $folder |
438
|
|
|
* @return array |
439
|
|
|
*/ |
440
|
|
|
public function getFiles($basename = false, $folder = '') |
441
|
|
|
{ |
442
|
|
|
$pattern = function_exists('config') ? config('logviewer.pattern', '*.log') : '*.log'; |
443
|
|
|
$files = glob( |
444
|
|
|
$this->storage_path . '/' . $folder . '/' . $pattern, |
445
|
|
|
preg_match($this->pattern->getPattern('files'), $pattern) ? GLOB_BRACE : 0 |
446
|
|
|
); |
447
|
|
|
if (is_array($this->storage_path)) { |
448
|
|
|
foreach ($this->storage_path as $value) { |
449
|
|
|
$files = array_merge( |
450
|
|
|
$files, |
451
|
|
|
glob( |
452
|
|
|
$value . '/' . $folder . '/' . $pattern, |
453
|
|
|
preg_match($this->pattern->getPattern('files'), $pattern) ? GLOB_BRACE : 0 |
454
|
|
|
) |
455
|
|
|
); |
456
|
|
|
} |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
$files = array_reverse($files); |
460
|
|
|
$files = array_filter($files, 'is_file'); |
461
|
|
|
if ($basename && is_array($files)) { |
462
|
|
|
foreach ($files as $k => $file) { |
463
|
|
|
$files[$k] = basename($file); |
464
|
|
|
} |
465
|
|
|
} |
466
|
|
|
return array_values($files); |
467
|
|
|
} |
468
|
|
|
} |
469
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.