Completed
Pull Request — master (#154)
by
unknown
01:41
created

LaravelLogViewer::tryJson()   B

Complexity

Conditions 7
Paths 15

Size

Total Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 38
rs 8.3786
c 0
b 0
f 0
cc 7
nc 15
nop 1
1
<?php
2
namespace Rap2hpoutre\LaravelLogViewer;
3
4
use Psr\Log\LogLevel;
5
6
/**
7
 * Class LaravelLogViewer
8
 * @package Rap2hpoutre\LaravelLogViewer
9
 */
10
class LaravelLogViewer
11
{
12
    /**
13
     * @var string file
14
     */
15
    private static $file;
16
17
    private static $levels_classes = [
18
        'debug' => 'info',
19
        'info' => 'info',
20
        'notice' => 'info',
21
        'warning' => 'warning',
22
        'error' => 'danger',
23
        'critical' => 'danger',
24
        'alert' => 'danger',
25
        'emergency' => 'danger',
26
        'processed' => 'info',
27
        'failed' => 'warning',
28
    ];
29
30
    private static $levels_imgs = [
31
        'debug' => 'info-circle',
32
        'info' => 'info-circle',
33
        'notice' => 'info-circle',
34
        'warning' => 'exclamation-triangle',
35
        'error' => 'exclamation-triangle',
36
        'critical' => 'exclamation-triangle',
37
        'alert' => 'exclamation-triangle',
38
        'emergency' => 'exclamation-triangle',
39
        'processed' => 'info-circle',
40
        'failed' => 'exclamation-triangle'
41
    ];
42
43
    /**
44
     * Log levels that are used
45
     * @var array
46
     */
47
    private static $log_levels = [
48
        'emergency',
49
        'alert',
50
        'critical',
51
        'error',
52
        'warning',
53
        'notice',
54
        'info',
55
        'debug',
56
        'processed',
57
        'failed'
58
    ];
59
60
    const MAX_FILE_SIZE = 52428800; // Why? Uh... Sorry
61
62
    /**
63
     * @param string $file
64
     */
65
    public static function setFile($file)
66
    {
67
        $file = self::pathToLogFile($file);
68
69
        if (app('files')->exists($file)) {
70
            self::$file = $file;
71
        }
72
    }
73
74
    /**
75
     * @param string $file
76
     * @return string
77
     * @throws \Exception
78
     */
79
    public static function pathToLogFile($file)
80
    {
81
        $logsPath = storage_path('logs');
82
83
        if (app('files')->exists($file)) { // try the absolute path
84
            return $file;
85
        }
86
87
        $file = $logsPath . '/' . $file;
88
89
        // check if requested file is really in the logs directory
90
        if (dirname($file) !== $logsPath) {
91
            throw new \Exception('No such log file');
92
        }
93
94
        return $file;
95
    }
96
97
    /**
98
     * @return string
99
     */
100
    public static function getFileName()
101
    {
102
        return basename(self::$file);
103
    }
104
105
    /**
106
     * @return array
107
     */
108
    public static function all()
109
    {
110
        if (!self::$file) {
111
            $log_file = self::getFiles();
112
            if(!count($log_file)) {
113
                return [];
114
            }
115
            self::$file = $log_file[0];
116
        }
117
118
        if (app('files')->size(self::$file) > self::MAX_FILE_SIZE) return null;
119
120
        $file = app('files')->get(self::$file);
121
122
        $log = static::tryJson($file);
0 ignored issues
show
Bug introduced by
Since tryJson() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of tryJson() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
123
        if(!empty($log)) return $log;
124
        $log = static::tryLine($file);
0 ignored issues
show
Bug introduced by
Since tryLine() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of tryLine() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
125
        if(!empty($log)) return $log;
126
        return static::tryDefault($file);
0 ignored issues
show
Bug introduced by
Since tryDefault() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of tryDefault() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
127
    }
128
129
    private static function tryLine(&$file)
130
    {
131
        $log = [];
132
        $pattern = '/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}([\+-]\d{4})?\].*/';
133
134
        preg_match_all($pattern, $file, $headings);
135
136
        if (!is_array($headings)) {
137
            return $log;
138
        }
139
140
        $log_data = preg_split($pattern, $file);
141
142
        if ($log_data[0] < 1) {
143
            array_shift($log_data);
144
        }
145
146
        foreach ($headings as $h) {
147
            for ($i=0, $j = count($h); $i < $j; $i++) {
148
                foreach (self::$log_levels as $level) {
149
                    if (strpos(strtolower($h[$i]), '.' . $level) || strpos(strtolower($h[$i]), $level . ':')) {
150
151
                        preg_match('/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}([\+-]\d{4})?)\](?:.*?(\w+)\.|.*?)' . $level . ': (.*?)( in .*?:[0-9]+)?$/i', $h[$i], $current);
152
                        if (!isset($current[4])) continue;
153
154
                        $log[] = array(
155
                            'channel' => $current[3],
156
                            'level' => $level,
157
                            'level_class' => self::$levels_classes[$level],
158
                            'level_img' => self::$levels_imgs[$level],
159
                            'date' => $current[1],
160
                            'text' => $current[4],
161
                            'in_file' => isset($current[5]) ? $current[5] : null,
162
                            'stack' => preg_replace("/^\n*/", '', $log_data[$i])
163
                        );
164
                    }
165
                }
166
            }
167
        }
168
169
        return array_reverse($log);
170
    }
171
    
172
    private static function tryJson(&$file)
173
    {
174
        try {
175
            $log = [];
176
            $lines = explode("\n", $file);
177
178
            if(json_decode($lines[0], true) === null) {
179
                return $log;
180
            }
181
182
            foreach($lines as $line) {
183
                if($line == '') continue;
184
                $decoded = json_decode($line, true);
185
                $level = strtolower($decoded['level_name']);
186
                $stack = null;
187
                $extra = array_merge($decoded['context'] ?? [], $decoded['extra'] ?? []);
188
                if(isset($decoded['context']) && isset($decoded['context']['exception'])) {
189
                    $stack = $decoded['context']['exception']['trace'];
190
                    unset($extra['exception']);
191
                }
192
                $log[] = [
193
                    'channel' => $decoded['channel'],
194
                    'level' => $level,
195
                    'level_class' => self::$levels_classes[$level],
196
                    'level_img' => self::$levels_imgs[$level],
197
                    'date' => $decoded['datetime']['date'],
198
                    'text' => $decoded['message'],
199
                    'in_file' => null,
200
                    'stack' => $stack,
201
                    'extra' => json_encode($extra, JSON_PRETTY_PRINT)
202
                ];
203
            }
204
205
            return array_reverse($log);
206
        } catch (\Exception $e) {
207
            return [];
208
        }
209
    }
210
211
    private static function tryDefault(&$file)
212
    {
213
        $lines = explode(PHP_EOL, $file);
214
        $log = [];
215
216
        foreach($lines as $key => $line) {
217
            $log[] = [
218
                'channel' => '',
219
                'level' => '',
220
                'level_class' => '',
221
                'level_img' => '',
222
                'date' => $key+1,
223
                'text' => $line,
224
                'in_file' => null,
225
                'stack' => '',
226
            ];
227
        }
228
229
        return array_reverse($log);
230
    }
231
232
    /**
233
     * @param bool $basename
234
     * @return array
235
     */
236
    public static function getFiles($basename = false)
237
    {
238
        $pattern = function_exists('config') ? config('logviewer.pattern', '*.log') : '*.log';
239
        $files = glob(storage_path() . '/logs/' . $pattern, preg_match('/\{.*?\,.*?\}/i', $pattern) ? GLOB_BRACE : 0);
240
        $files = array_reverse($files);
241
        $files = array_filter($files, 'is_file');
242
        if ($basename && is_array($files)) {
243
            foreach ($files as $k => $file) {
244
                $files[$k] = basename($file);
245
            }
246
        }
247
        return array_values($files);
248
    }
249
}
250