From eec4a8b3c846a9f08944a1d8a8329cefbc19c4f2 Mon Sep 17 00:00:00 2001 From: Yogesh Sharma Date: Tue, 14 May 2024 12:13:43 +0530 Subject: [PATCH 1/6] Added Support For JsonLogFormatter --- .github/workflows/run-tests.yml | 2 +- composer.json | 12 +- config/log-viewer.php | 9 + src/Contracts/LogEntryInterface.php | 22 +++ src/Entities/JsonLogEntry.php | 291 ++++++++++++++++++++++++++++ src/Entities/LogEntry.php | 3 +- src/Entities/LogEntryCollection.php | 35 +++- 7 files changed, 359 insertions(+), 15 deletions(-) create mode 100644 src/Contracts/LogEntryInterface.php create mode 100644 src/Entities/JsonLogEntry.php diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index b729062b..cc5f91d6 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: true matrix: - php: [8.2, 8.3] + php: [7.4,8.0,8.2,8.3] dependency-version: [prefer-lowest, prefer-stable] name: PHP ${{ matrix.php }} - ${{ matrix.dependency-version }} diff --git a/composer.json b/composer.json index 0c23d399..7cb60fed 100644 --- a/composer.json +++ b/composer.json @@ -14,16 +14,16 @@ "type": "library", "license": "MIT", "require": { - "php": "^8.2", + "php": ">=7.3", "ext-json": "*", - "arcanedev/support": "^11.0", + "arcanedev/support": ">=8.0", "psr/log": "^1.0|^2.0|^3.0" }, "require-dev": { - "laravel/framework": "^11.0", - "mockery/mockery": "^1.6", - "orchestra/testbench-core": "^9.0", - "phpunit/phpunit": "^10.5" + "laravel/framework": ">=8.0", + "mockery/mockery": ">=1.4.2", + "orchestra/testbench-core": ">=6.27", + "phpunit/phpunit": ">=9.5.10" }, "autoload": { "psr-4": { diff --git a/config/log-viewer.php b/config/log-viewer.php index 18ced1d8..e4222f55 100644 --- a/config/log-viewer.php +++ b/config/log-viewer.php @@ -70,6 +70,15 @@ 'per-page' => 30, + /* ----------------------------------------------------------------- + | Type of log formatter used + | ----------------------------------------------------------------- + | Supported formatters : + | 'LineFormatter', 'JsonFormatter' + */ + + 'formatter' => 'default', + /* ----------------------------------------------------------------- | Download settings | ----------------------------------------------------------------- diff --git a/src/Contracts/LogEntryInterface.php b/src/Contracts/LogEntryInterface.php new file mode 100644 index 00000000..06b8e9f7 --- /dev/null +++ b/src/Contracts/LogEntryInterface.php @@ -0,0 +1,22 @@ + + */ +interface LogEntryInterface { + + /** + * Check if same log level. + * + * @param string $level + * + * @return bool + */ + public function isSameLevel($level); +} \ No newline at end of file diff --git a/src/Entities/JsonLogEntry.php b/src/Entities/JsonLogEntry.php new file mode 100644 index 00000000..6f8bb1e1 --- /dev/null +++ b/src/Entities/JsonLogEntry.php @@ -0,0 +1,291 @@ + + */ +class JsonLogEntry implements Arrayable, Jsonable, JsonSerializable, LogEntryInterface +{ + /* ----------------------------------------------------------------- + | Properties + | ----------------------------------------------------------------- + */ + + /** @var string */ + public $env; + + /** @var string */ + public $level; + + /** @var \Carbon\Carbon */ + public $datetime; + + /** @var string */ + public $header; + + /** @var string */ + public $stack; + + /** @var array */ + public $context = []; + + /* ----------------------------------------------------------------- + | Constructor + | ----------------------------------------------------------------- + */ + + /** + * Construct the log entry instance. + * + * @param string $level + * @param string $header + * @param string|null $stack + */ + public function __construct($data) + { + $this->setLevel($data); + $this->setHeader($data); + $this->setStack($data); + } + + /* ----------------------------------------------------------------- + | Getters & Setters + | ----------------------------------------------------------------- + */ + + /** + * Set the entry level. + * + * @param string $level + * + * @return self + */ + private function setLevel($data) + { + $this->level = strtolower($data['level_name']); + + return $this; + } + + /** + * Set the entry header. + * + * @param string $header + * + * @return self + */ + private function setHeader($data) + { + $this->setDatetime($data['datetime']); + + $header = $data['message']; + + $this->header = trim($header); + + $this->setContext($data['context']); + + $this->setEnv($data['channel']); + + return $this; + } + + /** + * Set the context. + * + * @param array $context + * + * @return $this + */ + private function setContext(array $context) + { + $this->context = $context; + + return $this; + } + + /** + * Set entry environment. + * + * @param string $env + * + * @return self + */ + private function setEnv($env) + { + $this->env = head(explode('.', $env)); + + return $this; + } + + /** + * Set the entry date time. + * + * @param string $datetime + * + * @return \Arcanedev\LogViewer\Entities\LogEntry + */ + private function setDatetime($datetime) + { + $this->datetime = Carbon::createFromFormat('Y-m-d H:i:s', date('Y-m-d H:i:s',strtotime($datetime))); + + return $this; + } + + /** + * Set the entry stack. + * + * @param string $stack + * + * @return self + */ + private function setStack($data) + { + $this->stack = isset($data['stack']) ? json_encode($data['stack']): ""; + + return $this; + } + + /** + * Get translated level name with icon. + * + * @return string + */ + public function level() + { + return $this->icon()->toHtml().' '.$this->name(); + } + + /** + * Get translated level name. + * + * @return string + */ + public function name() + { + return log_levels()->get($this->level); + } + + /** + * Get level icon. + * + * @return \Illuminate\Support\HtmlString + */ + public function icon() + { + return log_styler()->icon($this->level); + } + + /** + * Get the entry stack. + * + * @return string + */ + public function stack() + { + return trim(htmlentities($this->stack)); + } + + /** + * Get the entry context as json pretty print. + * + * @return string + */ + public function context() + { + return json_encode($this->context, JSON_PRETTY_PRINT); + } + + /* ----------------------------------------------------------------- + | Check Methods + | ----------------------------------------------------------------- + */ + + /** + * Check if same log level. + * + * @param string $level + * + * @return bool + */ + public function isSameLevel($level) + { + return $this->level === $level; + } + + /* ----------------------------------------------------------------- + | Convert Methods + | ----------------------------------------------------------------- + */ + + /** + * Get the log entry as an array. + * + * @return array + */ + public function toArray() + { + return [ + 'level' => $this->level, + 'datetime' => $this->datetime->format('Y-m-d H:i:s'), + 'header' => $this->header, + 'stack' => $this->stack + ]; + } + + /** + * Convert the log entry to its JSON representation. + * + * @param int $options + * + * @return string + */ + public function toJson($options = 0) + { + return json_encode($this->toArray(), $options); + } + + /** + * Serialize the log entry object to json data. + * + * @return array + */ + public function jsonSerialize(): array + { + return $this->toArray(); + } + + /* ----------------------------------------------------------------- + | Check Methods + | ----------------------------------------------------------------- + */ + + /** + * Check if the entry has a stack. + * + * @return bool + */ + public function hasStack() + { + return $this->stack !== "\n"; + } + + /** + * Check if the entry has a context. + * + * @return bool + */ + public function hasContext() + { + return ! empty($this->context); + } +} diff --git a/src/Entities/LogEntry.php b/src/Entities/LogEntry.php index 9cb58c77..d09b4416 100644 --- a/src/Entities/LogEntry.php +++ b/src/Entities/LogEntry.php @@ -4,6 +4,7 @@ namespace Arcanedev\LogViewer\Entities; +use Arcanedev\LogViewer\Contracts\LogEntryInterface; use Arcanedev\LogViewer\Helpers\LogParser; use Carbon\Carbon; use Illuminate\Contracts\Support\{Arrayable, Jsonable}; @@ -14,7 +15,7 @@ * * @author ARCANEDEV */ -class LogEntry implements Arrayable, Jsonable, JsonSerializable +class LogEntry implements Arrayable, Jsonable, JsonSerializable,LogEntryInterface { /* ----------------------------------------------------------------- | Properties diff --git a/src/Entities/LogEntryCollection.php b/src/Entities/LogEntryCollection.php index 4c534609..69b3e304 100644 --- a/src/Entities/LogEntryCollection.php +++ b/src/Entities/LogEntryCollection.php @@ -4,6 +4,7 @@ namespace Arcanedev\LogViewer\Entities; +use Arcanedev\LogViewer\Contracts\LogEntryInterface; use Arcanedev\LogViewer\Helpers\LogParser; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\LazyCollection; @@ -29,13 +30,33 @@ class LogEntryCollection extends LazyCollection */ public static function load($raw) { - return new static(function () use ($raw) { - foreach (LogParser::parse($raw) as $entry) { - list($level, $header, $stack) = array_values($entry); + $formatter = config('log-viewer.formatter', 'default'); - yield new LogEntry($level, $header, $stack); - } - }); + if ( $formatter != 'default' ) + { + return new static(function () use ($raw ) { + foreach (LogParser::parse($raw) as $entry) { + list($level, $header, $stack) = array_values($entry); + + yield new LogEntry($level, $header, $stack); + } + }); + + } else { + $rows = explode("\n",$raw); + return new static(function () use ($rows ) { + foreach( $rows as $row) { + + $entry = json_decode($row,true); + + if( $entry ) { + yield new JsonLogEntry($entry); + } + + } + + }); + } } /** @@ -68,7 +89,7 @@ public function paginate($perPage = 20) */ public function filterByLevel($level) { - return $this->filter(function(LogEntry $entry) use ($level) { + return $this->filter(function(LogEntryInterface $entry) use ($level) { return $entry->isSameLevel($level); }); } From 484a32b7e7cb7eef58c88e81651137edf924ca02 Mon Sep 17 00:00:00 2001 From: Yogesh Sharma Date: Tue, 14 May 2024 12:19:53 +0530 Subject: [PATCH 2/6] modified composer.json --- composer.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 7cb60fed..2dd86180 100644 --- a/composer.json +++ b/composer.json @@ -1,19 +1,19 @@ { - "name": "arcanedev/log-viewer", + "name": "iyogesharma/log-viewer", "description": "Provides a Log Viewer for Laravel", - "keywords": ["arcanedev", "arcanesoft", "laravel", "log", "log viewer", "log-viewer", "logviewer"], - "homepage": "https://github.com/ARCANEDEV/LogViewer", + "keywords": ["iyogesharma", "laravel", "log", "log viewer", "log-viewer", "logviewer"], + "homepage": "https://github.com/iYogesharma/LogViewer", "authors": [ { - "name": "ARCANEDEV", - "email": "arcanedev.maroc@gmail.com", - "homepage": "https://github.com/arcanedev-maroc", + "name": "Yogesh Sharma", + "email": "iyogesharma@gmail.com", + "homepage": "https://github.com/iYogesharma", "role": "Developer" } ], "type": "library", "license": "MIT", - "require": { + "require": { "php": ">=7.3", "ext-json": "*", "arcanedev/support": ">=8.0", From 51949c989f3795ad12a48a90230cea4fcc10234f Mon Sep 17 00:00:00 2001 From: Yogesh Sharma Date: Tue, 14 May 2024 12:54:30 +0530 Subject: [PATCH 3/6] typo --- src/Entities/LogEntryCollection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entities/LogEntryCollection.php b/src/Entities/LogEntryCollection.php index 69b3e304..c59d5fe6 100644 --- a/src/Entities/LogEntryCollection.php +++ b/src/Entities/LogEntryCollection.php @@ -32,7 +32,7 @@ public static function load($raw) { $formatter = config('log-viewer.formatter', 'default'); - if ( $formatter != 'default' ) + if ( $formatter == 'default' ) { return new static(function () use ($raw ) { foreach (LogParser::parse($raw) as $entry) { From 7b4f7d60659552b2c941c2e9e267e70751a70680 Mon Sep 17 00:00:00 2001 From: Yogesh Sharma Date: Tue, 14 May 2024 12:58:57 +0530 Subject: [PATCH 4/6] updated composer.json --- composer.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 2dd86180..59f91249 100644 --- a/composer.json +++ b/composer.json @@ -1,16 +1,16 @@ { - "name": "iyogesharma/log-viewer", + "name": "arcanedev/log-viewer", "description": "Provides a Log Viewer for Laravel", - "keywords": ["iyogesharma", "laravel", "log", "log viewer", "log-viewer", "logviewer"], - "homepage": "https://github.com/iYogesharma/LogViewer", + "keywords": ["arcanedev", "arcanesoft", "laravel", "log", "log viewer", "log-viewer", "logviewer"], + "homepage": "https://github.com/ARCANEDEV/LogViewer", "authors": [ { - "name": "Yogesh Sharma", - "email": "iyogesharma@gmail.com", - "homepage": "https://github.com/iYogesharma", + "name": "ARCANEDEV", + "email": "arcanedev.maroc@gmail.com", + "homepage": "https://github.com/arcanedev-maroc", "role": "Developer" - } - ], + } + ], "type": "library", "license": "MIT", "require": { From 75f3349a48f52de447749753c9c2d85fd409e48c Mon Sep 17 00:00:00 2001 From: Yogesh Sharma Date: Tue, 14 May 2024 13:04:30 +0530 Subject: [PATCH 5/6] typo --- src/Entities/LogEntryCollection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entities/LogEntryCollection.php b/src/Entities/LogEntryCollection.php index 69b3e304..c59d5fe6 100644 --- a/src/Entities/LogEntryCollection.php +++ b/src/Entities/LogEntryCollection.php @@ -32,7 +32,7 @@ public static function load($raw) { $formatter = config('log-viewer.formatter', 'default'); - if ( $formatter != 'default' ) + if ( $formatter == 'default' ) { return new static(function () use ($raw ) { foreach (LogParser::parse($raw) as $entry) { From d75a807a6bf81c23880bd781ccfd30fff0da9df2 Mon Sep 17 00:00:00 2001 From: Yogesh Sharma Date: Thu, 27 Jun 2024 16:47:07 +0530 Subject: [PATCH 6/6] fixes exception in searching logs --- src/Contracts/LogEntryInterface.php | 6 ++++++ src/Entities/JsonLogEntry.php | 2 +- src/Entities/LogEntryCollection.php | 2 +- src/Http/Controllers/LogViewerController.php | 14 +++++++++----- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/Contracts/LogEntryInterface.php b/src/Contracts/LogEntryInterface.php index 06b8e9f7..a83e9351 100644 --- a/src/Contracts/LogEntryInterface.php +++ b/src/Contracts/LogEntryInterface.php @@ -19,4 +19,10 @@ interface LogEntryInterface { * @return bool */ public function isSameLevel($level); + + + /** + * Get the entry context as json pretty print. + */ + public function context(int $options = JSON_PRETTY_PRINT): string; } \ No newline at end of file diff --git a/src/Entities/JsonLogEntry.php b/src/Entities/JsonLogEntry.php index 6f8bb1e1..a2b391d1 100644 --- a/src/Entities/JsonLogEntry.php +++ b/src/Entities/JsonLogEntry.php @@ -200,7 +200,7 @@ public function stack() * * @return string */ - public function context() + public function context(int $options = JSON_PRETTY_PRINT):string { return json_encode($this->context, JSON_PRETTY_PRINT); } diff --git a/src/Entities/LogEntryCollection.php b/src/Entities/LogEntryCollection.php index c59d5fe6..4f0b7fd4 100644 --- a/src/Entities/LogEntryCollection.php +++ b/src/Entities/LogEntryCollection.php @@ -49,7 +49,7 @@ public static function load($raw) $entry = json_decode($row,true); - if( $entry ) { + if( $entry && gettype($entry) == 'array') { yield new JsonLogEntry($entry); } diff --git a/src/Http/Controllers/LogViewerController.php b/src/Http/Controllers/LogViewerController.php index 389dc7ad..ad0df3b7 100644 --- a/src/Http/Controllers/LogViewerController.php +++ b/src/Http/Controllers/LogViewerController.php @@ -4,8 +4,9 @@ namespace Arcanedev\LogViewer\Http\Controllers; +use Arcanedev\LogViewer\Contracts\LogEntryInterface; use Arcanedev\LogViewer\Contracts\LogViewer as LogViewerContract; -use Arcanedev\LogViewer\Entities\{LogEntry, LogEntryCollection}; +use Arcanedev\LogViewer\Entities\LogEntryCollection; use Arcanedev\LogViewer\Exceptions\LogNotFoundException; use Arcanedev\LogViewer\Tables\StatsTable; use Illuminate\Http\Request; @@ -144,9 +145,10 @@ public function search(Request $request, $date, $level = 'all') { $query = $request->get('query'); - if (is_null($query)) + if (is_null($query)){ return redirect()->route($this->showRoute, [$date]); - + } + $log = $this->getLogOrFail($date); $levels = $this->logViewer->levelsNames(); $needles = array_map(function ($needle) { @@ -154,10 +156,12 @@ public function search(Request $request, $date, $level = 'all') }, array_filter(explode(' ', $query))); $entries = $log->entries($level) ->unless(empty($needles), function (LogEntryCollection $entries) use ($needles) { - return $entries->filter(function (LogEntry $entry) use ($needles) { + return $entries->filter(function (LogEntryInterface $entry) use ($needles) { foreach ([$entry->header, $entry->stack, $entry->context()] as $subject) { - if (Str::containsAll(Str::lower($subject), $needles)) + if (Str::containsAll(Str::lower($subject), $needles)){ return true; + } + } return false;