diff --git a/src/Rap2hpoutre/LaravelLogViewer/LaravelLogViewer.php b/src/Rap2hpoutre/LaravelLogViewer/LaravelLogViewer.php index 5262a55..c842d7c 100644 --- a/src/Rap2hpoutre/LaravelLogViewer/LaravelLogViewer.php +++ b/src/Rap2hpoutre/LaravelLogViewer/LaravelLogViewer.php @@ -2,6 +2,11 @@ namespace Rap2hpoutre\LaravelLogViewer; +use Rap2hpoutre\LaravelLogViewer\Level; +use Rap2hpoutre\LaravelLogViewer\Pattern; +use Illuminate\Support\Arr; +use Illuminate\Support\Str; + /** * Class LaravelLogViewer * @package Rap2hpoutre\LaravelLogViewer @@ -138,7 +143,7 @@ public function getFileName() /** * @return array */ - public function all() + public function all($parseStack = false) { $log = array(); @@ -150,72 +155,249 @@ public function all() $this->file = $log_file[0]; } - $max_file_size = function_exists('config') ? config('logviewer.max_file_size', self::MAX_FILE_SIZE) : self::MAX_FILE_SIZE; + $max_file_size = function_exists('config') ? config('logviewer.max_file_size', LaravelLogViewer::MAX_FILE_SIZE) : LaravelLogViewer::MAX_FILE_SIZE; if (app('files')->size($this->file) > $max_file_size) { return null; } $file = app('files')->get($this->file); - preg_match_all($this->pattern->getPattern('logs'), $file, $headings); + + return $this->convertStringExceptionLogToArray($file, $parseStack); + + } + + /** + * Convert the given exception to an array. + * + * @param \Exception $e + * @return array + */ + //https://laravel.com/api/[x.x]/Illuminate/Foundation/Exceptions/Handler.html#method_convertExceptionToArray + public function convertExceptionToArray(\Exception $e) + { + return + // config('app.debug') ? + [ + 'message' => $e->getMessage(), + 'exception' => get_class($e), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'trace' => collect($e->getTrace())->map(function ($trace) { + return Arr::except($trace, ['args']); + })->all(), + // ] : [ + // 'message' => $this->isHttpException($e) ? $e->getMessage() : 'Server Error', + ]; + } + + /** + * + * @param String $e + * @return array + */ + public function convertStringExceptionLogToArray(String $e, $parseStack = false) + { + $log = array(); + + preg_match_all($this->pattern->getPattern('logs'), $e, $headings); if (!is_array($headings)) { return $log; } - $log_data = preg_split($this->pattern->getPattern('logs'), $file); + $log_data = preg_split($this->pattern->getPattern('logs'), $e); if ($log_data[0] < 1) { array_shift($log_data); } - foreach ($headings as $h) { for ($i = 0, $j = count($h); $i < $j; $i++) { foreach ($this->level->all() as $level) { if (strpos(strtolower($h[$i]), '.' . $level) || strpos(strtolower($h[$i]), $level . ':')) { - + // $h[$i] = str_replace('\\\\', '\\', $h[$i]); + $current = []; preg_match($this->pattern->getPattern('current_log', 0) . $level . $this->pattern->getPattern('current_log', 1), $h[$i], $current); if (!isset($current[4])) { - continue; + preg_match($this->pattern->getPattern('current_log', 0) . $level . $this->pattern->getPattern('current_log', 2), $h[$i], $current); + if (!$current) { + continue; + } + $log[] = array( + 'context' => $current[3], + 'level' => $level, + 'folder' => $this->folder, + 'level_class' => $this->level->cssClass($level), + 'level_img' => $this->level->img($level), + 'date' => $current[1], + 'message' => $current[4]." ".(isset($current[5]) ? $current[5] : ""). " ", + // 'exception' => null, + // 'code' => null, + // 'file' => null, + // 'line' => null, + // 'trace' => null, + ); + } else { + // $log_data[$i] = str_replace('\\\\', '\\', $log_data[$i]); + $trace = $log_data[$i]; + if ($parseStack) { + $trace = $this->convertStringExceptionStackToArray($log_data[$i]); + } + $log[] = array( + 'context' => $current[3], + 'level' => $level, + 'folder' => $this->folder, + 'level_class' => $this->level->cssClass($level), + 'level_img' => $this->level->img($level), + 'date' => $current[1], + 'message' => $current[6], + 'exception' => $current[4], + // 'code' => $current[5], + 'file' => isset($current[7]) ? $current[7] : null, + 'line' => isset($current[8]) ? intval($current[8]) : null, + 'trace' => $trace, + ); } - - $log[] = array( - 'context' => $current[3], - 'level' => $level, - 'folder' => $this->folder, - 'level_class' => $this->level->cssClass($level), - 'level_img' => $this->level->img($level), - 'date' => $current[1], - 'text' => $current[4], - 'in_file' => isset($current[5]) ? $current[5] : null, - 'stack' => preg_replace("/^\n*/", '', $log_data[$i]) - ); } } } } - if (empty($log)) { - - $lines = explode(PHP_EOL, $file); - $log = []; - - foreach ($lines as $key => $line) { - $log[] = [ - 'context' => '', - 'level' => '', - 'folder' => '', - 'level_class' => '', - 'level_img' => '', - 'date' => $key + 1, - 'text' => $line, - 'in_file' => null, - 'stack' => '', - ]; + return array_reverse($log); + } + + /** + * + * @param String $e + * @return array + */ + public function convertStringExceptionToArray(String $e) + { + $log = array(); + + $eArray = preg_split("/\n/", $e); + preg_match($this->pattern->getPattern('current_log_string'), $eArray[0], $current); + + $log['message'] = $current[2]; + $log['exception'] = $current[1]; + $log['file'] = isset($current[3]) ? $current[3] : null; + $log['line'] = isset($current[4]) ? intval($current[4]) : null; + $log['trace'] = $this->convertArrayExceptionStackStringToArray(array_slice($eArray, 2)); + + return $log; + } + + /** + * + * @param String $eStack + * @return array + */ + public function convertStringExceptionStackToArray(String $eStack) + { + $eStack = preg_replace($this->pattern->getPattern('stack_init_section'), '', $eStack); + $eStackArray = preg_split("/\\n *\#/", $eStack); + return $this->convertArrayExceptionStackStringToArray($eStackArray); + } + + /** + * + * @param array $eStackArray + * @return array + */ + public function convertArrayExceptionStackStringToArray(array $eStackArray) + { + $eStackArrayReturn = []; + + foreach ($eStackArray as $s) { + $s = preg_replace($this->pattern->getPattern('stack_startWith'), '', $s); + if (Str::startsWith($s, "{main}")) { + break; + } + if (Str::startsWith($s, "[internal function]: ")) { + $s = str_replace("[internal function]: ", "", $s); + $call = new \StdClass(); + preg_match($this->pattern->getPattern('stack', 0), $s, $matchs); + if ($matchs) { + $call->function = $matchs[3]; + $call->class = $matchs[1]; + $call->type = $matchs[2]; + // $call->args = $matchs[4]; + $eStackArrayReturn[] = $call; + } else { + preg_match($this->pattern->getPattern('stack', 1), $s, $matchs); + $call->function = $matchs[1]; + // $call->args = $matchs[2]; + $eStackArrayReturn[] = $call; + } + } else { + preg_match($this->pattern->getPattern('stack', 2), $s, $matchs); + if ($matchs) { + $call = new \StdClass(); + $call->file = $matchs[1]; + $call->line = intval($matchs[2]); + $tmp = $matchs[3]; + preg_match($this->pattern->getPattern('stack', 0), $tmp, $matchs); + if ($matchs) { + $call->function = $matchs[3]; + $call->class = $matchs[1]; + $call->type = $matchs[2]; + if (isset($matchs[4])) { + // $call->args = $matchs[4]; + } + } else { + preg_match($this->pattern->getPattern('stack', 1), $tmp, $matchs); + if ($matchs) { + $call->function = $matchs[1]; + } + if (isset($matchs[2])) { + // $call->args = $matchs[2]; + } + } + $eStackArrayReturn[] = $call; + } else { + $eStackArrayReturn[] = $s; + } } } + return $eStackArrayReturn; + } - return array_reverse($log); + /** + * + * @param array $stackArray + * @return String + */ + public static function convertArrayExceptionStacArrayToString(array $stackArray) + { + $stack = ""; + $stack .= "[stacktrace]"; + $stack .= "\n"; + foreach ($stackArray as $index=>$data) { + $dataArray = (array)$data; + $stack .= "#$index "; + if (isset($dataArray['file'])) { + $stack .= "{$dataArray['file']} ({$dataArray['line']}): "; + } + $strClass = ""; + if (isset($dataArray['class'])) { + $strClass = $dataArray['class']; + } + $strType = ""; + if (isset($dataArray['type'])) { + $strType = $dataArray['type']; + } + $strFunction = ""; + if (isset($dataArray['function'])) { + $strFunction = $dataArray['function']; + } + $strArgs = ""; + if (isset($dataArray['args'])) { + $strArgs = $dataArray['args']; + } + $stack .= "{$strClass}{$strType}{$strFunction}({$strArgs})"; + $stack .= "\n"; + } + return $stack; } /** diff --git a/src/Rap2hpoutre/LaravelLogViewer/Pattern.php b/src/Rap2hpoutre/LaravelLogViewer/Pattern.php index bba96fe..f1295b1 100644 --- a/src/Rap2hpoutre/LaravelLogViewer/Pattern.php +++ b/src/Rap2hpoutre/LaravelLogViewer/Pattern.php @@ -10,6 +10,10 @@ class Pattern { + /** + * @var \Illuminate\Foundation\Application | \Laravel\Lumen\Application + */ + private $app; /** * @var array @@ -18,11 +22,52 @@ class Pattern 'logs' => '/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}([\+-]\d{4})?\].*/', 'current_log' => [ '/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}([\+-]\d{4})?)\](?:.*?(\w+)\.|.*?)', - ': (.*?)( in .*?:[0-9]+)?$/i' + '.+ \{.*"exception":"\[object\] \(([^ ]+)\(code: (\d)\): *(.*?) at (.*?):([0-9]+)\) *\r*\n*$/i', + ': (.+) *((\{.+\}))? *\r*\n*$/i', + ], + 'current_log_string' => '/^([^ ]+): *(.*?) in (.*?):([0-9]+)$/', + 'stack_init_section' => '/^\n\[stacktrace\]\n/', + 'stack' => [ + '/^(.+)(->|::)([^\(]+)\((.*)\)$/', + '/^([^\(]+)\((.*)\)$/', + '/^(.+)\(([0-9]+)\): (.+)$/', + ], + 'stack_startWith' => '/^ ?\#? ?[0-9]+ ?/', + 'files' => '/\{.*?\,.*?\}/i', + ]; + + + /** + * @var array + */ + private $patternsLumen = [ + 'logs' => '/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}([\+-]\d{4})?\].*/', + 'current_log' => [ + '/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}([\+-]\d{4})?)\](?:.*?(\w+)\.|.*?)', + ': ([^ ]+):(\d)? *(.*?) in (.*?):([0-9]+)* *\r*\n*/i', + ': (.+) *((\{.+\}))? *\r*\n*$/i', ], + 'current_log_string' => '/^([^ ]+): *(.*?) in (.*?):([0-9]+)$/', + 'stack_init_section' => '/^\nStack trace:\n/', + 'stack' => [ + '/^(.+)(->|::)([^\(]+)\((.*)\)$/', + '/^([^\(]+)\((.*)\)$/', + '/^(.+)\(([0-9]+)\): (.+)$/', + ], + 'stack_startWith' => '/^ ?\#? ?[0-9]+ ?/', 'files' => '/\{.*?\,.*?\}/i', ]; + /** + * Pattern constructor. + */ + public function __construct() + { + if (function_exists('app')) { + $this->app = app(); + } + } + /** * @return array */ @@ -38,10 +83,37 @@ public function all() */ public function getPattern($pattern, $position = null) { + $patternVersion = $this->patterns; + if ($this->isLumen()) { + $patternVersion = $this->patternsLumen; + } if ($position !== null) { - return $this->patterns[$pattern][$position]; + return $patternVersion[$pattern][$position]; + } + return $patternVersion[$pattern]; + + } + + /** + * @return bool + */ + public function isLaravel() + { + if (is_a($this->app, '\Illuminate\Foundation\Application')) { + return true; } - return $this->patterns[$pattern]; - + return false; } + + /** + * @return bool + */ + public function isLumen() + { + if (is_a($this->app, '\Laravel\Lumen\Application')) { + return true; + } + return false; + } + } diff --git a/src/controllers/LogViewerController.php b/src/controllers/LogViewerController.php index 44e5338..873bd9d 100644 --- a/src/controllers/LogViewerController.php +++ b/src/controllers/LogViewerController.php @@ -29,7 +29,7 @@ class LogViewerController extends BaseController * @var string */ protected $view_log = 'laravel-log-viewer::log'; - + /** * LogViewerController constructor. */ @@ -58,8 +58,13 @@ public function index() return $early_return; } + $parseStack = false; + if ($this->request->wantsJson()) { + $parseStack = true; + } + $data = [ - 'logs' => $this->log_viewer->all(), + 'logs' => $this->log_viewer->all($parseStack), 'folders' => $this->log_viewer->getFolders(), 'current_folder' => $this->log_viewer->getFolderName(), 'folder_files' => $folderFiles, @@ -73,9 +78,11 @@ public function index() } if (is_array($data['logs']) && count($data['logs']) > 0) { - $firstLog = reset($data['logs']); - if (!$firstLog['context'] && !$firstLog['level']) { + foreach ($data['logs'] as $log) { $data['standardFormat'] = false; + if ($log['context'] || $log['level']) { + $data['standardFormat'] = true; + } } } diff --git a/src/views/log.blade.php b/src/views/log.blade.php index aeb8036..2327d02 100644 --- a/src/views/log.blade.php +++ b/src/views/log.blade.php @@ -132,21 +132,26 @@ class="list-group-item @if ($current_file == $file) llv-active @endif"> @endif {{{$log['date']}}} - @if ($log['stack']) + @if (isset($log['trace']) && $log['trace']) @endif - {{{$log['text']}}} - @if (isset($log['in_file'])) -
{{{$log['in_file']}}} + {{$log['message']}} + @if (isset($log['exception'])) +
{{$log['exception']}} @endif - @if ($log['stack']) - + @if (isset($log['file'])) +
{{{$log['file']}}}:{{{$log['line']}}} + @endif + @if (isset($log['trace']) && $log['trace']) + @if (is_array($log['trace'])) + + @else + + @endif @endif