Reverse order and multiple log files handler
This commit is contained in:
@ -4,8 +4,16 @@ namespace ProVM\Logview;
|
||||
use DateTimeInterface;
|
||||
use DateTimeImmutable;
|
||||
|
||||
class Log
|
||||
class Log implements \ProVM\Common\Define\Log, \JsonSerializable
|
||||
{
|
||||
public function __construct(?string $original = null)
|
||||
{
|
||||
if ($original !== null) {
|
||||
$this->setOriginal($original);
|
||||
}
|
||||
}
|
||||
|
||||
protected string $original;
|
||||
protected DateTimeInterface $dateTime;
|
||||
protected string $channel;
|
||||
protected string $severity;
|
||||
@ -14,6 +22,10 @@ class Log
|
||||
protected string $context;
|
||||
protected string $extra;
|
||||
|
||||
public function getOriginal(): string
|
||||
{
|
||||
return $this->original;
|
||||
}
|
||||
public function getDate(): DateTimeInterface
|
||||
{
|
||||
return $this->dateTime ?? new DateTimeImmutable();
|
||||
@ -36,13 +48,18 @@ class Log
|
||||
}
|
||||
public function getContext(): string
|
||||
{
|
||||
return $this->context;
|
||||
return $this->context ?? '';
|
||||
}
|
||||
public function getExtra(): string
|
||||
{
|
||||
return $this->extra ?? '';
|
||||
}
|
||||
|
||||
public function setOriginal(string $original): Log
|
||||
{
|
||||
$this->original = $original;
|
||||
return $this;
|
||||
}
|
||||
public function setDate(DateTimeInterface $dateTime): Log
|
||||
{
|
||||
$this->dateTime = $dateTime;
|
||||
@ -79,13 +96,18 @@ class Log
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function parsed(): bool
|
||||
{
|
||||
return isset($this->severity);
|
||||
}
|
||||
|
||||
public function hasStack(): bool
|
||||
{
|
||||
return isset($this->stack);
|
||||
}
|
||||
public function hasContext(): bool
|
||||
{
|
||||
return $this->context !== '';
|
||||
return isset($this->context) and $this->context !== '';
|
||||
}
|
||||
|
||||
public function getColor(): string
|
||||
@ -97,32 +119,21 @@ class Log
|
||||
return self::BACKGROUNDS[strtoupper($this->getSeverity())];
|
||||
}
|
||||
|
||||
public static function parse(string $content): Log
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
$log = new Log();
|
||||
|
||||
$regex = "/\[(?P<date>.*)\]\s(?<channel>\w*)\.(?<severity>\w*):\s(?<message>.*)\s[\[|\{](?<context>.*)[\]|\}]\s\[(?<extra>.*)\]/";
|
||||
preg_match($regex, $content, $matches);
|
||||
if (isset($matches['date'])) {
|
||||
$log->setDate(DateTimeImmutable::createFromFormat('Y-m-d\TH:i:s.uP', $matches['date']));
|
||||
}
|
||||
$log->setChannel($matches['channel']);
|
||||
$log->setSeverity($matches['severity']);
|
||||
$message = $matches['message'];
|
||||
if (str_contains($message, 'Stack trace')) {
|
||||
list($msg, $data) = explode('Stack trace:', $message);
|
||||
$message = trim($msg);
|
||||
$regex = '/\s#\d+\s/';
|
||||
$lines = preg_split($regex, $data);
|
||||
array_shift($lines);
|
||||
$log->setStack($lines);
|
||||
}
|
||||
$log->setMessage($message);
|
||||
$log->setContext($matches['context']);
|
||||
if (isset($matches['extra'])) {
|
||||
$log->setExtra($matches['extra']);
|
||||
}
|
||||
return $log;
|
||||
return ($this->parsed()) ? [
|
||||
'date' => $this->getDate()->format('Y-m-d H:i:s.u'),
|
||||
'channel' => $this->getChannel(),
|
||||
'severity' => $this->getSeverity(),
|
||||
'message' => $this->getMessage(),
|
||||
'stack' => $this->hasStack() ? $this->getStack() : [],
|
||||
'context' => $this->hasContext() ? $this->getContext() : '',
|
||||
'extra' => $this->getExtra(),
|
||||
'parsed' => $this->parsed(),
|
||||
] : [
|
||||
'parsed' => $this->parsed(),
|
||||
'original' => $this->getOriginal(),
|
||||
];
|
||||
}
|
||||
|
||||
const LEVELS = [
|
||||
@ -134,16 +145,18 @@ class Log
|
||||
'CRITICAL',
|
||||
'ALERT',
|
||||
'EMERGENCY',
|
||||
'DEPRECATED',
|
||||
];
|
||||
const COLORS = [
|
||||
'DEBUG' => '#000',
|
||||
'INFO' => '#000',
|
||||
'INFO' => '#fff',
|
||||
'NOTICE' => '#fff',
|
||||
'WARNING' => '#000',
|
||||
'ERROR' => '#fff',
|
||||
'CRITICAL' => '#fff',
|
||||
'ALERT' => '#fff',
|
||||
'EMERGENCY' => '#fff',
|
||||
'DEPRECATED' => '#fff',
|
||||
];
|
||||
const BACKGROUNDS = [
|
||||
'DEBUG' => '#fff',
|
||||
@ -154,5 +167,6 @@ class Log
|
||||
'CRITICAL' => '#f00',
|
||||
'ALERT' => '#f55',
|
||||
'EMERGENCY' => '#f55',
|
||||
'DEPRECATED' => '#f50',
|
||||
];
|
||||
}
|
||||
|
@ -1,21 +1,31 @@
|
||||
<?php
|
||||
namespace ProVM\Logview\Log;
|
||||
|
||||
use Generator;
|
||||
use DateTimeInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use ProVM\Logview\Log;
|
||||
use ProVM\Common\Define\Parser;
|
||||
|
||||
class File
|
||||
{
|
||||
protected LoggerInterface $logger;
|
||||
protected Parser $parser;
|
||||
protected string $fullname;
|
||||
protected string $filename;
|
||||
protected DateTimeInterface $dateTime;
|
||||
protected string $content;
|
||||
|
||||
public function getLogger(): LoggerInterface
|
||||
{
|
||||
return $this->logger;
|
||||
}
|
||||
public function getParser(): Parser
|
||||
{
|
||||
return $this->parser;
|
||||
}
|
||||
public function getFullname(): string
|
||||
{
|
||||
return $this->fullname;
|
||||
}
|
||||
public function getFilename(): string
|
||||
{
|
||||
return $this->filename;
|
||||
@ -24,16 +34,22 @@ class File
|
||||
{
|
||||
return $this->dateTime;
|
||||
}
|
||||
public function getContent(): string
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function setLogger(LoggerInterface $logger): File
|
||||
{
|
||||
$this->logger = $logger;
|
||||
return $this;
|
||||
}
|
||||
public function setParser(Parser $parser): File
|
||||
{
|
||||
$this->parser = $parser;
|
||||
return $this;
|
||||
}
|
||||
public function setFullname(string $fullname): File
|
||||
{
|
||||
$this->fullname = $fullname;
|
||||
return $this;
|
||||
}
|
||||
public function setFilename(string $filename): File
|
||||
{
|
||||
$this->filename = $filename;
|
||||
@ -44,27 +60,43 @@ class File
|
||||
$this->dateTime = $dateTime;
|
||||
return $this;
|
||||
}
|
||||
public function setContent(string $content): File
|
||||
{
|
||||
$this->content = $content;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLogs(): array
|
||||
public function getTotal(): int
|
||||
{
|
||||
$lines = explode(PHP_EOL, $this->getContent());
|
||||
$logs = [];
|
||||
foreach ($lines as $line) {
|
||||
return $this->getParser()->total($this->getFullname());
|
||||
}
|
||||
public function getLogs(int $start = 0, int $amount = 100): Generator
|
||||
{
|
||||
$total = $this->getParser()->total($this->getFullname());
|
||||
if ($start >= $total) {
|
||||
return;
|
||||
}
|
||||
$f = $total - $start;
|
||||
$i = $f - $amount + 1;
|
||||
if ($i <= 0) {
|
||||
$i = 0;
|
||||
}
|
||||
|
||||
$cnt = 1;
|
||||
$fh = \Safe\fopen($this->getFullname(), 'r');
|
||||
while (!feof($fh)) {
|
||||
$line = fgets($fh);
|
||||
if ($cnt < $i) {
|
||||
$cnt ++;
|
||||
continue;
|
||||
}
|
||||
if (!$line) {
|
||||
continue;
|
||||
}
|
||||
if (trim($line) === '') {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$logs []= Log::parse($line);
|
||||
} catch (\Error | \Exception $e) {
|
||||
$this->getLogger()->debug($line);
|
||||
$this->getLogger()->error($e);
|
||||
yield $this->getParser()->parse(trim($line));
|
||||
$cnt ++;
|
||||
if ($cnt > $f) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return array_reverse($logs);
|
||||
\Safe\fclose($fh);
|
||||
}
|
||||
}
|
||||
|
30
app/src/Parser/Access.php
Normal file
30
app/src/Parser/Access.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace ProVM\Logview\Parser;
|
||||
|
||||
use Safe\DateTimeImmutable;
|
||||
use ProVM\Common\Define\Log;
|
||||
use ProVM\Common\Implement\Parser;
|
||||
use Safe\Exceptions\DatetimeException;
|
||||
|
||||
class Access extends Parser
|
||||
{
|
||||
public function parse(string $content): Log
|
||||
{
|
||||
$log = parent::parse($content);
|
||||
$regex = "/(?<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) - - \[(?<date>\d{2}\/\w{3}\/\d{4}:\d{2}:\d{2}:\d{2} [\+|\-]\d{4})\] (?<message>.*)/";
|
||||
preg_match($regex, $content, $matches);
|
||||
try {
|
||||
$log->setDate(DateTimeImmutable::createFromFormat('d/M/Y:H:i:s P', $matches['date']));
|
||||
} catch (DatetimeException $e) {
|
||||
$log->setDate(new DateTimeImmutable());
|
||||
$log->setExtra(json_encode([
|
||||
'date' => $matches['date']
|
||||
], JSON_UNESCAPED_SLASHES));
|
||||
}
|
||||
$log->setSeverity('Info');
|
||||
$log->setChannel('');
|
||||
$log->setMessage($matches['message']);
|
||||
$log->setContext($matches['ip']);
|
||||
return $log;
|
||||
}
|
||||
}
|
8
app/src/Parser/Basic.php
Normal file
8
app/src/Parser/Basic.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace ProVM\Logview\Parser;
|
||||
|
||||
use ProVM\Common\Implement\Parser;
|
||||
|
||||
class Basic extends Parser
|
||||
{
|
||||
}
|
8
app/src/Parser/Error.php
Normal file
8
app/src/Parser/Error.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace ProVM\Logview\Parser;
|
||||
|
||||
use ProVM\Common\Implement\Parser;
|
||||
|
||||
class Error extends Parser
|
||||
{
|
||||
}
|
81
app/src/Parser/Monolog.php
Normal file
81
app/src/Parser/Monolog.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
namespace ProVM\Logview\Parser;
|
||||
|
||||
use Safe\DateTimeImmutable;
|
||||
use ProVM\Common\Define\Log;
|
||||
use ProVM\Common\Implement\Parser;
|
||||
|
||||
class Monolog extends Parser
|
||||
{
|
||||
public function total(string $filename): int
|
||||
{
|
||||
try {
|
||||
$regex = "/\[(?P<date>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}(?:\+|-)\d{2}:\d{2})\]/";
|
||||
$fh = \Safe\fopen($filename, 'r');
|
||||
$sum = 0;
|
||||
while(!feof($fh)) {
|
||||
$line = fgets($fh);
|
||||
$sum += \Safe\preg_match_all($regex, $line);
|
||||
}
|
||||
fclose($fh);
|
||||
return $sum;
|
||||
} catch (\Exception $e) {
|
||||
\Safe\error_log($e . PHP_EOL, 3, '/logs/total.log');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
public function parse(string $content): Log
|
||||
{
|
||||
$log = parent::parse($content);
|
||||
|
||||
$regex = [
|
||||
"\[(?P<date>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}(?:\+|-)\d{2}:\d{2})\]",
|
||||
"\s(?<channel>\w*)",
|
||||
"\.(?<severity>\w*)",
|
||||
":\s(?<message>.*)",
|
||||
"\s(?:\[|\{)(?<context>.*)(?:\]|\})",
|
||||
"\s(?:\{|\[)(?<extra>.*)(?:\}|\])"
|
||||
];
|
||||
$regex = implode('', $regex);
|
||||
//$regex = "/\[(?P<date>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}(?:\+|-)\d{2}:\d{2})\]\s(?<channel>\w*)\.(?<severity>\w*):\s(?<message>.*)\s(?:\[|\{)(?<context>.*)(?:\]|\})\s(?:\{|\[)(?<extra>.*)(?:\}|\])/";
|
||||
try {
|
||||
\Safe\preg_match("/{$regex}/", $content, $matches);
|
||||
} catch (\Exception $e) {
|
||||
\Safe\error_log($content . PHP_EOL, 3, '/logs/debug.log');
|
||||
\Safe\error_log($e . PHP_EOL, 3, '/logs/debug.log');
|
||||
return $log;
|
||||
}
|
||||
|
||||
try {
|
||||
$extra = [];
|
||||
try {
|
||||
$log->setDate(DateTimeImmutable::createFromFormat('Y-m-d\TH:i:s.uP', $matches['date']));
|
||||
} catch (\Exception $e) {
|
||||
$log->setDate(new DateTimeImmutable());
|
||||
$extra['date'] = $matches['date'];
|
||||
}
|
||||
$log->setChannel($matches['channel']);
|
||||
$log->setSeverity($matches['severity']);
|
||||
$message = $matches['message'];
|
||||
if (str_contains($message, 'Stack trace')) {
|
||||
list($msg, $data) = explode('Stack trace:', $message);
|
||||
$message = trim($msg);
|
||||
$regex = '/\s#\d+\s/';
|
||||
$lines = preg_split($regex, $data);
|
||||
array_shift($lines);
|
||||
$log->setStack($lines);
|
||||
}
|
||||
$log->setMessage($message);
|
||||
$log->setContext($matches['context']);
|
||||
if (isset($matches['extra'])) {
|
||||
$extra['extra'] = "{{$matches['extra']}}";
|
||||
}
|
||||
$log->setExtra(\Safe\json_encode($extra, JSON_UNESCAPED_SLASHES));
|
||||
} catch (\Error $e) {
|
||||
\Safe\error_log($e . PHP_EOL, 3, '/logs/debug.log');
|
||||
\Safe\error_log(var_export($matches, true) . PHP_EOL, 3, '/logs/debug.log');
|
||||
}
|
||||
|
||||
return $log;
|
||||
}
|
||||
}
|
54
app/src/Parser/PHPDefault.php
Normal file
54
app/src/Parser/PHPDefault.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
namespace ProVM\Logview\Parser;
|
||||
|
||||
use Safe\DateTimeImmutable;
|
||||
use ProVM\Common\Define\Log;
|
||||
use ProVM\Common\Implement\Parser;
|
||||
|
||||
class PHPDefault extends Parser
|
||||
{
|
||||
public function total(string $filename): int
|
||||
{
|
||||
try {
|
||||
$regex = "/\[(?<date>\d{2}-\w{3}-\d{4}\s\d{2}:\d{2}:\d{2}\s\w{3})\]/";
|
||||
$fh = \Safe\fopen($filename, 'r');
|
||||
$sum = 0;
|
||||
while(!feof($fh)) {
|
||||
$line = fgets($fh);
|
||||
$sum += \Safe\preg_match_all($regex, $line);
|
||||
}
|
||||
fclose($fh);
|
||||
return $sum;
|
||||
} catch (\Exception $e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
public function parse(string $content): Log
|
||||
{
|
||||
$log = parent::parse($content);
|
||||
$regex = "/\[(?<date>\d{2}-\w{3}-\d{4}\s\d{2}:\d{2}:\d{2}\s\w{3})\]\s(?<level>PHP|User)\s(?<severity>\w+):\s(?<message>.*)/";
|
||||
try {
|
||||
\Safe\preg_match($regex, $content, $matches);
|
||||
} catch (\Error $e) {
|
||||
\Safe\error_log($e . PHP_EOL, 3, '/logs/debug.log');
|
||||
return $log;
|
||||
}
|
||||
|
||||
$extra = [];
|
||||
try {
|
||||
$log->setDate(DateTimeImmutable::createFromFormat('d-M-Y H:i:s e', $matches['date']));
|
||||
} catch (\Exception $e) {
|
||||
$log->setDate(new DateTimeImmutable());
|
||||
$extra['date'] = $matches['date'];
|
||||
}
|
||||
$log->setChannel('');
|
||||
$log->setSeverity($matches['severity']);
|
||||
$log->setMessage($matches['message']);
|
||||
$log->setContext(\Safe\json_encode(['level' => $matches['level']], JSON_UNESCAPED_SLASHES));
|
||||
if (count($extra) > 0) {
|
||||
$log->setExtra(\Safe\json_encode($extra, JSON_UNESCAPED_SLASHES));
|
||||
}
|
||||
|
||||
return $log;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user