├── README.md
├── config
├── general.php
└── permissions.php
├── helpers
├── constants.php
└── helpers.php
├── plugin.json
├── public
├── css
│ └── log-viewer.css
└── images
│ └── screenshot.png
├── resources
├── assets
│ └── sass
│ │ └── log-viewer.scss
├── lang
│ └── en
│ │ ├── general.php
│ │ ├── levels.php
│ │ └── log-viewer.php
└── views
│ ├── logs.blade.php
│ ├── partials
│ ├── menu.blade.php
│ └── style.blade.php
│ └── show.blade.php
├── routes
└── web.php
├── src
├── Bases
│ └── Table.php
├── Contracts
│ ├── LogViewer.php
│ ├── Patternable.php
│ ├── Table.php
│ └── Utilities
│ │ ├── Factory.php
│ │ ├── Filesystem.php
│ │ ├── LogChecker.php
│ │ ├── LogLevels.php
│ │ ├── LogMenu.php
│ │ └── LogStyler.php
├── Entities
│ ├── Log.php
│ ├── LogCollection.php
│ ├── LogEntry.php
│ └── LogEntryCollection.php
├── Exceptions
│ ├── FilesystemException.php
│ ├── LogNotFoundException.php
│ └── LogViewerException.php
├── Helpers
│ └── LogParser.php
├── Http
│ ├── Controllers
│ │ └── LogViewerController.php
│ └── Requests
│ │ └── LogViewerRequest.php
├── LogViewer.php
├── Plugin.php
├── Providers
│ └── LogViewerServiceProvider.php
├── Tables
│ └── StatsTable.php
└── Utilities
│ ├── Factory.php
│ ├── Filesystem.php
│ ├── LogChecker.php
│ ├── LogLevels.php
│ ├── LogMenu.php
│ └── LogStyler.php
└── webpack.mix.js
/README.md:
--------------------------------------------------------------------------------
1 | # Overview
2 | This is a plugin for Botble CMS so you have to purchase Botble CMS first to use this plugin.
3 | Purchase it here: [https://codecanyon.net/item/botble-cms-php-platform-based-on-laravel-framework/16928182](https://1.envato.market/LWRBY)
4 |
5 | # Installation
6 | - Download and rename folder `log-viewer-master` to `log-viewer`.
7 | - Copy folder `log-viewer` into `/platform/plugins`.
8 | - Go to Admin -> Plugins then activate plugin Log Viewer.
9 |
10 | # Screenshots
11 |
12 | 
13 |
14 | # Credits
15 | This plugin is using source code from https://github.com/ARCANEDEV/LogViewer by [ARCANEDEV©](http://www.arcanedev.net)
16 |
17 | # Contact us
18 | - Website: [https://botble.com](https://botble.com)
19 | - Email: [contact@botble.com](mailto:contact@botble.com)
20 |
--------------------------------------------------------------------------------
/config/general.php:
--------------------------------------------------------------------------------
1 | storage_path('logs'),
9 |
10 | /* ------------------------------------------------------------------------------------------------
11 | | Log files pattern
12 | | ------------------------------------------------------------------------------------------------
13 | */
14 | 'pattern' => [
15 | 'prefix' => 'laravel-',
16 | 'date' => '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]',
17 | 'extension' => '.log',
18 | ],
19 |
20 | /* ------------------------------------------------------------------------------------------------
21 | | Locale
22 | | ------------------------------------------------------------------------------------------------
23 | | Supported locales :
24 | | 'auto', 'ar', 'de', 'en', 'es', 'fa', 'fr', 'hu', 'hy', 'it', 'nl', 'pl', 'pt-BR', 'ro', 'ru',
25 | | 'sv', 'th', 'tr', 'zh-TW', 'zh'
26 | */
27 | 'locale' => 'auto',
28 |
29 | /* ------------------------------------------------------------------------------------------------
30 | | Route settings
31 | | ------------------------------------------------------------------------------------------------
32 | */
33 | 'route' => [
34 | 'enabled' => true,
35 |
36 | 'attributes' => [
37 | 'prefix' => 'log-viewer',
38 |
39 | 'middleware' => null,
40 | ],
41 | ],
42 |
43 | /* ------------------------------------------------------------------------------------------------
44 | | Log entries per page
45 | | ------------------------------------------------------------------------------------------------
46 | | This defines how many log entries are displayed per page.
47 | */
48 | 'per-page' => 30,
49 |
50 | /* ------------------------------------------------------------------------------------------------
51 | | LogViewer's Facade
52 | | ------------------------------------------------------------------------------------------------
53 | */
54 | 'facade' => 'LogViewer',
55 |
56 | /* ------------------------------------------------------------------------------------------------
57 | | Download settings
58 | | ------------------------------------------------------------------------------------------------
59 | */
60 | 'download' => [
61 | 'prefix' => 'laravel-',
62 |
63 | 'extension' => 'log',
64 | ],
65 |
66 | /* ------------------------------------------------------------------------------------------------
67 | | Menu settings
68 | | ------------------------------------------------------------------------------------------------
69 | */
70 | 'menu' => [
71 | 'filter-route' => 'log-viewer::logs.filter',
72 |
73 | 'icons-enabled' => true,
74 | ],
75 |
76 | /* ------------------------------------------------------------------------------------------------
77 | | Icons
78 | | ------------------------------------------------------------------------------------------------
79 | */
80 | 'icons' => [
81 | /**
82 | * Font awesome >= 4.3
83 | * http://fontawesome.io/icons/
84 | */
85 | 'all' => 'fa fa-fw fa-list', // http://fontawesome.io/icon/list/
86 | 'emergency' => 'fa fa-fw fa-bug', // http://fontawesome.io/icon/bug/
87 | 'alert' => 'fa fa-fw fa-bullhorn', // http://fontawesome.io/icon/bullhorn/
88 | 'critical' => 'fa fa-fw fa-heartbeat', // http://fontawesome.io/icon/heartbeat/
89 | 'error' => 'fa fa-fw fa-times-circle', // http://fontawesome.io/icon/times-circle/
90 | 'warning' => 'fa fa-fw fa-exclamation-triangle', // http://fontawesome.io/icon/exclamation-triangle/
91 | 'notice' => 'fa fa-fw fa-exclamation-circle', // http://fontawesome.io/icon/exclamation-circle/
92 | 'info' => 'fa fa-fw fa-info-circle', // http://fontawesome.io/icon/info-circle/
93 | 'debug' => 'fa fa-fw fa-life-ring', // http://fontawesome.io/icon/life-ring/
94 | ],
95 |
96 | /* ------------------------------------------------------------------------------------------------
97 | | Colors
98 | | ------------------------------------------------------------------------------------------------
99 | */
100 | 'colors' => [
101 | 'levels' => [
102 | 'empty' => '#D1D1D1',
103 | 'all' => '#8A8A8A',
104 | 'emergency' => '#B71C1C',
105 | 'alert' => '#D32F2F',
106 | 'critical' => '#F44336',
107 | 'error' => '#FF5722',
108 | 'warning' => '#FF9100',
109 | 'notice' => '#4CAF50',
110 | 'info' => '#1976D2',
111 | 'debug' => '#90CAF9',
112 | ],
113 | ],
114 | ];
115 |
--------------------------------------------------------------------------------
/config/permissions.php:
--------------------------------------------------------------------------------
1 | 'Logs list',
6 | 'flag' => 'logs.index',
7 | 'parent_flag' => 'core.system',
8 | ],
9 | [
10 | 'name' => 'Delete',
11 | 'flag' => 'logs.destroy',
12 | 'parent_flag' => 'logs.index',
13 | ],
14 | ];
--------------------------------------------------------------------------------
/helpers/constants.php:
--------------------------------------------------------------------------------
1 | 'All',
5 | 'date' => 'Date',
6 | 'name' => 'System logs',
7 | ];
8 |
--------------------------------------------------------------------------------
/resources/lang/en/levels.php:
--------------------------------------------------------------------------------
1 | 'All',
5 | 'emergency' => 'Emergency',
6 | 'alert' => 'Alert',
7 | 'critical' => 'Critical',
8 | 'error' => 'Error',
9 | 'warning' => 'Warning',
10 | 'notice' => 'Notice',
11 | 'info' => 'Info',
12 | 'debug' => 'Debug',
13 | ];
14 |
--------------------------------------------------------------------------------
/resources/lang/en/log-viewer.php:
--------------------------------------------------------------------------------
1 | 'System Logs',
5 | 'system_logs_description' => 'View system errors log.',
6 | 'name' => 'LogViewers',
7 | 'list' => 'List LogViewer',
8 | 'levels' => 'Levels',
9 | 'no_error' => 'There is no error now.',
10 | 'entries' => 'entries',
11 | 'actions' => 'Actions',
12 | 'delete_log_file' => 'Delete log file',
13 | 'loading' => 'Loading...',
14 | 'delete_button' => 'Delete file',
15 | 'confirm_delete_msg' => 'Are you sure you want to DELETE this log file :date ?',
16 | 'download' => 'Download',
17 | 'delete' => 'Delete',
18 | 'file_path' => 'File path',
19 | 'log_entries' => 'Log entries',
20 | 'size' => 'Size',
21 | 'page' => 'Page',
22 | 'of' => 'of',
23 | 'env' => 'Env',
24 | 'level' => 'Level',
25 | 'time' => 'Time',
26 | 'header' => 'Header',
27 | 'stack' => 'Stack',
28 | 'log_info' => 'Log info',
29 | 'menu_name' => 'System logs',
30 | ];
31 |
--------------------------------------------------------------------------------
/resources/views/logs.blade.php:
--------------------------------------------------------------------------------
1 | @extends(BaseHelper::getAdminMasterLayoutTemplate())
2 |
3 | @section('content')
4 | @include('plugins/log-viewer::partials.style')
5 |
6 |
7 |
8 | @include('plugins/log-viewer::partials.menu')
9 |
10 |
11 |
12 |
13 | {{ trans('plugins/log-viewer::log-viewer.log_info') }} :
14 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | {{ trans('plugins/log-viewer::log-viewer.file_path') }} : |
29 | {{ $log->getPath() }} |
30 |
31 |
32 |
33 |
34 | {{ trans('plugins/log-viewer::log-viewer.log_entries') }} : |
35 |
36 | {{ $entries->total() }}
37 | |
38 | {{ trans('plugins/log-viewer::log-viewer.size') }} : |
39 |
40 | {{ $log->size() }}
41 | |
42 | {{ trans('core/base::tables.created_at') }} : |
43 |
44 | {{ $log->createdAt() }}
45 | |
46 | {{ trans('core/base::tables.updated_at') }} : |
47 |
48 | {{ $log->updatedAt() }}
49 | |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | @if ($entries->hasPages())
58 |
59 | {!! $entries->render() !!}
60 |
61 | {{ trans('plugins/log-viewer::log-viewer.page') }} {!! $entries->currentPage() !!} {{ trans('plugins/log-viewer::log-viewer.of') }} {!! $entries->lastPage() !!}
62 |
63 |
64 | @endif
65 |
66 |
67 |
68 |
69 |
70 | {{ trans('plugins/log-viewer::log-viewer.env') }} |
71 | {{ trans('plugins/log-viewer::log-viewer.level') }} |
72 | {{ trans('plugins/log-viewer::log-viewer.time') }} |
73 | {{ trans('plugins/log-viewer::log-viewer.header') }} |
74 | {{ trans('plugins/log-viewer::log-viewer.actions') }} |
75 |
76 |
77 |
78 | @foreach($entries as $key => $entry)
79 |
80 |
81 | {{ $entry->env }}
82 | |
83 |
84 | {!! $entry->level() !!}
85 | |
86 |
87 | {{ $entry->datetime->format('H:i:s') }}
88 | |
89 |
90 | {{ $entry->header }}
91 | |
92 |
93 | @if ($entry->hasStack())
94 |
97 | {{ trans('plugins/log-viewer::log-viewer.stack') }}
98 |
99 | @endif
100 | |
101 |
102 | @if ($entry->hasStack())
103 |
104 |
105 |
106 | {!! preg_replace("/\n/", ' ', $entry->stack) !!}
107 |
108 | |
109 |
110 | @endif
111 | @endforeach
112 |
113 |
114 |
115 |
116 | @if ($entries->hasPages())
117 |
123 | @endif
124 |
125 |
126 |
127 |
128 |
151 | @endsection
152 | @section('javascript')
153 |
188 | @endsection
189 |
--------------------------------------------------------------------------------
/routes/web.php:
--------------------------------------------------------------------------------
1 | 'Botble\LogViewer\Http\Controllers', 'middleware' => 'web'], function () {
4 | Route::group(['prefix' => config('core.base.general.admin_dir'), 'middleware' => 'auth'], function () {
5 | Route::group(['prefix' => 'system/logs'], function () {
6 |
7 | Route::get('list', [
8 | 'as' => 'log-viewer::logs.index',
9 | 'uses' => 'LogViewerController@listLogs',
10 | 'permission' => 'logs.index',
11 | ]);
12 |
13 | Route::delete('delete', [
14 | 'as' => 'log-viewer::logs.destroy',
15 | 'uses' => 'LogViewerController@delete',
16 | 'permission' => 'logs.destroy',
17 | ]);
18 |
19 | Route::group(['prefix' => '{date}'], function () {
20 | Route::get('', [
21 | 'as' => 'log-viewer::logs.show',
22 | 'uses' => 'LogViewerController@show',
23 | 'middleware' => 'preventDemo',
24 | 'permission' => 'logs.index',
25 | ]);
26 |
27 | Route::get('download', [
28 | 'as' => 'log-viewer::logs.download',
29 | 'uses' => 'LogViewerController@download',
30 | 'middleware' => 'preventDemo',
31 | 'permission' => 'logs.index',
32 | ]);
33 |
34 | Route::get('{level}', [
35 | 'as' => 'log-viewer::logs.filter',
36 | 'uses' => 'LogViewerController@showByLevel',
37 | 'permission' => 'logs.index',
38 | ]);
39 | });
40 | });
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/src/Bases/Table.php:
--------------------------------------------------------------------------------
1 | setLevels($levels);
50 | $this->setLocale(empty($locale) ? config('plugins.log-viewer.general.locale') : $locale);
51 | $this->setData($data);
52 | $this->init();
53 | }
54 |
55 | /**
56 | * Set LogLevels instance.
57 | *
58 | * @param LogLevelsContract $levels
59 | *
60 | * @return Table
61 | */
62 | protected function setLevels(LogLevelsContract $levels)
63 | {
64 | $this->levels = $levels;
65 |
66 | return $this;
67 | }
68 |
69 | /**
70 | * Set table locale.
71 | *
72 | * @param string|null $locale
73 | *
74 | * @return Table
75 | */
76 | protected function setLocale($locale)
77 | {
78 | if (empty($locale) || $locale === 'auto') {
79 | $locale = app()->getLocale();
80 | }
81 |
82 | $this->locale = $locale;
83 |
84 | return $this;
85 | }
86 |
87 | /**
88 | * Set table data.
89 | *
90 | * @param array $data
91 | *
92 | * @return self
93 | */
94 | private function setData(array $data)
95 | {
96 | $this->data = $data;
97 |
98 | return $this;
99 | }
100 |
101 | /**
102 | * Prepare the table.
103 | */
104 | private function init()
105 | {
106 | $this->header = $this->prepareHeader($this->data);
107 | $this->rows = $this->prepareRows($this->data);
108 | $this->footer = $this->prepareFooter($this->data);
109 | }
110 |
111 | /**
112 | * Prepare table header.
113 | *
114 | * @param array $data
115 | *
116 | * @return array
117 | */
118 | abstract protected function prepareHeader(array $data);
119 |
120 | /**
121 | * Prepare table rows.
122 | *
123 | * @param array $data
124 | *
125 | * @return array
126 | */
127 | abstract protected function prepareRows(array $data);
128 |
129 | /**
130 | * Prepare table footer.
131 | *
132 | * @param array $data
133 | *
134 | * @return array
135 | */
136 | abstract protected function prepareFooter(array $data);
137 |
138 | /**
139 | * Get table header.
140 | *
141 | * @return array
142 | */
143 | public function header()
144 | {
145 | return $this->header;
146 | }
147 |
148 | /**
149 | * Get table rows.
150 | *
151 | * @return array
152 | */
153 | public function rows()
154 | {
155 | return $this->rows;
156 | }
157 |
158 | /**
159 | * Get table footer.
160 | *
161 | * @return array
162 | */
163 | public function footer()
164 | {
165 | return $this->footer;
166 | }
167 |
168 | /**
169 | * Get raw data.
170 | *
171 | * @return array
172 | */
173 | public function data()
174 | {
175 | return $this->data;
176 | }
177 |
178 | /**
179 | * Translate.
180 | *
181 | * @param string $key
182 | *
183 | * @return string
184 | */
185 | protected function translate($key)
186 | {
187 | return trans('plugins/log-viewer::' . $key, [], $this->locale);
188 | }
189 |
190 | /**
191 | * Get log level color.
192 | *
193 | * @param string $level
194 | *
195 | * @return string
196 | */
197 | protected function color($level)
198 | {
199 | return log_styler()->color($level);
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/src/Contracts/LogViewer.php:
--------------------------------------------------------------------------------
1 | entries = new LogEntryCollection;
49 | $this->date = $date;
50 | $this->path = $path;
51 | $this->file = new SplFileInfo($path);
52 | $this->raw = $raw;
53 |
54 | $this->entries->load($raw);
55 | }
56 |
57 | /**
58 | * Get log path.
59 | *
60 | * @return string
61 | */
62 | public function getPath()
63 | {
64 | return $this->path;
65 | }
66 |
67 | /**
68 | * Get raw log content.
69 | *
70 | * @return string
71 | */
72 | public function getRaw()
73 | {
74 | return $this->raw;
75 | }
76 |
77 | /**
78 | * Get file info.
79 | *
80 | * @return SplFileInfo
81 | */
82 | public function file()
83 | {
84 | return $this->file;
85 | }
86 |
87 | /**
88 | * Get file size.
89 | *
90 | * @return string
91 | */
92 | public function size()
93 | {
94 | return $this->formatSize($this->file->getSize());
95 | }
96 |
97 | /**
98 | * Get file creation date.
99 | *
100 | * @return Carbon
101 | */
102 | public function createdAt()
103 | {
104 | return Carbon::createFromTimestamp($this->file()->getATime());
105 | }
106 |
107 | /**
108 | * Get file modification date.
109 | *
110 | * @return Carbon
111 | */
112 | public function updatedAt()
113 | {
114 | return Carbon::createFromTimestamp($this->file()->getMTime());
115 | }
116 |
117 | /**
118 | * Make a log object.
119 | *
120 | * @param string $date
121 | * @param string $path
122 | * @param string $raw
123 | *
124 | * @return self
125 | */
126 | public static function make($date, $path, $raw)
127 | {
128 | return new self($date, $path, $raw);
129 | }
130 |
131 | /**
132 | * Get log entries.
133 | *
134 | * @param string $level
135 | *
136 | * @return LogEntryCollection
137 | */
138 | public function entries($level = 'all')
139 | {
140 | if ($level === 'all') {
141 | return $this->entries;
142 | }
143 |
144 | return $this->getByLevel($level);
145 | }
146 |
147 | /**
148 | * Get filtered log entries by level.
149 | *
150 | * @param string $level
151 | *
152 | * @return LogEntryCollection
153 | */
154 | public function getByLevel($level)
155 | {
156 | return $this->entries->filterByLevel($level);
157 | }
158 |
159 | /**
160 | * Get log stats.
161 | *
162 | * @return array
163 | */
164 | public function stats()
165 | {
166 | return $this->entries->stats();
167 | }
168 |
169 | /**
170 | * Get the log navigation tree.
171 | *
172 | * @param bool $trans
173 | *
174 | * @return array
175 | */
176 | public function tree($trans = false)
177 | {
178 | return $this->entries->tree($trans);
179 | }
180 |
181 | /**
182 | * Get log entries menu.
183 | *
184 | * @param bool $trans
185 | *
186 | * @return array
187 | */
188 | public function menu($trans = true)
189 | {
190 | return log_menu()->make($this, $trans);
191 | }
192 |
193 | /**
194 | * Get the log as a plain array.
195 | *
196 | * @return array
197 | */
198 | public function toArray()
199 | {
200 | return [
201 | 'date' => $this->date,
202 | 'path' => $this->path,
203 | 'entries' => $this->entries->toArray(),
204 | ];
205 | }
206 |
207 | /**
208 | * Convert the object to its JSON representation.
209 | *
210 | * @param int $options
211 | *
212 | * @return string
213 | */
214 | public function toJson($options = 0)
215 | {
216 | return json_encode($this->toArray(), $options);
217 | }
218 |
219 | /**
220 | * Serialize the log object to json data.
221 | *
222 | * @return array
223 | */
224 | public function jsonSerialize()
225 | {
226 | return $this->toArray();
227 | }
228 |
229 | /**
230 | * Format the file size.
231 | *
232 | * @param int $bytes
233 | * @param int $precision
234 | * @return string
235 | */
236 | protected function formatSize($bytes, $precision = 2)
237 | {
238 | $units = ['B', 'KB', 'MB', 'GB', 'TB'];
239 |
240 | $bytes = max($bytes, 0);
241 | $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
242 | $pow = min($pow, count($units) - 1);
243 |
244 | return round($bytes / pow(1024, $pow), $precision) . ' ' . $units[$pow];
245 | }
246 | }
247 |
--------------------------------------------------------------------------------
/src/Entities/LogCollection.php:
--------------------------------------------------------------------------------
1 | setFilesystem(app('botble::log-viewer.filesystem'));
26 | parent::__construct($items);
27 |
28 | if (empty($items)) {
29 | $this->load();
30 | }
31 | }
32 |
33 | /**
34 | * Set the filesystem instance.
35 | *
36 | * @param Filesystem $filesystem
37 | *
38 | * @return LogCollection
39 | */
40 | public function setFilesystem(FilesystemContract $filesystem)
41 | {
42 | $this->filesystem = $filesystem;
43 |
44 | return $this;
45 | }
46 |
47 | /**
48 | * Load all logs.
49 | *
50 | * @return LogCollection
51 | */
52 | private function load()
53 | {
54 | foreach ($this->filesystem->dates(true) as $date => $path) {
55 | $raw = $this->filesystem->read($date);
56 |
57 | $this->put($date, Log::make($date, $path, $raw));
58 | }
59 |
60 | return $this;
61 | }
62 |
63 | /**
64 | * Get a log.
65 | *
66 | * @param string $date
67 | * @param mixed|null $default
68 | *
69 | * @return Log
70 | *
71 | * @throws LogNotFoundException
72 | */
73 | public function get($date, $default = null)
74 | {
75 | if (!$this->has($date)) {
76 | throw new LogNotFoundException('Log not found in this date [' . $date . ']');
77 | }
78 |
79 | return parent::get($date, $default);
80 | }
81 |
82 | /**
83 | * Paginate logs.
84 | *
85 | * @param int $perPage
86 | *
87 | * @return LengthAwarePaginator
88 | */
89 | public function paginate($perPage = 30)
90 | {
91 | $request = request();
92 | $currentPage = $request->input('page', 1);
93 | $paginator = new LengthAwarePaginator(
94 | $this->slice(($currentPage * $perPage) - $perPage, $perPage),
95 | $this->count(),
96 | $perPage,
97 | $currentPage
98 | );
99 |
100 | return $paginator->setPath($request->url());
101 | }
102 |
103 | /**
104 | * Get a log (alias).
105 | *
106 | * @param string $date
107 | *
108 | * @return Log
109 | * @throws LogNotFoundException
110 | * @see get()
111 | *
112 | */
113 | public function log($date)
114 | {
115 | return $this->get($date);
116 | }
117 |
118 |
119 | /**
120 | * Get log entries.
121 | *
122 | * @param string $date
123 | * @param string $level
124 | *
125 | * @return LogEntryCollection
126 | * @throws LogNotFoundException
127 | */
128 | public function entries($date, $level = 'all')
129 | {
130 | return $this->get($date)->entries($level);
131 | }
132 |
133 | /**
134 | * Get logs statistics.
135 | *
136 | * @return array
137 | */
138 | public function stats()
139 | {
140 | $stats = [];
141 |
142 | foreach ($this->items as $date => $log) {
143 | $stats[$date] = $log->stats();
144 | }
145 |
146 | return $stats;
147 | }
148 |
149 | /**
150 | * List the log files (dates).
151 | *
152 | * @return array
153 | */
154 | public function dates()
155 | {
156 | return $this->keys()->toArray();
157 | }
158 |
159 | /**
160 | * Get entries total.
161 | *
162 | * @param string $level
163 | *
164 | * @return int
165 | */
166 | public function total($level = 'all')
167 | {
168 | return (int)$this->sum(function (Log $log) use ($level) {
169 | return $log->entries($level)->count();
170 | });
171 | }
172 |
173 | /**
174 | * Get logs tree.
175 | *
176 | * @param bool $trans
177 | *
178 | * @return array
179 | */
180 | public function tree($trans = false)
181 | {
182 | $tree = [];
183 |
184 | foreach ($this->items as $date => $log) {
185 | $tree[$date] = $log->tree($trans);
186 | }
187 |
188 | return $tree;
189 | }
190 |
191 | /**
192 | * Get logs menu.
193 | *
194 | * @param bool $trans
195 | *
196 | * @return array
197 | */
198 | public function menu($trans = true)
199 | {
200 | $menu = [];
201 |
202 | foreach ($this->items as $date => $log) {
203 | $menu[$date] = $log->menu($trans);
204 | }
205 |
206 | return $menu;
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/src/Entities/LogEntry.php:
--------------------------------------------------------------------------------
1 | setLevel($level);
47 | $this->setHeader($header);
48 | $this->setStack($stack);
49 | }
50 |
51 | /**
52 | * Set the entry level.
53 | *
54 | * @param string $level
55 | *
56 | * @return self
57 | */
58 | private function setLevel($level)
59 | {
60 | $this->level = $level;
61 |
62 | return $this;
63 | }
64 |
65 | /**
66 | * Set the entry header.
67 | *
68 | * @param string $header
69 | *
70 | * @return self
71 | */
72 | private function setHeader($header)
73 | {
74 | $this->setDatetime($this->extractDatetime($header));
75 |
76 | $header = $this->cleanHeader($header);
77 |
78 | if (preg_match('/^[a-z]+.[A-Z]+:/', $header, $out)) {
79 | $this->setEnv($out[0]);
80 | $header = trim(str_replace($out[0], '', $header));
81 | }
82 |
83 | $this->header = $header;
84 |
85 | return $this;
86 | }
87 |
88 | /**
89 | * Set entry environment.
90 | *
91 | * @param string $env
92 | *
93 | * @return self
94 | */
95 | private function setEnv($env)
96 | {
97 | $this->env = head(explode('.', $env));
98 |
99 | return $this;
100 | }
101 |
102 | /**
103 | * Set the entry date time.
104 | *
105 | * @param string $datetime
106 | *
107 | * @return LogEntry
108 | */
109 | private function setDatetime($datetime)
110 | {
111 | $this->datetime = Carbon::createFromFormat('Y-m-d H:i:s', $datetime);
112 |
113 | return $this;
114 | }
115 |
116 | /**
117 | * Set the entry stack.
118 | *
119 | * @param string $stack
120 | *
121 | * @return self
122 | */
123 | private function setStack($stack)
124 | {
125 | $this->stack = $stack;
126 |
127 | return $this;
128 | }
129 |
130 | /**
131 | * Get translated level name with icon.
132 | *
133 | * @return string
134 | */
135 | public function level()
136 | {
137 | return $this->icon() . ' ' . $this->name();
138 | }
139 |
140 | /**
141 | * Get translated level name.
142 | *
143 | * @return string
144 | */
145 | public function name()
146 | {
147 | return log_levels()->get($this->level);
148 | }
149 |
150 | /**
151 | * Get level icon.
152 | *
153 | * @return string
154 | */
155 | public function icon()
156 | {
157 | return log_styler()->icon($this->level);
158 | }
159 |
160 | /**
161 | * Check if same log level.
162 | *
163 | * @param string $level
164 | *
165 | * @return bool
166 | */
167 | public function isSameLevel($level)
168 | {
169 | return $this->level === $level;
170 | }
171 |
172 | /**
173 | * Get the log entry as an array.
174 | *
175 | * @return array
176 | */
177 | public function toArray()
178 | {
179 | return [
180 | 'level' => $this->level,
181 | 'datetime' => $this->datetime->format('Y-m-d H:i:s'),
182 | 'header' => $this->header,
183 | 'stack' => $this->stack,
184 | ];
185 | }
186 |
187 | /**
188 | * Convert the log entry to its JSON representation.
189 | *
190 | * @param int $options
191 | *
192 | * @return string
193 | */
194 | public function toJson($options = 0)
195 | {
196 | return json_encode($this->toArray(), $options);
197 | }
198 |
199 | /**
200 | * Serialize the log entry object to json data.
201 | *
202 | * @return array
203 | */
204 | public function jsonSerialize()
205 | {
206 | return $this->toArray();
207 | }
208 |
209 | /**
210 | * Check if the entry has a stack.
211 | *
212 | * @return bool
213 | */
214 | public function hasStack()
215 | {
216 | return $this->stack !== "\n";
217 | }
218 |
219 | /**
220 | * Clean the entry header.
221 | *
222 | * @param string $header
223 | *
224 | * @return string
225 | */
226 | private function cleanHeader($header)
227 | {
228 | return preg_replace('/\[' . REGEX_DATETIME_PATTERN . '\][ ]/', '', $header);
229 | }
230 |
231 | /**
232 | * Extract datetime from the header.
233 | *
234 | * @param string $header
235 | *
236 | * @return string
237 | */
238 | private function extractDatetime($header)
239 | {
240 | return preg_replace('/^\[(' . REGEX_DATETIME_PATTERN . ')\].*/', '$1', $header);
241 | }
242 | }
243 |
--------------------------------------------------------------------------------
/src/Entities/LogEntryCollection.php:
--------------------------------------------------------------------------------
1 | push(new LogEntry($level, $header, $stack));
24 | }
25 |
26 | return $this;
27 | }
28 |
29 | /**
30 | * Paginate log entries.
31 | *
32 | * @param int $perPage
33 | *
34 | * @return LengthAwarePaginator
35 | */
36 | public function paginate($perPage = 20)
37 | {
38 | $request = request();
39 | $page = $request->input('page', 1);
40 | $paginator = new LengthAwarePaginator(
41 | $this->slice(($page * $perPage) - $perPage, $perPage),
42 | $this->count(),
43 | $perPage,
44 | $page
45 | );
46 |
47 | return $paginator->setPath($request->url());
48 | }
49 |
50 | /**
51 | * Get filtered log entries by level.
52 | *
53 | * @param string $level
54 | *
55 | * @return LogEntryCollection
56 | */
57 | public function filterByLevel($level)
58 | {
59 | return $this->filter(function (LogEntry $entry) use ($level) {
60 | return $entry->isSameLevel($level);
61 | });
62 | }
63 |
64 | /**
65 | * Get log entries stats.
66 | *
67 | * @return array
68 | */
69 | public function stats()
70 | {
71 | $counters = $this->initStats();
72 |
73 | foreach ($this->groupBy('level') as $level => $entries) {
74 | $counters[$level] = $count = count($entries);
75 | $counters['all'] += $count;
76 | }
77 |
78 | return $counters;
79 | }
80 |
81 | /**
82 | * Get the log entries navigation tree.
83 | *
84 | * @param bool|false $trans
85 | *
86 | * @return array
87 | */
88 | public function tree($trans = false)
89 | {
90 | $tree = $this->stats();
91 |
92 | array_walk($tree, function (&$count, $level) use ($trans) {
93 | $count = [
94 | 'name' => $trans ? log_levels()->get($level) : $level,
95 | 'count' => $count,
96 | ];
97 | });
98 |
99 | return $tree;
100 | }
101 |
102 | /**
103 | * Init stats counters.
104 | *
105 | * @return array
106 | */
107 | protected function initStats()
108 | {
109 | $levels = array_merge_recursive(
110 | ['all'],
111 | array_keys(log_viewer()->levels(true))
112 | );
113 |
114 | return array_map(function () {
115 | return 0;
116 | }, array_flip($levels));
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/Exceptions/FilesystemException.php:
--------------------------------------------------------------------------------
1 | $level,
76 | 'header' => $heading[$key],
77 | 'stack' => $data[$key],
78 | ];
79 | }
80 | }
81 | }
82 |
83 | /**
84 | * Check if header has a log level.
85 | *
86 | * @param string $heading
87 | * @param string $level
88 | * @return bool
89 | */
90 | protected static function hasLogLevel($heading, $level)
91 | {
92 | return Str::contains(strtolower($heading), strtolower('.' . $level));
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/Http/Controllers/LogViewerController.php:
--------------------------------------------------------------------------------
1 | logViewer = $logViewer;
43 | $this->perPage = config('plugins.log-viewer.general.per-page', $this->perPage);
44 | Assets::addScripts(['moment', 'datetimepicker'])
45 | ->addStyles(['datetimepicker']);
46 | }
47 |
48 | /**
49 | * List all logs.
50 | *
51 | * @param Request $request
52 | * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|View
53 | */
54 | public function listLogs(Request $request)
55 | {
56 | page_title()->setTitle(trans('plugins/log-viewer::general.name'));
57 |
58 | Assets::addStylesDirectly(['/vendor/core/plugins/log-viewer/css/log-viewer.css']);
59 |
60 | $stats = $this->logViewer->statsTable();
61 | $headers = $stats->header();
62 | $rows = $this->paginate($stats->rows(), $request);
63 |
64 | return view('plugins/log-viewer::logs', compact('headers', 'rows'));
65 | }
66 |
67 | /**
68 | * Paginate logs.
69 | *
70 | * @param array $data
71 | * @param Request $request
72 | * @return LengthAwarePaginator
73 | */
74 | protected function paginate(array $data, Request $request)
75 | {
76 | $page = $request->get('page', 1);
77 | $offset = ($page * $this->perPage) - $this->perPage;
78 | $items = array_slice($data, $offset, $this->perPage, true);
79 | $rows = new LengthAwarePaginator($items, count($data), $this->perPage, $page);
80 |
81 | $rows->setPath($request->url());
82 |
83 | return $rows;
84 | }
85 |
86 | /**
87 | * Show the log.
88 | *
89 | * @param string $date
90 | * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|View
91 | */
92 | public function show($date)
93 | {
94 | page_title()->setTitle(trans('plugins/log-viewer::general.name') . ' ' . $date);
95 |
96 | $log = $this->getLogOrFail($date);
97 | $levels = $this->logViewer->levelsNames();
98 | $entries = $log->entries()->paginate($this->perPage);
99 |
100 | return view('plugins/log-viewer::show', compact('log', 'levels', 'entries'));
101 | }
102 |
103 | /**
104 | * @param string|null $date
105 | * @return Log|null
106 | */
107 | protected function getLogOrFail($date)
108 | {
109 | try {
110 | return $this->logViewer->get($date);
111 | } catch (LogNotFoundException $ex) {
112 | abort(404, $ex->getMessage());
113 | }
114 | }
115 |
116 | /**
117 | * Filter the log entries by level.
118 | *
119 | * @param string $date
120 | * @param string $level
121 | * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|RedirectResponse|View
122 | */
123 | public function showByLevel($date, $level)
124 | {
125 | page_title()->setTitle(trans('plugins/log-viewer::general.name') . ' ' . $date);
126 |
127 | $log = $this->getLogOrFail($date);
128 |
129 | if ($level === 'all') {
130 | return redirect()->route($this->showRoute, [$date]);
131 | }
132 |
133 | $levels = $this->logViewer->levelsNames();
134 | $entries = $this->logViewer->entries($date, $level)->paginate($this->perPage);
135 |
136 | return view('plugins/log-viewer::show', compact('log', 'levels', 'entries'));
137 | }
138 |
139 | /**
140 | * Download the log
141 | *
142 | * @param string $date
143 | * @return BinaryFileResponse
144 | */
145 | public function download($date)
146 | {
147 | return $this->logViewer->download($date);
148 | }
149 |
150 | /**
151 | * Delete a log.
152 | *
153 | * @param Request $request
154 | * @return JsonResponse
155 | * @throws FilesystemException
156 | */
157 | public function delete(Request $request)
158 | {
159 | if (!$request->ajax()) {
160 | abort(405, 'Method Not Allowed');
161 | }
162 |
163 | $date = $request->get('date');
164 |
165 | return response()->json([
166 | 'result' => $this->logViewer->delete($date) ? 'success' : 'error',
167 | ]);
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/src/Http/Requests/LogViewerRequest.php:
--------------------------------------------------------------------------------
1 | 'required',
18 | ];
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/LogViewer.php:
--------------------------------------------------------------------------------
1 | factory = $factory;
53 | $this->filesystem = $filesystem;
54 | $this->levels = $levels;
55 | }
56 |
57 | /**
58 | * Get the log levels.
59 | *
60 | * @param bool $flip
61 | *
62 | * @return array
63 | */
64 | public function levels($flip = false)
65 | {
66 | return $this->levels->lists($flip);
67 | }
68 |
69 | /**
70 | * Get the translated log levels.
71 | *
72 | * @param string|null $locale
73 | *
74 | * @return array
75 | */
76 | public function levelsNames($locale = null)
77 | {
78 | return $this->levels->names($locale);
79 | }
80 |
81 | /**
82 | * Set the log storage path.
83 | *
84 | * @param string $path
85 | *
86 | * @return LogViewer
87 | */
88 | public function setPath($path)
89 | {
90 | $this->factory->setPath($path);
91 |
92 | return $this;
93 | }
94 |
95 | /**
96 | * Get the log pattern.
97 | *
98 | * @return string
99 | */
100 | public function getPattern()
101 | {
102 | return $this->factory->getPattern();
103 | }
104 |
105 | /**
106 | * Set the log pattern.
107 | *
108 | * @param string $date
109 | * @param string $prefix
110 | * @param string $extension
111 | * @return LogViewer
112 | */
113 | public function setPattern(
114 | $prefix = FilesystemContract::PATTERN_PREFIX,
115 | $date = FilesystemContract::PATTERN_DATE,
116 | $extension = FilesystemContract::PATTERN_EXTENSION
117 | ) {
118 | $this->factory->setPattern($prefix, $date, $extension);
119 |
120 | return $this;
121 | }
122 |
123 | /**
124 | * Get all logs.
125 | *
126 | * @return LogCollection
127 | */
128 | public function all()
129 | {
130 | return $this->factory->all();
131 | }
132 |
133 | /**
134 | * Paginate all logs.
135 | *
136 | * @param int $perPage
137 | *
138 | * @return LengthAwarePaginator
139 | */
140 | public function paginate($perPage = 30)
141 | {
142 | return $this->factory->paginate($perPage);
143 | }
144 |
145 | /**
146 | * Get a log.
147 | *
148 | * @param string $date
149 | *
150 | * @return Log
151 | */
152 | public function get($date)
153 | {
154 | return $this->factory->log($date);
155 | }
156 |
157 | /**
158 | * Get the log entries.
159 | *
160 | * @param string $date
161 | * @param string $level
162 | *
163 | * @return LogEntryCollection
164 | */
165 | public function entries($date, $level = 'all')
166 | {
167 | return $this->factory->entries($date, $level);
168 | }
169 |
170 | /**
171 | * Download a log file.
172 | *
173 | * @param string $date
174 | * @param string|null $filename
175 | * @param array $headers
176 | * @return BinaryFileResponse
177 | */
178 | public function download($date, $filename = null, $headers = [])
179 | {
180 | if (empty($filename)) {
181 | $filename = 'laravel-' . $date . '.log';
182 | }
183 |
184 | $path = $this->filesystem->path($date);
185 |
186 | return response()->download($path, $filename, $headers);
187 | }
188 |
189 | /**
190 | * Get logs statistics.
191 | *
192 | * @return array
193 | */
194 | public function stats()
195 | {
196 | return $this->factory->stats();
197 | }
198 |
199 | /**
200 | * Get logs statistics table.
201 | *
202 | * @param string|null $locale
203 | *
204 | * @return StatsTable
205 | */
206 | public function statsTable($locale = null)
207 | {
208 | return $this->factory->statsTable($locale);
209 | }
210 |
211 | /**
212 | * Delete the log.
213 | *
214 | * @param string $date
215 | *
216 | * @return bool
217 | * @throws Exceptions\FilesystemException
218 | */
219 | public function delete($date)
220 | {
221 | return $this->filesystem->delete($date);
222 | }
223 |
224 | /**
225 | * Get all valid log files.
226 | *
227 | * @return array
228 | */
229 | public function files()
230 | {
231 | return $this->filesystem->logs();
232 | }
233 |
234 | /**
235 | * List the log files (only dates).
236 | *
237 | * @return array
238 | */
239 | public function dates()
240 | {
241 | return $this->factory->dates();
242 | }
243 |
244 | /**
245 | * Get logs count.
246 | *
247 | * @return int
248 | */
249 | public function count()
250 | {
251 | return $this->factory->count();
252 | }
253 |
254 | /**
255 | * Get entries total from all logs.
256 | *
257 | * @param string $level
258 | *
259 | * @return int
260 | */
261 | public function total($level = 'all')
262 | {
263 | return $this->factory->total($level);
264 | }
265 |
266 | /**
267 | * Get logs tree.
268 | *
269 | * @param bool $trans
270 | *
271 | * @return array
272 | */
273 | public function tree($trans = false)
274 | {
275 | return $this->factory->tree($trans);
276 | }
277 |
278 | /**
279 | * Get logs menu.
280 | *
281 | * @param bool $trans
282 | *
283 | * @return array
284 | */
285 | public function menu($trans = true)
286 | {
287 | return $this->factory->menu($trans);
288 | }
289 |
290 | /**
291 | * Determine if the log folder is empty or not.
292 | *
293 | * @return bool
294 | */
295 | public function isEmpty()
296 | {
297 | return $this->factory->isEmpty();
298 | }
299 | }
300 |
--------------------------------------------------------------------------------
/src/Plugin.php:
--------------------------------------------------------------------------------
1 | app->bind('botble::log-viewer', LogViewer::class);
25 |
26 | Helper::autoload(__DIR__ . '/../../helpers');
27 |
28 | $this->app->singleton('botble::log-viewer.levels', function ($app) {
29 | return new Utilities\LogLevels($app['translator'], config('plugins.log-viewer.general.locale'));
30 | });
31 | $this->app->bind(Contracts\Utilities\LogLevels::class, 'botble::log-viewer.levels');
32 |
33 | $this->app->singleton('botble::log-viewer.styler', Utilities\LogStyler::class);
34 | $this->app->bind(Contracts\Utilities\LogStyler::class, 'botble::log-viewer.styler');
35 |
36 | $this->app->singleton('botble::log-viewer.menu', Utilities\LogMenu::class);
37 | $this->app->bind(Contracts\Utilities\LogMenu::class, 'botble::log-viewer.menu');
38 |
39 | $this->app->singleton('botble::log-viewer.filesystem', function ($app) {
40 | $filesystem = new Utilities\Filesystem($app['files'], config('plugins.log-viewer.general.storage-path'));
41 |
42 | $filesystem->setPattern(
43 | config('plugins.log-viewer.general.pattern.prefix', Utilities\Filesystem::PATTERN_PREFIX),
44 | config('plugins.log-viewer.general.pattern.date', Utilities\Filesystem::PATTERN_DATE),
45 | config('plugins.log-viewer.general.pattern.extension', Utilities\Filesystem::PATTERN_EXTENSION)
46 | );
47 |
48 | return $filesystem;
49 | });
50 | $this->app->bind(Contracts\Utilities\Filesystem::class, 'botble::log-viewer.filesystem');
51 |
52 | $this->app->singleton('botble::log-viewer.factory', Utilities\Factory::class);
53 | $this->app->bind(Contracts\Utilities\Factory::class, 'botble::log-viewer.factory');
54 |
55 | $this->app->singleton('botble::log-viewer.checker', Utilities\LogChecker::class);
56 | $this->app->bind(Contracts\Utilities\LogChecker::class, 'botble::log-viewer.checker');
57 | }
58 |
59 | public function boot()
60 | {
61 | $this->setNamespace('plugins/log-viewer')
62 | ->loadAndPublishConfigurations(['general', 'permissions'])
63 | ->loadRoutes(['web'])
64 | ->loadAndPublishViews()
65 | ->loadAndPublishTranslations()
66 | ->loadMigrations()
67 | ->publishAssets();
68 |
69 | if (version_compare('7.0.0', get_core_version(), '>=')) {
70 | $this->app['events']->listen(RouteMatched::class, function () {
71 | DashboardMenu::registerItem([
72 | 'id' => 'cms-plugin-log-viewer',
73 | 'priority' => 7,
74 | 'parent_id' => 'cms-core-platform-administration',
75 | 'name' => 'plugins/log-viewer::log-viewer.system_logs',
76 | 'icon' => null,
77 | 'url' => route('log-viewer::logs.index'),
78 | 'permissions' => ['log-viewer::logs.index'],
79 | ]);
80 | });
81 | } else {
82 | PanelSectionManager::group('system')->beforeRendering(function (Manager $manager) {
83 | $manager
84 | ->registerItem(
85 | SystemPanelSection::class,
86 | fn() => PanelSectionItem::make('system.log-viewer')
87 | ->setTitle(trans('plugins/log-viewer::log-viewer.system_logs'))
88 | ->withDescription(trans('plugins/log-viewer::log-viewer.system_logs_description'))
89 | ->withIcon('ti ti-report')
90 | ->withPriority(9980)
91 | ->withRoute('log-viewer::logs.index')
92 | );
93 | });
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/Tables/StatsTable.php:
--------------------------------------------------------------------------------
1 | setLocale($locale);
19 |
20 | $json = [];
21 | $levels = Arr::except($this->footer(), 'all');
22 |
23 | foreach ($levels as $level => $count) {
24 | $json[] = [
25 | 'label' => $this->translate('levels.' . $level),
26 | 'value' => $count,
27 | 'color' => $this->color($level),
28 | 'highlight' => $this->color($level),
29 | ];
30 | }
31 |
32 | return json_encode(array_values($json), JSON_PRETTY_PRINT);
33 | }
34 |
35 | protected function prepareHeader(array $data): array
36 | {
37 | return array_merge_recursive(
38 | [
39 | 'date' => $this->translate('general.date'),
40 | 'all' => $this->translate('general.all'),
41 | ],
42 | $this->levels->names($this->locale)
43 | );
44 | }
45 |
46 | protected function prepareRows(array $data): array
47 | {
48 | $rows = [];
49 |
50 | foreach ($data as $date => $levels) {
51 | $rows[$date] = array_merge(compact('date'), $levels);
52 | }
53 |
54 | return $rows;
55 | }
56 |
57 | protected function prepareFooter(array $data): array
58 | {
59 | $footer = [];
60 |
61 | foreach ($data as $levels) {
62 | foreach ($levels as $level => $count) {
63 | if (!isset($footer[$level])) {
64 | $footer[$level] = 0;
65 | }
66 |
67 | $footer[$level] += $count;
68 | }
69 | }
70 |
71 | return $footer;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Utilities/Factory.php:
--------------------------------------------------------------------------------
1 | setFilesystem($filesystem);
37 | $this->setLevels($levels);
38 | }
39 |
40 | /**
41 | * Get the filesystem instance.
42 | *
43 | * @return FilesystemContract
44 | */
45 | public function getFilesystem()
46 | {
47 | return $this->filesystem;
48 | }
49 |
50 | /**
51 | * Set the filesystem instance.
52 | *
53 | * @param FilesystemContract $filesystem
54 | *
55 | * @return Factory
56 | */
57 | public function setFilesystem(FilesystemContract $filesystem)
58 | {
59 | $this->filesystem = $filesystem;
60 |
61 | return $this;
62 | }
63 |
64 | /**
65 | * Get the log levels instance.
66 | *
67 | * @return LogLevelsContract
68 | */
69 | public function getLevels()
70 | {
71 | return $this->levels;
72 | }
73 |
74 | /**
75 | * Set the log levels instance.
76 | *
77 | * @param LogLevelsContract $levels
78 | *
79 | * @return Factory
80 | */
81 | public function setLevels(LogLevelsContract $levels)
82 | {
83 | $this->levels = $levels;
84 |
85 | return $this;
86 | }
87 |
88 | /**
89 | * Set the log storage path.
90 | *
91 | * @param string $storagePath
92 | *
93 | * @return Factory
94 | */
95 | public function setPath($storagePath)
96 | {
97 | $this->filesystem->setPath($storagePath);
98 |
99 | return $this;
100 | }
101 |
102 | /**
103 | * Get the log pattern.
104 | *
105 | * @return string
106 | */
107 | public function getPattern()
108 | {
109 | return $this->filesystem->getPattern();
110 | }
111 |
112 | /**
113 | * Set the log pattern.
114 | *
115 | * @param string $date
116 | * @param string $prefix
117 | * @param string $extension
118 | *
119 | * @return Factory
120 | */
121 | public function setPattern(
122 | $prefix = FilesystemContract::PATTERN_PREFIX,
123 | $date = FilesystemContract::PATTERN_DATE,
124 | $extension = FilesystemContract::PATTERN_EXTENSION
125 | ) {
126 | $this->filesystem->setPattern($prefix, $date, $extension);
127 |
128 | return $this;
129 | }
130 |
131 | /**
132 | * Get all logs.
133 | *
134 | * @return LogCollection
135 | */
136 | public function logs()
137 | {
138 | return LogCollection::make()->setFilesystem($this->filesystem);
139 | }
140 |
141 | /**
142 | * Get all logs (alias).
143 | *
144 | * @return LogCollection
145 | */
146 | public function all()
147 | {
148 | return $this->logs();
149 | }
150 |
151 | /**
152 | * Paginate all logs.
153 | *
154 | * @param int $perPage
155 | *
156 | * @return LengthAwarePaginator
157 | */
158 | public function paginate($perPage = 30)
159 | {
160 | return $this->logs()->paginate($perPage);
161 | }
162 |
163 | /**
164 | * Get a log by date.
165 | *
166 | * @param string $date
167 | *
168 | * @return Log
169 | */
170 | public function log($date)
171 | {
172 | return $this->logs()->log($date);
173 | }
174 |
175 | /**
176 | * Get a log by date (alias).
177 | *
178 | * @param string $date
179 | *
180 | * @return Log
181 | */
182 | public function get($date)
183 | {
184 | return $this->log($date);
185 | }
186 |
187 | /**
188 | * Get log entries.
189 | *
190 | * @param string $date
191 | * @param string $level
192 | *
193 | * @return LogEntryCollection
194 | */
195 | public function entries($date, $level = 'all')
196 | {
197 | return $this->logs()->entries($date, $level);
198 | }
199 |
200 | /**
201 | * Get logs statistics.
202 | *
203 | * @return array
204 | */
205 | public function stats()
206 | {
207 | return $this->logs()->stats();
208 | }
209 |
210 | /**
211 | * Get logs statistics table.
212 | *
213 | * @param string|null $locale
214 | *
215 | * @return StatsTable
216 | */
217 | public function statsTable($locale = null)
218 | {
219 | return StatsTable::make($this->stats(), $this->levels, $locale);
220 | }
221 |
222 | /**
223 | * List the log files (dates).
224 | *
225 | * @return array
226 | */
227 | public function dates()
228 | {
229 | return $this->logs()->dates();
230 | }
231 |
232 | /**
233 | * Get logs count.
234 | *
235 | * @return int
236 | */
237 | public function count()
238 | {
239 | return $this->logs()->count();
240 | }
241 |
242 | /**
243 | * Get total log entries.
244 | *
245 | * @param string $level
246 | *
247 | * @return int
248 | */
249 | public function total($level = 'all')
250 | {
251 | return $this->logs()->total($level);
252 | }
253 |
254 | /**
255 | * Get tree menu.
256 | *
257 | * @param bool $trans
258 | *
259 | * @return array
260 | */
261 | public function tree($trans = false)
262 | {
263 | return $this->logs()->tree($trans);
264 | }
265 |
266 | /**
267 | * Get tree menu.
268 | *
269 | * @param bool $trans
270 | *
271 | * @return array
272 | */
273 | public function menu($trans = true)
274 | {
275 | return $this->logs()->menu($trans);
276 | }
277 |
278 | /**
279 | * Determine if the log folder is empty or not.
280 | *
281 | * @return bool
282 | */
283 | public function isEmpty()
284 | {
285 | return $this->logs()->isEmpty();
286 | }
287 | }
288 |
--------------------------------------------------------------------------------
/src/Utilities/Filesystem.php:
--------------------------------------------------------------------------------
1 | filesystem = $files;
56 | $this->setPath($storagePath);
57 | $this->setPattern();
58 | }
59 |
60 | /**
61 | * Get the files instance.
62 | *
63 | * @return IlluminateFilesystem
64 | */
65 | public function getInstance()
66 | {
67 | return $this->filesystem;
68 | }
69 |
70 | /**
71 | * Set the log storage path.
72 | *
73 | * @param string $storagePath
74 | *
75 | * @return Filesystem
76 | */
77 | public function setPath($storagePath)
78 | {
79 | $this->storagePath = $storagePath;
80 |
81 | return $this;
82 | }
83 |
84 | /**
85 | * Get the log pattern.
86 | *
87 | * @return string
88 | */
89 | public function getPattern()
90 | {
91 | return $this->prefixPattern . $this->datePattern . $this->extension;
92 | }
93 |
94 | /**
95 | * Set the log pattern.
96 | *
97 | * @param string $date
98 | * @param string $prefix
99 | * @param string $extension
100 | *
101 | * @return Filesystem
102 | */
103 | public function setPattern(
104 | $prefix = self::PATTERN_PREFIX,
105 | $date = self::PATTERN_DATE,
106 | $extension = self::PATTERN_EXTENSION
107 | ) {
108 | $this->setPrefixPattern($prefix);
109 | $this->setDatePattern($date);
110 | $this->setExtension($extension);
111 |
112 | return $this;
113 | }
114 |
115 | /**
116 | * Set the log date pattern.
117 | *
118 | * @param string $datePattern
119 | *
120 | * @return Filesystem
121 | */
122 | public function setDatePattern($datePattern)
123 | {
124 | $this->datePattern = $datePattern;
125 |
126 | return $this;
127 | }
128 |
129 | /**
130 | * Set the log prefix pattern.
131 | *
132 | * @param string $prefixPattern
133 | *
134 | * @return Filesystem
135 | */
136 | public function setPrefixPattern($prefixPattern)
137 | {
138 | $this->prefixPattern = $prefixPattern;
139 |
140 | return $this;
141 | }
142 |
143 | /**
144 | * Set the log extension.
145 | *
146 | * @param string $extension
147 | *
148 | * @return Filesystem
149 | */
150 | public function setExtension($extension)
151 | {
152 | $this->extension = $extension;
153 |
154 | return $this;
155 | }
156 |
157 | /**
158 | * Get all log files.
159 | *
160 | * @return array
161 | */
162 | public function all()
163 | {
164 | return $this->getFiles('*' . $this->extension);
165 | }
166 |
167 | /**
168 | * Get all valid log files.
169 | *
170 | * @return array
171 | */
172 | public function logs()
173 | {
174 | return $this->getFiles($this->getPattern());
175 | }
176 |
177 | /**
178 | * List the log files (Only dates).
179 | *
180 | * @param bool $withPaths
181 | *
182 | * @return array
183 | */
184 | public function dates($withPaths = false)
185 | {
186 | $files = array_reverse($this->logs());
187 | $dates = $this->extractDates($files);
188 |
189 | if ($withPaths) {
190 | $dates = array_combine($dates, $files); // [date => file]
191 | }
192 |
193 | return $dates;
194 | }
195 |
196 | /**
197 | * Read the log.
198 | *
199 | * @param string $date
200 | *
201 | * @return string
202 | *
203 | * @throws FilesystemException
204 | */
205 | public function read($date)
206 | {
207 | try {
208 | $path = $this->getLogPath($date);
209 |
210 | return $this->filesystem->get($path);
211 | } catch (Exception $exception) {
212 | throw new FilesystemException($exception->getMessage());
213 | }
214 | }
215 |
216 | /**
217 | * Delete the log.
218 | *
219 | * @param string $date
220 | *
221 | * @return bool
222 | *
223 | * @throws FilesystemException
224 | */
225 | public function delete($date)
226 | {
227 | $path = $this->getLogPath($date);
228 |
229 | // @codeCoverageIgnoreStart
230 | if (!$this->filesystem->delete($path)) {
231 | throw new FilesystemException('There was an error deleting the log.');
232 | }
233 | // @codeCoverageIgnoreEnd
234 |
235 | return true;
236 | }
237 |
238 | /**
239 | * Get the log file path.
240 | *
241 | * @param string $date
242 | *
243 | * @return string
244 | * @throws FilesystemException
245 | */
246 | public function path($date)
247 | {
248 | return $this->getLogPath($date);
249 | }
250 |
251 | /**
252 | * Get all files.
253 | *
254 | * @param string $pattern
255 | *
256 | * @return array
257 | */
258 | private function getFiles($pattern)
259 | {
260 | $files = $this->filesystem->glob(
261 | $this->storagePath . DIRECTORY_SEPARATOR . $pattern,
262 | GLOB_BRACE
263 | );
264 |
265 | return array_filter(array_map('realpath', $files));
266 | }
267 |
268 | /**
269 | * Get the log file path.
270 | *
271 | * @param string $date
272 | *
273 | * @return string
274 | *
275 | * @throws FilesystemException
276 | */
277 | private function getLogPath($date)
278 | {
279 | $path = $this->storagePath . DIRECTORY_SEPARATOR . $this->prefixPattern . $date . $this->extension;
280 |
281 | if (!$this->filesystem->exists($path)) {
282 | throw new FilesystemException('The log(s) could not be located at : ' . $path);
283 | }
284 |
285 | return realpath($path);
286 | }
287 |
288 | /**
289 | * Extract dates from files.
290 | *
291 | * @param array $files
292 | *
293 | * @return array
294 | */
295 | private function extractDates(array $files)
296 | {
297 | return array_map(function ($file) {
298 | return extract_date(basename($file));
299 | }, $files);
300 | }
301 | }
302 |
--------------------------------------------------------------------------------
/src/Utilities/LogChecker.php:
--------------------------------------------------------------------------------
1 | files = [];
62 | $this->setConfig($config);
63 | $this->setFilesystem($filesystem);
64 | $this->refresh();
65 | }
66 |
67 | /**
68 | * Set the config instance.
69 | *
70 | * @param ConfigContract $config
71 | *
72 | * @return LogChecker
73 | */
74 | public function setConfig(ConfigContract $config)
75 | {
76 | $this->config = $config;
77 |
78 | return $this;
79 | }
80 |
81 | /**
82 | * Set the Filesystem instance.
83 | *
84 | * @param FilesystemContract $filesystem
85 | *
86 | * @return LogChecker
87 | */
88 | public function setFilesystem(FilesystemContract $filesystem)
89 | {
90 | $this->filesystem = $filesystem;
91 |
92 | return $this;
93 | }
94 |
95 | /**
96 | * Refresh the checks.
97 | *
98 | * @return LogChecker
99 | */
100 | private function refresh()
101 | {
102 | $this->setHandler($this->config->get('app.log', 'single'));
103 |
104 | $this->messages = [
105 | 'handler' => '',
106 | 'files' => [],
107 | ];
108 | $this->files = [];
109 |
110 | $this->checkHandler();
111 | $this->checkLogFiles();
112 |
113 | return $this;
114 | }
115 |
116 | /**
117 | * Set the log handler mode.
118 | *
119 | * @param string $handler
120 | *
121 | * @return LogChecker
122 | */
123 | protected function setHandler($handler)
124 | {
125 | $this->handler = strtolower($handler);
126 |
127 | return $this;
128 | }
129 |
130 | /**
131 | * Check the handler mode.
132 | */
133 | private function checkHandler()
134 | {
135 | if ($this->isDaily()) {
136 | return;
137 | }
138 |
139 | $this->messages['handler'] = 'You should set the log handler to `daily` mode. Please check the LogViewer wiki page (Requirements) for more details.';
140 | }
141 |
142 | /**
143 | * Is a daily handler mode ?
144 | *
145 | * @return bool
146 | */
147 | protected function isDaily()
148 | {
149 | return $this->isSameHandler(self::HANDLER_DAILY);
150 | }
151 |
152 | /**
153 | * Is the handler is the same as the application log handler.
154 | *
155 | * @param string $handler
156 | *
157 | * @return bool
158 | */
159 | protected function isSameHandler($handler)
160 | {
161 | return $this->handler === $handler;
162 | }
163 |
164 | /**
165 | * Check all log files.
166 | */
167 | protected function checkLogFiles()
168 | {
169 | foreach ($this->filesystem->all() as $path) {
170 | $this->checkLogFile($path);
171 | }
172 | }
173 |
174 | /**
175 | * Check a log file.
176 | *
177 | * @param string $path
178 | */
179 | protected function checkLogFile($path)
180 | {
181 | $status = true;
182 | $file = basename($path);
183 | $message = 'The log file [' . $file . '] is valid.';
184 |
185 | if ($this->isSingleLogFile($file)) {
186 | $this->status = $status = false;
187 | $this->messages['files'][$file] = $message =
188 | 'You have a single log file in your application, you should split the [' . $file . '] into seperate log files.';
189 | } elseif ($this->isInvalidLogDate($file)) {
190 | $this->status = $status = false;
191 | $this->messages['files'][$file] = $message =
192 | 'The log file [' . $file . '] has an invalid date, the format must be like laravel-YYYY-MM-DD.log.';
193 | }
194 |
195 | $this->files[$file] = compact('status', 'message', 'path');
196 | }
197 |
198 | /**
199 | * Check if it's not a single log file.
200 | *
201 | * @param string $file
202 | *
203 | * @return bool
204 | */
205 | protected function isSingleLogFile($file)
206 | {
207 | return $file === 'laravel.log';
208 | }
209 |
210 | /**
211 | * Check the date of the log file.
212 | *
213 | * @param string $file
214 | *
215 | * @return bool
216 | */
217 | protected function isInvalidLogDate($file)
218 | {
219 | $pattern = '/laravel-(\d){4}-(\d){2}-(\d){2}.log/';
220 |
221 | if ((bool)preg_match($pattern, $file) === false) {
222 | return true;
223 | }
224 |
225 | return false;
226 | }
227 |
228 | /**
229 | * Get messages.
230 | *
231 | * @return array
232 | */
233 | public function messages()
234 | {
235 | $this->refresh();
236 |
237 | return $this->messages;
238 | }
239 |
240 | /**
241 | * Check if the checker fails.
242 | *
243 | * @return bool
244 | */
245 | public function fails()
246 | {
247 | return !$this->passes();
248 | }
249 |
250 | /**
251 | * Check if the checker passes.
252 | *
253 | * @return bool
254 | */
255 | public function passes()
256 | {
257 | $this->refresh();
258 |
259 | return $this->status;
260 | }
261 |
262 | /**
263 | * Get the requirements.
264 | *
265 | * @return array
266 | */
267 | public function requirements()
268 | {
269 | $this->refresh();
270 |
271 | return $this->isDaily() ? [
272 | 'status' => 'success',
273 | 'header' => 'Application requirements fulfilled.',
274 | 'message' => 'Are you ready to rock ?',
275 | ] : [
276 | 'status' => 'failed',
277 | 'header' => 'Application requirements failed.',
278 | 'message' => $this->messages['handler'],
279 | ];
280 | }
281 | }
282 |
--------------------------------------------------------------------------------
/src/Utilities/LogLevels.php:
--------------------------------------------------------------------------------
1 | setTranslator($translator);
42 | $this->setLocale($locale);
43 | }
44 |
45 | /**
46 | * Set the Translator instance.
47 | *
48 | * @param Translator $translator
49 | *
50 | * @return LogLevels
51 | */
52 | public function setTranslator(Translator $translator)
53 | {
54 | $this->translator = $translator;
55 |
56 | return $this;
57 | }
58 |
59 | /**
60 | * Get the selected locale.
61 | *
62 | * @return string
63 | */
64 | public function getLocale()
65 | {
66 | return $this->locale === 'auto'
67 | ? $this->translator->getLocale()
68 | : $this->locale;
69 | }
70 |
71 | /**
72 | * Set the selected locale.
73 | *
74 | * @param string $locale
75 | *
76 | * @return LogLevels
77 | */
78 | public function setLocale($locale)
79 | {
80 | $this->locale = empty($locale) ? 'auto' : $locale;
81 |
82 | return $this;
83 | }
84 |
85 | /**
86 | * Get the log levels.
87 | *
88 | * @param bool $flip
89 | *
90 | * @return array
91 | */
92 | public function lists($flip = false)
93 | {
94 | return self::all($flip);
95 | }
96 |
97 | /**
98 | * Get translated levels.
99 | *
100 | * @param string|null $locale
101 | *
102 | * @return array
103 | */
104 | public function names($locale = null)
105 | {
106 | $levels = self::all(true);
107 |
108 | array_walk($levels, function (&$name, $level) use ($locale) {
109 | $name = $this->get($level, $locale);
110 | });
111 |
112 | return $levels;
113 | }
114 |
115 | /**
116 | * Get PSR log levels.
117 | *
118 | * @param bool $flip
119 | *
120 | * @return array
121 | */
122 | public static function all($flip = false)
123 | {
124 | if (empty(self::$levels)) {
125 | self::$levels = (new ReflectionClass(LogLevel::class))
126 | ->getConstants();
127 | }
128 |
129 | return $flip ? array_flip(self::$levels) : self::$levels;
130 | }
131 |
132 | /**
133 | * Get the translated level.
134 | *
135 | * @param string $key
136 | * @param string|null $locale
137 | *
138 | * @return string
139 | */
140 | public function get($key, $locale = null)
141 | {
142 | if (empty($locale) || $locale === 'auto') {
143 | $locale = $this->getLocale();
144 | }
145 |
146 | return $this->translator->get('plugins/log-viewer::levels.' . $key, [], $locale);
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/Utilities/LogMenu.php:
--------------------------------------------------------------------------------
1 | setConfig($config);
35 | $this->setLogStyler($styler);
36 | }
37 |
38 | /**
39 | * Set the config instance.
40 | *
41 | * @param ConfigContract $config
42 | *
43 | * @return LogMenu
44 | */
45 | public function setConfig(ConfigContract $config)
46 | {
47 | $this->config = $config;
48 |
49 | return $this;
50 | }
51 |
52 | /**
53 | * Set the log styler instance.
54 | *
55 | * @param LogStylerContract $styler
56 | *
57 | * @return LogMenu
58 | */
59 | public function setLogStyler(LogStylerContract $styler)
60 | {
61 | $this->styler = $styler;
62 |
63 | return $this;
64 | }
65 |
66 | /**
67 | * Make log menu.
68 | *
69 | * @param Log $log
70 | * @param bool $trans
71 | *
72 | * @return array
73 | */
74 | public function make(Log $log, $trans = true)
75 | {
76 | $items = [];
77 | $route = $this->config('menu.filter-route');
78 |
79 | foreach ($log->tree($trans) as $level => $item) {
80 | $items[$level] = array_merge($item, [
81 | 'url' => route($route, [$log->date, $level]),
82 | 'icon' => $this->isIconsEnabled() ? $this->styler->icon($level) : '',
83 | ]);
84 | }
85 |
86 | return $items;
87 | }
88 |
89 | /**
90 | * Check if the icons are enabled.
91 | *
92 | * @return bool
93 | */
94 | private function isIconsEnabled()
95 | {
96 | return (bool)$this->config('menu.icons-enabled', false);
97 | }
98 |
99 | /**
100 | * Get config.
101 | *
102 | * @param string $key
103 | * @param mixed $default
104 | *
105 | * @return mixed
106 | */
107 | private function config($key, $default = null)
108 | {
109 | return $this->config->get('plugins.log-viewer.general.' . $key, $default);
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/Utilities/LogStyler.php:
--------------------------------------------------------------------------------
1 | config = $config;
25 | }
26 |
27 | /**
28 | * Get config.
29 | *
30 | * @param string $key
31 | * @param mixed $default
32 | *
33 | * @return mixed
34 | */
35 | private function get($key, $default = null)
36 | {
37 | return $this->config->get('plugins.log-viewer.general.' . $key, $default);
38 | }
39 |
40 | /**
41 | * Make level icon.
42 | *
43 | * @param string $level
44 | * @param string|null $default
45 | *
46 | * @return string
47 | */
48 | public function icon($level, $default = null)
49 | {
50 | return '
';
51 | }
52 |
53 | /**
54 | * Get level color.
55 | *
56 | * @param string $level
57 | * @param string|null $default
58 | *
59 | * @return string
60 | */
61 | public function color($level, $default = null)
62 | {
63 | return $this->get('colors.levels.' . $level, $default);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/webpack.mix.js:
--------------------------------------------------------------------------------
1 | let mix = require('laravel-mix');
2 |
3 | const publicPath = 'public/vendor/core/plugins/log-viewer';
4 | const resourcePath = './platform/plugins/log-viewer';
5 |
6 | mix
7 | .sass(resourcePath + '/resources/assets/sass/log-viewer.scss', publicPath + '/css')
8 | .copy(publicPath + '/css/log-viewer.css', resourcePath + '/public/css');
--------------------------------------------------------------------------------