Uploaded From CV. Swandhana Server

This commit is contained in:
Duidev Software House
2025-01-27 08:16:55 +07:00
commit 6b3be42361
15186 changed files with 2328862 additions and 0 deletions
@@ -0,0 +1,112 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Commands;
use Arcanedev\LogViewer\Contracts\Utilities\LogChecker as LogCheckerContract;
/**
* Class CheckCommand
*
* @author ARCANEDEV <[email protected]>
*/
class CheckCommand extends Command
{
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/**
* The console command name.
*
* @var string
*/
protected $name = 'log-viewer:check';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Check all LogViewer requirements.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'log-viewer:check';
/* -----------------------------------------------------------------
| Getter & Setters
| -----------------------------------------------------------------
*/
/**
* Get the Log Checker instance.
*
* @return \Arcanedev\LogViewer\Contracts\Utilities\LogChecker
*/
protected function getChecker()
{
return $this->laravel[LogCheckerContract::class];
}
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Execute the console command.
*/
public function handle(): int
{
$this->displayLogViewer();
$this->displayRequirements();
$this->displayMessages();
return static::SUCCESS;
}
/* -----------------------------------------------------------------
| Other Methods
| -----------------------------------------------------------------
*/
/**
* Display LogViewer requirements.
*/
private function displayRequirements()
{
$requirements = $this->getChecker()->requirements();
$this->frame('Application requirements');
$this->table([
'Status', 'Message'
], [
[$requirements['status'], $requirements['message']]
]);
}
/**
* Display LogViewer messages.
*/
private function displayMessages()
{
$messages = $this->getChecker()->messages();
$rows = [];
foreach ($messages['files'] as $file => $message) {
$rows[] = [$file, $message];
}
if ( ! empty($rows)) {
$this->frame('LogViewer messages');
$this->table(['File', 'Message'], $rows);
}
}
}
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Commands;
/**
* Class ClearCommand
*
* @author ARCANEDEV <[email protected]>
*/
class ClearCommand extends Command
{
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'log-viewer:clear';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Clear all generated log files';
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Execute the console command.
*/
public function handle(): int
{
if ($this->confirm('This will delete all the log files, Do you wish to continue?')) {
$this->logViewer->clear();
$this->info('Successfully cleared the logs!');
}
return static::SUCCESS;
}
}
+65
View File
@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Commands;
use Arcanedev\LogViewer\Contracts\LogViewer as LogViewerContract;
use Arcanedev\Support\Console\Command as BaseCommand;
/**
* Class Command
*
* @author ARCANEDEV <[email protected]>
*/
abstract class Command extends BaseCommand
{
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/** @var \Arcanedev\LogViewer\Contracts\LogViewer */
protected $logViewer;
/* -----------------------------------------------------------------
| Constructor
| -----------------------------------------------------------------
*/
/**
* Create the command instance.
*
* @param \Arcanedev\LogViewer\Contracts\LogViewer $logViewer
*/
public function __construct(LogViewerContract $logViewer)
{
parent::__construct();
$this->logViewer = $logViewer;
}
/* -----------------------------------------------------------------
| Other Methods
| -----------------------------------------------------------------
*/
/**
* Display LogViewer Logo and Copyrights.
*/
protected function displayLogViewer()
{
// LOGO
$this->comment(' __ _ ');
$this->comment(' / / ___ __ _/\ /(_) _____ _____ _ __ ');
$this->comment(' / / / _ \ / _` \ \ / / |/ _ \ \ /\ / / _ \ \'__|');
$this->comment('/ /__| (_) | (_| |\ V /| | __/\ V V / __/ | ');
$this->comment('\____/\___/ \__, | \_/ |_|\___| \_/\_/ \___|_| ');
$this->comment(' |___/ ');
$this->line('');
// Copyright
$this->comment('Version '.$this->logViewer->version().' - Created by ARCANEDEV'.chr(169));
$this->line('');
}
}
@@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Commands;
use Arcanedev\LogViewer\LogViewerServiceProvider;
use Symfony\Component\Console\Input\InputOption;
/**
* Class PublishCommand
*
* @author ARCANEDEV <[email protected]>
*/
class PublishCommand extends Command
{
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/**
* The console command name.
*
* @var string
*/
protected $name = 'log-viewer:publish';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Publish all LogViewer resources and config files';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'log-viewer:publish
{--tag= : One or many tags that have assets you want to publish.}
{--force : Overwrite any existing files.}';
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Execute the console command.
*/
public function handle(): int
{
$args = [
'--provider' => LogViewerServiceProvider::class,
];
if ((bool) $this->option('force')) {
$args['--force'] = true;
}
$args['--tag'] = [$this->option('tag')];
$this->displayLogViewer();
$this->call('vendor:publish', $args);
return static::SUCCESS;
}
/**
* Get the console command options.
*
* @return array
*
* @codeCoverageIgnore
*/
protected function getOptions()
{
return [
['tag', 't', InputOption::VALUE_OPTIONAL, 'One or many tags that have assets you want to publish.', ''],
['force', 'f', InputOption::VALUE_OPTIONAL, 'Overwrite any existing files.', false],
];
}
}
@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Commands;
use Arcanedev\LogViewer\Tables\StatsTable;
/**
* Class StatsCommand
*
* @author ARCANEDEV <[email protected]>
*/
class StatsCommand extends Command
{
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/**
* The console command name.
*
* @var string
*/
protected $name = 'log-viewer:stats';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Display stats of all logs.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'log-viewer:stats';
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Execute the console command.
*/
public function handle()
{
// Load Data
$stats = $this->logViewer->statsTable('en');
$rows = $stats->rows();
$rows[] = $this->tableSeparator();
$rows[] = $this->prepareFooter($stats);
// Display Data
$this->displayLogViewer();
$this->table($stats->header(), $rows);
return static::SUCCESS;
}
/* -----------------------------------------------------------------
| Other Methods
| -----------------------------------------------------------------
*/
/**
* Prepare footer.
*
* @param \Arcanedev\LogViewer\Tables\StatsTable $stats
*
* @return array
*/
private function prepareFooter(StatsTable $stats)
{
$files = [
'count' => count($stats->rows()).' log file(s)'
];
return $files + $stats->footer();
}
}
+202
View File
@@ -0,0 +1,202 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Contracts;
/**
* Interface LogViewer
*
* @author ARCANEDEV <[email protected]>
*/
interface LogViewer extends Patternable
{
/* -----------------------------------------------------------------
| Getters & Methods
| -----------------------------------------------------------------
*/
/**
* Get the log levels.
*
* @param bool|false $flip
*
* @return array
*/
public function levels($flip = false);
/**
* Get the translated log levels.
*
* @param string|null $locale
*
* @return array
*/
public function levelsNames($locale = null);
/**
* Set the log storage path.
*
* @param string $path
*
* @return self
*/
public function setPath($path);
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Get all logs.
*
* @return \Arcanedev\LogViewer\Entities\LogCollection|\Arcanedev\LogViewer\Entities\Log[]
*/
public function all();
/**
* Paginate all logs.
*
* @param int $perPage
*
* @return \Illuminate\Pagination\LengthAwarePaginator
*/
public function paginate($perPage = 30);
/**
* Get a log.
*
* @param string $date
*
* @return \Arcanedev\LogViewer\Entities\Log
*/
public function get($date);
/**
* Get the log entries.
*
* @param string $date
* @param string $level
*
* @return \Arcanedev\LogViewer\Entities\LogEntryCollection
*/
public function entries($date, $level = 'all');
/**
* Download a log file.
*
* @param string $date
* @param string|null $filename
* @param array $headers
*
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
*/
public function download($date, $filename = null, $headers = []);
/**
* Get logs statistics.
*
* @return array
*/
public function stats();
/**
* Get logs statistics table.
*
* @param string|null $locale
*
* @return \Arcanedev\LogViewer\Tables\StatsTable
*/
public function statsTable($locale = null);
/**
* Delete the log.
*
* @param string $date
*
* @return bool
*
* @throws \Arcanedev\LogViewer\Exceptions\FilesystemException
*/
public function delete($date);
/**
* Clear the log files.
*
* @return bool
*/
public function clear();
/**
* List the log files.
*
* @return array
*/
public function files();
/**
* List the log files (only dates).
*
* @return array
*/
public function dates();
/**
* Get logs count.
*
* @return int
*/
public function count();
/**
* Get entries total from all logs.
*
* @param string $level
*
* @return int
*/
public function total($level = 'all');
/**
* Get logs tree.
*
* @param bool|false $trans
*
* @return array
*/
public function tree($trans = false);
/**
* Get logs menu.
*
* @param bool|true $trans
*
* @return array
*/
public function menu($trans = true);
/* -----------------------------------------------------------------
| Check Methods
| -----------------------------------------------------------------
*/
/**
* Determine if the log folder is empty or not.
*
* @return bool
*/
public function isEmpty();
/* -----------------------------------------------------------------
| Other Methods
| -----------------------------------------------------------------
*/
/**
* Get the LogViewer version.
*
* @return string
*/
public function version();
}
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Contracts;
use Arcanedev\LogViewer\Contracts\Utilities\Filesystem;
/**
* Interface Patternable
*
* @author ARCANEDEV <[email protected]>
*/
interface Patternable
{
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Get the log pattern.
*
* @return string
*/
public function getPattern();
/**
* Set the log pattern.
*
* @param string $date
* @param string $prefix
* @param string $extension
*
* @return self
*/
public function setPattern(
$prefix = Filesystem::PATTERN_PREFIX,
$date = Filesystem::PATTERN_DATE,
$extension = Filesystem::PATTERN_EXTENSION
);
}
+39
View File
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Contracts;
/**
* Interface Table
*
* @author ARCANEDEV <[email protected]>
*/
interface Table
{
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Get table header.
*
* @return array
*/
public function header();
/**
* Get table rows.
*
* @return array
*/
public function rows();
/**
* Get table footer.
*
* @return array
*/
public function footer();
}
@@ -0,0 +1,188 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Contracts\Utilities;
use Arcanedev\LogViewer\Contracts\Patternable;
/**
* Interface Factory
*
* @author ARCANEDEV <[email protected]>
*/
interface Factory extends Patternable
{
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Get the filesystem instance.
*
* @return \Arcanedev\LogViewer\Contracts\Utilities\Filesystem
*/
public function getFilesystem();
/**
* Set the filesystem instance.
*
* @param \Arcanedev\LogViewer\Contracts\Utilities\Filesystem $filesystem
*
* @return self
*/
public function setFilesystem(Filesystem $filesystem);
/**
* Get the log levels instance.
*
* @return \Arcanedev\LogViewer\Contracts\Utilities\LogLevels $levels
*/
public function getLevels();
/**
* Set the log levels instance.
*
* @param \Arcanedev\LogViewer\Contracts\Utilities\LogLevels $levels
*
* @return self
*/
public function setLevels(LogLevels $levels);
/**
* Set the log storage path.
*
* @param string $storagePath
*
* @return self
*/
public function setPath($storagePath);
/**
* Get all logs.
*
* @return \Arcanedev\LogViewer\Entities\LogCollection
*/
public function logs();
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Get all logs (alias).
*
* @see logs
*
* @return \Arcanedev\LogViewer\Entities\LogCollection
*/
public function all();
/**
* Paginate all logs.
*
* @param int $perPage
*
* @return \Illuminate\Pagination\LengthAwarePaginator
*/
public function paginate($perPage = 30);
/**
* Get a log by date.
*
* @param string $date
*
* @return \Arcanedev\LogViewer\Entities\Log
*/
public function log($date);
/**
* Get a log by date (alias).
*
* @param string $date
*
* @return \Arcanedev\LogViewer\Entities\Log
*/
public function get($date);
/**
* Get log entries.
*
* @param string $date
* @param string $level
*
* @return \Arcanedev\LogViewer\Entities\LogEntryCollection
*/
public function entries($date, $level = 'all');
/**
* List the log files (dates).
*
* @return array
*/
public function dates();
/**
* Get logs count.
*
* @return int
*/
public function count();
/**
* Get total log entries.
*
* @param string $level
*
* @return int
*/
public function total($level = 'all');
/**
* Get tree menu.
*
* @param bool $trans
*
* @return array
*/
public function tree($trans = false);
/**
* Get tree menu.
*
* @param bool $trans
*
* @return array
*/
public function menu($trans = true);
/**
* Get logs statistics.
*
* @return array
*/
public function stats();
/**
* Get logs statistics table.
*
* @param string|null $locale
*
* @return \Arcanedev\LogViewer\Tables\StatsTable
*/
public function statsTable($locale = null);
/* -----------------------------------------------------------------
| Check Methods
| -----------------------------------------------------------------
*/
/**
* Determine if the log folder is empty or not.
*
* @return bool
*/
public function isEmpty();
}
@@ -0,0 +1,138 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Contracts\Utilities;
use Arcanedev\LogViewer\Contracts\Patternable;
/**
* Interface Filesystem
*
* @author ARCANEDEV <[email protected]>
*/
interface Filesystem extends Patternable
{
/* -----------------------------------------------------------------
| Constants
| -----------------------------------------------------------------
*/
const PATTERN_PREFIX = 'laravel-';
const PATTERN_DATE = '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]';
const PATTERN_EXTENSION = '.log';
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Get the files instance.
*
* @return \Illuminate\Filesystem\Filesystem
*/
public function getInstance();
/**
* Set the log storage path.
*
* @param string $storagePath
*
* @return $this
*/
public function setPath($storagePath);
/**
* Set the log date pattern.
*
* @param string $datePattern
*
* @return $this
*/
public function setDatePattern($datePattern);
/**
* Set the log prefix pattern.
*
* @param string $prefixPattern
*
* @return $this
*/
public function setPrefixPattern($prefixPattern);
/**
* Set the log extension.
*
* @param string $extension
*
* @return $this
*/
public function setExtension($extension);
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Get all log files.
*
* @return array
*/
public function all();
/**
* Get all valid log files.
*
* @return array
*/
public function logs();
/**
* List the log files (Only dates).
*
* @param bool $withPaths
*
* @return array
*/
public function dates($withPaths = false);
/**
* Read the log.
*
* @param string $date
*
* @return string
*
* @throws \Arcanedev\LogViewer\Exceptions\FilesystemException
*/
public function read($date);
/**
* Delete the log.
*
* @param string $date
*
* @return bool
*
* @throws \Arcanedev\LogViewer\Exceptions\FilesystemException
*/
public function delete(string $date);
/**
* Clear the log files.
*
* @return bool
*/
public function clear();
/**
* Get the log file path.
*
* @param string $date
*
* @return string
*/
public function path($date);
}
@@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Contracts\Utilities;
use Illuminate\Contracts\Config\Repository as ConfigContract;
/**
* Interface LogChecker
*
* @author ARCANEDEV <[email protected]>
*/
interface LogChecker
{
/* -----------------------------------------------------------------
| Constants
| -----------------------------------------------------------------
*/
/**
* @link http://laravel.com/docs/5.4/errors#configuration
* @link https://github.com/Seldaek/monolog/blob/master/doc/02-handlers-formatters-processors.md#log-to-files-and-syslog
*/
const HANDLER_DAILY = 'daily';
const HANDLER_SINGLE = 'single';
const HANDLER_SYSLOG = 'syslog';
const HANDLER_ERRORLOG = 'errorlog';
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Set the config instance.
*
* @param \Illuminate\Contracts\Config\Repository $config
*
* @return self
*/
public function setConfig(ConfigContract $config);
/**
* Set the Filesystem instance.
*
* @param \Arcanedev\LogViewer\Contracts\Utilities\Filesystem $filesystem
*
* @return self
*/
public function setFilesystem(Filesystem $filesystem);
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Get messages.
*
* @return array
*/
public function messages();
/**
* Check passes ??
*
* @return bool
*/
public function passes();
/**
* Check fails ??
*
* @return bool
*/
public function fails();
/**
* Get the requirements
*
* @return array
*/
public function requirements();
}
@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Contracts\Utilities;
use Illuminate\Translation\Translator;
/**
* Interface LogLevels
*
* @author ARCANEDEV <[email protected]>
*/
interface LogLevels
{
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Set the Translator instance.
*
* @param \Illuminate\Translation\Translator $translator
*
* @return self
*/
public function setTranslator(Translator $translator);
/**
* Get the selected locale.
*
* @return string
*/
public function getLocale();
/**
* Set the selected locale.
*
* @param string $locale
*
* @return self
*/
public function setLocale($locale);
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Get the log levels.
*
* @param bool $flip
*
* @return array
*/
public function lists($flip = false);
/**
* Get translated levels.
*
* @param string|null $locale
*
* @return array
*/
public function names($locale = null);
/**
* Get PSR log levels.
*
* @param bool $flip
*
* @return array
*/
public static function all($flip = false);
/**
* Get the translated level.
*
* @param string $key
* @param string|null $locale
*
* @return string
*/
public function get($key, $locale = null);
}
@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Contracts\Utilities;
use Arcanedev\LogViewer\Entities\Log;
use Illuminate\Contracts\Config\Repository as ConfigContract;
/**
* Interface LogMenu
*
* @author ARCANEDEV <[email protected]>
*/
interface LogMenu
{
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Set the config instance.
*
* @param \Illuminate\Contracts\Config\Repository $config
*
* @return self
*/
public function setConfig(ConfigContract $config);
/**
* Set the log styler instance.
*
* @param \Arcanedev\LogViewer\Contracts\Utilities\LogStyler $styler
*
* @return self
*/
public function setLogStyler(LogStyler $styler);
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Make log menu.
*
* @param \Arcanedev\LogViewer\Entities\Log $log
* @param bool $trans
*
* @return array
*/
public function make(Log $log, $trans = true);
}
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Contracts\Utilities;
/**
* Interface LogStyler
*
* @author ARCANEDEV <[email protected]>
*/
interface LogStyler
{
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Make level icon.
*
* @param string $level
* @param string|null $default
*
* @return \Illuminate\Support\HtmlString
*/
public function icon($level, $default = null);
/**
* Get level color.
*
* @param string $level
* @param string|null $default
*
* @return string
*/
public function color($level, $default = null);
/**
* Get strings to highlight.
*
* @param array $default
*
* @return array
*/
public function toHighlight(array $default = []);
}
+254
View File
@@ -0,0 +1,254 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Entities;
use Illuminate\Contracts\Support\{Arrayable, Jsonable};
use Illuminate\Support\Carbon;
use JsonSerializable;
use SplFileInfo;
/**
* Class Log
*
* @author ARCANEDEV <[email protected]>
*/
class Log implements Arrayable, Jsonable, JsonSerializable
{
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/** @var string */
public $date;
/** @var string */
private $path;
/** @var \Arcanedev\LogViewer\Entities\LogEntryCollection */
private $entries;
/** @var \SplFileInfo */
private $file;
/* -----------------------------------------------------------------
| Constructor
| -----------------------------------------------------------------
*/
/**
* Log constructor.
*
* @param string $date
* @param string $path
* @param string $raw
*/
public function __construct($date, $path, $raw)
{
$this->date = $date;
$this->path = $path;
$this->file = new SplFileInfo($path);
$this->entries = LogEntryCollection::load($raw);
}
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Get log path.
*
* @return string
*/
public function getPath()
{
return $this->path;
}
/**
* Get file info.
*
* @return \SplFileInfo
*/
public function file()
{
return $this->file;
}
/**
* Get file size.
*
* @return string
*/
public function size()
{
return $this->formatSize($this->file->getSize());
}
/**
* Get file creation date.
*
* @return \Carbon\Carbon
*/
public function createdAt()
{
return Carbon::createFromTimestamp($this->file()->getATime());
}
/**
* Get file modification date.
*
* @return \Carbon\Carbon
*/
public function updatedAt()
{
return Carbon::createFromTimestamp($this->file()->getMTime());
}
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Make a log object.
*
* @param string $date
* @param string $path
* @param string $raw
*
* @return self
*/
public static function make($date, $path, $raw)
{
return new self($date, $path, $raw);
}
/**
* Get log entries.
*
* @param string $level
*
* @return \Arcanedev\LogViewer\Entities\LogEntryCollection
*/
public function entries($level = 'all')
{
return $level === 'all'
? $this->entries
: $this->getByLevel($level);
}
/**
* Get filtered log entries by level.
*
* @param string $level
*
* @return \Arcanedev\LogViewer\Entities\LogEntryCollection
*/
public function getByLevel($level)
{
return $this->entries->filterByLevel($level);
}
/**
* Get log stats.
*
* @return array
*/
public function stats()
{
return $this->entries->stats();
}
/**
* Get the log navigation tree.
*
* @param bool $trans
*
* @return array
*/
public function tree($trans = false)
{
return $this->entries->tree($trans);
}
/**
* Get log entries menu.
*
* @param bool $trans
*
* @return array
*/
public function menu($trans = true)
{
return log_menu()->make($this, $trans);
}
/* -----------------------------------------------------------------
| Convert Methods
| -----------------------------------------------------------------
*/
/**
* Get the log as a plain array.
*
* @return array
*/
public function toArray()
{
return [
'date' => $this->date,
'path' => $this->path,
'entries' => $this->entries->toArray()
];
}
/**
* Convert the object to its JSON representation.
*
* @param int $options
*
* @return string
*/
public function toJson($options = 0)
{
return json_encode($this->toArray(), $options);
}
/**
* Serialize the log object to json data.
*
* @return array
*/
public function jsonSerialize(): array
{
return $this->toArray();
}
/* -----------------------------------------------------------------
| Other Methods
| -----------------------------------------------------------------
*/
/**
* Format the file size.
*
* @param int $bytes
* @param int $precision
*
* @return string
*/
private function formatSize($bytes, $precision = 2)
{
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
return round($bytes / pow(1024, $pow), $precision).' '.$units[$pow];
}
}
@@ -0,0 +1,220 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Entities;
use Arcanedev\LogViewer\Contracts\Utilities\Filesystem as FilesystemContract;
use Arcanedev\LogViewer\Exceptions\LogNotFoundException;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\LazyCollection;
/**
* Class LogCollection
*
* @author ARCANEDEV <[email protected]>
*/
class LogCollection extends LazyCollection
{
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/** @var \Arcanedev\LogViewer\Contracts\Utilities\Filesystem */
private $filesystem;
/* -----------------------------------------------------------------
| Constructor
| -----------------------------------------------------------------
*/
/**
* LogCollection constructor.
*
* @param mixed $source
*/
public function __construct($source = null)
{
$this->setFilesystem(app(FilesystemContract::class));
if (is_null($source))
$source = function () {
foreach($this->filesystem->dates(true) as $date => $path) {
yield $date => Log::make($date, $path, $this->filesystem->read($date));
}
};
parent::__construct($source);
}
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Set the filesystem instance.
*
* @param \Arcanedev\LogViewer\Contracts\Utilities\Filesystem $filesystem
*
* @return \Arcanedev\LogViewer\Entities\LogCollection
*/
public function setFilesystem(FilesystemContract $filesystem)
{
$this->filesystem = $filesystem;
return $this;
}
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Get a log.
*
* @param string $date
* @param mixed|null $default
*
* @return \Arcanedev\LogViewer\Entities\Log
*
* @throws \Arcanedev\LogViewer\Exceptions\LogNotFoundException
*/
public function get($date, $default = null)
{
if ( ! $this->has($date))
throw LogNotFoundException::make($date);
return parent::get($date, $default);
}
/**
* Paginate logs.
*
* @param int $perPage
*
* @return \Illuminate\Pagination\LengthAwarePaginator
*/
public function paginate($perPage = 30)
{
$page = request()->get('page', 1);
$path = request()->url();
return new LengthAwarePaginator(
$this->forPage($page, $perPage),
$this->count(),
$perPage,
$page,
compact('path')
);
}
/**
* Get a log (alias).
*
* @see get()
*
* @param string $date
*
* @return \Arcanedev\LogViewer\Entities\Log
*/
public function log($date)
{
return $this->get($date);
}
/**
* Get log entries.
*
* @param string $date
* @param string $level
*
* @return \Arcanedev\LogViewer\Entities\LogEntryCollection
*/
public function entries($date, $level = 'all')
{
return $this->get($date)->entries($level);
}
/**
* Get logs statistics.
*
* @return array
*/
public function stats()
{
$stats = [];
foreach ($this->all() as $date => $log) {
/** @var \Arcanedev\LogViewer\Entities\Log $log */
$stats[$date] = $log->stats();
}
return $stats;
}
/**
* List the log files (dates).
*
* @return array
*/
public function dates()
{
return $this->keys()->toArray();
}
/**
* Get entries total.
*
* @param string $level
*
* @return int
*/
public function total($level = 'all')
{
return (int) $this->sum(function (Log $log) use ($level) {
return $log->entries($level)->count();
});
}
/**
* Get logs tree.
*
* @param bool $trans
*
* @return array
*/
public function tree($trans = false)
{
$tree = [];
foreach ($this->all() as $date => $log) {
/** @var \Arcanedev\LogViewer\Entities\Log $log */
$tree[$date] = $log->tree($trans);
}
return $tree;
}
/**
* Get logs menu.
*
* @param bool $trans
*
* @return array
*/
public function menu($trans = true)
{
$menu = [];
foreach ($this->all() as $date => $log) {
/** @var \Arcanedev\LogViewer\Entities\Log $log */
$menu[$date] = $log->menu($trans);
}
return $menu;
}
}
+328
View File
@@ -0,0 +1,328 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Entities;
use Arcanedev\LogViewer\Helpers\LogParser;
use Carbon\Carbon;
use Illuminate\Contracts\Support\{Arrayable, Jsonable};
use JsonSerializable;
/**
* Class LogEntry
*
* @author ARCANEDEV <[email protected]>
*/
class LogEntry implements Arrayable, Jsonable, JsonSerializable
{
/* -----------------------------------------------------------------
| 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($level, $header, $stack = null)
{
$this->setLevel($level);
$this->setHeader($header);
$this->setStack($stack);
}
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Set the entry level.
*
* @param string $level
*
* @return self
*/
private function setLevel($level)
{
$this->level = $level;
return $this;
}
/**
* Set the entry header.
*
* @param string $header
*
* @return self
*/
private function setHeader($header)
{
$this->setDatetime($this->extractDatetime($header));
$header = $this->cleanHeader($header);
$this->header = trim($header);
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', $datetime);
return $this;
}
/**
* Set the entry stack.
*
* @param string $stack
*
* @return self
*/
private function setStack($stack)
{
$this->stack = $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.
*/
public function context(int $options = JSON_PRETTY_PRINT): string
{
return json_encode($this->context, $options);
}
/* -----------------------------------------------------------------
| 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.
*/
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);
}
/* -----------------------------------------------------------------
| Other Methods
| -----------------------------------------------------------------
*/
/**
* Clean the entry header.
*
* @param string $header
*
* @return string
*/
private function cleanHeader($header)
{
// REMOVE THE DATE
$header = preg_replace('/\['.LogParser::REGEX_DATETIME_PATTERN.'\][ ]/', '', $header);
// EXTRACT ENV
if (preg_match('/^[a-z]+.[A-Z]+:/', $header, $out)) {
$this->setEnv($out[0]);
$header = trim(str_replace($out[0], '', $header));
}
// EXTRACT CONTEXT (Regex from https://stackoverflow.com/a/21995025)
preg_match_all('/{(?:[^{}]|(?R))*}/x', $header, $out);
if (isset($out[0][0]) && ! is_null($context = json_decode($out[0][0], true))) {
$header = str_replace($out[0][0], '', $header);
$this->setContext($context);
}
return $header;
}
/**
* Extract datetime from the header.
*
* @param string $header
*
* @return string
*/
private function extractDatetime($header)
{
return preg_replace('/^\[('.LogParser::REGEX_DATETIME_PATTERN.')\].*/', '$1', $header);
}
}
@@ -0,0 +1,135 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Entities;
use Arcanedev\LogViewer\Helpers\LogParser;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\LazyCollection;
/**
* Class LogEntryCollection
*
* @author ARCANEDEV <[email protected]>
*/
class LogEntryCollection extends LazyCollection
{
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Load raw log entries.
*
* @param string $raw
*
* @return self
*/
public static function load($raw)
{
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);
}
});
}
/**
* Paginate log entries.
*
* @param int $perPage
*
* @return \Illuminate\Pagination\LengthAwarePaginator
*/
public function paginate($perPage = 20)
{
$page = request()->get('page', 1);
$path = request()->url();
return new LengthAwarePaginator(
$this->forPage($page, $perPage),
$this->count(),
$perPage,
$page,
compact('path')
);
}
/**
* Get filtered log entries by level.
*
* @param string $level
*
* @return self
*/
public function filterByLevel($level)
{
return $this->filter(function(LogEntry $entry) use ($level) {
return $entry->isSameLevel($level);
});
}
/**
* Get log entries stats.
*
* @return array
*/
public function stats()
{
$counters = $this->initStats();
foreach ($this->groupBy('level') as $level => $entries) {
$counters[$level] = $count = count($entries);
$counters['all'] += $count;
}
return $counters;
}
/**
* Get the log entries navigation tree.
*
* @param bool|false $trans
*
* @return array
*/
public function tree($trans = false)
{
$tree = $this->stats();
array_walk($tree, function(&$count, $level) use ($trans) {
$count = [
'name' => $trans ? log_levels()->get($level) : $level,
'count' => $count,
];
});
return $tree;
}
/* -----------------------------------------------------------------
| Other Methods
| -----------------------------------------------------------------
*/
/**
* Init stats counters.
*
* @return array
*/
private function initStats()
{
$levels = array_merge_recursive(
['all'],
array_keys(log_viewer()->levels(true))
);
return array_map(function () {
return 0;
}, array_flip($levels));
}
}
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Exceptions;
/**
* Class FilesystemException
*
* @author ARCANEDEV <[email protected]>
*/
class FilesystemException extends LogViewerException
{
public static function cannotDeleteLog()
{
return new static('There was an error deleting the log.');
}
public static function invalidPath(string $path)
{
return new static("The log(s) could not be located at : $path");
}
}
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Exceptions;
/**
* Class LogNotFoundException
*
* @author ARCANEDEV <[email protected]>
*/
class LogNotFoundException extends LogViewerException
{
/**
* Make the exception.
*
* @param string $date
*
* @return static
*/
public static function make(string $date)
{
return new static("Log not found in this date [{$date}]");
}
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Exceptions;
/**
* Class LogViewerException
*
* @author ARCANEDEV <[email protected]>
*/
class LogViewerException extends \Exception {}
+142
View File
@@ -0,0 +1,142 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Helpers;
use Arcanedev\LogViewer\Utilities\LogLevels;
use Illuminate\Support\Str;
/**
* Class LogParser
*
* @author ARCANEDEV <[email protected]>
*/
class LogParser
{
/* -----------------------------------------------------------------
| Constants
| -----------------------------------------------------------------
*/
const REGEX_DATE_PATTERN = '\d{4}(-\d{2}){2}';
const REGEX_TIME_PATTERN = '\d{2}(:\d{2}){2}';
const REGEX_DATETIME_PATTERN = self::REGEX_DATE_PATTERN.' '.self::REGEX_TIME_PATTERN;
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/**
* Parsed data.
*
* @var array
*/
protected static $parsed = [];
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Parse file content.
*
* @param string $raw
*
* @return array
*/
public static function parse($raw)
{
static::$parsed = [];
list($headings, $data) = static::parseRawData($raw);
// @codeCoverageIgnoreStart
if ( ! is_array($headings)) {
return static::$parsed;
}
// @codeCoverageIgnoreEnd
foreach ($headings as $heading) {
for ($i = 0, $j = count($heading); $i < $j; $i++) {
static::populateEntries($heading, $data, $i);
}
};
unset($headings, $data);
return array_reverse(static::$parsed);
}
/* -----------------------------------------------------------------
| Other Methods
| -----------------------------------------------------------------
*/
/**
* Extract the date.
*
* @param string $string
*
* @return string
*/
public static function extractDate(string $string): string
{
return preg_replace('/.*('.self::REGEX_DATE_PATTERN.').*/', '$1', $string);
}
/**
* Parse raw data.
*
* @param string $raw
*
* @return array
*/
private static function parseRawData($raw)
{
$pattern = '/\['.self::REGEX_DATETIME_PATTERN.'\].*/';
preg_match_all($pattern, $raw, $headings);
$data = preg_split($pattern, $raw);
if ($data[0] < 1) {
$trash = array_shift($data);
unset($trash);
}
return [$headings, $data];
}
/**
* Populate entries.
*
* @param array $heading
* @param array $data
* @param int $key
*/
private static function populateEntries($heading, $data, $key)
{
foreach (LogLevels::all() as $level) {
if (static::hasLogLevel($heading[$key], $level)) {
static::$parsed[] = [
'level' => $level,
'header' => $heading[$key],
'stack' => $data[$key]
];
}
}
}
/**
* Check if header has a log level.
*
* @param string $heading
* @param string $level
*
* @return bool
*/
private static function hasLogLevel($heading, $level)
{
return Str::contains($heading, strtoupper(".{$level}:"));
}
}
@@ -0,0 +1,312 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Http\Controllers;
use Arcanedev\LogViewer\Contracts\LogViewer as LogViewerContract;
use Arcanedev\LogViewer\Entities\{LogEntry, LogEntryCollection};
use Arcanedev\LogViewer\Exceptions\LogNotFoundException;
use Arcanedev\LogViewer\Tables\StatsTable;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Routing\Controller;
use Illuminate\Support\{Arr, Collection, Str};
/**
* Class LogViewerController
*
* @author ARCANEDEV <[email protected]>
*/
class LogViewerController extends Controller
{
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/**
* The log viewer instance
*
* @var \Arcanedev\LogViewer\Contracts\LogViewer
*/
protected $logViewer;
/** @var int */
protected $perPage = 30;
/** @var string */
protected $showRoute = 'log-viewer::logs.show';
/* -----------------------------------------------------------------
| Constructor
| -----------------------------------------------------------------
*/
/**
* LogViewerController constructor.
*
* @param \Arcanedev\LogViewer\Contracts\LogViewer $logViewer
*/
public function __construct(LogViewerContract $logViewer)
{
$this->logViewer = $logViewer;
$this->perPage = config('log-viewer.per-page', $this->perPage);
$this->showRoute = config('log-viewer.route.show', $this->showRoute);
}
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Show the dashboard.
*
* @return \Illuminate\View\View
*/
public function index()
{
$stats = $this->logViewer->statsTable();
$chartData = $this->prepareChartData($stats);
$percents = $this->calcPercentages($stats->footer(), $stats->header());
return $this->view('dashboard', compact('chartData', 'percents'));
}
/**
* List all logs.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\View\View
*/
public function listLogs(Request $request)
{
$stats = $this->logViewer->statsTable();
$headers = $stats->header();
$rows = $this->paginate($stats->rows(), $request);
return $this->view('logs', compact('headers', 'rows'));
}
/**
* Show the log.
*
* @param \Illuminate\Http\Request $request
* @param string $date
*
* @return \Illuminate\View\View
*/
public function show(Request $request, $date)
{
$level = 'all';
$log = $this->getLogOrFail($date);
$query = $request->get('query');
$levels = $this->logViewer->levelsNames();
$entries = $log->entries($level)->paginate($this->perPage);
return $this->view('show', compact('level', 'log', 'query', 'levels', 'entries'));
}
/**
* Filter the log entries by level.
*
* @param \Illuminate\Http\Request $request
* @param string $date
* @param string $level
*
* @return \Illuminate\View\View|\Illuminate\Http\RedirectResponse
*/
public function showByLevel(Request $request, $date, $level)
{
if ($level === 'all')
return redirect()->route($this->showRoute, [$date]);
$log = $this->getLogOrFail($date);
$query = $request->get('query');
$levels = $this->logViewer->levelsNames();
$entries = $this->logViewer->entries($date, $level)->paginate($this->perPage);
return $this->view('show', compact('level', 'log', 'query', 'levels', 'entries'));
}
/**
* Show the log with the search query.
*
* @param \Illuminate\Http\Request $request
* @param string $date
* @param string $level
*
* @return \Illuminate\View\View|\Illuminate\Http\RedirectResponse
*/
public function search(Request $request, $date, $level = 'all')
{
$query = $request->get('query');
if (is_null($query))
return redirect()->route($this->showRoute, [$date]);
$log = $this->getLogOrFail($date);
$levels = $this->logViewer->levelsNames();
$needles = array_map(function ($needle) {
return Str::lower($needle);
}, array_filter(explode(' ', $query)));
$entries = $log->entries($level)
->unless(empty($needles), function (LogEntryCollection $entries) use ($needles) {
return $entries->filter(function (LogEntry $entry) use ($needles) {
foreach ([$entry->header, $entry->stack, $entry->context()] as $subject) {
if (Str::containsAll(Str::lower($subject), $needles))
return true;
}
return false;
});
})
->paginate($this->perPage);
return $this->view('show', compact('level', 'log', 'query', 'levels', 'entries'));
}
/**
* Download the log
*
* @param string $date
*
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
*/
public function download($date)
{
return $this->logViewer->download($date);
}
/**
* Delete a log.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\JsonResponse
*/
public function delete(Request $request)
{
abort_unless($request->ajax(), 405, 'Method Not Allowed');
$date = $request->input('date');
return response()->json([
'result' => $this->logViewer->delete($date) ? 'success' : 'error'
]);
}
/* -----------------------------------------------------------------
| Other Methods
| -----------------------------------------------------------------
*/
/**
* Get the evaluated view contents for the given view.
*
* @param string $view
* @param array $data
* @param array $mergeData
*
* @return \Illuminate\View\View
*/
protected function view($view, $data = [], $mergeData = [])
{
$theme = config('log-viewer.theme');
return view()->make("log-viewer::{$theme}.{$view}", $data, $mergeData);
}
/**
* Paginate logs.
*
* @param array $data
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Pagination\LengthAwarePaginator
*/
protected function paginate(array $data, Request $request)
{
$data = new Collection($data);
$page = $request->get('page', 1);
$path = $request->url();
return new LengthAwarePaginator(
$data->forPage($page, $this->perPage),
$data->count(),
$this->perPage,
$page,
compact('path')
);
}
/**
* Get a log or fail
*
* @param string $date
*
* @return \Arcanedev\LogViewer\Entities\Log|null
*/
protected function getLogOrFail($date)
{
$log = null;
try {
$log = $this->logViewer->get($date);
}
catch (LogNotFoundException $e) {
abort(404, $e->getMessage());
}
return $log;
}
/**
* Prepare chart data.
*
* @param \Arcanedev\LogViewer\Tables\StatsTable $stats
*
* @return string
*/
protected function prepareChartData(StatsTable $stats)
{
$totals = $stats->totals()->all();
return json_encode([
'labels' => Arr::pluck($totals, 'label'),
'datasets' => [
[
'data' => Arr::pluck($totals, 'value'),
'backgroundColor' => Arr::pluck($totals, 'color'),
'hoverBackgroundColor' => Arr::pluck($totals, 'highlight'),
],
],
]);
}
/**
* Calculate the percentage.
*
* @param array $total
* @param array $names
*
* @return array
*/
protected function calcPercentages(array $total, array $names)
{
$percents = [];
$all = Arr::get($total, 'all');
foreach ($total as $level => $count) {
$percents[$level] = [
'name' => $names[$level],
'count' => $count,
'percent' => $all ? round(($count / $all) * 100, 2) : 0,
];
}
return $percents;
}
}
@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Http\Routes;
use Arcanedev\LogViewer\Http\Controllers\LogViewerController;
use Arcanedev\Support\Routing\RouteRegistrar;
/**
* Class LogViewerRoute
*
* @author ARCANEDEV <[email protected]>
*/
class LogViewerRoute extends RouteRegistrar
{
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Map all routes.
*/
public function map(): void
{
$attributes = (array) config('log-viewer.route.attributes');
$this->group($attributes, function() {
$this->name('log-viewer::')->group(function () {
$this->get('/', [LogViewerController::class, 'index'])
->name('dashboard'); // log-viewer::dashboard
$this->mapLogsRoutes();
});
});
}
/**
* Map the logs routes.
*/
private function mapLogsRoutes(): void
{
$this->prefix('logs')->name('logs.')->group(function() {
$this->get('/', [LogViewerController::class, 'listLogs'])
->name('list'); // log-viewer::logs.list
$this->delete('delete', [LogViewerController::class, 'delete'])
->name('delete'); // log-viewer::logs.delete
$this->prefix('{date}')->group(function() {
$this->get('/', [LogViewerController::class, 'show'])
->name('show'); // log-viewer::logs.show
$this->get('download', [LogViewerController::class, 'download'])
->name('download'); // log-viewer::logs.download
$this->get('{level}', [LogViewerController::class, 'showByLevel'])
->name('filter'); // log-viewer::logs.filter
$this->get('{level}/search', [LogViewerController::class, 'search'])
->name('search'); // log-viewer::logs.search
});
});
}
}
+364
View File
@@ -0,0 +1,364 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer;
use Arcanedev\LogViewer\Contracts\Utilities\Filesystem as FilesystemContract;
use Arcanedev\LogViewer\Contracts\Utilities\Factory as FactoryContract;
use Arcanedev\LogViewer\Contracts\Utilities\LogLevels as LogLevelsContract;
use Arcanedev\LogViewer\Contracts\LogViewer as LogViewerContract;
/**
* Class LogViewer
*
* @author ARCANEDEV <[email protected]>
*/
class LogViewer implements LogViewerContract
{
/* -----------------------------------------------------------------
| Constants
| -----------------------------------------------------------------
*/
/**
* LogViewer Version
*/
const VERSION = '10.1.1';
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/**
* The factory instance.
*
* @var \Arcanedev\LogViewer\Contracts\Utilities\Factory
*/
protected $factory;
/**
* The filesystem instance.
*
* @var \Arcanedev\LogViewer\Contracts\Utilities\Filesystem
*/
protected $filesystem;
/**
* The log levels instance.
*
* @var \Arcanedev\LogViewer\Contracts\Utilities\LogLevels
*/
protected $levels;
/* -----------------------------------------------------------------
| Constructor
| -----------------------------------------------------------------
*/
/**
* Create a new instance.
*
* @param \Arcanedev\LogViewer\Contracts\Utilities\Factory $factory
* @param \Arcanedev\LogViewer\Contracts\Utilities\Filesystem $filesystem
* @param \Arcanedev\LogViewer\Contracts\Utilities\LogLevels $levels
*/
public function __construct(
FactoryContract $factory,
FilesystemContract $filesystem,
LogLevelsContract $levels
) {
$this->factory = $factory;
$this->filesystem = $filesystem;
$this->levels = $levels;
}
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Get the log levels.
*
* @param bool $flip
*
* @return array
*/
public function levels($flip = false)
{
return $this->levels->lists($flip);
}
/**
* Get the translated log levels.
*
* @param string|null $locale
*
* @return array
*/
public function levelsNames($locale = null)
{
return $this->levels->names($locale);
}
/**
* Set the log storage path.
*
* @param string $path
*
* @return self
*/
public function setPath($path)
{
$this->factory->setPath($path);
return $this;
}
/**
* Get the log pattern.
*
* @return string
*/
public function getPattern()
{
return $this->factory->getPattern();
}
/**
* Set the log pattern.
*
* @param string $date
* @param string $prefix
* @param string $extension
*
* @return self
*/
public function setPattern(
$prefix = FilesystemContract::PATTERN_PREFIX,
$date = FilesystemContract::PATTERN_DATE,
$extension = FilesystemContract::PATTERN_EXTENSION
) {
$this->factory->setPattern($prefix, $date, $extension);
return $this;
}
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Get all logs.
*
* @return \Arcanedev\LogViewer\Entities\LogCollection
*/
public function all()
{
return $this->factory->all();
}
/**
* Paginate all logs.
*
* @param int $perPage
*
* @return \Illuminate\Pagination\LengthAwarePaginator
*/
public function paginate($perPage = 30)
{
return $this->factory->paginate($perPage);
}
/**
* Get a log.
*
* @param string $date
*
* @return \Arcanedev\LogViewer\Entities\Log
*/
public function get($date)
{
return $this->factory->log($date);
}
/**
* Get the log entries.
*
* @param string $date
* @param string $level
*
* @return \Arcanedev\LogViewer\Entities\LogEntryCollection
*/
public function entries($date, $level = 'all')
{
return $this->factory->entries($date, $level);
}
/**
* Download a log file.
*
* @param string $date
* @param string|null $filename
* @param array $headers
*
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
*/
public function download($date, $filename = null, $headers = [])
{
if (is_null($filename)) {
$filename = sprintf(
"%s{$date}.%s",
config('log-viewer.download.prefix', 'laravel-'),
config('log-viewer.download.extension', 'log')
);
}
$path = $this->filesystem->path($date);
return response()->download($path, $filename, $headers);
}
/**
* Get logs statistics.
*
* @return array
*/
public function stats()
{
return $this->factory->stats();
}
/**
* Get logs statistics table.
*
* @param string|null $locale
*
* @return \Arcanedev\LogViewer\Tables\StatsTable
*/
public function statsTable($locale = null)
{
return $this->factory->statsTable($locale);
}
/**
* Delete the log.
*
* @param string $date
*
* @return bool
*/
public function delete($date)
{
return $this->filesystem->delete($date);
}
/**
* Clear the log files.
*
* @return bool
*/
public function clear()
{
return $this->filesystem->clear();
}
/**
* Get all valid log files.
*
* @return array
*/
public function files()
{
return $this->filesystem->logs();
}
/**
* List the log files (only dates).
*
* @return array
*/
public function dates()
{
return $this->factory->dates();
}
/**
* Get logs count.
*
* @return int
*/
public function count()
{
return $this->factory->count();
}
/**
* Get entries total from all logs.
*
* @param string $level
*
* @return int
*/
public function total($level = 'all')
{
return $this->factory->total($level);
}
/**
* Get logs tree.
*
* @param bool $trans
*
* @return array
*/
public function tree($trans = false)
{
return $this->factory->tree($trans);
}
/**
* Get logs menu.
*
* @param bool $trans
*
* @return array
*/
public function menu($trans = true)
{
return $this->factory->menu($trans);
}
/* -----------------------------------------------------------------
| Check Methods
| -----------------------------------------------------------------
*/
/**
* Determine if the log folder is empty or not.
*
* @return bool
*/
public function isEmpty()
{
return $this->factory->isEmpty();
}
/* -----------------------------------------------------------------
| Other Methods
| -----------------------------------------------------------------
*/
/**
* Get the LogViewer version.
*
* @return string
*/
public function version()
{
return self::VERSION;
}
}
@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer;
use Arcanedev\Support\Providers\PackageServiceProvider;
/**
* Class LogViewerServiceProvider
*
* @author ARCANEDEV <[email protected]>
*/
class LogViewerServiceProvider extends PackageServiceProvider
{
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/**
* Package name.
*
* @var string
*/
protected $package = 'log-viewer';
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Register the service provider.
*/
public function register(): void
{
parent::register();
$this->registerConfig();
$this->registerProvider(Providers\RouteServiceProvider::class);
$this->registerCommands([
Commands\PublishCommand::class,
Commands\StatsCommand::class,
Commands\CheckCommand::class,
Commands\ClearCommand::class,
]);
}
/**
* Boot the service provider.
*/
public function boot(): void
{
$this->loadTranslations();
$this->loadViews();
if ($this->app->runningInConsole()) {
$this->publishConfig();
$this->publishTranslations();
$this->publishViews();
}
}
}
@@ -0,0 +1,138 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Providers;
use Arcanedev\LogViewer\Contracts\LogViewer as LogViewerContract;
use Arcanedev\LogViewer\Contracts\Utilities\Factory as FactoryContract;
use Arcanedev\LogViewer\Contracts\Utilities\Filesystem as FilesystemContract;
use Arcanedev\LogViewer\Contracts\Utilities\LogChecker as LogCheckerContract;
use Arcanedev\LogViewer\Contracts\Utilities\LogLevels as LogLevelsContract;
use Arcanedev\LogViewer\Contracts\Utilities\LogMenu as LogMenuContract;
use Arcanedev\LogViewer\Contracts\Utilities\LogStyler as LogStylerContract;
use Arcanedev\LogViewer\LogViewer;
use Arcanedev\LogViewer\Utilities;
use Arcanedev\Support\Providers\ServiceProvider;
use Illuminate\Contracts\Support\DeferrableProvider;
/**
* Class DeferredServicesProvider
*
* @author ARCANEDEV <[email protected]>
*/
class DeferredServicesProvider extends ServiceProvider implements DeferrableProvider
{
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Register the service provider.
*/
public function register(): void
{
$this->registerLogViewer();
$this->registerLogLevels();
$this->registerStyler();
$this->registerLogMenu();
$this->registerFilesystem();
$this->registerFactory();
$this->registerChecker();
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides(): array
{
return [
LogViewerContract::class,
LogLevelsContract::class,
LogStylerContract::class,
LogMenuContract::class,
FilesystemContract::class,
FactoryContract::class,
LogCheckerContract::class,
];
}
/* -----------------------------------------------------------------
| LogViewer Utilities
| -----------------------------------------------------------------
*/
/**
* Register the log viewer service.
*/
private function registerLogViewer(): void
{
$this->singleton(LogViewerContract::class, LogViewer::class);
}
/**
* Register the log levels.
*/
private function registerLogLevels(): void
{
$this->singleton(LogLevelsContract::class, function ($app) {
return new Utilities\LogLevels(
$app['translator'],
$app['config']->get('log-viewer.locale')
);
});
}
/**
* Register the log styler.
*/
private function registerStyler(): void
{
$this->singleton(LogStylerContract::class, Utilities\LogStyler::class);
}
/**
* Register the log menu builder.
*/
private function registerLogMenu(): void
{
$this->singleton(LogMenuContract::class, Utilities\LogMenu::class);
}
/**
* Register the log filesystem.
*/
private function registerFilesystem(): void
{
$this->singleton(FilesystemContract::class, function ($app) {
/** @var \Illuminate\Config\Repository $config */
$config = $app['config'];
$filesystem = new Utilities\Filesystem($app['files'], $config->get('log-viewer.storage-path'));
return $filesystem->setPattern(
$config->get('log-viewer.pattern.prefix', FilesystemContract::PATTERN_PREFIX),
$config->get('log-viewer.pattern.date', FilesystemContract::PATTERN_DATE),
$config->get('log-viewer.pattern.extension', FilesystemContract::PATTERN_EXTENSION)
);
});
}
/**
* Register the log factory class.
*/
private function registerFactory(): void
{
$this->singleton(FactoryContract::class, Utilities\Factory::class);
}
/**
* Register the log checker service.
*/
private function registerChecker(): void
{
$this->singleton(LogCheckerContract::class, Utilities\LogChecker::class);
}
}
@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Providers;
use Arcanedev\LogViewer\Http\Routes\LogViewerRoute;
use Arcanedev\Support\Providers\RouteServiceProvider as ServiceProvider;
/**
* Class RouteServiceProvider
*
* @author ARCANEDEV <[email protected]>
*/
class RouteServiceProvider extends ServiceProvider
{
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Check if routes is enabled
*
* @return bool
*/
public function isEnabled(): bool
{
return (bool) $this->config('enabled', false);
}
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Boot the service provider.
*/
public function boot(): void
{
if ($this->isEnabled()) {
$this->routes(function () {
static::mapRouteClasses([LogViewerRoute::class]);
});
}
}
/* -----------------------------------------------------------------
| Other Methods
| -----------------------------------------------------------------
*/
/**
* Get config value by key
*
* @param string $key
* @param mixed|null $default
*
* @return mixed
*/
private function config($key, $default = null)
{
return $this->app['config']->get("log-viewer.route.$key", $default);
}
}
+209
View File
@@ -0,0 +1,209 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Tables;
use Arcanedev\LogViewer\Contracts\Table as TableContract;
use Arcanedev\LogViewer\Contracts\Utilities\LogLevels as LogLevelsContract;
/**
* Class AbstractTable
*
* @author ARCANEDEV <[email protected]>
*/
abstract class AbstractTable implements TableContract
{
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/** @var array */
private $header = [];
/** @var array */
private $rows = [];
/** @var array */
private $footer = [];
/** @var \Arcanedev\LogViewer\Contracts\Utilities\LogLevels */
protected $levels;
/** @var string|null */
protected $locale;
/** @var array */
private $data = [];
/* -----------------------------------------------------------------
| Constructor
| -----------------------------------------------------------------
*/
/**
* Create a table instance.
*
* @param array $data
* @param \Arcanedev\LogViewer\Contracts\Utilities\LogLevels $levels
* @param string|null $locale
*/
public function __construct(array $data, LogLevelsContract $levels, $locale = null)
{
$this->setLevels($levels);
$this->setLocale(is_null($locale) ? config('log-viewer.locale') : $locale);
$this->setData($data);
$this->init();
}
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Set LogLevels instance.
*
* @param \Arcanedev\LogViewer\Contracts\Utilities\LogLevels $levels
*
* @return $this
*/
protected function setLevels(LogLevelsContract $levels)
{
$this->levels = $levels;
return $this;
}
/**
* Set table locale.
*
* @param string|null $locale
*
* @return $this
*/
protected function setLocale($locale)
{
if (is_null($locale) || $locale === 'auto') {
$locale = app()->getLocale();
}
$this->locale = $locale;
return $this;
}
/**
* Get table header.
*
* @return array
*/
public function header()
{
return $this->header;
}
/**
* Get table rows.
*
* @return array
*/
public function rows()
{
return $this->rows;
}
/**
* Get table footer.
*
* @return array
*/
public function footer()
{
return $this->footer;
}
/**
* Get raw data.
*
* @return array
*/
public function data()
{
return $this->data;
}
/**
* Set table data.
*
* @param array $data
*
* @return $this
*/
private function setData(array $data)
{
$this->data = $data;
return $this;
}
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Prepare the table.
*/
private function init()
{
$this->header = $this->prepareHeader($this->data);
$this->rows = $this->prepareRows($this->data);
$this->footer = $this->prepareFooter($this->data);
}
/**
* Prepare table header.
*
* @param array $data
*
* @return array
*/
abstract protected function prepareHeader(array $data);
/**
* Prepare table rows.
*
* @param array $data
*
* @return array
*/
abstract protected function prepareRows(array $data);
/**
* Prepare table footer.
*
* @param array $data
*
* @return array
*/
abstract protected function prepareFooter(array $data);
/* -----------------------------------------------------------------
| Other Methods
| -----------------------------------------------------------------
*/
/**
* Get log level color.
*
* @param string $level
*
* @return string
*/
protected function color($level)
{
return log_styler()->color($level);
}
}
+135
View File
@@ -0,0 +1,135 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Tables;
use Arcanedev\LogViewer\Contracts\Utilities\LogLevels as LogLevelsContract;
use Illuminate\Support\{Arr, Collection};
/**
* Class StatsTable
*
* @author ARCANEDEV <[email protected]>
*/
class StatsTable extends AbstractTable
{
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Make a stats table instance.
*
* @param array $data
* @param \Arcanedev\LogViewer\Contracts\Utilities\LogLevels $levels
* @param string|null $locale
*
* @return $this
*/
public static function make(array $data, LogLevelsContract $levels, $locale = null)
{
return new static($data, $levels, $locale);
}
/* -----------------------------------------------------------------
| Other Methods
| -----------------------------------------------------------------
*/
/**
* Prepare table header.
*
* @param array $data
*
* @return array
*/
protected function prepareHeader(array $data)
{
return array_merge_recursive(
[
'date' => __('Date'),
'all' => __('All'),
],
$this->levels->names($this->locale)
);
}
/**
* Prepare table rows.
*
* @param array $data
*
* @return array
*/
protected function prepareRows(array $data)
{
$rows = [];
foreach ($data as $date => $levels) {
$rows[$date] = array_merge(compact('date'), $levels);
}
return $rows;
}
/**
* Prepare table footer.
*
* @param array $data
*
* @return array
*/
protected function prepareFooter(array $data)
{
$footer = [];
foreach ($data as $date => $levels) {
foreach ($levels as $level => $count) {
if ( ! isset($footer[$level])) {
$footer[$level] = 0;
}
$footer[$level] += $count;
}
}
return $footer;
}
/**
* Get totals.
*
* @param string|null $locale
*
* @return \Illuminate\Support\Collection
*/
public function totals($locale = null)
{
$totals = Collection::make();
foreach (Arr::except($this->footer(), 'all') as $level => $count) {
$totals->put($level, [
'label' => log_levels()->get($level, $locale),
'value' => $count,
'color' => $this->color($level),
'highlight' => $this->color($level),
]);
}
return $totals;
}
/**
* Get json totals data.
*
* @param string|null $locale
*
* @return string
*/
public function totalsJson($locale = null)
{
return $this->totals($locale)->toJson(JSON_PRETTY_PRINT);
}
}
+326
View File
@@ -0,0 +1,326 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Utilities;
use Arcanedev\LogViewer\Contracts\Utilities\Factory as FactoryContract;
use Arcanedev\LogViewer\Contracts\Utilities\Filesystem as FilesystemContract;
use Arcanedev\LogViewer\Contracts\Utilities\LogLevels as LogLevelsContract;
use Arcanedev\LogViewer\Entities\LogCollection;
use Arcanedev\LogViewer\Entities\Log;
use Arcanedev\LogViewer\Exceptions\LogNotFoundException;
use Arcanedev\LogViewer\Tables\StatsTable;
/**
* Class Factory
*
* @author ARCANEDEV <[email protected]>
*/
class Factory implements FactoryContract
{
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/**
* The filesystem instance.
*
* @var \Arcanedev\LogViewer\Contracts\Utilities\Filesystem
*/
protected $filesystem;
/**
* The log levels instance.
*
* @var \Arcanedev\LogViewer\Contracts\Utilities\LogLevels
*/
private $levels;
/* -----------------------------------------------------------------
| Constructor
| -----------------------------------------------------------------
*/
/**
* Create a new instance.
*
* @param \Arcanedev\LogViewer\Contracts\Utilities\Filesystem $filesystem
* @param \Arcanedev\LogViewer\Contracts\Utilities\LogLevels $levels
*/
public function __construct(FilesystemContract $filesystem, LogLevelsContract $levels) {
$this->setFilesystem($filesystem);
$this->setLevels($levels);
}
/* -----------------------------------------------------------------
| Getter & Setters
| -----------------------------------------------------------------
*/
/**
* Get the filesystem instance.
*
* @return \Arcanedev\LogViewer\Contracts\Utilities\Filesystem
*/
public function getFilesystem()
{
return $this->filesystem;
}
/**
* Set the filesystem instance.
*
* @param \Arcanedev\LogViewer\Contracts\Utilities\Filesystem $filesystem
*
* @return self
*/
public function setFilesystem(FilesystemContract $filesystem)
{
$this->filesystem = $filesystem;
return $this;
}
/**
* Get the log levels instance.
*
* @return \Arcanedev\LogViewer\Contracts\Utilities\LogLevels
*/
public function getLevels()
{
return $this->levels;
}
/**
* Set the log levels instance.
*
* @param \Arcanedev\LogViewer\Contracts\Utilities\LogLevels $levels
*
* @return self
*/
public function setLevels(LogLevelsContract $levels)
{
$this->levels = $levels;
return $this;
}
/**
* Set the log storage path.
*
* @param string $storagePath
*
* @return self
*/
public function setPath($storagePath)
{
$this->filesystem->setPath($storagePath);
return $this;
}
/**
* Get the log pattern.
*
* @return string
*/
public function getPattern()
{
return $this->filesystem->getPattern();
}
/**
* Set the log pattern.
*
* @param string $date
* @param string $prefix
* @param string $extension
*
* @return self
*/
public function setPattern(
$prefix = FilesystemContract::PATTERN_PREFIX,
$date = FilesystemContract::PATTERN_DATE,
$extension = FilesystemContract::PATTERN_EXTENSION
) {
$this->filesystem->setPattern($prefix, $date, $extension);
return $this;
}
/**
* Get all logs.
*
* @return \Arcanedev\LogViewer\Entities\LogCollection
*/
public function logs()
{
return (new LogCollection)->setFilesystem($this->filesystem);
}
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Get all logs (alias).
*
* @see logs
*
* @return \Arcanedev\LogViewer\Entities\LogCollection
*/
public function all()
{
return $this->logs();
}
/**
* Paginate all logs.
*
* @param int $perPage
*
* @return \Illuminate\Pagination\LengthAwarePaginator
*/
public function paginate($perPage = 30)
{
return $this->logs()->paginate($perPage);
}
/**
* Get a log by date.
*
* @param string $date
*
* @return \Arcanedev\LogViewer\Entities\Log
*/
public function log($date)
{
$dates = $this->filesystem->dates(true);
if (!isset($dates[$date])) {
throw new LogNotFoundException("Log not found in this date [$date]");
}
return new Log($date, $dates[$date], $this->filesystem->read($date));
}
/**
* Get a log by date (alias).
*
* @param string $date
*
* @return \Arcanedev\LogViewer\Entities\Log
*/
public function get($date)
{
return $this->log($date);
}
/**
* Get log entries.
*
* @param string $date
* @param string $level
*
* @return \Arcanedev\LogViewer\Entities\LogEntryCollection
*/
public function entries($date, $level = 'all')
{
return $this->log($date)->entries($level);
}
/**
* Get logs statistics.
*
* @return array
*/
public function stats()
{
return $this->logs()->stats();
}
/**
* Get logs statistics table.
*
* @param string|null $locale
*
* @return \Arcanedev\LogViewer\Tables\StatsTable
*/
public function statsTable($locale = null)
{
return StatsTable::make($this->stats(), $this->levels, $locale);
}
/**
* List the log files (dates).
*
* @return array
*/
public function dates()
{
return $this->filesystem->dates();
}
/**
* Get logs count.
*
* @return int
*/
public function count()
{
return $this->logs()->count();
}
/**
* Get total log entries.
*
* @param string $level
*
* @return int
*/
public function total($level = 'all')
{
return $this->logs()->total($level);
}
/**
* Get tree menu.
*
* @param bool $trans
*
* @return array
*/
public function tree($trans = false)
{
return $this->logs()->tree($trans);
}
/**
* Get tree menu.
*
* @param bool $trans
*
* @return array
*/
public function menu($trans = true)
{
return $this->logs()->menu($trans);
}
/* -----------------------------------------------------------------
| Check Methods
| -----------------------------------------------------------------
*/
/**
* Determine if the log folder is empty or not.
*
* @return bool
*/
public function isEmpty()
{
return $this->logs()->isEmpty();
}
}
+341
View File
@@ -0,0 +1,341 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Utilities;
use Arcanedev\LogViewer\Contracts\Utilities\Filesystem as FilesystemContract;
use Arcanedev\LogViewer\Exceptions\FilesystemException;
use Arcanedev\LogViewer\Helpers\LogParser;
use Exception;
use Illuminate\Filesystem\Filesystem as IlluminateFilesystem;
/**
* Class Filesystem
*
* @author ARCANEDEV <[email protected]>
*/
class Filesystem implements FilesystemContract
{
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/**
* The filesystem instance.
*
* @var \Illuminate\Filesystem\Filesystem
*/
protected $filesystem;
/**
* The base storage path.
*
* @var string
*/
protected $storagePath;
/**
* The log files prefix pattern.
*
* @var string
*/
protected $prefixPattern;
/**
* The log files date pattern.
*
* @var string
*/
protected $datePattern;
/**
* The log files extension.
*
* @var string
*/
protected $extension;
/* -----------------------------------------------------------------
| Constructor
| -----------------------------------------------------------------
*/
/**
* Filesystem constructor.
*
* @param \Illuminate\Filesystem\Filesystem $files
* @param string $storagePath
*/
public function __construct(IlluminateFilesystem $files, $storagePath)
{
$this->filesystem = $files;
$this->setPath($storagePath);
$this->setPattern();
}
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Get the files instance.
*
* @return \Illuminate\Filesystem\Filesystem
*/
public function getInstance()
{
return $this->filesystem;
}
/**
* Set the log storage path.
*
* @param string $storagePath
*
* @return $this
*/
public function setPath($storagePath)
{
$this->storagePath = $storagePath;
return $this;
}
/**
* Get the log pattern.
*
* @return string
*/
public function getPattern(): string
{
return $this->prefixPattern.$this->datePattern.$this->extension;
}
/**
* Set the log pattern.
*
* @param string $date
* @param string $prefix
* @param string $extension
*
* @return $this
*/
public function setPattern(
$prefix = self::PATTERN_PREFIX,
$date = self::PATTERN_DATE,
$extension = self::PATTERN_EXTENSION
) {
$this->setPrefixPattern($prefix);
$this->setDatePattern($date);
$this->setExtension($extension);
return $this;
}
/**
* Set the log date pattern.
*
* @param string $datePattern
*
* @return $this
*/
public function setDatePattern($datePattern)
{
$this->datePattern = $datePattern;
return $this;
}
/**
* Set the log prefix pattern.
*
* @param string $prefixPattern
*
* @return $this
*/
public function setPrefixPattern($prefixPattern)
{
$this->prefixPattern = $prefixPattern;
return $this;
}
/**
* Set the log extension.
*
* @param string $extension
*
* @return $this
*/
public function setExtension($extension)
{
$this->extension = $extension;
return $this;
}
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Get all log files.
*
* @return array
*/
public function all()
{
return $this->getFiles('*'.$this->extension);
}
/**
* Get all valid log files.
*
* @return array
*/
public function logs()
{
return $this->getFiles($this->getPattern());
}
/**
* List the log files (Only dates).
*
* @param bool $withPaths
*
* @return array
*/
public function dates($withPaths = false)
{
$files = array_reverse($this->logs());
$dates = $this->extractDates($files);
if ($withPaths) {
$dates = array_combine($dates, $files); // [date => file]
}
return $dates;
}
/**
* Read the log.
*
* @param string $date
*
* @return string
*
* @throws \Arcanedev\LogViewer\Exceptions\FilesystemException
*/
public function read($date)
{
try {
$log = $this->filesystem->get(
$this->getLogPath($date)
);
}
catch (Exception $e) {
throw new FilesystemException($e->getMessage());
}
return $log;
}
/**
* Delete the log.
*
* @param string $date
*
* @return bool
*
* @throws \Arcanedev\LogViewer\Exceptions\FilesystemException
*/
public function delete(string $date)
{
$path = $this->getLogPath($date);
throw_unless($this->filesystem->delete($path), FilesystemException::cannotDeleteLog());
return true;
}
/**
* Clear the log files.
*
* @return bool
*/
public function clear()
{
return $this->filesystem->delete($this->logs());
}
/**
* Get the log file path.
*
* @param string $date
*
* @return string
*/
public function path($date)
{
return $this->getLogPath($date);
}
/* -----------------------------------------------------------------
| Other Methods
| -----------------------------------------------------------------
*/
/**
* Get all files.
*
* @param string $pattern
*
* @return array
*/
private function getFiles($pattern)
{
$files = $this->filesystem->glob(
$this->storagePath.DIRECTORY_SEPARATOR.$pattern, defined('GLOB_BRACE') ? GLOB_BRACE : 0
);
return array_filter(array_map('realpath', $files));
}
/**
* Get the log file path.
*
* @param string $date
*
* @return string
*
* @throws \Arcanedev\LogViewer\Exceptions\FilesystemException
*/
private function getLogPath(string $date)
{
$path = $this->storagePath.DIRECTORY_SEPARATOR.$this->prefixPattern.$date.$this->extension;
if ( ! $this->filesystem->exists($path)) {
throw FilesystemException::invalidPath($path);
}
return realpath($path);
}
/**
* Extract dates from files.
*
* @param array $files
*
* @return array
*/
private function extractDates(array $files)
{
return array_map(function ($file) {
return LogParser::extractDate(basename($file));
}, $files);
}
}
+312
View File
@@ -0,0 +1,312 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Utilities;
use Arcanedev\LogViewer\Contracts\Utilities\Filesystem as FilesystemContract;
use Arcanedev\LogViewer\Contracts\Utilities\LogChecker as LogCheckerContract;
use Illuminate\Contracts\Config\Repository as ConfigContract;
/**
* Class LogChecker
*
* @author ARCANEDEV <[email protected]>
*/
class LogChecker implements LogCheckerContract
{
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/**
* The config repository instance.
*
* @var \Illuminate\Contracts\Config\Repository
*/
private $config;
/**
* The filesystem instance.
*
* @var \Arcanedev\LogViewer\Contracts\Utilities\Filesystem
*/
private $filesystem;
/**
* Log handler mode.
*
* @var string
*/
protected $handler = '';
/**
* The check status.
*
* @var bool
*/
private $status = true;
/**
* The check messages.
*
* @var array
*/
private $messages;
/**
* Log files statuses.
*
* @var array
*/
private $files = [];
/* -----------------------------------------------------------------
| Constructor
| -----------------------------------------------------------------
*/
/**
* LogChecker constructor.
*
* @param \Illuminate\Contracts\Config\Repository $config
* @param \Arcanedev\LogViewer\Contracts\Utilities\Filesystem $filesystem
*/
public function __construct(ConfigContract $config, FilesystemContract $filesystem)
{
$this->setConfig($config);
$this->setFilesystem($filesystem);
$this->refresh();
}
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Set the config instance.
*
* @param \Illuminate\Contracts\Config\Repository $config
*
* @return self
*/
public function setConfig(ConfigContract $config)
{
$this->config = $config;
return $this;
}
/**
* Set the Filesystem instance.
*
* @param \Arcanedev\LogViewer\Contracts\Utilities\Filesystem $filesystem
*
* @return self
*/
public function setFilesystem(FilesystemContract $filesystem)
{
$this->filesystem = $filesystem;
return $this;
}
/**
* Set the log handler mode.
*
* @param string $handler
*
* @return self
*/
protected function setHandler($handler)
{
$this->handler = strtolower($handler);
return $this;
}
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Get messages.
*
* @return array
*/
public function messages()
{
$this->refresh();
return $this->messages;
}
/**
* Check if the checker passes.
*
* @return bool
*/
public function passes()
{
$this->refresh();
return $this->status;
}
/**
* Check if the checker fails.
*
* @return bool
*/
public function fails()
{
return ! $this->passes();
}
/**
* Get the requirements.
*
* @return array
*/
public function requirements()
{
$this->refresh();
return $this->isDaily() ? [
'status' => 'success',
'header' => 'Application requirements fulfilled.',
'message' => 'Are you ready to rock ?',
] : [
'status' => 'failed',
'header' => 'Application requirements failed.',
'message' => $this->messages['handler']
];
}
/* -----------------------------------------------------------------
| Check Methods
| -----------------------------------------------------------------
*/
/**
* Is a daily handler mode ?
*
* @return bool
*/
protected function isDaily()
{
return $this->isSameHandler(self::HANDLER_DAILY);
}
/**
* Is the handler is the same as the application log handler.
*
* @param string $handler
*
* @return bool
*/
private function isSameHandler($handler)
{
return $this->handler === $handler;
}
/* -----------------------------------------------------------------
| Other Methods
| -----------------------------------------------------------------
*/
/**
* Refresh the checks.
*
* @return \Arcanedev\LogViewer\Utilities\LogChecker
*/
private function refresh()
{
$this->setHandler($this->config->get('logging.default', 'stack'));
$this->messages = [
'handler' => '',
'files' => [],
];
$this->files = [];
$this->checkHandler();
$this->checkLogFiles();
return $this;
}
/**
* Check the handler mode.
*/
private function checkHandler()
{
if ($this->isDaily()) return;
$this->messages['handler'] = 'You should set the log handler to `daily` mode. Please check the LogViewer wiki page (Requirements) for more details.';
}
/**
* Check all log files.
*/
private function checkLogFiles()
{
foreach ($this->filesystem->all() as $path) {
$this->checkLogFile($path);
}
}
/**
* Check a log file.
*
* @param string $path
*/
private function checkLogFile($path)
{
$status = true;
$filename = basename($path);
$message = "The log file [$filename] is valid.";
$pattern = $this->filesystem->getPattern();
if ($this->isSingleLogFile($filename)) {
$this->status = $status = false;
$this->messages['files'][$filename] = $message =
"You have a single log file in your application, you should split the [$filename] into separate log files.";
}
elseif ($this->isInvalidLogPattern($filename, $pattern)) {
$this->status = $status = false;
$this->messages['files'][$filename] = $message =
"The log file [$filename] has an invalid date, the format must be like {$pattern}.";
}
$this->files[$filename] = compact('filename', 'status', 'message', 'path');
}
/**
* Check if it's not a single log file.
*
* @param string $file
*
* @return bool
*/
private function isSingleLogFile($file)
{
return $file === 'laravel.log';
}
/**
* Check the date of the log file.
*
* @param string $file
* @param string $pattern
*
* @return bool
*/
private function isInvalidLogPattern($file, $pattern)
{
return ((bool) preg_match("/{$pattern}/", $file, $matches)) === false;
}
}
+183
View File
@@ -0,0 +1,183 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Utilities;
use Arcanedev\LogViewer\Contracts\Utilities\LogLevels as LogLevelsContract;
use Illuminate\Support\Arr;
use Illuminate\Translation\Translator;
use Psr\Log\LogLevel;
use ReflectionClass;
/**
* Class LogLevels
*
* @author ARCANEDEV <[email protected]>
*/
class LogLevels implements LogLevelsContract
{
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/**
* The log levels.
*
* @var array
*/
protected static $levels = [];
/**
* The Translator instance.
*
* @var \Illuminate\Translation\Translator
*/
private $translator;
/**
* The selected locale.
*
* @var string
*/
private $locale;
/* -----------------------------------------------------------------
| Constructor
| -----------------------------------------------------------------
*/
/**
* LogLevels constructor.
*
* @param \Illuminate\Translation\Translator $translator
* @param string $locale
*/
public function __construct(Translator $translator, $locale)
{
$this->setTranslator($translator);
$this->setLocale($locale);
}
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Set the Translator instance.
*
* @param \Illuminate\Translation\Translator $translator
*
* @return $this
*/
public function setTranslator(Translator $translator)
{
$this->translator = $translator;
return $this;
}
/**
* Get the selected locale.
*
* @return string
*/
public function getLocale()
{
return $this->locale === 'auto'
? $this->translator->getLocale()
: $this->locale;
}
/**
* Set the selected locale.
*
* @param string $locale
*
* @return $this
*/
public function setLocale($locale)
{
$this->locale = is_null($locale) ? 'auto' : $locale;
return $this;
}
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Get the log levels.
*
* @param bool $flip
*
* @return array
*/
public function lists($flip = false)
{
return static::all($flip);
}
/**
* Get translated levels.
*
* @param string|null $locale
*
* @return array
*/
public function names($locale = null)
{
$levels = static::all(true);
array_walk($levels, function (&$name, $level) use ($locale) {
$name = $this->get($level, $locale);
});
return $levels;
}
/**
* Get PSR log levels.
*
* @param bool $flip
*
* @return array
*/
public static function all($flip = false)
{
if (empty(static::$levels)) {
static::$levels = (new ReflectionClass(LogLevel::class))->getConstants();
}
return $flip ? array_flip(static::$levels) : static::$levels;
}
/**
* Get the translated level.
*
* @param string $key
* @param string|null $locale
*
* @return string
*/
public function get($key, $locale = null)
{
$translations = [
'all' => 'All',
LogLevel::EMERGENCY => 'Emergency',
LogLevel::ALERT => 'Alert',
LogLevel::CRITICAL => 'Critical',
LogLevel::ERROR => 'Error',
LogLevel::WARNING => 'Warning',
LogLevel::NOTICE => 'Notice',
LogLevel::INFO => 'Info',
LogLevel::DEBUG => 'Debug',
];
return $this->translator->get(Arr::get($translations, $key, $key), [], $locale ?: $this->getLocale());
}
}
+148
View File
@@ -0,0 +1,148 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Utilities;
use Arcanedev\LogViewer\Contracts\Utilities\LogMenu as LogMenuContract;
use Arcanedev\LogViewer\Contracts\Utilities\LogStyler as LogStylerContract;
use Arcanedev\LogViewer\Entities\Log;
use Illuminate\Contracts\Config\Repository as ConfigContract;
/**
* Class LogMenu
*
* @author ARCANEDEV <[email protected]>
*/
class LogMenu implements LogMenuContract
{
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/**
* The config repository instance.
*
* @var \Illuminate\Contracts\Config\Repository
*/
protected $config;
/**
* The log styler instance.
*
* @var \Arcanedev\LogViewer\Contracts\Utilities\LogStyler
*/
private $styler;
/* -----------------------------------------------------------------
| Constructor
| -----------------------------------------------------------------
*/
/**
* LogMenu constructor.
*
* @param \Illuminate\Contracts\Config\Repository $config
* @param \Arcanedev\LogViewer\Contracts\Utilities\LogStyler $styler
*/
public function __construct(ConfigContract $config, LogStylerContract $styler)
{
$this->setConfig($config);
$this->setLogStyler($styler);
}
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Set the config instance.
*
* @param \Illuminate\Contracts\Config\Repository $config
*
* @return self
*/
public function setConfig(ConfigContract $config)
{
$this->config = $config;
return $this;
}
/**
* Set the log styler instance.
*
* @param \Arcanedev\LogViewer\Contracts\Utilities\LogStyler $styler
*
* @return self
*/
public function setLogStyler(LogStylerContract $styler)
{
$this->styler = $styler;
return $this;
}
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Make log menu.
*
* @param \Arcanedev\LogViewer\Entities\Log $log
* @param bool $trans
*
* @return array
*/
public function make(Log $log, $trans = true)
{
$items = [];
$route = $this->config('menu.filter-route');
foreach($log->tree($trans) as $level => $item) {
$items[$level] = array_merge($item, [
'url' => route($route, [$log->date, $level]),
'icon' => $this->isIconsEnabled() ? $this->styler->icon($level)->toHtml() : '',
]);
}
return $items;
}
/* -----------------------------------------------------------------
| Check Methods
| -----------------------------------------------------------------
*/
/**
* Check if the icons are enabled.
*
* @return bool
*/
private function isIconsEnabled()
{
return (bool) $this->config('menu.icons-enabled', false);
}
/* -----------------------------------------------------------------
| Other Methods
| -----------------------------------------------------------------
*/
/**
* Get config.
*
* @param string $key
* @param mixed $default
*
* @return mixed
*/
private function config($key, $default = null)
{
return $this->config->get("log-viewer.$key", $default);
}
}
+107
View File
@@ -0,0 +1,107 @@
<?php
declare(strict_types=1);
namespace Arcanedev\LogViewer\Utilities;
use Arcanedev\LogViewer\Contracts\Utilities\LogStyler as LogStylerContract;
use Illuminate\Contracts\Config\Repository as ConfigContract;
use Illuminate\Support\HtmlString;
/**
* Class LogStyler
*
* @author ARCANEDEV <[email protected]>
*/
class LogStyler implements LogStylerContract
{
/* -----------------------------------------------------------------
| Properties
| -----------------------------------------------------------------
*/
/**
* The config repository instance.
*
* @var \Illuminate\Contracts\Config\Repository
*/
protected $config;
/* -----------------------------------------------------------------
| Constructor
| -----------------------------------------------------------------
*/
/**
* Create a new instance.
*
* @param \Illuminate\Contracts\Config\Repository $config
*/
public function __construct(ConfigContract $config)
{
$this->config = $config;
}
/* -----------------------------------------------------------------
| Getters & Setters
| -----------------------------------------------------------------
*/
/**
* Get config.
*
* @param string $key
* @param mixed $default
*
* @return mixed
*/
private function get($key, $default = null)
{
return $this->config->get("log-viewer.$key", $default);
}
/* -----------------------------------------------------------------
| Main Methods
| -----------------------------------------------------------------
*/
/**
* Make level icon.
*
* @param string $level
* @param string|null $default
*
* @return \Illuminate\Support\HtmlString
*/
public function icon($level, $default = null)
{
return new HtmlString(
'<i class="'.$this->get("icons.$level", $default).'"></i>'
);
}
/**
* Get level color.
*
* @param string $level
* @param string|null $default
*
* @return string
*/
public function color($level, $default = null)
{
return $this->get("colors.levels.$level", $default);
}
/**
* Get strings to highlight.
*
* @param array $default
*
* @return array
*/
public function toHighlight(array $default = [])
{
return $this->get('highlight', $default);
}
}