mirror of
https://github.com/getgrav/grav.git
synced 2025-12-05 23:39:58 +01:00
Added LogViewer class and CLI command
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
1. [](#new)
|
1. [](#new)
|
||||||
* Added index file support for Flex Objects
|
* Added index file support for Flex Objects
|
||||||
|
* Added `LogViewer` helper class and CLI command: `bin/grav logviewer`
|
||||||
1. [](#improved)
|
1. [](#improved)
|
||||||
* Improved error detection for broken Flex Objects
|
* Improved error detection for broken Flex Objects
|
||||||
* Removed apc and xcache support, made apc alias of apcu
|
* Removed apc and xcache support, made apc alias of apcu
|
||||||
|
|||||||
1
bin/grav
1
bin/grav
@@ -45,5 +45,6 @@ $app->addCommands(array(
|
|||||||
new \Grav\Console\Cli\NewProjectCommand(),
|
new \Grav\Console\Cli\NewProjectCommand(),
|
||||||
new \Grav\Console\Cli\SchedulerCommand(),
|
new \Grav\Console\Cli\SchedulerCommand(),
|
||||||
new \Grav\Console\Cli\SecurityCommand(),
|
new \Grav\Console\Cli\SecurityCommand(),
|
||||||
|
new \Grav\Console\Cli\LogViewerCommand(),
|
||||||
));
|
));
|
||||||
$app->run();
|
$app->run();
|
||||||
|
|||||||
130
system/src/Grav/Common/Helpers/LogViewer.php
Normal file
130
system/src/Grav/Common/Helpers/LogViewer.php
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package Grav\Common\Helpers
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||||
|
* @license MIT License; see LICENSE file for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Grav\Common\Helpers;
|
||||||
|
|
||||||
|
class LogViewer
|
||||||
|
{
|
||||||
|
protected $pattern = '/\[(?P<date>.*)\] (?P<logger>\w+).(?P<level>\w+): (?P<message>.*[^ ]+) (?P<context>[^ ]+) (?P<extra>[^ ]+)/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the objects of a tailed file
|
||||||
|
*
|
||||||
|
* @param $filepath
|
||||||
|
* @param int $lines
|
||||||
|
* @param bool $desc
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function objectTail($filepath, $lines = 1, $desc = true)
|
||||||
|
{
|
||||||
|
$data = $this->tail($filepath, $lines);
|
||||||
|
$tailed_log = explode(PHP_EOL, $data);
|
||||||
|
$line_objects = [];
|
||||||
|
|
||||||
|
foreach ($tailed_log as $line) {
|
||||||
|
$line_objects[] = $this->parse($line);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $desc ? $line_objects : array_reverse($line_objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optimized way to get just the last few entries of a log file
|
||||||
|
*
|
||||||
|
* @param $filepath
|
||||||
|
* @param int $lines
|
||||||
|
* @return bool|string
|
||||||
|
*/
|
||||||
|
public function tail($filepath, $lines = 1) {
|
||||||
|
|
||||||
|
$f = @fopen($filepath, "rb");
|
||||||
|
if ($f === false) return false;
|
||||||
|
|
||||||
|
else $buffer = ($lines < 2 ? 64 : ($lines < 10 ? 512 : 4096));
|
||||||
|
|
||||||
|
fseek($f, -1, SEEK_END);
|
||||||
|
if (fread($f, 1) != "\n") $lines -= 1;
|
||||||
|
|
||||||
|
// Start reading
|
||||||
|
$output = '';
|
||||||
|
$chunk = '';
|
||||||
|
// While we would like more
|
||||||
|
while (ftell($f) > 0 && $lines >= 0) {
|
||||||
|
// Figure out how far back we should jump
|
||||||
|
$seek = min(ftell($f), $buffer);
|
||||||
|
// Do the jump (backwards, relative to where we are)
|
||||||
|
fseek($f, -$seek, SEEK_CUR);
|
||||||
|
// Read a chunk and prepend it to our output
|
||||||
|
$output = ($chunk = fread($f, $seek)) . $output;
|
||||||
|
// Jump back to where we started reading
|
||||||
|
fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR);
|
||||||
|
// Decrease our line counter
|
||||||
|
$lines -= substr_count($chunk, "\n");
|
||||||
|
}
|
||||||
|
// While we have too many lines
|
||||||
|
// (Because of buffer size we might have read too many)
|
||||||
|
while ($lines++ < 0) {
|
||||||
|
// Find first newline and remove all text before that
|
||||||
|
$output = substr($output, strpos($output, "\n") + 1);
|
||||||
|
}
|
||||||
|
// Close file and return
|
||||||
|
fclose($f);
|
||||||
|
|
||||||
|
return trim($output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function levelColor($level)
|
||||||
|
{
|
||||||
|
$colors = [
|
||||||
|
'DEBUG' => 'green',
|
||||||
|
'INFO' => 'cyan',
|
||||||
|
'NOTICE' => 'yellow',
|
||||||
|
'WARNING' => 'yellow',
|
||||||
|
'ERROR' => 'red',
|
||||||
|
'CRITICAL' => 'red',
|
||||||
|
'ALERT' => 'red',
|
||||||
|
'EMERGENCY' => 'magenta'
|
||||||
|
];
|
||||||
|
return $colors[$level] ?? 'white';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a monolog row into array bits
|
||||||
|
*
|
||||||
|
* @param $line
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function parse($line)
|
||||||
|
{
|
||||||
|
if( !is_string($line) || strlen($line) === 0) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
preg_match($this->pattern, $line, $data);
|
||||||
|
if (!isset($data['date'])) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
preg_match('/(.*)- Trace:(.*)/', $data['message'], $matches);
|
||||||
|
if (is_array($matches) && isset($matches[1])) {
|
||||||
|
$data['message'] = trim($matches[1]);
|
||||||
|
$data['trace'] = trim($matches[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'date' => \DateTime::createFromFormat('Y-m-d H:i:s', $data['date']),
|
||||||
|
'logger' => $data['logger'],
|
||||||
|
'level' => $data['level'],
|
||||||
|
'message' => $data['message'],
|
||||||
|
'trace' => $data['trace'] ?? null,
|
||||||
|
'context' => json_decode($data['context'], true),
|
||||||
|
'extra' => json_decode($data['extra'], true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
89
system/src/Grav/Console/Cli/LogViewerCommand.php
Normal file
89
system/src/Grav/Console/Cli/LogViewerCommand.php
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package Grav\Console\Cli
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||||
|
* @license MIT License; see LICENSE file for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Grav\Console\Cli;
|
||||||
|
|
||||||
|
|
||||||
|
use Grav\Common\Grav;
|
||||||
|
use Grav\Common\Helpers\LogViewer;
|
||||||
|
use Grav\Common\Utils;
|
||||||
|
use Grav\Console\ConsoleCommand;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
|
||||||
|
class LogViewerCommand extends ConsoleCommand
|
||||||
|
{
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->setName('logviewer')
|
||||||
|
->addOption(
|
||||||
|
'file',
|
||||||
|
'f',
|
||||||
|
InputOption::VALUE_OPTIONAL,
|
||||||
|
'custom log file location (default = grav.log)'
|
||||||
|
)
|
||||||
|
->addOption(
|
||||||
|
'lines',
|
||||||
|
'l',
|
||||||
|
InputOption::VALUE_OPTIONAL,
|
||||||
|
'number of lines (default = 10)'
|
||||||
|
)
|
||||||
|
->setDescription('Display the last few entries of Grav log')
|
||||||
|
->setHelp("Display the last few entries of Grav log");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int|null|void
|
||||||
|
*/
|
||||||
|
protected function serve()
|
||||||
|
{
|
||||||
|
$grav = Grav::instance();
|
||||||
|
$grav->setup();
|
||||||
|
|
||||||
|
$file = $this->input->getOption('file') ?? 'grav.log';
|
||||||
|
$lines = $this->input->getOption('lines') ?? 20;
|
||||||
|
$verbose = $this->input->getOption('verbose', false);
|
||||||
|
|
||||||
|
$io = new SymfonyStyle($this->input, $this->output);
|
||||||
|
|
||||||
|
$io->title('Log Viewer');
|
||||||
|
|
||||||
|
$io->writeln(sprintf('viewing last %s entries in <white>%s</white>', $lines, $file));
|
||||||
|
$io->newLine();
|
||||||
|
|
||||||
|
$viewer = new LogViewer();
|
||||||
|
|
||||||
|
$logfile = $grav['locator']->findResource("log://" . $file);
|
||||||
|
|
||||||
|
if ($logfile) {
|
||||||
|
$rows = $viewer->objectTail($logfile, $lines, true);
|
||||||
|
foreach ($rows as $log) {
|
||||||
|
$date = $log['date'];
|
||||||
|
$level_color = LogViewer::levelColor($log['level']);
|
||||||
|
|
||||||
|
if ($date instanceof \DateTime) {
|
||||||
|
$output = "<yellow>{$log['date']->format('Y-m-d h:i:s')}</yellow> [<{$level_color}>{$log['level']}</{$level_color}>]";
|
||||||
|
if ($log['trace'] && $verbose) {
|
||||||
|
$output .= " <white>{$log['message']}</white> - {$log['trace']}";
|
||||||
|
} else {
|
||||||
|
$output .= " {$log['message']}";
|
||||||
|
}
|
||||||
|
$io->writeln($output);
|
||||||
|
if ($verbose) {
|
||||||
|
$io->newLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$io->error('cannot find the log file: logs/' . $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user