1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Rap2hpoutre\LaravelLogViewer; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Class LaravelLogViewer |
7
|
|
|
* @package Rap2hpoutre\LaravelLogViewer |
8
|
|
|
*/ |
9
|
|
|
class LaravelLogViewer |
10
|
|
|
{ |
11
|
|
|
/** |
12
|
|
|
* @var string file |
13
|
|
|
*/ |
14
|
|
|
private $file; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* @var string folder |
18
|
|
|
*/ |
19
|
|
|
private $folder; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* @var string storage_path |
23
|
|
|
*/ |
24
|
|
|
private $storage_path; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* Why? Uh... Sorry |
28
|
|
|
*/ |
29
|
|
|
const MAX_FILE_SIZE = 52428800; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var Level level |
33
|
|
|
*/ |
34
|
|
|
private $level; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var Pattern pattern |
38
|
|
|
*/ |
39
|
|
|
private $pattern; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* LaravelLogViewer constructor. |
43
|
|
|
*/ |
44
|
|
|
public function __construct() |
45
|
|
|
{ |
46
|
|
|
$this->level = new Level(); |
47
|
|
|
$this->pattern = new Pattern(); |
48
|
|
|
$this->storage_path = function_exists('config') ? config('logviewer.storage_path', storage_path('logs')) : storage_path('logs'); |
49
|
|
|
|
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @param string $folder |
54
|
|
|
*/ |
55
|
|
|
public function setFolder($folder) |
56
|
|
|
{ |
57
|
|
|
if (app('files')->exists($folder)) { |
58
|
|
|
$this->folder = $folder; |
59
|
|
|
} |
60
|
|
|
if(is_array($this->storage_path)) { |
61
|
|
View Code Duplication |
foreach ($this->storage_path as $value) { |
|
|
|
|
62
|
|
|
$logsPath = $value . '/' . $folder; |
63
|
|
|
if (app('files')->exists($logsPath)) { |
64
|
|
|
$this->folder = $folder; |
65
|
|
|
break; |
66
|
|
|
} |
67
|
|
|
} |
68
|
|
|
} else { |
69
|
|
|
if ($this->storage_path) { |
70
|
|
|
$logsPath = $this->storage_path . '/' . $folder; |
71
|
|
|
if (app('files')->exists($logsPath)) { |
72
|
|
|
$this->folder = $folder; |
73
|
|
|
} |
74
|
|
|
} |
75
|
|
|
} |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @param string $file |
80
|
|
|
* @throws \Exception |
81
|
|
|
*/ |
82
|
|
|
public function setFile($file) |
83
|
|
|
{ |
84
|
|
|
$file = $this->pathToLogFile($file); |
85
|
|
|
|
86
|
|
|
if (app('files')->exists($file)) { |
87
|
|
|
$this->file = $file; |
88
|
|
|
} |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* @param string $file |
93
|
|
|
* @return string |
94
|
|
|
* @throws \Exception |
95
|
|
|
*/ |
96
|
|
|
public function pathToLogFile($file) |
97
|
|
|
{ |
98
|
|
|
|
99
|
|
|
if (app('files')->exists($file)) { // try the absolute path |
100
|
|
|
return $file; |
101
|
|
|
} |
102
|
|
|
if (is_array($this->storage_path)) { |
103
|
|
View Code Duplication |
foreach ($this->storage_path as $folder) { |
|
|
|
|
104
|
|
|
if (app('files')->exists($folder . '/' . $file)) { // try the absolute path |
105
|
|
|
$file = $folder . '/' . $file; |
106
|
|
|
break; |
107
|
|
|
} |
108
|
|
|
} |
109
|
|
|
return $file; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
$logsPath = $this->storage_path; |
113
|
|
|
$logsPath .= ($this->folder) ? '/' . $this->folder : ''; |
114
|
|
|
$file = $logsPath . '/' . $file; |
115
|
|
|
// check if requested file is really in the logs directory |
116
|
|
|
if (dirname($file) !== $logsPath) { |
117
|
|
|
throw new \Exception('No such log file: '.$file); |
118
|
|
|
} |
119
|
|
|
return $file; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* @return string |
124
|
|
|
*/ |
125
|
|
|
public function getFolderName() |
126
|
|
|
{ |
127
|
|
|
return $this->folder; |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* @return string |
132
|
|
|
*/ |
133
|
|
|
public function getFileName() |
134
|
|
|
{ |
135
|
|
|
return basename($this->file); |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* @return array |
140
|
|
|
*/ |
141
|
|
|
public function all() |
142
|
|
|
{ |
143
|
|
|
$log = array(); |
144
|
|
|
|
145
|
|
|
if (!$this->file) { |
146
|
|
|
$log_file = (!$this->folder) ? $this->getFiles() : $this->getFolderFiles(); |
147
|
|
|
if (!count($log_file)) { |
148
|
|
|
return []; |
149
|
|
|
} |
150
|
|
|
$this->file = $log_file[0]; |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
$max_file_size = function_exists('config') ? config('logviewer.max_file_size', self::MAX_FILE_SIZE) : self::MAX_FILE_SIZE; |
154
|
|
|
if (app('files')->size($this->file) > $max_file_size) { |
155
|
|
|
return null; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
if (!is_readable($this->file)) { |
159
|
|
|
return [[ |
160
|
|
|
'context' => '', |
161
|
|
|
'level' => '', |
162
|
|
|
'date' => null, |
163
|
|
|
'text' => 'Log file "' . $this->file . '" not readable', |
164
|
|
|
'stack' => '', |
165
|
|
|
]]; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
$file = app('files')->get($this->file); |
169
|
|
|
|
170
|
|
|
preg_match_all($this->pattern->getPattern('logs'), $file, $headings); |
171
|
|
|
|
172
|
|
|
if (!is_array($headings)) { |
173
|
|
|
return $log; |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
$log_data = preg_split($this->pattern->getPattern('logs'), $file); |
177
|
|
|
|
178
|
|
|
if ($log_data[0] < 1) { |
179
|
|
|
array_shift($log_data); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
foreach ($headings as $h) { |
183
|
|
|
for ($i = 0, $j = count($h); $i < $j; $i++) { |
184
|
|
|
foreach ($this->level->all() as $level) { |
185
|
|
|
if (strpos(strtolower($h[$i]), '.' . $level) || strpos(strtolower($h[$i]), $level . ':')) { |
186
|
|
|
|
187
|
|
|
preg_match($this->pattern->getPattern('current_log', 0) . $level . $this->pattern->getPattern('current_log', 1), $h[$i], $current); |
188
|
|
|
if (!isset($current[4])) { |
189
|
|
|
continue; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
$log[] = array( |
193
|
|
|
'context' => $current[3], |
194
|
|
|
'level' => $level, |
195
|
|
|
'folder' => $this->folder, |
196
|
|
|
'level_class' => $this->level->cssClass($level), |
197
|
|
|
'level_img' => $this->level->img($level), |
198
|
|
|
'date' => $current[1], |
199
|
|
|
'text' => $current[4], |
200
|
|
|
'in_file' => isset($current[5]) ? $current[5] : null, |
201
|
|
|
'stack' => preg_replace("/^\n*/", '', $log_data[$i]) |
202
|
|
|
); |
203
|
|
|
} |
204
|
|
|
} |
205
|
|
|
} |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
if (empty($log)) { |
209
|
|
|
|
210
|
|
|
$lines = explode(PHP_EOL, $file); |
211
|
|
|
$log = []; |
212
|
|
|
|
213
|
|
|
foreach ($lines as $key => $line) { |
214
|
|
|
$log[] = [ |
215
|
|
|
'context' => '', |
216
|
|
|
'level' => '', |
217
|
|
|
'folder' => '', |
218
|
|
|
'level_class' => '', |
219
|
|
|
'level_img' => '', |
220
|
|
|
'date' => $key + 1, |
221
|
|
|
'text' => $line, |
222
|
|
|
'in_file' => null, |
223
|
|
|
'stack' => '', |
224
|
|
|
]; |
225
|
|
|
} |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
return array_reverse($log); |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/**Creates a multidimensional array |
232
|
|
|
* of subdirectories and files |
233
|
|
|
* |
234
|
|
|
* @param null $path |
235
|
|
|
* |
236
|
|
|
* @return array |
237
|
|
|
*/ |
238
|
|
|
public function foldersAndFiles($path = null) |
239
|
|
|
{ |
240
|
|
|
$contents = array(); |
241
|
|
|
$dir = $path ? $path : $this->storage_path; |
242
|
|
|
foreach (scandir($dir) as $node) { |
243
|
|
|
if ($node == '.' || $node == '..') continue; |
244
|
|
|
$path = $dir . '\\' . $node; |
245
|
|
|
if (is_dir($path)) { |
246
|
|
|
$contents[$path] = $this->foldersAndFiles($path); |
247
|
|
|
} else { |
248
|
|
|
$contents[] = $path; |
249
|
|
|
} |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
return $contents; |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/**Returns an array of |
256
|
|
|
* all subdirectories of specified directory |
257
|
|
|
* |
258
|
|
|
* @param string $folder |
259
|
|
|
* |
260
|
|
|
* @return array |
261
|
|
|
*/ |
262
|
|
|
public function getFolders($folder = '') |
263
|
|
|
{ |
264
|
|
|
$folders = []; |
265
|
|
|
$listObject = new \RecursiveIteratorIterator( |
266
|
|
|
new \RecursiveDirectoryIterator($this->storage_path.'/'.$folder, \RecursiveDirectoryIterator::SKIP_DOTS), |
267
|
|
|
\RecursiveIteratorIterator::CHILD_FIRST |
268
|
|
|
); |
269
|
|
|
foreach ($listObject as $fileinfo) { |
270
|
|
|
if($fileinfo->isDir()) $folders[] = $fileinfo->getRealPath(); |
271
|
|
|
} |
272
|
|
|
return $folders; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
|
276
|
|
|
/** |
277
|
|
|
* @param bool $basename |
278
|
|
|
* @return array |
279
|
|
|
*/ |
280
|
|
|
public function getFolderFiles($basename = false) |
281
|
|
|
{ |
282
|
|
|
return $this->getFiles($basename, $this->folder); |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* @param bool $basename |
287
|
|
|
* @param string $folder |
288
|
|
|
* @return array |
289
|
|
|
*/ |
290
|
|
|
public function getFiles($basename = false, $folder = '') |
291
|
|
|
{ |
292
|
|
|
$files = []; |
293
|
|
|
$pattern = function_exists('config') ? config('logviewer.pattern', '*.log') : '*.log'; |
294
|
|
|
$fullPath = $this->storage_path.'/'.$folder; |
295
|
|
|
|
296
|
|
|
$listObject = new \RecursiveIteratorIterator( |
297
|
|
|
new \RecursiveDirectoryIterator($fullPath, \RecursiveDirectoryIterator::SKIP_DOTS), |
298
|
|
|
\RecursiveIteratorIterator::CHILD_FIRST |
299
|
|
|
); |
300
|
|
|
|
301
|
|
|
foreach ($listObject as $fileinfo) { |
302
|
|
|
if(!$fileinfo->isDir() && strtolower(pathinfo($fileinfo->getRealPath(), PATHINFO_EXTENSION)) == explode('.', $pattern)[1]) |
303
|
|
|
$files[] = $basename ? basename($fileinfo->getRealPath()) : $fileinfo->getRealPath(); |
304
|
|
|
} |
305
|
|
|
return $files; |
306
|
|
|
|
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
/** |
310
|
|
|
* @return string |
311
|
|
|
*/ |
312
|
|
|
public function getStoragePath() |
313
|
|
|
{ |
314
|
|
|
return $this->storage_path; |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* @param $path |
319
|
|
|
* |
320
|
|
|
* @return void |
321
|
|
|
*/ |
322
|
|
|
public function setStoragePath($path) |
323
|
|
|
{ |
324
|
|
|
$this->storage_path = $path; |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
public static function directoryTreeStructure($storage_path, array $array) |
328
|
|
|
{ |
329
|
|
|
foreach ($array as $k => $v) { |
330
|
|
|
if(is_dir( $k )) { |
331
|
|
|
|
332
|
|
|
$exploded = explode( "\\", $k ); |
333
|
|
|
$show = last( $exploded ); |
334
|
|
|
|
335
|
|
|
echo '<div class="list-group folder"> |
336
|
|
|
<a href="?f='. \Illuminate\Support\Facades\Crypt::encrypt($k).'"> |
337
|
|
|
<span> </span><span |
338
|
|
|
class="fa fa-folder"></span> '.$show.' |
339
|
|
|
</a> |
340
|
|
|
</div>'; |
341
|
|
|
|
342
|
|
|
if ( is_array( $v ) ) { |
343
|
|
|
self::directoryTreeStructure( $storage_path, $v ); |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
} |
347
|
|
|
else { |
348
|
|
|
|
349
|
|
|
$exploded = explode( "\\", $v ); |
350
|
|
|
$show2 = last( $exploded ); |
351
|
|
|
$folder = str_replace( $storage_path, "", rtrim( str_replace( $show2, "", $v ), "\\" ) ); |
352
|
|
|
$file = $v; |
353
|
|
|
|
354
|
|
|
|
355
|
|
|
echo '<div class="list-group"> |
356
|
|
|
<a href="?l='.\Illuminate\Support\Facades\Crypt::encrypt($file).'&f='.\Illuminate\Support\Facades\Crypt::encrypt($folder).'"> |
357
|
|
|
<span> </span> <span |
358
|
|
|
class="fa fa-file"></span> '.$show2.' |
359
|
|
|
</a> |
360
|
|
|
</div>'; |
361
|
|
|
|
362
|
|
|
} |
363
|
|
|
} |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
|
367
|
|
|
} |
368
|
|
|
|
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.