├── 1.png ├── 2.png ├── 3.png ├── resources ├── css │ └── tool.css └── js │ ├── utils │ ├── color.js │ └── dark.js │ ├── tool.js │ ├── components │ ├── ShowLogsRow.vue │ └── LogsToolRow.vue │ └── pages │ ├── Dashboard.vue │ ├── LogsTool.vue │ └── ShowLogs.vue ├── .prettierrc ├── CHANGELOG.md ├── config └── nova-logs.php ├── tailwind.config.js ├── .circleci └── config.yml ├── webpack.mix.js ├── .eslintrc ├── dist ├── js │ └── tool.js.LICENSE.txt └── css │ └── tool.css ├── src ├── Http │ ├── Middleware │ │ └── Authorize.php │ └── Controllers │ │ └── NovaLogViewerController.php ├── Tool.php └── ToolServiceProvider.php ├── .github └── FUNDING.yml ├── package.json ├── nova.mix.js ├── routes ├── api.php └── inertia.php ├── LICENSE.md ├── composer.json └── README.md /1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PHPJunior/nova-logs/HEAD/1.png -------------------------------------------------------------------------------- /2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PHPJunior/nova-logs/HEAD/2.png -------------------------------------------------------------------------------- /3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PHPJunior/nova-logs/HEAD/3.png -------------------------------------------------------------------------------- /resources/css/tool.css: -------------------------------------------------------------------------------- 1 | /* Nova Tool CSS */ 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /resources/js/utils/color.js: -------------------------------------------------------------------------------- 1 | const config = Nova.appConfig.viewer 2 | export default (level) => config[level] 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "singleQuote": true, 4 | "tabWidth": 4, 5 | "trailingComma": "es5" 6 | } 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `nova-log-viewer` will be documented in this file 4 | 5 | ## 1.0.0 - 2018-09-01 6 | 7 | - initial release 8 | -------------------------------------------------------------------------------- /config/nova-logs.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'force_disable_routing' => true, // see https://github.com/PHPJunior/nova-logs/issues/36 6 | ], 7 | ]; 8 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | './resources/**/*.js', 4 | './resources/js/**/*.vue', 5 | ], 6 | theme: { 7 | extend: {}, 8 | }, 9 | darkMode: 'class', 10 | plugins: [], 11 | important: '.nova-logs' 12 | } 13 | -------------------------------------------------------------------------------- /resources/js/tool.js: -------------------------------------------------------------------------------- 1 | import Dashboard from './pages/Dashboard'; 2 | import LogsTool from './pages/LogsTool'; 3 | import ShowLogs from './pages/ShowLogs'; 4 | 5 | Nova.booting((app, store) => { 6 | Nova.inertia('NovaLogs', Dashboard); 7 | Nova.inertia('LogsTool', LogsTool); 8 | Nova.inertia('ShowLogs', ShowLogs); 9 | }) 10 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: circleci/node 6 | steps: 7 | - checkout 8 | - run: 9 | name: Install dependencies 10 | command: yarn 11 | - run: 12 | name: Run lint 13 | command: yarn lint 14 | - run: 15 | name: Run prettier 16 | command: yarn check-format 17 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | let mix = require('laravel-mix') 2 | let tailwindcss = require('tailwindcss') 3 | 4 | require('./nova.mix') 5 | 6 | mix 7 | .setPublicPath('dist') 8 | .js('resources/js/tool.js', 'js') 9 | .vue({ version: 3 }) 10 | .postCss('resources/css/tool.css', 'css', [ 11 | tailwindcss('tailwind.config.js'), 12 | ]) 13 | .nova('php-junior/nova-logs') 14 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", 4 | "plugin:vue/essential", 5 | "prettier" 6 | ], 7 | "parserOptions": { 8 | "ecmaVersion": 2017 9 | }, 10 | "globals": { 11 | "Nova": true 12 | }, 13 | "env": { 14 | "browser": true, 15 | "node": true 16 | }, 17 | "rules": { 18 | "vue/html-indent": ["error", 4] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /dist/js/tool.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * @kurkle/color v0.2.1 3 | * https://github.com/kurkle/color#readme 4 | * (c) 2022 Jukka Kurkela 5 | * Released under the MIT License 6 | */ 7 | 8 | /*! 9 | * Chart.js v3.9.1 10 | * https://www.chartjs.org 11 | * (c) 2022 Chart.js Contributors 12 | * Released under the MIT License 13 | */ 14 | 15 | /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ 16 | -------------------------------------------------------------------------------- /src/Http/Middleware/Authorize.php: -------------------------------------------------------------------------------- 1 | authorize($request) 15 | ? $next($request) 16 | : abort(403); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /resources/js/utils/dark.js: -------------------------------------------------------------------------------- 1 | export default (callback) => { 2 | const options = { 3 | attributes: true 4 | } 5 | 6 | const observer = new MutationObserver((mutationList, observer) => { 7 | mutationList.forEach(function (mutation) { 8 | if (mutation.type === 'attributes' && mutation.attributeName === 'class') { 9 | callback(mutation.target.classList.contains('dark')); 10 | } 11 | }) 12 | }); 13 | observer.observe(document.documentElement, options); 14 | callback(document.documentElement.classList.contains('dark')); 15 | }; 16 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: PHPJunior 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: nyinyilwin 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "npm run development", 5 | "development": "mix", 6 | "watch": "mix watch", 7 | "watch-poll": "mix watch -- --watch-options-poll=1000", 8 | "hot": "mix watch --hot", 9 | "prod": "npm run production", 10 | "production": "mix --production", 11 | "nova:install": "npm --prefix='../../vendor/laravel/nova' ci" 12 | }, 13 | "devDependencies": { 14 | "@vue/compiler-sfc": "^3.2.22", 15 | "animated-scroll-to": "^1.2.2", 16 | "chart.js": "^3.0.0", 17 | "laravel-mix": "^6.0.41", 18 | "postcss": "^8.3.11", 19 | "tailwindcss": "^3.0.24", 20 | "vue-chartjs": "^4.1.2", 21 | "vue-loader": "^16.8.3" 22 | }, 23 | "dependencies": {} 24 | } 25 | -------------------------------------------------------------------------------- /nova.mix.js: -------------------------------------------------------------------------------- 1 | const mix = require('laravel-mix') 2 | const webpack = require('webpack') 3 | const path = require('path') 4 | 5 | class NovaExtension { 6 | name() { 7 | return 'nova-extension' 8 | } 9 | 10 | register(name) { 11 | this.name = name 12 | } 13 | 14 | webpackConfig(webpackConfig) { 15 | webpackConfig.externals = { 16 | vue: 'Vue', 17 | } 18 | 19 | webpackConfig.resolve.alias = { 20 | ...(webpackConfig.resolve.alias || {}), 21 | 'laravel-nova': path.join( 22 | __dirname, 23 | '../../vendor/laravel/nova/resources/js/mixins/packages.js' 24 | ), 25 | } 26 | 27 | webpackConfig.output = { 28 | uniqueName: this.name, 29 | } 30 | } 31 | } 32 | 33 | mix.extend('nova', new NovaExtension()) 34 | -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | 'PhpJunior\NovaLogViewer\Http\Controllers'], function () { 18 | Route::get('get_chart_data','NovaLogViewerController@getChartData'); 19 | Route::get('get_list_logs','NovaLogViewerController@getListLogs'); 20 | Route::get('download/{date}','NovaLogViewerController@download'); 21 | Route::get('get/{date}/{level}','NovaLogViewerController@showByLevel'); 22 | 23 | Route::delete('delete','NovaLogViewerController@delete'); 24 | }); 25 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) PhpJunior 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Tool.php: -------------------------------------------------------------------------------- 1 | collapsable() 38 | ->icon('document-text'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /routes/inertia.php: -------------------------------------------------------------------------------- 1 | \Laravel\Nova\Nova::url('/nova-logs') 20 | ]); 21 | }); 22 | 23 | Route::get('/list', function (NovaRequest $request) { 24 | return inertia('LogsTool', [ 25 | 'baseUrl' => \Laravel\Nova\Nova::url('/nova-logs') 26 | ]); 27 | }); 28 | 29 | Route::get('/list/{date}/{level}', function (NovaRequest $request, $date, $level) { 30 | return inertia('ShowLogs', [ 31 | 'baseUrl' => \Laravel\Nova\Nova::url('/nova-logs'), 32 | 'date' => $date, 33 | 'level' => $level, 34 | ]); 35 | }); 36 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "php-junior/nova-logs", 3 | "description": "Provides a Log Viewer for Laravel Nova", 4 | "keywords": [ 5 | "laravel", 6 | "nova" 7 | ], 8 | "license": "MIT", 9 | "authors": [ 10 | { 11 | "name": "Nyi Nyi Lwin", 12 | "email": "nyinyilwin1992@hotmail.com" 13 | } 14 | ], 15 | "require": { 16 | "php": "^7.3|^8.0", 17 | "laravel/nova": "^4.0", 18 | "arcanedev/log-viewer": "*" 19 | }, 20 | "require-dev": { 21 | "orchestra/testbench": "^3.6", 22 | "phpunit/phpunit": "7.1" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "PhpJunior\\NovaLogViewer\\": "src/" 27 | } 28 | }, 29 | "autoload-dev": { 30 | "psr-4": { 31 | "PhpJunior\\NovaLogViewer\\Tests\\": "tests" 32 | } 33 | }, 34 | "extra": { 35 | "laravel": { 36 | "providers": [ 37 | "PhpJunior\\NovaLogViewer\\ToolServiceProvider" 38 | ] 39 | } 40 | }, 41 | "config": { 42 | "sort-packages": true 43 | }, 44 | "minimum-stability": "dev", 45 | "prefer-stable": true 46 | } 47 | -------------------------------------------------------------------------------- /resources/js/components/ShowLogsRow.vue: -------------------------------------------------------------------------------- 1 | 27 | co 28 | 41 | 42 | 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nova Log Viewer 2 | 3 | [![Latest Stable Version](https://poser.pugx.org/php-junior/nova-logs/v/stable)](https://packagist.org/packages/php-junior/nova-logs) 4 | [![Total Downloads](https://poser.pugx.org/php-junior/nova-logs/downloads)](https://packagist.org/packages/php-junior/nova-logs) 5 | 6 | ![screenshot 1](1.png) 7 | 8 | ![screenshot 2](2.png) 9 | 10 | ![screenshot 3](3.png) 11 | 12 | ## Requirements 13 | 14 | - `php: ^7.4 | ^8` 15 | - `laravel/nova: ^4` 16 | 17 | For Laravel Nova Version 3, please use v1 instead. 18 | 19 | ## Installation 20 | 21 | You can install the package in to a Laravel app that uses [Nova](https://nova.laravel.com) via composer: 22 | 23 | ```bash 24 | composer require php-junior/nova-logs 25 | ``` 26 | 27 | **LogViewer** support only the **daily** log channel, so make sure that the `LOG_CHANNEL` is set to `daily` instead of `stack` in your `.env` file. 28 | 29 | For Laravel 5.5 and below, set this in your `.env` file 30 | 31 | ` 32 | APP_LOG=daily 33 | ` 34 | 35 | Next up, you must register the tool with Nova. This is typically done in the `tools` method of the `NovaServiceProvider`. 36 | 37 | ```php 38 | // in app/Providers/NovaServiceProvider.php 39 | 40 | public function tools() 41 | { 42 | return [ 43 | // ... 44 | new \PhpJunior\NovaLogViewer\Tool(), 45 | ]; 46 | } 47 | ``` 48 | 49 | To publish the configuration use: 50 | 51 | ```php 52 | php artisan vendor:publish --tag=nova-logs-config 53 | ``` 54 | 55 | ### Changelog 56 | 57 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 58 | 59 | ## Credits 60 | 61 | - [ARCANEDEV](https://github.com/ARCANEDEV/LogViewer) 62 | 63 | ## License 64 | 65 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 66 | -------------------------------------------------------------------------------- /src/ToolServiceProvider.php: -------------------------------------------------------------------------------- 1 | registerPublishing(); 22 | 23 | $this->app->booted(function () { 24 | $this->routes(); 25 | }); 26 | 27 | Nova::serving(function (ServingNova $event) { 28 | Nova::provideToScript([ 29 | 'viewer' => config('log-viewer.colors.levels'), 30 | ]); 31 | }); 32 | } 33 | 34 | /** 35 | * Register the tool's routes. 36 | * 37 | * @return void 38 | */ 39 | protected function routes() 40 | { 41 | if ($this->app->routesAreCached()) { 42 | return; 43 | } 44 | 45 | Nova::router(['nova', Authenticate::class, Authorize::class], 'nova-logs') 46 | ->group(__DIR__ . '/../routes/inertia.php'); 47 | 48 | Route::middleware(['nova', Authorize::class]) 49 | ->prefix('nova-vendor/php-junior/nova-log-viewer') 50 | ->group(__DIR__ . '/../routes/api.php'); 51 | } 52 | 53 | /** 54 | * Register any application services. 55 | * 56 | * @return void 57 | */ 58 | public function register() 59 | { 60 | $this->mergeConfigFrom(__DIR__ . '/../config/nova-logs.php', 'nova-logs'); 61 | 62 | if (config('nova-logs.log-viewer.force_disable_routing', true)) { 63 | config()->set('log-viewer.route.enabled', false); 64 | } 65 | } 66 | 67 | /** 68 | * Register the package's publishable resources. 69 | * 70 | * @return void 71 | */ 72 | private function registerPublishing() 73 | { 74 | if ($this->app->runningInConsole()) { 75 | $this->publishes([ 76 | __DIR__ . '/../config/nova-logs.php' => config_path('nova-logs.php'), 77 | ], 'nova-logs-config'); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /resources/js/components/LogsToolRow.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 62 | -------------------------------------------------------------------------------- /resources/js/pages/Dashboard.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 73 | 74 | 85 | -------------------------------------------------------------------------------- /resources/js/pages/LogsTool.vue: -------------------------------------------------------------------------------- 1 | 66 | 67 | 131 | 132 | 134 | -------------------------------------------------------------------------------- /dist/css/tool.css: -------------------------------------------------------------------------------- 1 | .nova-logs .absolute{position:absolute}.nova-logs .relative{position:relative}.nova-logs .top-4{top:1rem}.nova-logs .right-6{right:1.5rem}.nova-logs .float-right{float:right}.nova-logs .float-left{float:left}.nova-logs .mr-2{margin-right:.5rem}.nova-logs .mr-3{margin-right:.75rem}.nova-logs .mt-2{margin-top:.5rem}.nova-logs .mb-6{margin-bottom:1.5rem}.nova-logs .mb-4{margin-bottom:1rem}.nova-logs .mb-2{margin-bottom:.5rem}.nova-logs .mt-3{margin-top:.75rem}.nova-logs .mt-6{margin-top:1.5rem}.nova-logs .mr-1{margin-right:.25rem}.nova-logs .mt-1{margin-top:.25rem}.nova-logs .inline-block{display:inline-block}.nova-logs .flex{display:flex}.nova-logs .inline-flex{display:inline-flex}.nova-logs .table{display:table}.nova-logs .h-4{height:1rem}.nova-logs .max-h-\[calc\(100vh-3rem\)\]{max-height:calc(100vh - 3rem)}.nova-logs .w-1\/3{width:33.333333%}.nova-logs .w-full{width:100%}.nova-logs .w-1\/6{width:16.666667%}.nova-logs .w-4{width:1rem}.nova-logs .w-5\/6{width:83.333333%}.nova-logs .max-w-sm{max-width:24rem}.nova-logs .cursor-pointer{cursor:pointer}.nova-logs .appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.nova-logs .flex-col{flex-direction:column}.nova-logs .flex-wrap{flex-wrap:wrap}.nova-logs .items-center{align-items:center}.nova-logs .justify-end{justify-content:flex-end}.nova-logs .justify-center{justify-content:center}.nova-logs .overflow-auto{overflow:auto}.nova-logs .overflow-hidden{overflow:hidden}.nova-logs .rounded{border-radius:.25rem}.nova-logs .rounded-none{border-radius:0}.nova-logs .rounded-full{border-radius:9999px}.nova-logs .rounded-lg{border-radius:.5rem}.nova-logs .rounded-l{border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.nova-logs .rounded-r{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.nova-logs .border{border-width:1px}.nova-logs .border-0{border-width:0}.nova-logs .border-t{border-top-width:1px}.nova-logs .border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity))}.nova-logs .bg-transparent{background-color:transparent}.nova-logs .bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.nova-logs .bg-green-100{--tw-bg-opacity:1;background-color:rgb(220 252 231/var(--tw-bg-opacity))}.nova-logs .bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity))}.nova-logs .bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.nova-logs .p-1{padding:.25rem}.nova-logs .p-4{padding:1rem}.nova-logs .p-2{padding:.5rem}.nova-logs .px-2{padding-left:.5rem;padding-right:.5rem}.nova-logs .py-2{padding-bottom:.5rem;padding-top:.5rem}.nova-logs .px-3{padding-left:.75rem;padding-right:.75rem}.nova-logs .py-1{padding-bottom:.25rem;padding-top:.25rem}.nova-logs .px-4{padding-left:1rem;padding-right:1rem}.nova-logs .px-6{padding-left:1.5rem;padding-right:1.5rem}.nova-logs .py-4{padding-bottom:1rem;padding-top:1rem}.nova-logs .pl-5{padding-left:1.25rem}.nova-logs .pr-5{padding-right:1.25rem}.nova-logs .pr-2{padding-right:.5rem}.nova-logs .pl-0{padding-left:0}.nova-logs .text-center{text-align:center}.nova-logs .align-baseline{vertical-align:baseline}.nova-logs .text-sm{font-size:.875rem;line-height:1.25rem}.nova-logs .text-xl{font-size:1.25rem;line-height:1.75rem}.nova-logs .text-base{font-size:1rem;line-height:1.5rem}.nova-logs .text-xs{font-size:.75rem;line-height:1rem}.nova-logs .font-semibold{font-weight:600}.nova-logs .font-bold{font-weight:700}.nova-logs .uppercase{text-transform:uppercase}.nova-logs .leading-normal{line-height:1.5}.nova-logs .tracking-wide{letter-spacing:.025em}.nova-logs .text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.nova-logs .text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.nova-logs .text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity))}.nova-logs .text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity))}.nova-logs .shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.nova-logs .hover\:border-transparent:hover{border-color:transparent}.nova-logs .hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.nova-logs .group:hover .group-hover\:bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.nova-logs .dark .dark\:border-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity))}.nova-logs .dark .dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.nova-logs .dark .dark\:bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity))}.nova-logs .dark .dark\:bg-red-400{--tw-bg-opacity:1;background-color:rgb(248 113 113/var(--tw-bg-opacity))}.nova-logs .dark .dark\:text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.nova-logs .dark .dark\:text-green-900{--tw-text-opacity:1;color:rgb(20 83 45/var(--tw-text-opacity))}.nova-logs .dark .dark\:text-red-900{--tw-text-opacity:1;color:rgb(127 29 29/var(--tw-text-opacity))}.nova-logs .dark .dark\:shadow-gray-600{--tw-shadow-color:#4b5563;--tw-shadow:var(--tw-shadow-colored)}.nova-logs .dark .group:hover .dark\:group-hover\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))} 2 | -------------------------------------------------------------------------------- /src/Http/Controllers/NovaLogViewerController.php: -------------------------------------------------------------------------------- 1 | logViewer = $logViewer; 32 | } 33 | 34 | /** 35 | * @return \Illuminate\Http\JsonResponse 36 | */ 37 | public function getChartData() 38 | { 39 | $stats = $this->logViewer->statsTable(); 40 | $chartData = $this->prepareChartData($stats); 41 | $percents = $this->calcPercentages($stats->footer(), $stats->header()); 42 | 43 | return response()->json([ 44 | 'chartData' => $chartData, 45 | 'percents' => $percents 46 | ], 200); 47 | } 48 | 49 | /** 50 | * List all logs. 51 | * 52 | * @param \Illuminate\Http\Request $request 53 | * 54 | * @return \Illuminate\Http\JsonResponse 55 | */ 56 | public function getListLogs(Request $request) 57 | { 58 | $stats = $this->logViewer->statsTable(); 59 | $headers = $stats->header(); 60 | $rows = $this->paginate($stats->rows(), $request); 61 | 62 | return response()->json([ 63 | 'headers' => $headers, 64 | 'rows' => $rows 65 | ]); 66 | } 67 | 68 | /** 69 | * Filter the log entries by level. 70 | * 71 | * @param string $date 72 | * 73 | * @param $level 74 | * @return \Illuminate\View\View|\Illuminate\Http\RedirectResponse 75 | */ 76 | public function showByLevel($date, $level) 77 | { 78 | $log = $this->getLogOrFail($date); 79 | $levels = $this->logViewer->levelsNames(); 80 | $entries = $this->logViewer->entries($date, $level)->paginate($this->perPage); 81 | $menus = $this->getMenus($log); 82 | 83 | return response()->json([ 84 | 'log' => $log, 85 | 'info' => [ 86 | 'path' => $log->getPath(), 87 | 'size' => $log->size(), 88 | 'entries' => $entries->total(), 89 | 'created_at' => $log->createdAt()->format('Y-m-d H:i:s'), 90 | 'updated_at' => $log->updatedAt()->format('Y-m-d H:i:s') 91 | ], 92 | 'levels' => $levels, 93 | 'level' => $level, 94 | 'entries' => $entries, 95 | 'menus' => $menus, 96 | ], 200); 97 | } 98 | 99 | /** 100 | * Download the log 101 | * 102 | * @param string $date 103 | * 104 | * @return \Symfony\Component\HttpFoundation\BinaryFileResponse 105 | */ 106 | public function download($date) 107 | { 108 | return $this->logViewer->download($date); 109 | } 110 | 111 | /** 112 | * Delete a log. 113 | * 114 | * @param \Illuminate\Http\Request $request 115 | * 116 | * @return \Illuminate\Http\JsonResponse 117 | * @throws \Arcanedev\LogViewer\Exceptions\FilesystemException 118 | */ 119 | public function delete(Request $request) 120 | { 121 | $date = $request->get('date'); 122 | return response()->json([ 123 | 'result' => $this->logViewer->delete($date) ? 'success' : 'error' 124 | ]); 125 | } 126 | 127 | /** 128 | * Prepare chart data. 129 | * 130 | * @param \Arcanedev\LogViewer\Tables\StatsTable $stats 131 | * 132 | * @return array 133 | */ 134 | protected function prepareChartData(StatsTable $stats) 135 | { 136 | $totals = $stats->totals()->all(); 137 | 138 | return [ 139 | 'labels' => Arr::pluck($totals, 'label'), 140 | 'datasets' => [ 141 | [ 142 | 'data' => Arr::pluck($totals, 'value'), 143 | 'backgroundColor' => Arr::pluck($totals, 'color'), 144 | 'hoverBackgroundColor' => Arr::pluck($totals, 'highlight'), 145 | ], 146 | ], 147 | ]; 148 | } 149 | 150 | /** 151 | * Creates the log menus - without the need for active routes as in Arcanedev\LogViewer\Utilities\LogMenu 152 | * 153 | * @param Log $log 154 | * @param $trans 155 | * @return array 156 | */ 157 | protected function getMenus(Log $log, $trans = true) 158 | { 159 | $items = []; 160 | foreach ($log->tree($trans) as $level => $item) { 161 | $items[$level] = $item; 162 | } 163 | 164 | return $items; 165 | } 166 | 167 | /** 168 | * Calculate the percentage. 169 | * 170 | * @param array $total 171 | * @param array $names 172 | * 173 | * @return array 174 | */ 175 | protected function calcPercentages(array $total, array $names) 176 | { 177 | $percents = []; 178 | $all = Arr::get($total, 'all'); 179 | 180 | foreach ($total as $level => $count) { 181 | $percents[$level] = [ 182 | 'name' => $names[$level], 183 | 'count' => $count, 184 | 'percent' => $all ? round(($count / $all) * 100, 2) : 0, 185 | 'backgroundColor' => config('log-viewer.colors.levels.' . $level) 186 | ]; 187 | } 188 | 189 | return $percents; 190 | } 191 | 192 | /** 193 | * Paginate logs. 194 | * 195 | * @param array $data 196 | * @param \Illuminate\Http\Request $request 197 | * 198 | * @return LengthAwarePaginator 199 | */ 200 | protected function paginate(array $data, Request $request) 201 | { 202 | $data = new Collection($data); 203 | $page = $request->get('page', 1); 204 | $path = $request->url(); 205 | 206 | return new LengthAwarePaginator( 207 | $data->forPage($page, $this->perPage), 208 | $data->count(), 209 | $this->perPage, 210 | $page, 211 | compact('path') 212 | ); 213 | } 214 | 215 | /** 216 | * Get a log or fail 217 | * 218 | * @param string $date 219 | * 220 | * @return \Arcanedev\LogViewer\Entities\Log|null 221 | */ 222 | protected function getLogOrFail($date) 223 | { 224 | $log = null; 225 | 226 | try { 227 | $log = $this->logViewer->get($date); 228 | } catch (LogNotFoundException $e) { 229 | abort(404, $e->getMessage()); 230 | } 231 | 232 | return $log; 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /resources/js/pages/ShowLogs.vue: -------------------------------------------------------------------------------- 1 | 165 | 166 | 268 | 269 | 283 | --------------------------------------------------------------------------------