ubah ke docker mode
This commit is contained in:
No files matched your search
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) ARCANEDEV <arcanedev.maroc@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
# LogViewer [![Packagist License][badge_license]](LICENSE.md) [![For Laravel][badge_laravel]][link-github-repo]
|
||||
|
||||
[![Github Workflow Status][badge_build]][link-github-status]
|
||||
[![Coverage Status][badge_coverage]][link-scrutinizer]
|
||||
[![Scrutinizer Code Quality][badge_quality]][link-scrutinizer]
|
||||
[![SensioLabs Insight][badge_insight]][link-insight]
|
||||
[![Github Issues][badge_issues]][link-github-issues]
|
||||
|
||||
[![Packagist][badge_package]][link-packagist]
|
||||
[![Packagist Release][badge_release]][link-packagist]
|
||||
[![Packagist Downloads][badge_downloads]][link-packagist]
|
||||
|
||||
*By [ARCANEDEV©](http://www.arcanedev.net/)*
|
||||
|
||||
This package allows you to manage and keep track of each one of your log files.
|
||||
|
||||
> **NOTE: You can also use LogViewer as an API.**
|
||||
|
||||
Official documentation for LogViewer can be found at the [_docs folder](_docs/1.Installation-and-Setup.md).
|
||||
|
||||
Feel free to check out the [releases](https://github.com/ARCANEDEV/LogViewer/releases), [license](LICENSE.md), and [contribution guidelines](CONTRIBUTING.md).
|
||||
|
||||
## Features
|
||||
|
||||
- A great Log viewer API.
|
||||
- Laravel `5.x` to `10.x` are supported.
|
||||
- Ready to use (Views, Routes, controllers … Out of the box) [Note: No need to publish assets]
|
||||
- View, paginate, filter, download and delete logs.
|
||||
- Load a custom logs storage path.
|
||||
- Localized log levels.
|
||||
- Logs menu/tree generator.
|
||||
- Grouped logs by dates and levels.
|
||||
- Customized log levels icons (font awesome by default).
|
||||
- Works great with big logs !!
|
||||
- Well documented package (IDE Friendly).
|
||||
- Well tested (100% code coverage with maximum code quality).
|
||||
|
||||
## Table of contents
|
||||
|
||||
1. [Installation and Setup](_docs/1.Installation-and-Setup.md)
|
||||
2. [Configuration](_docs/2.Configuration.md)
|
||||
3. [Usage](_docs/3.Usage.md)
|
||||
|
||||
### Supported localizations
|
||||
|
||||
> Dear artisans, i'm counting on you to help me out to add more translations ( ^_^)b
|
||||
|
||||
| Local | Language |
|
||||
|---------|-----------------------|
|
||||
| `ar` | Arabic |
|
||||
| `bg` | Bulgarian |
|
||||
| `bn` | Bengali |
|
||||
| `de` | German |
|
||||
| `en` | English |
|
||||
| `es` | Spanish |
|
||||
| `et` | Estonian |
|
||||
| `fa` | Farsi |
|
||||
| `fr` | French |
|
||||
| `he` | Hebrew |
|
||||
| `hu` | Hungarian |
|
||||
| `hy` | Armenian |
|
||||
| `id` | Indonesian |
|
||||
| `it` | Italian |
|
||||
| `ja` | Japanese |
|
||||
| `ko` | Korean |
|
||||
| `ms` | Malay |
|
||||
| `nl` | Dutch |
|
||||
| `pl` | Polish |
|
||||
| `pt-BR` | Brazilian Portuguese |
|
||||
| `ro` | Romanian |
|
||||
| `ru` | Russian |
|
||||
| `si` | Sinhalese |
|
||||
| `sv` | Swedish |
|
||||
| `th` | Thai |
|
||||
| `tr` | Turkish |
|
||||
| `uz` | Uzbek |
|
||||
| `uk` | Ukrainian |
|
||||
| `zh` | Chinese (Simplified) |
|
||||
| `zh-TW` | Chinese (Traditional) |
|
||||
|
||||
## Contribution
|
||||
|
||||
Any ideas are welcome. Feel free to submit any issues or pull requests, please check the [contribution guidelines](CONTRIBUTING.md).
|
||||
|
||||
## Security
|
||||
|
||||
If you discover any security related issues, please email arcanedev.maroc@gmail.com instead of using the issue tracker.
|
||||
|
||||
## Credits
|
||||
|
||||
- [ARCANEDEV][link-author]
|
||||
- [All Contributors][link-contributors]
|
||||
|
||||
## PREVIEW
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
[badge_laravel]: https://img.shields.io/badge/Laravel-5.x%20to%2010.x-orange.svg?style=flat-square
|
||||
[badge_license]: https://img.shields.io/packagist/l/arcanedev/log-viewer.svg?style=flat-square
|
||||
[badge_build]: https://img.shields.io/github/workflow/status/ARCANEDEV/LogViewer/run-tests?style=flat-square
|
||||
[badge_coverage]: https://img.shields.io/scrutinizer/coverage/g/ARCANEDEV/LogViewer.svg?style=flat-square
|
||||
[badge_quality]: https://img.shields.io/scrutinizer/g/ARCANEDEV/LogViewer.svg?style=flat-square
|
||||
[badge_insight]: https://img.shields.io/sensiolabs/i/d6b4587a-e00d-4dac-98db-f979731fdc4d.svg?style=flat-square
|
||||
[badge_issues]: https://img.shields.io/github/issues/ARCANEDEV/LogViewer.svg?style=flat-square
|
||||
[badge_package]: https://img.shields.io/badge/package-arcanedev/log--viewer-blue.svg?style=flat-square
|
||||
[badge_release]: https://img.shields.io/packagist/v/arcanedev/log-viewer.svg?style=flat-square
|
||||
[badge_downloads]: https://img.shields.io/packagist/dt/arcanedev/log-viewer.svg?style=flat-square
|
||||
|
||||
[link-author]: https://github.com/arcanedev-maroc
|
||||
[link-github-status]: https://github.com/ARCANEDEV/LogViewer/actions
|
||||
[link-github-repo]: https://github.com/ARCANEDEV/LogViewer
|
||||
[link-github-issues]: https://github.com/ARCANEDEV/LogViewer/issues
|
||||
[link-contributors]: https://github.com/ARCANEDEV/LogViewer/graphs/contributors
|
||||
[link-packagist]: https://packagist.org/packages/arcanedev/log-viewer
|
||||
[link-scrutinizer]: https://scrutinizer-ci.com/g/ARCANEDEV/LogViewer/?branch=master
|
||||
[link-insight]: https://insight.sensiolabs.com/projects/d6b4587a-e00d-4dac-98db-f979731fdc4d
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "arcanedev/log-viewer",
|
||||
"description": "Provides a Log Viewer for Laravel",
|
||||
"keywords": ["arcanedev", "arcanesoft", "laravel", "log", "log viewer", "log-viewer", "logviewer"],
|
||||
"homepage": "https://github.com/ARCANEDEV/LogViewer",
|
||||
"authors": [
|
||||
{
|
||||
"name": "ARCANEDEV",
|
||||
"email": "[email protected]",
|
||||
"homepage": "https://github.com/arcanedev-maroc",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"ext-json": "*",
|
||||
"arcanedev/support": "^10.0",
|
||||
"psr/log": "^1.0|^2.0|^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/framework": "^10.0",
|
||||
"mockery/mockery": "^1.5.1",
|
||||
"orchestra/testbench-core": "^8.0",
|
||||
"phpunit/phpunit": "^10.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Arcanedev\\LogViewer\\": "src/"
|
||||
},
|
||||
"files": ["helpers.php"]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Arcanedev\\LogViewer\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpunit --colors=always",
|
||||
"test:dox": "phpunit --testdox --colors=always",
|
||||
"test:cov": "phpunit --coverage-html build/coverage"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-develop": "10.x-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Arcanedev\\LogViewer\\LogViewerServiceProvider",
|
||||
"Arcanedev\\LogViewer\\Providers\\DeferredServicesProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
use Arcanedev\LogViewer\Contracts\Utilities\Filesystem;
|
||||
|
||||
return [
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Log files storage path
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'storage-path' => storage_path('logs'),
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Log files pattern
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'pattern' => [
|
||||
'prefix' => Filesystem::PATTERN_PREFIX, // 'laravel-'
|
||||
'date' => Filesystem::PATTERN_DATE, // '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
|
||||
'extension' => Filesystem::PATTERN_EXTENSION, // '.log'
|
||||
],
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Locale
|
||||
| -----------------------------------------------------------------
|
||||
| Supported locales :
|
||||
| 'auto', 'ar', 'bg', 'de', 'en', 'es', 'et', 'fa', 'fr', 'hu', 'hy', 'id', 'it', 'ja', 'ko', 'nl',
|
||||
| 'pl', 'pt-BR', 'ro', 'ru', 'sv', 'th', 'tr', 'zh-TW', 'zh'
|
||||
*/
|
||||
|
||||
'locale' => 'auto',
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Theme
|
||||
| -----------------------------------------------------------------
|
||||
| Supported themes :
|
||||
| 'bootstrap-5'
|
||||
| 'bootstrap-4'
|
||||
| 'bootstrap-3'
|
||||
|
|
||||
| You can make your own theme by adding a folder to the views directory and specifying it here.
|
||||
*/
|
||||
|
||||
'theme' => 'bootstrap-5',
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Route settings
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'route' => [
|
||||
'enabled' => true,
|
||||
|
||||
'attributes' => [
|
||||
'prefix' => 'log-viewer',
|
||||
|
||||
'middleware' => env('ARCANEDEV_LOGVIEWER_MIDDLEWARE') ? explode(',', env('ARCANEDEV_LOGVIEWER_MIDDLEWARE')) : null,
|
||||
],
|
||||
|
||||
'show' => 'log-viewer::logs.show'
|
||||
],
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Log entries per page
|
||||
| -----------------------------------------------------------------
|
||||
| This defines how many logs & entries are displayed per page.
|
||||
*/
|
||||
|
||||
'per-page' => 30,
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Download settings
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'download' => [
|
||||
'prefix' => 'laravel-',
|
||||
|
||||
'extension' => 'log',
|
||||
],
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Menu settings
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'menu' => [
|
||||
'filter-route' => 'log-viewer::logs.filter',
|
||||
|
||||
'icons-enabled' => true,
|
||||
],
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Icons
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'icons' => [
|
||||
/**
|
||||
* Font awesome >= 4.3
|
||||
* http://fontawesome.io/icons/
|
||||
*/
|
||||
'all' => 'fa fa-fw fa-list', // http://fontawesome.io/icon/list/
|
||||
'emergency' => 'fa fa-fw fa-bug', // http://fontawesome.io/icon/bug/
|
||||
'alert' => 'fa fa-fw fa-bullhorn', // http://fontawesome.io/icon/bullhorn/
|
||||
'critical' => 'fa fa-fw fa-heartbeat', // http://fontawesome.io/icon/heartbeat/
|
||||
'error' => 'fa fa-fw fa-times-circle', // http://fontawesome.io/icon/times-circle/
|
||||
'warning' => 'fa fa-fw fa-exclamation-triangle', // http://fontawesome.io/icon/exclamation-triangle/
|
||||
'notice' => 'fa fa-fw fa-exclamation-circle', // http://fontawesome.io/icon/exclamation-circle/
|
||||
'info' => 'fa fa-fw fa-info-circle', // http://fontawesome.io/icon/info-circle/
|
||||
'debug' => 'fa fa-fw fa-life-ring', // http://fontawesome.io/icon/life-ring/
|
||||
],
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Colors
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'colors' => [
|
||||
'levels' => [
|
||||
'empty' => '#D1D1D1',
|
||||
'all' => '#8A8A8A',
|
||||
'emergency' => '#B71C1C',
|
||||
'alert' => '#D32F2F',
|
||||
'critical' => '#F44336',
|
||||
'error' => '#FF5722',
|
||||
'warning' => '#FF9100',
|
||||
'notice' => '#4CAF50',
|
||||
'info' => '#1976D2',
|
||||
'debug' => '#90CAF9',
|
||||
],
|
||||
],
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Strings to highlight in stack trace
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'highlight' => [
|
||||
'^#\d+',
|
||||
'^Stack trace:',
|
||||
],
|
||||
|
||||
];
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
use Arcanedev\LogViewer\Contracts;
|
||||
|
||||
if ( ! function_exists('log_viewer')) {
|
||||
/**
|
||||
* Get the LogViewer instance.
|
||||
*
|
||||
* @return Arcanedev\LogViewer\Contracts\LogViewer
|
||||
*/
|
||||
function log_viewer()
|
||||
{
|
||||
return app(Contracts\LogViewer::class);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists('log_levels')) {
|
||||
/**
|
||||
* Get the LogLevels instance.
|
||||
*
|
||||
* @return Arcanedev\LogViewer\Contracts\Utilities\LogLevels
|
||||
*/
|
||||
function log_levels()
|
||||
{
|
||||
return app(Contracts\Utilities\LogLevels::class);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists('log_menu')) {
|
||||
/**
|
||||
* Get the LogMenu instance.
|
||||
*
|
||||
* @return Arcanedev\LogViewer\Contracts\Utilities\LogMenu
|
||||
*/
|
||||
function log_menu()
|
||||
{
|
||||
return app(Contracts\Utilities\LogMenu::class);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists('log_styler')) {
|
||||
/**
|
||||
* Get the LogStyler instance.
|
||||
*
|
||||
* @return Arcanedev\LogViewer\Contracts\Utilities\LogStyler
|
||||
*/
|
||||
function log_styler()
|
||||
{
|
||||
return app(Contracts\Utilities\LogStyler::class);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
@@ -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 = []);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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}:"));
|
||||
}
|
||||
}
|
||||
+312
@@ -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
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+138
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "تاريخ",
|
||||
"The list of logs is empty!": "قائمة سجلات فارغة!",
|
||||
"All": "الجميع",
|
||||
"Emergency": "حالات الطوارئ",
|
||||
"Alert": "إنذار",
|
||||
"Critical": "حرج",
|
||||
"Error": "خطأ",
|
||||
"Warning": "تحذير",
|
||||
"Notice": "ملاحظة",
|
||||
"Info": "المعلومات",
|
||||
"Debug": "التصحيح"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Дата",
|
||||
"The list of logs is empty!": "Не са намерени логове!",
|
||||
"All": "Всички",
|
||||
"Emergency": "Emergency",
|
||||
"Alert": "Alert",
|
||||
"Critical": "Critical",
|
||||
"Error": "Error",
|
||||
"Warning": "Warning",
|
||||
"Notice": "Notice",
|
||||
"Info": "Info",
|
||||
"Debug": "Debug"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "তারিখ",
|
||||
"The list of logs is empty!": "কোন লগ পাওয়া যায়নি!",
|
||||
"All": "সবগুলি",
|
||||
"Emergency": "জরুরী",
|
||||
"Alert": "সতর্ক",
|
||||
"Critical": "সংকটপূর্ণ",
|
||||
"Error": "ত্রুটি",
|
||||
"Warning": "সতর্কতা",
|
||||
"Notice": "নোটিস",
|
||||
"Info": "তথ্য",
|
||||
"Debug": "রুটি অনুসন্ধান"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Datum",
|
||||
"The list of logs is empty!": "KeineLogDateiengefunden!",
|
||||
"All": "Alle",
|
||||
"Emergency": "Notfall",
|
||||
"Alert": "Alarm",
|
||||
"Critical": "Kritisch",
|
||||
"Error": "Fehler",
|
||||
"Warning": "Warnung",
|
||||
"Notice": "Hinweis",
|
||||
"Info": "Info",
|
||||
"Debug": "Debug"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Fecha",
|
||||
"The list of logs is empty!": "La lista del log está vacía!",
|
||||
"All": "Todos",
|
||||
"Emergency": "Emergencia",
|
||||
"Alert": "Alerta",
|
||||
"Critical": "Criticos",
|
||||
"Error": "Errores",
|
||||
"Warning": "Advertencia",
|
||||
"Notice": "Aviso",
|
||||
"Info": "Info",
|
||||
"Debug": "Debug"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Kuupäev",
|
||||
"The list of logs is empty!": "Logide nimekiri on tühi!",
|
||||
"All": "Kõik",
|
||||
"Emergency": "Erakorraline",
|
||||
"Alert": "Häire",
|
||||
"Critical": "Kriitiline",
|
||||
"Error": "Viga",
|
||||
"Warning": "Hoiatus",
|
||||
"Notice": "Teade",
|
||||
"Info": "Info",
|
||||
"Debug": "Silumine"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "تاریخ",
|
||||
"The list of logs is empty!": "لیست لاگ ها خالی است!",
|
||||
"All": "همه",
|
||||
"Emergency": "اورژانسی",
|
||||
"Alert": "اخطار",
|
||||
"Critical": "بحرانی",
|
||||
"Error": "خطا",
|
||||
"Warning": "هشدار",
|
||||
"Notice": "اعلان",
|
||||
"Info": "اطلاعات",
|
||||
"Debug": "دیباگ"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Date",
|
||||
"The list of logs is empty!": "La liste des logs est vide !",
|
||||
"All": "Tous",
|
||||
"Emergency": "Urgence",
|
||||
"Alert": "Alerte",
|
||||
"Critical": "Critique",
|
||||
"Error": "Erreur",
|
||||
"Warning": "Avertissement",
|
||||
"Notice": "Notice",
|
||||
"Info": "Info",
|
||||
"Debug": "Debug"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "תאריך",
|
||||
"The list of logs is empty!": "רשימת הלוגים ריקה!",
|
||||
"All": "הכל",
|
||||
"Emergency": "חרום",
|
||||
"Alert": "אזעקה",
|
||||
"Critical": "קריטי",
|
||||
"Error": "שגיאה",
|
||||
"Warning": "אזהרה",
|
||||
"Notice": "הודעה",
|
||||
"Info": "מידע",
|
||||
"Debug": "ניפוי"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Dátum",
|
||||
"The list of logs is empty!": "A naplók listája üres!",
|
||||
"All": "Összes",
|
||||
"Emergency": "Vészhelyzet",
|
||||
"Alert": "Riasztás",
|
||||
"Critical": "Kritikus",
|
||||
"Error": "Hiba",
|
||||
"Warning": "Figyelmeztetés",
|
||||
"Notice": "Értesítés",
|
||||
"Info": "Információ",
|
||||
"Debug": "Hibakeresés"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Ամսաթիվ",
|
||||
"The list of logs is empty!": "Լոգերի ցուցակը դատարկ է։",
|
||||
"All": "Բոլորը",
|
||||
"Emergency": "Վթարային",
|
||||
"Alert": "Նախազգուշացում",
|
||||
"Critical": "Կրիտիկական",
|
||||
"Error": "Սխալ",
|
||||
"Warning": "Նախազգուշացում",
|
||||
"Notice": "Ծանուցում",
|
||||
"Info": "Տեղեկատվություն",
|
||||
"Debug": "Կարգաբերում"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Tanggal",
|
||||
"The list of logs is empty!": "Daftar Log Kosong",
|
||||
"All": "Semua",
|
||||
"Emergency": "Darurat",
|
||||
"Alert": "Waspada",
|
||||
"Critical": "Kritis",
|
||||
"Error": "Kesalahan",
|
||||
"Warning": "Peringatan",
|
||||
"Notice": "Pemberitahuan",
|
||||
"Info": "Info",
|
||||
"Debug": "Debug"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Data",
|
||||
"The list of logs is empty!": "L'elenco dei log è vuoto!",
|
||||
"All": "Tutti",
|
||||
"Emergency": "Emergenza",
|
||||
"Alert": "Allarme",
|
||||
"Critical": "Critico",
|
||||
"Error": "Errore",
|
||||
"Warning": "Avviso",
|
||||
"Notice": "Notifica",
|
||||
"Info": "Info",
|
||||
"Debug": "Debug"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "日付",
|
||||
"The list of logs is empty!": "ログリストが空です!",
|
||||
"All": "すべて",
|
||||
"Emergency": "緊急",
|
||||
"Alert": "警戒",
|
||||
"Critical": "致命的",
|
||||
"Error": "エラー",
|
||||
"Warning": "警告",
|
||||
"Notice": "通知",
|
||||
"Info": "情報",
|
||||
"Debug": "デバッグ"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "날짜",
|
||||
"The list of logs is empty!": "로그가 없습니다.",
|
||||
"All": "전체",
|
||||
"Emergency": "긴급",
|
||||
"Alert": "경고",
|
||||
"Critical": "심각",
|
||||
"Error": "오류",
|
||||
"Warning": "주의",
|
||||
"Notice": "알림",
|
||||
"Info": "정보",
|
||||
"Debug": "디버그"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Tarikh",
|
||||
"The list of logs is empty!": "Senarai log kosong!",
|
||||
"All": "Semua",
|
||||
"Emergency": "Kecemasan",
|
||||
"Alert": "Waspada",
|
||||
"Critical": "Kritikal",
|
||||
"Error": "Ralat",
|
||||
"Warning": "Amaran",
|
||||
"Notice": "Notis",
|
||||
"Info": "Info",
|
||||
"Debug": "Debug"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Datum",
|
||||
"The list of logs is empty!": "De lijst met logs is leeg!",
|
||||
"All": "Alle",
|
||||
"Emergency": "Noodgeval",
|
||||
"Alert": "Alarm",
|
||||
"Critical": "Cruciaal",
|
||||
"Error": "Error",
|
||||
"Warning": "Waarschuwing",
|
||||
"Notice": "Opmerking",
|
||||
"Info": "Informatie",
|
||||
"Debug": "Debug"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Data",
|
||||
"The list of logs is empty!": "Lista logów jest pusta!",
|
||||
"All": "Wszystkie",
|
||||
"Emergency": "Awaryjne",
|
||||
"Alert": "Alerty",
|
||||
"Critical": "Krytyczne",
|
||||
"Error": "Błędy",
|
||||
"Warning": "Ostrzeżenia",
|
||||
"Notice": "Warte uwagi",
|
||||
"Info": "Informacje",
|
||||
"Debug": "Debug"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Data",
|
||||
"The list of logs is empty!": "A lista de logs está vazia!",
|
||||
"All": "Todos",
|
||||
"Emergency": "Emergência",
|
||||
"Alert": "Alerta",
|
||||
"Critical": "Crítico",
|
||||
"Error": "Erro",
|
||||
"Warning": "Aviso",
|
||||
"Notice": "Notícia",
|
||||
"Info": "Informação",
|
||||
"Debug": "Debug"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Dată",
|
||||
"The list of logs is empty!": "Nu există niciun log!",
|
||||
"All": "Toate",
|
||||
"Emergency": "Urgență",
|
||||
"Alert": "Alertă",
|
||||
"Critical": "Critic",
|
||||
"Error": "Eroare",
|
||||
"Warning": "Pericol",
|
||||
"Notice": "Avertisment",
|
||||
"Info": "Informare",
|
||||
"Debug": "Depanare"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Дата",
|
||||
"The list of logs is empty!": "Список журналов пуст!",
|
||||
"All": "Все",
|
||||
"Emergency": "Аварийная",
|
||||
"Alert": "Предупреждение",
|
||||
"Critical": "Критический",
|
||||
"Error": "Ошибка",
|
||||
"Warning": "Предупреждение",
|
||||
"Notice": "Уведомление",
|
||||
"Info": "Информация",
|
||||
"Debug": "Отладка"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "දිනය",
|
||||
"The list of logs is empty!": "සටහන් ලැයිස්තුව හිස්ය",
|
||||
"All": "සියල්ල",
|
||||
"Emergency": "හදිසි",
|
||||
"Alert": "පරීක්ෂාකාරී",
|
||||
"Critical": "අවදානම්",
|
||||
"Error": "දෝෂය",
|
||||
"Warning": "අවවාදය",
|
||||
"Notice": "නිවේදනය",
|
||||
"Info": "තොරතුරු",
|
||||
"Debug": "නිදොස්කරණය"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Datum",
|
||||
"The list of logs is empty!": "Det finns inga loggar att visa.",
|
||||
"All": "Alla",
|
||||
"Emergency": "Akut",
|
||||
"Alert": "Alarmerande",
|
||||
"Critical": "Kritisk",
|
||||
"Error": "Error",
|
||||
"Warning": "Varning",
|
||||
"Notice": "Notis",
|
||||
"Info": "Information",
|
||||
"Debug": "Debug"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "วันที่",
|
||||
"The list of logs is empty!": "ไม่มีรายการล็อก!",
|
||||
"All": "ทั้งหมด",
|
||||
"Emergency": "ฉุกเฉิน",
|
||||
"Alert": "วิกฤติ",
|
||||
"Critical": "ร้ายแรง",
|
||||
"Error": "ข้อผิดพลาด",
|
||||
"Warning": "คำเตือน",
|
||||
"Notice": "ประกาศ",
|
||||
"Info": "ข้อมูล",
|
||||
"Debug": "ดีบัก"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Tarih",
|
||||
"The list of logs is empty!": "Günlük listesi boş!",
|
||||
"All": "Tümü",
|
||||
"Emergency": "Acil",
|
||||
"Alert": "Alarm",
|
||||
"Critical": "Kritik",
|
||||
"Error": "Hata",
|
||||
"Warning": "Uyarı",
|
||||
"Notice": "Bildirim",
|
||||
"Info": "Bilgi",
|
||||
"Debug": "Hata ayıklama"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Дата",
|
||||
"The list of logs is empty!": "Список журналів порожній!",
|
||||
"All": "Всі",
|
||||
"Emergency": "Аварійна",
|
||||
"Alert": "Попередження",
|
||||
"Critical": "Критична",
|
||||
"Error": "Помилка",
|
||||
"Warning": "Попереждення",
|
||||
"Notice": "Сповіщення",
|
||||
"Info": "Інформація",
|
||||
"Debug": "Відладка"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "Sana",
|
||||
"The list of logs is empty!": "Loglar ro'yhati bo'sh!",
|
||||
"All": "Hammasi",
|
||||
"Emergency": "Favqulodda holat",
|
||||
"Alert": "Ogohlantirish",
|
||||
"Critical": "Xavfli(Muhim)",
|
||||
"Error": "Xato",
|
||||
"Warning": "Ogohlantirish",
|
||||
"Notice": "Eslatma",
|
||||
"Info": "Ma'lumot",
|
||||
"Debug": "Debug"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "日期",
|
||||
"The list of logs is empty!": "列表中沒有任何紀錄!",
|
||||
"All": "全部",
|
||||
"Emergency": "緊急",
|
||||
"Alert": "警報",
|
||||
"Critical": "嚴重",
|
||||
"Error": "錯誤",
|
||||
"Warning": "警告",
|
||||
"Notice": "注意",
|
||||
"Info": "訊息",
|
||||
"Debug": "除錯"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Date": "日期",
|
||||
"The list of logs is empty!": "日志列表为空!",
|
||||
"All": "全部",
|
||||
"Emergency": "危急",
|
||||
"Alert": "紧急",
|
||||
"Critical": "严重",
|
||||
"Error": "错误",
|
||||
"Warning": "警告",
|
||||
"Notice": "注意",
|
||||
"Info": "信息",
|
||||
"Debug": "调试"
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ app()->getLocale() }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>LogViewer - Created by ARCANEDEV</title>
|
||||
<meta name="description" content="LogViewer">
|
||||
<meta name="author" content="ARCANEDEV">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
|
||||
<link href='https://fonts.googleapis.com/css?family=Montserrat:400,700|Source+Sans+Pro:400,600' rel='stylesheet' type='text/css'>
|
||||
<style>
|
||||
html {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-top: 50px;
|
||||
/* Margin bottom by footer height */
|
||||
margin-bottom: 50px;
|
||||
font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, sans-serif;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
font-family: 'Montserrat', 'Helvetica Neue', Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.sub-header {
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #EEE;
|
||||
}
|
||||
|
||||
.navbar-inverse {
|
||||
background-color: #1a237e;
|
||||
border-color: #1a237e;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-nav>.active>a,
|
||||
.navbar-inverse .navbar-nav>.active>a:focus,
|
||||
.navbar-inverse .navbar-nav>.active>a:hover {
|
||||
background-color: #3949ab;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-brand {
|
||||
color: #c5cae9;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-nav>li>a {
|
||||
color: #c5cae9;
|
||||
}
|
||||
|
||||
.navbar-fixed-top {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.main {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.main .page-header {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
footer.main-footer {
|
||||
position: absolute;
|
||||
padding: 10px 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: #e8eaf6;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
footer.main-footer p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
footer.main-footer i.fa.fa-heart {
|
||||
color: #C62828;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.pagination > li > a,
|
||||
.pagination > li > span {
|
||||
padding: 4px 10px;
|
||||
}
|
||||
|
||||
.table-condensed > tbody > tr > td.stack,
|
||||
.table-condensed > tfoot > tr > td.stack,
|
||||
.table-condensed > thead > tr > td.stack {
|
||||
padding: 0;
|
||||
border-top: none;
|
||||
background-color: #F6F6F6;
|
||||
border-top: 1px solid #D1D1D1;
|
||||
max-width: 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.table-condensed > tbody > tr > td > p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.stack-content {
|
||||
padding: 8px;
|
||||
color: #AE0E0E;
|
||||
font-family: consolas, Menlo, Courier, monospace;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.info-box.level {
|
||||
display: block;
|
||||
padding: 0;
|
||||
margin-bottom: 15px;
|
||||
min-height: 70px;
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.1);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.info-box.level .info-box-text,
|
||||
.info-box.level .info-box-number,
|
||||
.info-box.level .info-box-icon > i {
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.info-box.level .info-box-text {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.info-box.level .info-box-content {
|
||||
padding: 5px 10px;
|
||||
margin-left: 70px;
|
||||
}
|
||||
|
||||
.info-box.level .info-box-number {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.info-box.level .info-box-icon {
|
||||
border-radius: 2px 0 0 2px;
|
||||
display: block;
|
||||
float: left;
|
||||
height: 70px; width: 70px;
|
||||
text-align: center;
|
||||
font-size: 40px;
|
||||
line-height: 70px;
|
||||
background: rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.info-box.level .progress {
|
||||
background: rgba(0,0,0,0.2);
|
||||
margin: 5px -10px 5px -10px;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
.info-box.level .progress .progress-bar {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.info-box.level-empty {
|
||||
opacity: .6;
|
||||
-webkit-filter: grayscale(1);
|
||||
-moz-filter: grayscale(1);
|
||||
-ms-filter: grayscale(1);
|
||||
filter: grayscale(1);
|
||||
-webkit-transition: all 0.2s ease-in-out;
|
||||
-moz-transition: all 0.2s ease-in-out;
|
||||
-o-transition: all 0.2s ease-in-out;
|
||||
transition: all 0.2s ease-in-out;
|
||||
-webkit-transition-property: -webkit-filter, opacity;
|
||||
-moz-transition-property: -moz-filter, opacity;
|
||||
-o-transition-property: filter, opacity;
|
||||
transition-property: -webkit-filter, -moz-filter, -o-filter, filter, opacity;
|
||||
}
|
||||
|
||||
.info-box.level-empty:hover {
|
||||
opacity: 1;
|
||||
-webkit-filter: grayscale(0);
|
||||
-moz-filter: grayscale(0);
|
||||
-ms-filter: grayscale(0);
|
||||
filter: grayscale(0);
|
||||
}
|
||||
|
||||
.level {
|
||||
padding: 2px 6px;
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);
|
||||
border-radius: 2px;
|
||||
font-size: .9em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.badge.level-all,
|
||||
.badge.level-emergency,
|
||||
.badge.level-alert,
|
||||
.badge.level-critical,
|
||||
.badge.level-error,
|
||||
.badge.level-warning,
|
||||
.badge.level-notice,
|
||||
.badge.level-info,
|
||||
.badge.level-debug,
|
||||
.level, .level i,
|
||||
.info-box.level-all,
|
||||
.info-box.level-emergency,
|
||||
.info-box.level-alert,
|
||||
.info-box.level-critical,
|
||||
.info-box.level-error,
|
||||
.info-box.level-warning,
|
||||
.info-box.level-notice,
|
||||
.info-box.level-info,
|
||||
.info-box.level-debug {
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.label-env {
|
||||
font-size: .85em;
|
||||
}
|
||||
|
||||
.badge.level-all, .level.level-all, .info-box.level-all {
|
||||
background-color: {{ log_styler()->color('all') }};
|
||||
}
|
||||
|
||||
.badge.level-emergency, .level.level-emergency, .info-box.level-emergency {
|
||||
background-color: {{ log_styler()->color('emergency') }};
|
||||
}
|
||||
|
||||
.badge.level-alert, .level.level-alert, .info-box.level-alert {
|
||||
background-color: {{ log_styler()->color('alert') }};
|
||||
}
|
||||
|
||||
.badge.level-critical, .level.level-critical, .info-box.level-critical {
|
||||
background-color: {{ log_styler()->color('critical') }};
|
||||
}
|
||||
|
||||
.badge.level-error, .level.level-error, .info-box.level-error {
|
||||
background-color: {{ log_styler()->color('error') }};
|
||||
}
|
||||
|
||||
.badge.level-warning, .level.level-warning, .info-box.level-warning {
|
||||
background-color: {{ log_styler()->color('warning') }};
|
||||
}
|
||||
|
||||
.badge.level-notice, .level.level-notice, .info-box.level-notice {
|
||||
background-color: {{ log_styler()->color('notice') }};
|
||||
}
|
||||
|
||||
.badge.level-info, .level.level-info, .info-box.level-info {
|
||||
background-color: {{ log_styler()->color('info') }};
|
||||
}
|
||||
|
||||
.badge.level-debug, .level.level-debug, .info-box.level-debug {
|
||||
background-color: {{ log_styler()->color('debug') }};
|
||||
}
|
||||
|
||||
.badge.level-empty, .level.level-empty {
|
||||
background-color: {{ log_styler()->color('empty') }};
|
||||
}
|
||||
|
||||
.badge.label-env, .label.label-env {
|
||||
background-color: #6A1B9A;
|
||||
}
|
||||
</style>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
{{-- Navbar --}}
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">@lang('Toggle navigation')</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a href="{{ route('log-viewer::dashboard') }}" class="navbar-brand">
|
||||
<i class="fa fa-fw fa-book"></i> LogViewer
|
||||
</a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse" id="navbar">
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="{{ Route::is('log-viewer::dashboard') ? 'active' : '' }}">
|
||||
<a href="{{ route('log-viewer::dashboard') }}">
|
||||
<i class="fa fa-dashboard"></i> @lang('Dashboard')
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{ Route::is('log-viewer::logs.list') ? 'active' : '' }}">
|
||||
<a href="{{ route('log-viewer::logs.list') }}">
|
||||
<i class="fa fa-archive"></i> @lang('Logs')
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{{-- Main container --}}
|
||||
<main class="container-fluid">
|
||||
@yield('content')
|
||||
</main>
|
||||
|
||||
{{-- Footer --}}
|
||||
<footer class="main-footer">
|
||||
<div class="container">
|
||||
<p class="text-muted pull-left">
|
||||
LogViewer - <span class="label label-info">version {{ log_viewer()->version() }}</span>
|
||||
</p>
|
||||
<p class="text-muted pull-right">
|
||||
Created with <i class="fa fa-heart"></i> by ARCANEDEV <sup>©</sup>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
{{-- Scripts --}}
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js"></script>
|
||||
<script>
|
||||
Chart.defaults.global.responsive = true;
|
||||
Chart.defaults.global.scaleFontFamily = "'Source Sans Pro'";
|
||||
Chart.defaults.global.animationEasing = "easeOutQuart";
|
||||
</script>
|
||||
@yield('modals')
|
||||
@yield('scripts')
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,52 @@
|
||||
@extends('log-viewer::bootstrap-3._master')
|
||||
|
||||
@section('content')
|
||||
<h1 class="page-header">@lang('Dashboard')</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<canvas id="stats-doughnut-chart" height="300"></canvas>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<section class="box-body">
|
||||
<div class="row">
|
||||
@foreach($percents as $level => $item)
|
||||
<div class="col-md-4">
|
||||
<div class="info-box level level-{{ $level }} {{ $item['count'] === 0 ? 'level-empty' : '' }}">
|
||||
<span class="info-box-icon">
|
||||
{{ log_styler()->icon($level) }}
|
||||
</span>
|
||||
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">{{ $item['name'] }}</span>
|
||||
<span class="info-box-number">
|
||||
{{ $item['count'] }} @lang('entries') - {!! $item['percent'] !!} %
|
||||
</span>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" style="width: {{ $item['percent'] }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
$(function() {
|
||||
new Chart($('canvas#stats-doughnut-chart'), {
|
||||
type: 'doughnut',
|
||||
data: {!! $chartData !!},
|
||||
options: {
|
||||
legend: {
|
||||
position: 'bottom'
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,152 @@
|
||||
@extends('log-viewer::bootstrap-3._master')
|
||||
|
||||
<?php /** @var Illuminate\Pagination\LengthAwarePaginator $rows */ ?>
|
||||
|
||||
@section('content')
|
||||
<h1 class="page-header">@lang('Logs')</h1>
|
||||
|
||||
{{ $rows->render() }}
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed table-hover table-stats">
|
||||
<thead>
|
||||
<tr>
|
||||
@foreach($headers as $key => $header)
|
||||
<th class="{{ $key == 'date' ? 'text-left' : 'text-center' }}">
|
||||
@if ($key == 'date')
|
||||
<span class="label label-info">{{ $header }}</span>
|
||||
@else
|
||||
<span class="level level-{{ $key }}">
|
||||
{{ log_styler()->icon($key) }} {{ $header }}
|
||||
</span>
|
||||
@endif
|
||||
</th>
|
||||
@endforeach
|
||||
<th class="text-right">@lang('Actions')</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($rows as $date => $row)
|
||||
<tr>
|
||||
@foreach($row as $key => $value)
|
||||
<td class="{{ $key == 'date' ? 'text-left' : 'text-center' }}">
|
||||
@if ($key == 'date')
|
||||
<span class="label label-primary">{{ $value }}</span>
|
||||
@elseif ($value == 0)
|
||||
<span class="level level-empty">{{ $value }}</span>
|
||||
@else
|
||||
<a href="{{ route('log-viewer::logs.filter', [$date, $key]) }}">
|
||||
<span class="level level-{{ $key }}">{{ $value }}</span>
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
@endforeach
|
||||
<td class="text-right">
|
||||
<a href="{{ route('log-viewer::logs.show', [$date]) }}" class="btn btn-xs btn-info">
|
||||
<i class="fa fa-search"></i>
|
||||
</a>
|
||||
<a href="{{ route('log-viewer::logs.download', [$date]) }}" class="btn btn-xs btn-success">
|
||||
<i class="fa fa-download"></i>
|
||||
</a>
|
||||
<a href="#delete-log-modal" class="btn btn-xs btn-danger" data-log-date="{{ $date }}">
|
||||
<i class="fa fa-trash-o"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="11" class="text-center">
|
||||
<span class="label label-default">{{ trans('log-viewer::general.empty-logs') }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{ $rows->render() }}
|
||||
@endsection
|
||||
|
||||
@section('modals')
|
||||
{{-- DELETE MODAL --}}
|
||||
<div id="delete-log-modal" class="modal fade">
|
||||
<div class="modal-dialog">
|
||||
<form id="delete-log-form" action="{{ route('log-viewer::logs.delete') }}" method="POST">
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="date" value="">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">@lang('Delete log file')</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-sm btn-default pull-left" data-dismiss="modal">@lang('Cancel')</button>
|
||||
<button type="submit" class="btn btn-sm btn-danger" data-loading-text="@lang('Loading')…">@lang('Delete')</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
$(function () {
|
||||
var deleteLogModal = $('div#delete-log-modal'),
|
||||
deleteLogForm = $('form#delete-log-form'),
|
||||
submitBtn = deleteLogForm.find('button[type=submit]');
|
||||
|
||||
$("a[href=#delete-log-modal]").on('click', function(event) {
|
||||
event.preventDefault();
|
||||
var date = $(this).data('log-date'),
|
||||
message = "@lang('Are you sure you want to DELETE this log file: :date ?')";
|
||||
|
||||
deleteLogForm.find('input[name=date]').val(date);
|
||||
deleteLogModal.find('.modal-body p').html(message.replace(':date', date));
|
||||
|
||||
deleteLogModal.modal('show');
|
||||
});
|
||||
|
||||
deleteLogForm.on('submit', function(event) {
|
||||
event.preventDefault();
|
||||
submitBtn.button('loading');
|
||||
|
||||
$.ajax({
|
||||
url: $(this).attr('action'),
|
||||
type: $(this).attr('method'),
|
||||
dataType: 'json',
|
||||
data: $(this).serialize(),
|
||||
success: function(data) {
|
||||
submitBtn.button('reset');
|
||||
if (data.result === 'success') {
|
||||
deleteLogModal.modal('hide');
|
||||
location.reload();
|
||||
}
|
||||
else {
|
||||
alert('AJAX ERROR ! Check the console !');
|
||||
console.error(data);
|
||||
}
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
alert('AJAX ERROR ! Check the console !');
|
||||
console.error(errorThrown);
|
||||
submitBtn.button('reset');
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
deleteLogModal.on('hidden.bs.modal', function() {
|
||||
deleteLogForm.find('input[name=date]').val('');
|
||||
deleteLogModal.find('.modal-body p').html('');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,286 @@
|
||||
<?php
|
||||
/**
|
||||
* @var Arcanedev\LogViewer\Entities\Log $log
|
||||
* @var Illuminate\Pagination\LengthAwarePaginator|array<string|int, Arcanedev\LogViewer\Entities\LogEntry> $entries
|
||||
* @var string|null $query
|
||||
*/
|
||||
?>
|
||||
|
||||
@extends('log-viewer::bootstrap-3._master')
|
||||
|
||||
@section('content')
|
||||
<h1 class="page-header">@lang('Log') [{{ $log->date }}]</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><i class="fa fa-fw fa-flag"></i> @lang('Levels')</div>
|
||||
<ul class="list-group">
|
||||
@foreach($log->menu() as $levelKey => $item)
|
||||
@if ($item['count'] === 0)
|
||||
<a href="#" class="list-group-item disabled">
|
||||
<span class="badge">
|
||||
{{ $item['count'] }}
|
||||
</span>
|
||||
{!! $item['icon'] !!} {{ $item['name'] }}
|
||||
</a>
|
||||
@else
|
||||
<a href="{{ $item['url'] }}" class="list-group-item {{ $levelKey }}">
|
||||
<span class="badge level-{{ $levelKey }}">
|
||||
{{ $item['count'] }}
|
||||
</span>
|
||||
<span class="level level-{{ $levelKey }}">
|
||||
{!! $item['icon'] !!} {{ $item['name'] }}
|
||||
</span>
|
||||
</a>
|
||||
@endif
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
{{-- Log Details --}}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
@lang('Log info') :
|
||||
|
||||
<div class="group-btns pull-right">
|
||||
<a href="{{ route('log-viewer::logs.download', [$log->date]) }}" class="btn btn-xs btn-success">
|
||||
<i class="fa fa-download"></i> @lang('Download')
|
||||
</a>
|
||||
<a href="#delete-log-modal" class="btn btn-xs btn-danger" data-toggle="modal">
|
||||
<i class="fa fa-trash-o"></i> @lang('Delete')
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>@lang('File path') :</td>
|
||||
<td colspan="5">{{ $log->getPath() }}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>@lang('Log entries') :</td>
|
||||
<td>
|
||||
<span class="label label-primary">{{ $entries->total() }}</span>
|
||||
</td>
|
||||
<td>@lang('Size') :</td>
|
||||
<td>
|
||||
<span class="label label-primary">{{ $log->size() }}</span>
|
||||
</td>
|
||||
<td>@lang('Created at') :</td>
|
||||
<td>
|
||||
<span class="label label-primary">{{ $log->createdAt() }}</span>
|
||||
</td>
|
||||
<td>@lang('Updated at') :</td>
|
||||
<td>
|
||||
<span class="label label-primary">{{ $log->updatedAt() }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
{{-- Search --}}
|
||||
<form action="{{ route('log-viewer::logs.search', [$log->date, $level]) }}" method="GET">
|
||||
<div class=form-group">
|
||||
<div class="input-group">
|
||||
<input id="query" name="query" class="form-control" value="{{ $query }}" placeholder="@lang('Type here to search')">
|
||||
<span class="input-group-btn">
|
||||
@unless (is_null($query))
|
||||
<a href="{{ route('log-viewer::logs.show', [$log->date]) }}" class="btn btn-default">
|
||||
(@lang(':count results', ['count' => $entries->count()])) <span class="glyphicon glyphicon-remove"></span>
|
||||
</a>
|
||||
@endunless
|
||||
<button id="search-btn" class="btn btn-primary">
|
||||
<span class="glyphicon glyphicon-search"></span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Log Entries --}}
|
||||
<div class="panel panel-default">
|
||||
@if ($entries->hasPages())
|
||||
<div class="panel-heading">
|
||||
{{ $entries->appends(compact('query'))->render() }}
|
||||
|
||||
<span class="label label-info pull-right">
|
||||
{{ __('Page :current of :last', ['current' => $entries->currentPage(), 'last' => $entries->lastPage()]) }}
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="table-responsive">
|
||||
<table id="entries" class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>@lang('ENV')</th>
|
||||
<th style="width: 120px;">@lang('Level')</th>
|
||||
<th style="width: 65px;">@lang('Time')</th>
|
||||
<th>@lang('Header')</th>
|
||||
<th class="text-right">@lang('Actions')</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($entries as $key => $entry)
|
||||
<tr>
|
||||
<td>
|
||||
<span class="label label-env">{{ $entry->env }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="level level-{{ $entry->level }}">{!! $entry->level() !!}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="label label-default">
|
||||
{{ $entry->datetime->format('H:i:s') }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<p>{{ $entry->header }}</p>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
@if ($entry->hasStack())
|
||||
<a class="btn btn-xs btn-default" role="button" data-toggle="collapse"
|
||||
href="#log-stack-{{ $key }}" aria-expanded="false" aria-controls="log-stack-{{ $key }}">
|
||||
<i class="fa fa-toggle-on"></i> @lang('Stack')
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@if ($entry->hasContext())
|
||||
<a class="btn btn-xs btn-default" role="button" data-toggle="collapse"
|
||||
href="#log-context-{{ $key }}" aria-expanded="false" aria-controls="log-context-{{ $key }}">
|
||||
<i class="fa fa-toggle-on"></i> @lang('Context')
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@if ($entry->hasStack() || $entry->hasContext())
|
||||
<tr>
|
||||
<td colspan="5" class="stack">
|
||||
@if ($entry->hasStack())
|
||||
<div class="stack-content collapse" id="log-stack-{{ $key }}">
|
||||
{!! $entry->stack() !!}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($entry->hasContext())
|
||||
<div class="stack-content collapse" id="log-context-{{ $key }}">
|
||||
<pre>{{ $entry->context() }}</pre>
|
||||
</div>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">
|
||||
<span class="label label-default">@lang('The list of logs is empty!')</span>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@if ($entries->hasPages())
|
||||
<div class="panel-footer">
|
||||
{!! $entries->appends(compact('query'))->render() !!}
|
||||
|
||||
<span class="label label-info pull-right">
|
||||
@lang('Page :current of :last', ['current' => $entries->currentPage(), 'last' => $entries->lastPage()])
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('modals')
|
||||
{{-- DELETE MODAL --}}
|
||||
<div id="delete-log-modal" class="modal fade">
|
||||
<div class="modal-dialog">
|
||||
<form id="delete-log-form" action="{{ route('log-viewer::logs.delete') }}" method="POST">
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="date" value="{{ $log->date }}">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">@lang('Delete log file')</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>@lang('Are you sure you want to delete this log file: :date ?', ['date' => $log->date])</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-sm btn-default pull-left" data-dismiss="modal">@lang('Cancel')</button>
|
||||
<button type="submit" class="btn btn-sm btn-danger" data-loading-text="@lang('Loading')…">@lang('Delete')</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
$(function () {
|
||||
var deleteLogModal = $('div#delete-log-modal'),
|
||||
deleteLogForm = $('form#delete-log-form'),
|
||||
submitBtn = deleteLogForm.find('button[type=submit]');
|
||||
|
||||
deleteLogForm.on('submit', function(event) {
|
||||
event.preventDefault();
|
||||
submitBtn.button('loading');
|
||||
|
||||
$.ajax({
|
||||
url: $(this).attr('action'),
|
||||
type: $(this).attr('method'),
|
||||
dataType: 'json',
|
||||
data: $(this).serialize(),
|
||||
success: function(data) {
|
||||
submitBtn.button('reset');
|
||||
if (data.result === 'success') {
|
||||
deleteLogModal.modal('hide');
|
||||
location.replace("{{ route('log-viewer::logs.list') }}");
|
||||
}
|
||||
else {
|
||||
alert('OOPS ! This is a lack of coffee exception !')
|
||||
}
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
alert('AJAX ERROR ! Check the console !');
|
||||
console.error(errorThrown);
|
||||
submitBtn.button('reset');
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
@unless (empty(log_styler()->toHighlight()))
|
||||
@php
|
||||
$htmlHighlight = version_compare(PHP_VERSION, '7.4.0') >= 0
|
||||
? join('|', log_styler()->toHighlight())
|
||||
: join(log_styler()->toHighlight(), '|');
|
||||
@endphp
|
||||
$('.stack-content').each(function() {
|
||||
var $this = $(this);
|
||||
var html = $this.html().trim()
|
||||
.replace(/({!! $htmlHighlight !!})/gm, '<strong>$1</strong>');
|
||||
|
||||
$this.html(html);
|
||||
});
|
||||
@endunless
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,270 @@
|
||||
<!doctype html>
|
||||
<html lang="{{ app()->getLocale() }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="LogViewer">
|
||||
<meta name="author" content="ARCANEDEV">
|
||||
<title>LogViewer - Created by ARCANEDEV</title>
|
||||
{{-- Styles --}}
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
|
||||
<link href='https://fonts.googleapis.com/css?family=Montserrat:400,700|Source+Sans+Pro:400,600' rel='stylesheet' type='text/css'>
|
||||
<style>
|
||||
html {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: .875rem;
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
.main-footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
background-color: #E8EAF6;
|
||||
}
|
||||
|
||||
.main-footer p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.main-footer .fa.fa-heart {
|
||||
color: #C62828;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
border-bottom: 1px solid #8a8a8a;
|
||||
}
|
||||
|
||||
/*
|
||||
* Navbar
|
||||
*/
|
||||
|
||||
.navbar-brand {
|
||||
padding: .75rem 1rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.navbar-nav .nav-link {
|
||||
padding-right: .5rem;
|
||||
padding-left: .5rem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Boxes
|
||||
*/
|
||||
|
||||
.box {
|
||||
display: block;
|
||||
padding: 0;
|
||||
min-height: 70px;
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.1);
|
||||
border-radius: .25rem;
|
||||
}
|
||||
|
||||
.box > .box-icon > i,
|
||||
.box .box-content .box-text,
|
||||
.box .box-content .box-number {
|
||||
color: #FFF;
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.box > .box-icon {
|
||||
border-radius: 2px 0 0 2px;
|
||||
display: block;
|
||||
float: left;
|
||||
height: 70px; width: 70px;
|
||||
text-align: center;
|
||||
font-size: 40px;
|
||||
line-height: 70px;
|
||||
background: rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.box .box-content {
|
||||
padding: 5px 10px;
|
||||
margin-left: 70px;
|
||||
}
|
||||
|
||||
.box .box-content .box-text {
|
||||
display: block;
|
||||
font-size: 1rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.box .box-content .box-number {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.box .box-content .progress {
|
||||
background: rgba(0,0,0,0.2);
|
||||
margin: 5px -10px 5px -10px;
|
||||
}
|
||||
|
||||
.box .box-content .progress .progress-bar {
|
||||
background-color: #FFF;
|
||||
}
|
||||
|
||||
/*
|
||||
* Log Menu
|
||||
*/
|
||||
|
||||
.log-menu .list-group-item.disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.log-menu .list-group-item.disabled .level-name {
|
||||
color: #D1D1D1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Log Entry
|
||||
*/
|
||||
|
||||
.stack-content {
|
||||
color: #AE0E0E;
|
||||
font-family: consolas, Menlo, Courier, monospace;
|
||||
white-space: pre-line;
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Colors: Badge & Infobox
|
||||
*/
|
||||
|
||||
.badge.badge-env,
|
||||
.badge.badge-level-all,
|
||||
.badge.badge-level-emergency,
|
||||
.badge.badge-level-alert,
|
||||
.badge.badge-level-critical,
|
||||
.badge.badge-level-error,
|
||||
.badge.badge-level-warning,
|
||||
.badge.badge-level-notice,
|
||||
.badge.badge-level-info,
|
||||
.badge.badge-level-debug,
|
||||
.badge.empty {
|
||||
color: #FFF;
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.badge.badge-level-all,
|
||||
.box.level-all {
|
||||
background-color: {{ log_styler()->color('all') }};
|
||||
}
|
||||
|
||||
.badge.badge-level-emergency,
|
||||
.box.level-emergency {
|
||||
background-color: {{ log_styler()->color('emergency') }};
|
||||
}
|
||||
|
||||
.badge.badge-level-alert,
|
||||
.box.level-alert {
|
||||
background-color: {{ log_styler()->color('alert') }};
|
||||
}
|
||||
|
||||
.badge.badge-level-critical,
|
||||
.box.level-critical {
|
||||
background-color: {{ log_styler()->color('critical') }};
|
||||
}
|
||||
|
||||
.badge.badge-level-error,
|
||||
.box.level-error {
|
||||
background-color: {{ log_styler()->color('error') }};
|
||||
}
|
||||
|
||||
.badge.badge-level-warning,
|
||||
.box.level-warning {
|
||||
background-color: {{ log_styler()->color('warning') }};
|
||||
}
|
||||
|
||||
.badge.badge-level-notice,
|
||||
.box.level-notice {
|
||||
background-color: {{ log_styler()->color('notice') }};
|
||||
}
|
||||
|
||||
.badge.badge-level-info,
|
||||
.box.level-info {
|
||||
background-color: {{ log_styler()->color('info') }};
|
||||
}
|
||||
|
||||
.badge.badge-level-debug,
|
||||
.box.level-debug {
|
||||
background-color: {{ log_styler()->color('debug') }};
|
||||
}
|
||||
|
||||
.badge.empty,
|
||||
.box.empty {
|
||||
background-color: {{ log_styler()->color('empty') }};
|
||||
}
|
||||
|
||||
.badge.badge-env {
|
||||
background-color: #6A1B9A;
|
||||
}
|
||||
|
||||
#entries {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-md navbar-dark sticky-top bg-dark p-0">
|
||||
<a href="{{ route('log-viewer::dashboard') }}" class="navbar-brand mr-0">
|
||||
<i class="fa fa-fw fa-book"></i> LogViewer
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item {{ Route::is('log-viewer::dashboard') ? 'active' : '' }}">
|
||||
<a href="{{ route('log-viewer::dashboard') }}" class="nav-link">
|
||||
<i class="fa fa-dashboard"></i> @lang('Dashboard')
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item {{ Route::is('log-viewer::logs.list') ? 'active' : '' }}">
|
||||
<a href="{{ route('log-viewer::logs.list') }}" class="nav-link">
|
||||
<i class="fa fa-archive"></i> @lang('Logs')
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid">
|
||||
<main role="main" class="pt-3">
|
||||
@yield('content')
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{{-- Footer --}}
|
||||
<footer class="main-footer">
|
||||
<div class="container-fluid">
|
||||
<p class="text-muted pull-left">
|
||||
LogViewer - <span class="badge badge-info">version {{ log_viewer()->version() }}</span>
|
||||
</p>
|
||||
<p class="text-muted pull-right">
|
||||
Created with <i class="fa fa-heart"></i> by ARCANEDEV <sup>©</sup>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
{{-- Scripts --}}
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.min.js"></script>
|
||||
|
||||
@yield('modals')
|
||||
@yield('scripts')
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,53 @@
|
||||
@extends('log-viewer::bootstrap-4._master')
|
||||
|
||||
@section('content')
|
||||
<div class="page-header mb-4">
|
||||
<h1>@lang('Dashboard')</h1>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<canvas id="stats-doughnut-chart" height="300" class="mb-3"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-9">
|
||||
<div class="row">
|
||||
@foreach($percents as $level => $item)
|
||||
<div class="col-sm-6 col-md-12 col-lg-4 mb-3">
|
||||
<div class="box level-{{ $level }} {{ $item['count'] === 0 ? 'empty' : '' }}">
|
||||
<div class="box-icon">
|
||||
{!! log_styler()->icon($level) !!}
|
||||
</div>
|
||||
|
||||
<div class="box-content">
|
||||
<span class="box-text">{{ $item['name'] }}</span>
|
||||
<span class="box-number">
|
||||
{{ $item['count'] }} @lang('entries') - {!! $item['percent'] !!} %
|
||||
</span>
|
||||
<div class="progress" style="height: 3px;">
|
||||
<div class="progress-bar" style="width: {{ $item['percent'] }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
$(function() {
|
||||
new Chart(document.getElementById("stats-doughnut-chart"), {
|
||||
type: 'doughnut',
|
||||
data: {!! $chartData !!},
|
||||
options: {
|
||||
legend: {
|
||||
position: 'bottom'
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,152 @@
|
||||
@extends('log-viewer::bootstrap-4._master')
|
||||
|
||||
<?php /** @var Illuminate\Pagination\LengthAwarePaginator $rows */ ?>
|
||||
|
||||
@section('content')
|
||||
<div class="page-header mb-4">
|
||||
<h1>@lang('Logs')</h1>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
@foreach($headers as $key => $header)
|
||||
<th scope="col" class="{{ $key == 'date' ? 'text-left' : 'text-center' }}">
|
||||
@if ($key == 'date')
|
||||
<span class="badge badge-info">{{ $header }}</span>
|
||||
@else
|
||||
<span class="badge badge-level-{{ $key }}">
|
||||
{{ log_styler()->icon($key) }} {{ $header }}
|
||||
</span>
|
||||
@endif
|
||||
</th>
|
||||
@endforeach
|
||||
<th scope="col" class="text-right">@lang('Actions')</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($rows as $date => $row)
|
||||
<tr>
|
||||
@foreach($row as $key => $value)
|
||||
<td class="{{ $key == 'date' ? 'text-left' : 'text-center' }}">
|
||||
@if ($key == 'date')
|
||||
<span class="badge badge-primary">{{ $value }}</span>
|
||||
@elseif ($value == 0)
|
||||
<span class="badge empty">{{ $value }}</span>
|
||||
@else
|
||||
<a href="{{ route('log-viewer::logs.filter', [$date, $key]) }}">
|
||||
<span class="badge badge-level-{{ $key }}">{{ $value }}</span>
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
@endforeach
|
||||
<td class="text-right">
|
||||
<a href="{{ route('log-viewer::logs.show', [$date]) }}" class="btn btn-sm btn-info">
|
||||
<i class="fa fa-search"></i>
|
||||
</a>
|
||||
<a href="{{ route('log-viewer::logs.download', [$date]) }}" class="btn btn-sm btn-success">
|
||||
<i class="fa fa-download"></i>
|
||||
</a>
|
||||
<a href="#delete-log-modal" class="btn btn-sm btn-danger" data-log-date="{{ $date }}">
|
||||
<i class="fa fa-trash-o"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="11" class="text-center">
|
||||
<span class="badge badge-secondary">@lang('The list of logs is empty!')</span>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{ $rows->render() }}
|
||||
@endsection
|
||||
|
||||
@section('modals')
|
||||
{{-- DELETE MODAL --}}
|
||||
<div id="delete-log-modal" class="modal fade" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form id="delete-log-form" action="{{ route('log-viewer::logs.delete') }}" method="POST">
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="date" value="">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@lang('Delete log file')</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-sm btn-secondary mr-auto" data-dismiss="modal">@lang('Cancel')</button>
|
||||
<button type="submit" class="btn btn-sm btn-danger" data-loading-text="@lang('Loading')…">@lang('Delete')</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
$(function () {
|
||||
var deleteLogModal = $('div#delete-log-modal'),
|
||||
deleteLogForm = $('form#delete-log-form'),
|
||||
submitBtn = deleteLogForm.find('button[type=submit]');
|
||||
|
||||
$("a[href='#delete-log-modal']").on('click', function(event) {
|
||||
event.preventDefault();
|
||||
var date = $(this).data('log-date'),
|
||||
message = "{{ __('Are you sure you want to delete this log file: :date ?') }}";
|
||||
|
||||
deleteLogForm.find('input[name=date]').val(date);
|
||||
deleteLogModal.find('.modal-body p').html(message.replace(':date', date));
|
||||
|
||||
deleteLogModal.modal('show');
|
||||
});
|
||||
|
||||
deleteLogForm.on('submit', function(event) {
|
||||
event.preventDefault();
|
||||
submitBtn.button('loading');
|
||||
|
||||
$.ajax({
|
||||
url: $(this).attr('action'),
|
||||
type: $(this).attr('method'),
|
||||
dataType: 'json',
|
||||
data: $(this).serialize(),
|
||||
success: function(data) {
|
||||
submitBtn.button('reset');
|
||||
if (data.result === 'success') {
|
||||
deleteLogModal.modal('hide');
|
||||
location.reload();
|
||||
}
|
||||
else {
|
||||
alert('AJAX ERROR ! Check the console !');
|
||||
console.error(data);
|
||||
}
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
alert('AJAX ERROR ! Check the console !');
|
||||
console.error(errorThrown);
|
||||
submitBtn.button('reset');
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
deleteLogModal.on('hidden.bs.modal', function() {
|
||||
deleteLogForm.find('input[name=date]').val('');
|
||||
deleteLogModal.find('.modal-body p').html('');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
/**
|
||||
* @var Arcanedev\LogViewer\Entities\Log $log
|
||||
* @var Illuminate\Pagination\LengthAwarePaginator|array<string|int, Arcanedev\LogViewer\Entities\LogEntry> $entries
|
||||
* @var string|null $query
|
||||
*/
|
||||
?>
|
||||
|
||||
@extends('log-viewer::bootstrap-4._master')
|
||||
|
||||
@section('content')
|
||||
<div class="page-header mb-4">
|
||||
<h1>@lang('Log') [{{ $log->date }}]</h1>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-2">
|
||||
{{-- Log Menu --}}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header"><i class="fa fa-fw fa-flag"></i> @lang('Levels')</div>
|
||||
<div class="list-group list-group-flush log-menu">
|
||||
@foreach($log->menu() as $levelKey => $item)
|
||||
@if ($item['count'] === 0)
|
||||
<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center disabled">
|
||||
<span class="level-name">{!! $item['icon'] !!} {{ $item['name'] }}</span>
|
||||
<span class="badge empty">{{ $item['count'] }}</span>
|
||||
</a>
|
||||
@else
|
||||
<a href="{{ $item['url'] }}" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center level-{{ $levelKey }}{{ $level === $levelKey ? ' active' : ''}}">
|
||||
<span class="level-name">{!! $item['icon'] !!} {{ $item['name'] }}</span>
|
||||
<span class="badge badge-level-{{ $levelKey }}">{{ $item['count'] }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-10">
|
||||
{{-- Log Details --}}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
@lang('Log info') :
|
||||
<div class="group-btns pull-right">
|
||||
<a href="{{ route('log-viewer::logs.download', [$log->date]) }}" class="btn btn-sm btn-success">
|
||||
<i class="fa fa-download"></i> @lang('Download')
|
||||
</a>
|
||||
<a href="#delete-log-modal" class="btn btn-sm btn-danger" data-toggle="modal">
|
||||
<i class="fa fa-trash-o"></i> @lang('Delete')
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed mb-0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>@lang('File path') :</td>
|
||||
<td colspan="7">{{ $log->getPath() }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>@lang('Log entries') :</td>
|
||||
<td>
|
||||
<span class="badge badge-primary">{{ $entries->total() }}</span>
|
||||
</td>
|
||||
<td>@lang('Size') :</td>
|
||||
<td>
|
||||
<span class="badge badge-primary">{{ $log->size() }}</span>
|
||||
</td>
|
||||
<td>@lang('Created at') :</td>
|
||||
<td>
|
||||
<span class="badge badge-primary">{{ $log->createdAt() }}</span>
|
||||
</td>
|
||||
<td>@lang('Updated at') :</td>
|
||||
<td>
|
||||
<span class="badge badge-primary">{{ $log->updatedAt() }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
{{-- Search --}}
|
||||
<form action="{{ route('log-viewer::logs.search', [$log->date, $level]) }}" method="GET">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<input id="query" name="query" class="form-control" value="{{ $query }}" placeholder="@lang('Type here to search')">
|
||||
<div class="input-group-append">
|
||||
@unless (is_null($query))
|
||||
<a href="{{ route('log-viewer::logs.show', [$log->date]) }}" class="btn btn-secondary">
|
||||
(@lang(':count results', ['count' => $entries->count()])) <i class="fa fa-fw fa-times"></i>
|
||||
</a>
|
||||
@endunless
|
||||
<button id="search-btn" class="btn btn-primary">
|
||||
<span class="fa fa-fw fa-search"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Log Entries --}}
|
||||
<div class="card mb-4">
|
||||
@if ($entries->hasPages())
|
||||
<div class="card-header">
|
||||
<span class="badge badge-info float-right">
|
||||
{{ __('Page :current of :last', ['current' => $entries->currentPage(), 'last' => $entries->lastPage()]) }}
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="table-responsive">
|
||||
<table id="entries" class="table mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>@lang('ENV')</th>
|
||||
<th style="width: 120px;">@lang('Level')</th>
|
||||
<th style="width: 65px;">@lang('Time')</th>
|
||||
<th>@lang('Header')</th>
|
||||
<th class="text-right">@lang('Actions')</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($entries as $key => $entry)
|
||||
<tr>
|
||||
<td>
|
||||
<span class="badge badge-env">{{ $entry->env }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-level-{{ $entry->level }}">
|
||||
{!! $entry->level() !!}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-secondary">
|
||||
{{ $entry->datetime->format('H:i:s') }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
{{ $entry->header }}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
@if ($entry->hasStack())
|
||||
<a class="btn btn-sm btn-light" role="button" data-toggle="collapse"
|
||||
href="#log-stack-{{ $key }}" aria-expanded="false" aria-controls="log-stack-{{ $key }}">
|
||||
<i class="fa fa-toggle-on"></i> @lang('Stack')
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@if ($entry->hasContext())
|
||||
<a class="btn btn-sm btn-light" role="button" data-toggle="collapse"
|
||||
href="#log-context-{{ $key }}" aria-expanded="false" aria-controls="log-context-{{ $key }}">
|
||||
<i class="fa fa-toggle-on"></i> @lang('Context')
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@if ($entry->hasStack() || $entry->hasContext())
|
||||
<tr>
|
||||
<td colspan="5" class="stack py-0">
|
||||
@if ($entry->hasStack())
|
||||
<div class="stack-content collapse" id="log-stack-{{ $key }}">
|
||||
{!! $entry->stack() !!}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($entry->hasContext())
|
||||
<div class="stack-content collapse" id="log-context-{{ $key }}">
|
||||
<pre>{{ $entry->context() }}</pre>
|
||||
</div>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">
|
||||
<span class="badge badge-secondary">@lang('The list of logs is empty!')</span>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!! $entries->appends(compact('query'))->render() !!}
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('modals')
|
||||
{{-- DELETE MODAL --}}
|
||||
<div id="delete-log-modal" class="modal fade" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form id="delete-log-form" action="{{ route('log-viewer::logs.delete') }}" method="POST">
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="date" value="{{ $log->date }}">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@lang('Delete log file')</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>@lang('Are you sure you want to delete this log file: :date ?', ['date' => $log->date])</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-sm btn-secondary mr-auto" data-dismiss="modal">@lang('Cancel')</button>
|
||||
<button type="submit" class="btn btn-sm btn-danger" data-loading-text="@lang('Loading')…">@lang('Delete')</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
$(function () {
|
||||
var deleteLogModal = $('div#delete-log-modal'),
|
||||
deleteLogForm = $('form#delete-log-form'),
|
||||
submitBtn = deleteLogForm.find('button[type=submit]');
|
||||
|
||||
deleteLogForm.on('submit', function(event) {
|
||||
event.preventDefault();
|
||||
submitBtn.button('loading');
|
||||
|
||||
$.ajax({
|
||||
url: $(this).attr('action'),
|
||||
type: $(this).attr('method'),
|
||||
dataType: 'json',
|
||||
data: $(this).serialize(),
|
||||
success: function(data) {
|
||||
submitBtn.button('reset');
|
||||
if (data.result === 'success') {
|
||||
deleteLogModal.modal('hide');
|
||||
location.replace("{{ route('log-viewer::logs.list') }}");
|
||||
}
|
||||
else {
|
||||
alert('OOPS ! This is a lack of coffee exception !')
|
||||
}
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
alert('AJAX ERROR ! Check the console !');
|
||||
console.error(errorThrown);
|
||||
submitBtn.button('reset');
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
@unless (empty(log_styler()->toHighlight()))
|
||||
@php
|
||||
$htmlHighlight = version_compare(PHP_VERSION, '7.4.0') >= 0
|
||||
? join('|', log_styler()->toHighlight())
|
||||
: join(log_styler()->toHighlight(), '|');
|
||||
@endphp
|
||||
|
||||
$('.stack-content').each(function() {
|
||||
var $this = $(this);
|
||||
var html = $this.html().trim()
|
||||
.replace(/({!! $htmlHighlight !!})/gm, '<strong>$1</strong>');
|
||||
|
||||
$this.html(html);
|
||||
});
|
||||
@endunless
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,277 @@
|
||||
<!doctype html>
|
||||
<html lang="{{ app()->getLocale() }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="LogViewer">
|
||||
<meta name="author" content="ARCANEDEV">
|
||||
<title>LogViewer - Created by ARCANEDEV</title>
|
||||
{{-- Styles --}}
|
||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
|
||||
<link href='https://fonts.googleapis.com/css?family=Montserrat:400,700|Source+Sans+Pro:400,600' rel='stylesheet' type='text/css'>
|
||||
<style>
|
||||
html {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: .875rem;
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
.main-footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
background-color: #E8EAF6;
|
||||
}
|
||||
|
||||
.main-footer p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.main-footer .fa.fa-heart {
|
||||
color: #C62828;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
border-bottom: 1px solid #8a8a8a;
|
||||
}
|
||||
|
||||
/*
|
||||
* Navbar
|
||||
*/
|
||||
|
||||
.navbar-brand {
|
||||
padding: .75rem 1rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.navbar-nav .nav-link {
|
||||
padding-right: .5rem;
|
||||
padding-left: .5rem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Boxes
|
||||
*/
|
||||
|
||||
.box {
|
||||
display: block;
|
||||
padding: 0;
|
||||
min-height: 70px;
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.1);
|
||||
border-radius: .25rem;
|
||||
}
|
||||
|
||||
.box > .box-icon > i,
|
||||
.box .box-content .box-text,
|
||||
.box .box-content .box-number {
|
||||
color: #FFF;
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.box > .box-icon {
|
||||
border-radius: 2px 0 0 2px;
|
||||
display: block;
|
||||
float: left;
|
||||
height: 70px; width: 70px;
|
||||
text-align: center;
|
||||
font-size: 40px;
|
||||
line-height: 70px;
|
||||
background: rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.box .box-content {
|
||||
padding: 5px 10px;
|
||||
margin-left: 70px;
|
||||
}
|
||||
|
||||
.box .box-content .box-text {
|
||||
display: block;
|
||||
font-size: 1rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.box .box-content .box-number {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.box .box-content .progress {
|
||||
background: rgba(0,0,0,0.2);
|
||||
margin: 5px -10px 5px -10px;
|
||||
}
|
||||
|
||||
.box .box-content .progress .progress-bar {
|
||||
background-color: #FFF;
|
||||
}
|
||||
|
||||
/*
|
||||
* Log Menu
|
||||
*/
|
||||
|
||||
.log-menu .list-group-item.disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.log-menu .list-group-item.disabled .level-name {
|
||||
color: #D1D1D1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Log Entry
|
||||
*/
|
||||
|
||||
.stack-content {
|
||||
color: #AE0E0E;
|
||||
font-family: consolas, Menlo, Courier, monospace;
|
||||
white-space: pre-line;
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Colors: Badge & Infobox
|
||||
*/
|
||||
|
||||
.badge.badge-env,
|
||||
.badge.badge-level-all,
|
||||
.badge.badge-level-emergency,
|
||||
.badge.badge-level-alert,
|
||||
.badge.badge-level-critical,
|
||||
.badge.badge-level-error,
|
||||
.badge.badge-level-warning,
|
||||
.badge.badge-level-notice,
|
||||
.badge.badge-level-info,
|
||||
.badge.badge-level-debug,
|
||||
.badge.empty {
|
||||
color: #FFF;
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.badge.badge-level-all,
|
||||
.box.level-all {
|
||||
background-color: {{ log_styler()->color('all') }};
|
||||
}
|
||||
|
||||
.badge.badge-level-emergency,
|
||||
.box.level-emergency {
|
||||
background-color: {{ log_styler()->color('emergency') }};
|
||||
}
|
||||
|
||||
.badge.badge-level-alert,
|
||||
.box.level-alert {
|
||||
background-color: {{ log_styler()->color('alert') }};
|
||||
}
|
||||
|
||||
.badge.badge-level-critical,
|
||||
.box.level-critical {
|
||||
background-color: {{ log_styler()->color('critical') }};
|
||||
}
|
||||
|
||||
.badge.badge-level-error,
|
||||
.box.level-error {
|
||||
background-color: {{ log_styler()->color('error') }};
|
||||
}
|
||||
|
||||
.badge.badge-level-warning,
|
||||
.box.level-warning {
|
||||
background-color: {{ log_styler()->color('warning') }};
|
||||
}
|
||||
|
||||
.badge.badge-level-notice,
|
||||
.box.level-notice {
|
||||
background-color: {{ log_styler()->color('notice') }};
|
||||
}
|
||||
|
||||
.badge.badge-level-info,
|
||||
.box.level-info {
|
||||
background-color: {{ log_styler()->color('info') }};
|
||||
}
|
||||
|
||||
.badge.badge-level-debug,
|
||||
.box.level-debug {
|
||||
background-color: {{ log_styler()->color('debug') }};
|
||||
}
|
||||
|
||||
.badge.empty,
|
||||
.box.empty {
|
||||
background-color: {{ log_styler()->color('empty') }};
|
||||
}
|
||||
|
||||
.badge.badge-env {
|
||||
background-color: #6A1B9A;
|
||||
}
|
||||
|
||||
#entries {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-md navbar-dark sticky-top bg-dark p-0">
|
||||
<a href="{{ route('log-viewer::dashboard') }}" class="navbar-brand mr-0">
|
||||
<i class="fa fa-fw fa-book"></i> LogViewer
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item {{ Route::is('log-viewer::dashboard') ? 'active' : '' }}">
|
||||
<a href="{{ route('log-viewer::dashboard') }}" class="nav-link">
|
||||
<i class="fa fa-dashboard"></i> @lang('Dashboard')
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item {{ Route::is('log-viewer::logs.list') ? 'active' : '' }}">
|
||||
<a href="{{ route('log-viewer::logs.list') }}" class="nav-link">
|
||||
<i class="fa fa-archive"></i> @lang('Logs')
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid">
|
||||
<main role="main" class="pt-3">
|
||||
@yield('content')
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{{-- Footer --}}
|
||||
<footer class="main-footer">
|
||||
<div class="container-fluid">
|
||||
<p class="text-muted pull-left">
|
||||
LogViewer - <span class="badge text-bg-info">version {{ log_viewer()->version() }}</span>
|
||||
</p>
|
||||
<p class="text-muted pull-right">
|
||||
Created with <i class="fa fa-heart"></i> by ARCANEDEV <sup>©</sup>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
{{-- Scripts --}}
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-BBtl+eGJRgqQAUMxJ7pMwbEyER4l1g+O15P+16Ep7Q9Q+zqX6gSbd85u4mG4QzX+" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.0/chart.umd.min.js" integrity="sha512-SIMGYRUjwY8+gKg7nn9EItdD8LCADSDfJNutF9TPrvEo86sQmFMh6MyralfIyhADlajSxqc7G0gs7+MwWF/ogQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script>
|
||||
function ready(fn) {
|
||||
if (document.readyState !== 'loading'){
|
||||
fn();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', fn);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@yield('modals')
|
||||
@yield('scripts')
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,53 @@
|
||||
@extends('log-viewer::bootstrap-5._master')
|
||||
|
||||
@section('content')
|
||||
<div class="page-header mb-4">
|
||||
<h1>@lang('Dashboard')</h1>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<canvas id="stats-doughnut-chart" height="300" class="mb-3"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-9">
|
||||
<div class="row">
|
||||
@foreach($percents as $level => $item)
|
||||
<div class="col-sm-6 col-md-12 col-lg-4 mb-3">
|
||||
<div class="box level-{{ $level }} {{ $item['count'] === 0 ? 'empty' : '' }}">
|
||||
<div class="box-icon">
|
||||
{!! log_styler()->icon($level) !!}
|
||||
</div>
|
||||
|
||||
<div class="box-content">
|
||||
<span class="box-text">{{ $item['name'] }}</span>
|
||||
<span class="box-number">
|
||||
{{ $item['count'] }} @lang('entries') - {!! $item['percent'] !!} %
|
||||
</span>
|
||||
<div class="progress" style="height: 3px;">
|
||||
<div class="progress-bar" style="width: {{ $item['percent'] }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
ready(function () {
|
||||
new Chart(document.getElementById("stats-doughnut-chart"), {
|
||||
type: 'doughnut',
|
||||
data: {!! $chartData !!},
|
||||
options: {
|
||||
legend: {
|
||||
position: 'bottom'
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,157 @@
|
||||
@extends('log-viewer::bootstrap-5._master')
|
||||
|
||||
<?php /** @var Illuminate\Pagination\LengthAwarePaginator $rows */ ?>
|
||||
|
||||
@section('content')
|
||||
<div class="page-header mb-4">
|
||||
<h1>@lang('Logs')</h1>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
@foreach($headers as $key => $header)
|
||||
<th scope="col" class="{{ $key == 'date' ? 'text-left' : 'text-center' }}">
|
||||
@if ($key == 'date')
|
||||
<span class="badge text-bg-info">{{ $header }}</span>
|
||||
@else
|
||||
<span class="badge badge-level-{{ $key }}">
|
||||
{{ log_styler()->icon($key) }} {{ $header }}
|
||||
</span>
|
||||
@endif
|
||||
</th>
|
||||
@endforeach
|
||||
<th scope="col" class="text-end">@lang('Actions')</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($rows as $date => $row)
|
||||
<tr>
|
||||
@foreach($row as $key => $value)
|
||||
<td class="{{ $key == 'date' ? 'text-left' : 'text-center' }}">
|
||||
@if ($key == 'date')
|
||||
<span class="badge text-bg-primary">{{ $value }}</span>
|
||||
@elseif ($value == 0)
|
||||
<span class="badge empty">{{ $value }}</span>
|
||||
@else
|
||||
<a href="{{ route('log-viewer::logs.filter', [$date, $key]) }}">
|
||||
<span class="badge badge-level-{{ $key }}">{{ $value }}</span>
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
@endforeach
|
||||
<td class="text-end">
|
||||
<a href="{{ route('log-viewer::logs.show', [$date]) }}" class="btn btn-sm btn-info">
|
||||
<i class="fa fa-fw fa-search"></i>
|
||||
</a>
|
||||
<a href="{{ route('log-viewer::logs.download', [$date]) }}" class="btn btn-sm btn-success">
|
||||
<i class="fa fa-fw fa-download"></i>
|
||||
</a>
|
||||
<a href="#delete-log-modal" class="btn btn-sm btn-danger" data-log-date="{{ $date }}">
|
||||
<i class="fa fa-fw fa-trash-o"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="11" class="text-center">
|
||||
<span class="badge text-bg-secondary">@lang('The list of logs is empty!')</span>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{ $rows->render() }}
|
||||
@endsection
|
||||
|
||||
@section('modals')
|
||||
{{-- DELETE MODAL --}}
|
||||
<div class="modal fade" id="delete-log-modal" tabindex="-1" aria-labelledby="delete-log-modal-label" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<form id="delete-log-form" action="{{ route('log-viewer::logs.delete') }}" method="POST">
|
||||
{{ csrf_field() }}
|
||||
{{ method_field('DELETE') }}
|
||||
<input type="hidden" name="date" value="">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="delete-log-modal-label">@lang('Delete log file')</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer justify-content-between">
|
||||
<button type="button" class="btn btn-sm btn-light" data-bs-dismiss="modal">@lang('Cancel')</button>
|
||||
<button type="submit" class="btn btn-sm btn-danger" data-loading-text="@lang('Loading')…">@lang('Delete')</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
ready(() => {
|
||||
let deleteLogModal = new bootstrap.Modal('div#delete-log-modal')
|
||||
let deleteLogModalElt = deleteLogModal._element
|
||||
let deleteLogForm = document.querySelector('form#delete-log-form')
|
||||
let submitBtn = new bootstrap.Button(deleteLogForm.querySelector('button[type=submit]'))
|
||||
|
||||
document.querySelectorAll("a[href='#delete-log-modal']").forEach((elt) => {
|
||||
elt.addEventListener('click', (event) => {
|
||||
event.preventDefault()
|
||||
|
||||
let date = event.currentTarget.getAttribute('data-log-date')
|
||||
let message = "{{ __('Are you sure you want to delete this log file: :date ?') }}"
|
||||
|
||||
deleteLogForm.querySelector('input[name=date]').value = date
|
||||
deleteLogModalElt.querySelector('.modal-body p').innerHTML = message.replace(':date', date)
|
||||
|
||||
deleteLogModal.show()
|
||||
})
|
||||
})
|
||||
|
||||
deleteLogForm.addEventListener('submit', (event) => {
|
||||
event.preventDefault()
|
||||
submitBtn.toggle('loading')
|
||||
|
||||
fetch(event.currentTarget.getAttribute('action'), {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
'Content-type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
date: event.currentTarget.querySelector("input[name='date']").value,
|
||||
})
|
||||
})
|
||||
.then((resp) => resp.json())
|
||||
.then((resp) => {
|
||||
if (resp.result === 'success') {
|
||||
deleteLogModal.hide()
|
||||
location.reload()
|
||||
}
|
||||
else {
|
||||
alert('AJAX ERROR ! Check the console !')
|
||||
console.error(resp)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
alert('AJAX ERROR ! Check the console !')
|
||||
console.error(err)
|
||||
})
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
deleteLogModalElt.addEventListener('hidden.bs.modal', () => {
|
||||
deleteLogForm.querySelector('input[name=date]').value = ''
|
||||
deleteLogModalElt.querySelector('.modal-body p').innerHTML = ''
|
||||
})
|
||||
})
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
/**
|
||||
* @var Arcanedev\LogViewer\Entities\Log $log
|
||||
* @var Illuminate\Pagination\LengthAwarePaginator $entries
|
||||
* @var string|null $query
|
||||
*/
|
||||
?>
|
||||
|
||||
@extends('log-viewer::bootstrap-5._master')
|
||||
|
||||
@section('content')
|
||||
<div class="page-header mb-4">
|
||||
<h1>@lang('Log') [{{ $log->date }}]</h1>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-2">
|
||||
{{-- Log Menu --}}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header"><i class="fa fa-fw fa-flag"></i> @lang('Levels')</div>
|
||||
<div class="list-group list-group-flush log-menu">
|
||||
@foreach($log->menu() as $levelKey => $item)
|
||||
@if ($item['count'] === 0)
|
||||
<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center disabled">
|
||||
<span class="level-name">{!! $item['icon'] !!} {{ $item['name'] }}</span>
|
||||
<span class="badge empty">{{ $item['count'] }}</span>
|
||||
</a>
|
||||
@else
|
||||
<a href="{{ $item['url'] }}" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center level-{{ $levelKey }}{{ $level === $levelKey ? ' active' : ''}}">
|
||||
<span class="level-name">{!! $item['icon'] !!} {{ $item['name'] }}</span>
|
||||
<span class="badge badge-level-{{ $levelKey }}">{{ $item['count'] }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-10">
|
||||
{{-- Log Details --}}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
@lang('Log info') :
|
||||
<div class="group-btns pull-right">
|
||||
<a href="{{ route('log-viewer::logs.download', [$log->date]) }}" class="btn btn-sm btn-success">
|
||||
<i class="fa fa-download"></i> @lang('Download')
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-danger" data-bs-toggle="modal" data-bs-target="#delete-log-modal">
|
||||
<i class="fa fa-trash-o"></i> @lang('Delete')
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed mb-0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>@lang('File path') :</td>
|
||||
<td colspan="7">{{ $log->getPath() }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>@lang('Log entries') :</td>
|
||||
<td>
|
||||
<span class="badge text-bg-primary">{{ $entries->total() }}</span>
|
||||
</td>
|
||||
<td>@lang('Size') :</td>
|
||||
<td>
|
||||
<span class="badge text-bg-primary">{{ $log->size() }}</span>
|
||||
</td>
|
||||
<td>@lang('Created at') :</td>
|
||||
<td>
|
||||
<span class="badge text-bg-primary">{{ $log->createdAt() }}</span>
|
||||
</td>
|
||||
<td>@lang('Updated at') :</td>
|
||||
<td>
|
||||
<span class="badge text-bg-primary">{{ $log->updatedAt() }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
{{-- Search --}}
|
||||
<form action="{{ route('log-viewer::logs.search', [$log->date, $level]) }}" method="GET">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<input type="search" id="query" name="query" class="form-control" value="{{ $query }}" placeholder="@lang('Type here to search')">
|
||||
@unless (is_null($query))
|
||||
<a href="{{ route('log-viewer::logs.show', [$log->date]) }}" class="btn btn-light">
|
||||
(@lang(':count results', ['count' => $entries->count()])) <i class="fa fa-fw fa-times"></i>
|
||||
</a>
|
||||
@endunless
|
||||
<button id="search-btn" class="btn btn-primary">
|
||||
<span class="fa fa-fw fa-search"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Log Entries --}}
|
||||
<div class="card mb-4">
|
||||
@if ($entries->hasPages())
|
||||
<div class="card-header">
|
||||
<span class="badge text-bg-info float-right">
|
||||
{{ __('Page :current of :last', ['current' => $entries->currentPage(), 'last' => $entries->lastPage()]) }}
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="table-responsive">
|
||||
<table id="entries" class="table mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>@lang('ENV')</th>
|
||||
<th style="width: 120px;">@lang('Level')</th>
|
||||
<th style="width: 65px;">@lang('Time')</th>
|
||||
<th>@lang('Header')</th>
|
||||
<th class="text-end">@lang('Actions')</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($entries as $key => $entry)
|
||||
<?php /** @var Arcanedev\LogViewer\Entities\LogEntry $entry */ ?>
|
||||
<tr>
|
||||
<td>
|
||||
<span class="badge badge-env">{{ $entry->env }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-level-{{ $entry->level }}">
|
||||
{!! $entry->level() !!}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge text-bg-secondary">
|
||||
{{ $entry->datetime->format('H:i:s') }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
{{ $entry->header }}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
@if ($entry->hasStack())
|
||||
<a class="btn btn-sm btn-light" role="button" data-bs-toggle="collapse"
|
||||
href="#log-stack-{{ $key }}" aria-expanded="false" aria-controls="log-stack-{{ $key }}">
|
||||
<i class="fa fa-toggle-on"></i> @lang('Stack')
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@if ($entry->hasContext())
|
||||
<a class="btn btn-sm btn-light" role="button" data-bs-toggle="collapse"
|
||||
href="#log-context-{{ $key }}" aria-expanded="false" aria-controls="log-context-{{ $key }}">
|
||||
<i class="fa fa-toggle-on"></i> @lang('Context')
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@if ($entry->hasStack() || $entry->hasContext())
|
||||
<tr>
|
||||
<td colspan="5" class="stack py-0">
|
||||
@if ($entry->hasStack())
|
||||
<div class="stack-content collapse" id="log-stack-{{ $key }}">
|
||||
{!! $entry->stack() !!}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($entry->hasContext())
|
||||
<div class="stack-content collapse" id="log-context-{{ $key }}">
|
||||
<pre>{{ $entry->context() }}</pre>
|
||||
</div>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">
|
||||
<span class="badge text-bg-secondary">@lang('The list of logs is empty!')</span>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!! $entries->appends(compact('query'))->render() !!}
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('modals')
|
||||
{{-- DELETE MODAL --}}
|
||||
<div class="modal fade" id="delete-log-modal" tabindex="-1" aria-labelledby="delete-log-modal-label" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<form id="delete-log-form" action="{{ route('log-viewer::logs.delete') }}" method="POST">
|
||||
{{ csrf_field() }}
|
||||
{{ method_field('DELETE') }}
|
||||
<input type="hidden" name="date" value="{{ $log->date }}">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title fs-5" id="delete-log-modal-label">@lang('Delete log file')</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>@lang('Are you sure you want to delete this log file: :date ?', ['date' => $log->date])</p>
|
||||
</div>
|
||||
<div class="modal-footer justify-content-between">
|
||||
<button type="button" class="btn btn-sm btn-light" data-bs-dismiss="modal">@lang('Cancel')</button>
|
||||
<button type="submit" class="btn btn-sm btn-danger" data-loading-text="@lang('Loading')…">@lang('Delete')</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
ready(() => {
|
||||
let deleteLogModal = new bootstrap.Modal('div#delete-log-modal')
|
||||
let deleteLogForm = document.querySelector('form#delete-log-form')
|
||||
let submitBtn = new bootstrap.Button(deleteLogForm.querySelector('button[type=submit]'))
|
||||
|
||||
deleteLogForm.addEventListener('submit', (event) => {
|
||||
event.preventDefault()
|
||||
submitBtn.toggle('loading')
|
||||
|
||||
fetch(event.currentTarget.getAttribute('action'), {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
'Content-type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
date: event.currentTarget.querySelector("input[name='date']").value,
|
||||
})
|
||||
})
|
||||
.then((resp) => resp.json())
|
||||
.then((resp) => {
|
||||
if (resp.result === 'success') {
|
||||
deleteLogModal.hide();
|
||||
location.replace("{{ route('log-viewer::logs.list') }}");
|
||||
}
|
||||
else {
|
||||
alert('AJAX ERROR ! Check the console !')
|
||||
console.error(resp)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
alert('AJAX ERROR ! Check the console !')
|
||||
console.error(err)
|
||||
})
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
@unless (empty(log_styler()->toHighlight()))
|
||||
@php
|
||||
$htmlHighlight = version_compare(PHP_VERSION, '7.4.0') >= 0
|
||||
? join('|', log_styler()->toHighlight())
|
||||
: join(log_styler()->toHighlight(), '|');
|
||||
@endphp
|
||||
|
||||
document.querySelectorAll('.stack-content').forEach((elt) => {
|
||||
elt.innerHTML = elt.innerHTML.trim()
|
||||
.replace(/({!! $htmlHighlight !!})/gm, '<strong>$1</strong>')
|
||||
})
|
||||
@endunless
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) ARCANEDEV <arcanedev.maroc@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
# Support [![Packagist License][badge_license]](LICENSE.md) [![For Laravel][badge_laravel]][link-github-repo]
|
||||
|
||||
[![Github Workflow Status][badge_build]][link-github-status]
|
||||
[![Coverage Status][badge_coverage]][link-scrutinizer]
|
||||
[![Scrutinizer Code Quality][badge_quality]][link-scrutinizer]
|
||||
[![SensioLabs Insight][badge_insight]][link-insight]
|
||||
[![Github Issues][badge_issues]][link-github-issues]
|
||||
|
||||
[![Packagist][badge_package]][link-packagist]
|
||||
[![Packagist Release][badge_release]][link-packagist]
|
||||
[![Packagist Downloads][badge_downloads]][link-packagist]
|
||||
|
||||
*By [ARCANEDEV©](http://www.arcanedev.net/)*
|
||||
|
||||
Support package is a collection of helpers and tools for ARCANEDEV + Laravel projects.
|
||||
|
||||
Feel free to check out the [releases](https://github.com/ARCANEDEV/Support/releases), [license](LICENSE.md), and [contribution guidelines](CONTRIBUTING.md).
|
||||
|
||||
## Contribution
|
||||
|
||||
Any ideas are welcome. Feel free to submit any issues or pull requests, please check the [contribution guidelines](CONTRIBUTING.md).
|
||||
|
||||
## Security
|
||||
|
||||
If you discover any security related issues, please email arcanedev.maroc@gmail.com instead of using the issue tracker.
|
||||
|
||||
## Credits
|
||||
|
||||
- [ARCANEDEV][link-author]
|
||||
- [All Contributors][link-contributors]
|
||||
|
||||
[badge_license]: http://img.shields.io/packagist/l/arcanedev/support.svg?style=flat-square
|
||||
[badge_laravel]: https://img.shields.io/badge/Laravel-5.1%20to%2010.x-orange.svg?style=flat-square
|
||||
[badge_build]: https://img.shields.io/github/workflow/status/ARCANEDEV/Support/run-tests?style=flat-square
|
||||
[badge_coverage]: https://img.shields.io/scrutinizer/coverage/g/ARCANEDEV/Support.svg?style=flat-square
|
||||
[badge_quality]: https://img.shields.io/scrutinizer/g/ARCANEDEV/Support.svg?style=flat-square
|
||||
[badge_insight]: https://img.shields.io/sensiolabs/i/de0353dd-df17-4656-b9c0-1eea95aa30a2.svg?style=flat-square
|
||||
[badge_issues]: https://img.shields.io/github/issues/ARCANEDEV/Support.svg?style=flat-square
|
||||
[badge_package]: https://img.shields.io/badge/package-arcanedev/support-blue.svg?style=flat-square
|
||||
[badge_release]: https://img.shields.io/packagist/v/arcanedev/support.svg?style=flat-square
|
||||
[badge_downloads]: https://img.shields.io/packagist/dt/arcanedev/support.svg?style=flat-square
|
||||
|
||||
[link-author]: https://github.com/arcanedev-maroc
|
||||
[link-github-repo]: https://github.com/ARCANEDEV/Support
|
||||
[link-github-status]: https://github.com/ARCANEDEV/Support/actions
|
||||
[link-github-issues]: https://github.com/ARCANEDEV/Support/issues
|
||||
[link-contributors]: https://github.com/ARCANEDEV/Support/graphs/contributors
|
||||
[link-packagist]: https://packagist.org/packages/arcanedev/support
|
||||
[link-scrutinizer]: https://scrutinizer-ci.com/g/ARCANEDEV/Support/?branch=master
|
||||
[link-insight]: https://insight.sensiolabs.com/projects/de0353dd-df17-4656-b9c0-1eea95aa30a2
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "arcanedev/support",
|
||||
"description": "ARCANEDEV Support Helpers",
|
||||
"keywords": ["arcanedev", "arcanesoft", "support", "laravel"],
|
||||
"homepage": "https://github.com/ARCANEDEV/Support",
|
||||
"authors": [
|
||||
{
|
||||
"name": "ARCANEDEV",
|
||||
"email": "[email protected]",
|
||||
"homepage": "https://github.com/arcanedev-maroc"
|
||||
}
|
||||
],
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"illuminate/contracts": "^10.0",
|
||||
"illuminate/support": "^10.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/framework": "^10.0",
|
||||
"orchestra/testbench-core": "^8.0",
|
||||
"phpunit/phpunit": "^10.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Arcanedev\\Support\\": "src/"
|
||||
},
|
||||
"files": ["helpers.php"]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Arcanedev\\Support\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpunit --colors=always",
|
||||
"test:dox": "phpunit --testdox --colors=always",
|
||||
"test:cov": "phpunit --coverage-html coverage"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-develop": "10.x-dev"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
if ( ! function_exists('laravel_version')) {
|
||||
/**
|
||||
* Get laravel version or check if the same version
|
||||
*
|
||||
* @param string|null $version
|
||||
*
|
||||
* @return string|bool
|
||||
*/
|
||||
function laravel_version(string $version = null) {
|
||||
$appVersion = app()->version();
|
||||
|
||||
if (is_null($version)) {
|
||||
return $appVersion;
|
||||
}
|
||||
|
||||
return substr($appVersion, 0, strlen($version)) === $version;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists('route_is')) {
|
||||
/**
|
||||
* Check if route(s) is the current route.
|
||||
*
|
||||
* @param array|string $routes
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function route_is($routes): bool
|
||||
{
|
||||
if ( ! is_array($routes)) {
|
||||
$routes = [$routes];
|
||||
}
|
||||
|
||||
/** @var Illuminate\Routing\Router $router */
|
||||
$router = app('router');
|
||||
|
||||
return call_user_func_array([$router, 'is'], $routes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arcanedev\Support\Console;
|
||||
|
||||
use Illuminate\Console\Command as IlluminateCommand;
|
||||
use Symfony\Component\Console\Helper\TableSeparator;
|
||||
|
||||
/**
|
||||
* Class Command
|
||||
*
|
||||
* @author ARCANEDEV <[email protected]>
|
||||
*/
|
||||
abstract class Command extends IlluminateCommand
|
||||
{
|
||||
/* -----------------------------------------------------------------
|
||||
| Main Methods
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
abstract public function handle();
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Other Methods
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create table separator
|
||||
*
|
||||
* @return \Symfony\Component\Console\Helper\TableSeparator
|
||||
*/
|
||||
protected function tableSeparator()
|
||||
{
|
||||
return new TableSeparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display frame the text info.
|
||||
*
|
||||
* @param string $text
|
||||
*/
|
||||
protected function frame(string $text)
|
||||
{
|
||||
$line = '+'.str_repeat('-', strlen($text) + 4).'+';
|
||||
$this->info($line);
|
||||
$this->info("| $text |");
|
||||
$this->info($line);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arcanedev\Support\Database;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Database\Migrations\Migration as IlluminateMigration;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
/**
|
||||
* Class Migration
|
||||
*
|
||||
* @author ARCANEDEV <[email protected]>
|
||||
*/
|
||||
abstract class Migration extends IlluminateMigration
|
||||
{
|
||||
/* -----------------------------------------------------------------
|
||||
| Properties
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* The table name.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $table;
|
||||
|
||||
/**
|
||||
* The table prefix.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $prefix;
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Getters & Setters
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get a schema builder instance for the connection.
|
||||
*
|
||||
* @return \Illuminate\Database\Schema\Builder
|
||||
*/
|
||||
protected function getSchemaBuilder(): Builder
|
||||
{
|
||||
/** @var \Illuminate\Database\DatabaseManager $db */
|
||||
$db = app()->make('db');
|
||||
|
||||
return $db->connection($this->hasConnection() ? $this->getConnection() : null)
|
||||
->getSchemaBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the migration connection name.
|
||||
*
|
||||
* @param string $connection
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setConnection($connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the prefixed table name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTableName()
|
||||
{
|
||||
return $this->hasPrefix()
|
||||
? $this->prefix.$this->table
|
||||
: $this->table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the table name.
|
||||
*
|
||||
* @param string $table
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTable($table)
|
||||
{
|
||||
$this->table = $table;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the prefix name.
|
||||
*
|
||||
* @param string $prefix
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPrefix($prefix)
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Main Methods
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Migrate to database.
|
||||
*/
|
||||
abstract public function up(): void;
|
||||
|
||||
/**
|
||||
* Rollback the migration.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
$this->getSchemaBuilder()->dropIfExists($this->getTableName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Table Schema.
|
||||
*
|
||||
* @param \Closure $blueprint
|
||||
*/
|
||||
protected function createSchema(Closure $blueprint): void
|
||||
{
|
||||
$this->getSchemaBuilder()->create($this->getTableName(), $blueprint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify a table on the schema.
|
||||
*
|
||||
* @param \Closure $callback
|
||||
*/
|
||||
protected function table(Closure $callback): void
|
||||
{
|
||||
$this->getSchemaBuilder()->table($this->getTableName(), $callback);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Check Methods
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Check if connection exists.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasConnection(): bool
|
||||
{
|
||||
return $this->isNotEmpty($this->getConnection());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if table has prefix.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasPrefix(): bool
|
||||
{
|
||||
return $this->isNotEmpty($this->prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the value is not empty.
|
||||
*
|
||||
* @param string|null $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isNotEmpty($value): bool
|
||||
{
|
||||
return ! (is_null($value) || empty($value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arcanedev\Support\Database;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* Class PrefixedModel
|
||||
*
|
||||
* @author ARCANEDEV <[email protected]>
|
||||
*/
|
||||
abstract class PrefixedModel extends Model
|
||||
{
|
||||
/* -----------------------------------------------------------------
|
||||
| Properties
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* The table prefix.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $prefix;
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Getters & Setters
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the table associated with the model.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTable()
|
||||
{
|
||||
return $this->getPrefix().parent::getTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the prefix table associated with the model.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrefix(): string
|
||||
{
|
||||
return $this->isPrefixed() ? $this->prefix : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the prefix table associated with the model.
|
||||
*
|
||||
* @param string|null $prefix
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPrefix(?string $prefix)
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Check Methods
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Check if table is prefixed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPrefixed(): bool
|
||||
{
|
||||
return ! is_null($this->prefix);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arcanedev\Support\Database;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model as Eloquent;
|
||||
use Illuminate\Database\Seeder as IlluminateSeeder;
|
||||
|
||||
/**
|
||||
* Class Seeder
|
||||
*
|
||||
* @author ARCANEDEV <[email protected]>
|
||||
*/
|
||||
abstract class Seeder extends IlluminateSeeder
|
||||
{
|
||||
/* -----------------------------------------------------------------
|
||||
| Properties
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Seeder collection.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $seeds = [];
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Main Methods
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
Eloquent::unguard();
|
||||
|
||||
foreach ($this->seeds as $seed) {
|
||||
$this->call($seed);
|
||||
}
|
||||
|
||||
Eloquent::reguard();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arcanedev\Support\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class PackageException
|
||||
*
|
||||
* @author ARCANEDEV <[email protected]>
|
||||
*/
|
||||
class PackageException extends Exception
|
||||
{
|
||||
public static function unspecifiedName(): self
|
||||
{
|
||||
return new static('You must specify the vendor/package name.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arcanedev\Support\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\{JsonResponse, Request, Response};
|
||||
|
||||
/**
|
||||
* Class VerifyJsonRequest
|
||||
*
|
||||
* @author ARCANEDEV <[email protected]>
|
||||
*/
|
||||
class VerifyJsonRequest
|
||||
{
|
||||
/* -----------------------------------------------------------------
|
||||
| Properties
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Supported request method verbs.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Main Methods
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string|array|null $methods
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, $methods = null)
|
||||
{
|
||||
if ($this->isJsonRequestValid($request, $methods)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return $this->jsonErrorResponse();
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Check Methods
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Validate json Request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string|array|null $methods
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isJsonRequestValid(Request $request, $methods)
|
||||
{
|
||||
$methods = $this->getMethods($methods);
|
||||
|
||||
if ( ! in_array($request->method(), $methods)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $request->isJson();
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Other Methods
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the error as json response.
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function jsonErrorResponse()
|
||||
{
|
||||
$data = [
|
||||
'status' => 'error',
|
||||
'code' => $statusCode = Response::HTTP_BAD_REQUEST,
|
||||
'message' => 'Request must be JSON',
|
||||
];
|
||||
|
||||
return new JsonResponse($data, $statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get request methods.
|
||||
*
|
||||
* @param string|array|null $methods
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getMethods($methods): array
|
||||
{
|
||||
$methods = $methods ?? $this->methods;
|
||||
|
||||
if (is_string($methods)) {
|
||||
$methods = (array) $methods;
|
||||
}
|
||||
|
||||
return is_array($methods) ? array_map('strtoupper', $methods) : [];
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arcanedev\Support\Providers;
|
||||
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
/**
|
||||
* Class AuthorizationServiceProvider
|
||||
*
|
||||
* @author ARCANEDEV <[email protected]>
|
||||
*/
|
||||
abstract class AuthorizationServiceProvider extends AuthServiceProvider
|
||||
{
|
||||
/* -----------------------------------------------------------------
|
||||
| Main Methods
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Define policies.
|
||||
*
|
||||
* @param string $class
|
||||
* @param array $policies
|
||||
*/
|
||||
protected function defineMany($class, array $policies)
|
||||
{
|
||||
foreach ($policies as $ability => $method) {
|
||||
Gate::define($ability, "$class@$method");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arcanedev\Support\Providers;
|
||||
|
||||
/**
|
||||
* Class CommandServiceProvider
|
||||
*
|
||||
* @author ARCANEDEV <[email protected]>
|
||||
*/
|
||||
abstract class CommandServiceProvider extends ServiceProvider
|
||||
{
|
||||
/* -----------------------------------------------------------------
|
||||
| Properties
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* The commands to be registered.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commands = [];
|
||||
|
||||
/**
|
||||
* Indicates if loading of the provider is deferred.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $defer = true;
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Main Methods
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Boot the service provider.
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->commands($this->commands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the provided commands.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provides()
|
||||
{
|
||||
return $this->commands;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arcanedev\Support\Providers\Concerns;
|
||||
|
||||
/**
|
||||
* Trait HasAssets
|
||||
*
|
||||
* @author ARCANEDEV <[email protected]>
|
||||
*/
|
||||
trait HasAssets
|
||||
{
|
||||
/* -----------------------------------------------------------------
|
||||
| Getters & Setters
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the assets path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getAssetsFolder(): string
|
||||
{
|
||||
return realpath($this->getBasePath().DIRECTORY_SEPARATOR.'assets');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the assets destination path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function assetsDestinationPath(): string
|
||||
{
|
||||
return base_path('assets'.DIRECTORY_SEPARATOR.$this->getPackageName());
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Getters & Setters
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Publish the assets.
|
||||
*/
|
||||
protected function publishAssets(): void
|
||||
{
|
||||
$this->publishes([
|
||||
$this->getAssetsFolder() => $this->assetsDestinationPath(),
|
||||
], $this->getPublishedTags('assets'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arcanedev\Support\Providers\Concerns;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Trait HasConfig
|
||||
*
|
||||
* @author ARCANEDEV <[email protected]>
|
||||
*/
|
||||
trait HasConfig
|
||||
{
|
||||
/* -----------------------------------------------------------------
|
||||
| Properties
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Merge multiple config files into one instance (package name as root key)
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $multiConfigs = false;
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
| Main Methods
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get config folder.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getConfigFolder(): string
|
||||
{
|
||||
return realpath($this->getBasePath().DIRECTORY_SEPARATOR.'config');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get config key.
|
||||
*
|
||||
* @param bool $withVendor
|
||||
* @param string $separator
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getConfigKey(bool $withVendor = false, string $separator = '.'): string
|
||||
{
|
||||
$package = Str::slug($this->getPackageName());
|
||||
|
||||
return $withVendor
|
||||
? Str::slug($this->getVendorName()).$separator.$package
|
||||
: $package;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get config file path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getConfigFile(): string
|
||||
{
|
||||
return $this->getConfigFolder().DIRECTORY_SEPARATOR."{$this->getPackageName()}.php";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the config files (paths).
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
protected function configFilesPaths()
|
||||
{
|
||||
return glob($this->getConfigFolder().DIRECTORY_SEPARATOR.'*.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register configs.
|
||||
*
|
||||
* @param string $separator
|
||||
*/
|
||||
protected function registerConfig(string $separator = '.'): void
|
||||
{
|
||||
$this->multiConfigs
|
||||
? $this->registerMultipleConfigs($separator)
|
||||
: $this->registerSingleConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a single config file.
|
||||
*/
|
||||
protected function registerSingleConfig(): void
|
||||
{
|
||||
$this->mergeConfigFrom($this->getConfigFile(), $this->getConfigKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all package configs.
|
||||
*
|
||||
* @param string $separator
|
||||
*/
|
||||
protected function registerMultipleConfigs(string $separator = '.'): void
|
||||
{
|
||||
foreach ($this->configFilesPaths() as $path) {
|
||||
$key = $this->getConfigKey(true, $separator).$separator.basename($path, '.php');
|
||||
|
||||
$this->mergeConfigFrom($path, $key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish the config file.
|
||||
*
|
||||
* @param string|null $path
|
||||
*/
|
||||
protected function publishConfig(?string $path = null): void
|
||||
{
|
||||
$this->multiConfigs
|
||||
? $this->publishMultipleConfigs()
|
||||
: $this->publishSingleConfig($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish a single config file.
|
||||
*
|
||||
* @param string|null $path
|
||||
*/
|
||||
protected function publishSingleConfig(?string $path = null): void
|
||||
{
|
||||
$this->publishes([
|
||||
$this->getConfigFile() => $path ?: config_path("{$this->getPackageName()}.php"),
|
||||
], $this->getPublishedTags('config'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish multiple config files.
|
||||
*/
|
||||
protected function publishMultipleConfigs(): void
|
||||
{
|
||||
$paths = [];
|
||||
$package = $this->getConfigKey(true, DIRECTORY_SEPARATOR);
|
||||
|
||||
foreach ($this->configFilesPaths() as $file) {
|
||||
$paths[$file] = config_path($package.DIRECTORY_SEPARATOR.basename($file));
|
||||
}
|
||||
|
||||
$this->publishes($paths, $this->getPublishedTags('config'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arcanedev\Support\Providers\Concerns;
|
||||
|
||||
/**
|
||||
* Trait HasFactories
|
||||
*
|
||||
* @author ARCANEDEV <[email protected]>
|
||||
*/
|
||||
trait HasFactories
|
||||
{
|
||||
/* -----------------------------------------------------------------
|
||||
| Main Methods
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the migrations path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getFactoriesPath(): string
|
||||
{
|
||||
return $this->getBasePath().DIRECTORY_SEPARATOR.'database'.DIRECTORY_SEPARATOR.'factories';
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish the factories.
|
||||
*
|
||||
* @param string|null $path
|
||||
*/
|
||||
protected function publishFactories(?string $path = null): void
|
||||
{
|
||||
$this->publishes([
|
||||
$this->getFactoriesPath() => $path ?: database_path('factories'),
|
||||
], $this->getPublishedTags('factories'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the factories.
|
||||
*/
|
||||
protected function loadFactories(): void
|
||||
{
|
||||
$this->loadFactoriesFrom($this->getFactoriesPath());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arcanedev\Support\Providers\Concerns;
|
||||
|
||||
/**
|
||||
* Trait HasMigrations
|
||||
*
|
||||
* @author ARCANEDEV <[email protected]>
|
||||
*/
|
||||
trait HasMigrations
|
||||
{
|
||||
/* -----------------------------------------------------------------
|
||||
| Main Methods
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the migrations path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getMigrationsPath(): string
|
||||
{
|
||||
return $this->getBasePath().DIRECTORY_SEPARATOR.'database'.DIRECTORY_SEPARATOR.'migrations';
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish the migration files.
|
||||
*
|
||||
* @param string|null $path
|
||||
*/
|
||||
protected function publishMigrations(?string $path = null): void
|
||||
{
|
||||
$this->publishes([
|
||||
$this->getMigrationsPath() => $path ?: database_path('migrations')
|
||||
], $this->getPublishedTags('migrations'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the migrations files.
|
||||
*/
|
||||
protected function loadMigrations(): void
|
||||
{
|
||||
$this->loadMigrationsFrom($this->getMigrationsPath());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arcanedev\Support\Providers\Concerns;
|
||||
|
||||
/**
|
||||
* Trait HasTranslations
|
||||
*
|
||||
* @author ARCANEDEV <[email protected]>
|
||||
*/
|
||||
trait HasTranslations
|
||||
{
|
||||
/* -----------------------------------------------------------------
|
||||
| Main Methods
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the translations' folder name.
|
||||
*/
|
||||
protected function getTranslationsFolderName(): string
|
||||
{
|
||||
return 'translations';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the translations' path.
|
||||
*/
|
||||
protected function getTranslationsPath(): string
|
||||
{
|
||||
return $this->getBasePath().DIRECTORY_SEPARATOR.$this->getTranslationsFolderName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the destination views path.
|
||||
*/
|
||||
protected function getTranslationsDestinationPath(): string
|
||||
{
|
||||
return $this->app->langPath(
|
||||
'vendor'.DIRECTORY_SEPARATOR.$this->getPackageName()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish the translations.
|
||||
*/
|
||||
protected function publishTranslations(?string $path = null): void
|
||||
{
|
||||
$this->publishes([
|
||||
$this->getTranslationsPath() => $path ?: $this->getTranslationsDestinationPath(),
|
||||
], $this->getPublishedTags('translations'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the translations files.
|
||||
*/
|
||||
protected function loadTranslations(): void
|
||||
{
|
||||
$packagePath = $this->getTranslationsPath();
|
||||
$vendorPath = $this->getTranslationsDestinationPath();
|
||||
|
||||
$this->loadTranslationsFrom($packagePath, $this->getPackageName());
|
||||
$this->loadJsonTranslationsFrom(file_exists($vendorPath) ? $vendorPath : $packagePath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arcanedev\Support\Providers\Concerns;
|
||||
|
||||
/**
|
||||
* Trait HasViews
|
||||
*
|
||||
* @author ARCANEDEV <[email protected]>
|
||||
*/
|
||||
trait HasViews
|
||||
{
|
||||
/* -----------------------------------------------------------------
|
||||
| Main Methods
|
||||
| -----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the base views path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getViewsPath(): string
|
||||
{
|
||||
return $this->getBasePath().DIRECTORY_SEPARATOR.'views';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the destination views path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getViewsDestinationPath(): string
|
||||
{
|
||||
return $this->app['config']['view.paths'][0].DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.$this->getPackageName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish the views.
|
||||
*
|
||||
* @param string|null $path
|
||||
*/
|
||||
protected function publishViews(?string $path = null): void
|
||||
{
|
||||
$this->publishes([
|
||||
$this->getViewsPath() => $path ?: $this->getViewsDestinationPath(),
|
||||
], $this->getPublishedTags('views'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the views files.
|
||||
*/
|
||||
protected function loadViews(): void
|
||||
{
|
||||
$this->loadViewsFrom($this->getViewsPath(), $this->getPackageName());
|
||||
}
|
||||
}
|
||||
Loaded 100 of 15161 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in New Issue
Block a user