Skip to content

Commit 3be9720

Browse files
author
songzou
committed
first commit
0 parents  commit 3be9720

File tree

9 files changed

+751
-0
lines changed

9 files changed

+751
-0
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.DS_Store
2+
phpunit.phar
3+
/vendor
4+
composer.phar
5+
composer.lock
6+
*.project
7+
.idea/

LICENSE

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2015 Jens Segers
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of
6+
this software and associated documentation files (the "Software"), to deal in
7+
the Software without restriction, including without limitation the rights to
8+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9+
the Software, and to permit persons to whom the Software is furnished to do so,
10+
subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
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, FITNESS
17+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
laravel-admin-ext/log-viewer
2+
============================
3+
4+
## Installation
5+
6+
```
7+
$ composer require laravel-admin-ext/log-viewer -vvv
8+
9+
```
10+
11+
Open `app/Providers/AppServiceProvider.php`, and call the `LogViewer::boot` method within the `boot` method:
12+
13+
```php
14+
<?php
15+
16+
namespace App\Providers;
17+
18+
use Encore\Admin\LogViewer\LogViewer;
19+
use Illuminate\Support\ServiceProvider;
20+
21+
class AppServiceProvider extends ServiceProvider
22+
{
23+
public function boot()
24+
{
25+
LogViewer::boot();
26+
}
27+
}
28+
```
29+
30+
At last run:
31+
32+
```
33+
$ php artisan admin:import log-viewer
34+
```
35+
36+
Finally open `http://localhost/admin/logs`.
37+
38+
License
39+
------------
40+
Licensed under [The MIT License (MIT)](LICENSE).

composer.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "laravel-admin-ext/log-viewer",
3+
"description": "Log viewer for laravel",
4+
"type": "library",
5+
"keywords": ["laravel-admin", "log", "viewer"],
6+
"homepage": "https://github.com/laravel-admin-extensions/log-viewer",
7+
"license": "MIT",
8+
"authors": [
9+
{
10+
"name": "z-song",
11+
"email": "[email protected]"
12+
}
13+
],
14+
"require": {
15+
"php": ">=7.0.0",
16+
"laravel/framework": "5.5.*",
17+
"encore/laravel-admin": "1.5.*"
18+
},
19+
"require-dev": {
20+
"phpunit/phpunit": "~6.0",
21+
"laravel/laravel": "5.*"
22+
},
23+
"autoload": {
24+
"psr-4": {
25+
"Encore\\Admin\\LogViewer\\": "src/"
26+
}
27+
},
28+
"extra": {
29+
"laravel": {
30+
"providers": [
31+
"Encore\\Admin\\LogViewer\\LogViewerServiceProvider"
32+
]
33+
34+
}
35+
}
36+
}

resources/views/logs.blade.php

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
<script data-exec-on-popstate xmlns="http://www.w3.org/1999/html">
2+
3+
$(function () {
4+
LA.intervalIds = [];
5+
LA.addIntervalId = function (intervalId, persist) {
6+
this.intervalIds.push({id:intervalId, persist:persist});
7+
};
8+
9+
LA.clearIntervalId = function (intervalId) {
10+
for (var id in this.intervalIds) {
11+
if (intervalId == this.intervalIds[id].id && !this.intervalIds[id].persist) {
12+
clearInterval(intervalId);
13+
this.intervalIds.splice(id, 1);
14+
}
15+
}
16+
};
17+
18+
LA.cleanIntervalId = function () {
19+
for (var id in this.intervalIds) {
20+
if (!this.intervalIds[id].persist) {
21+
clearInterval(this.intervalIds[id].id);
22+
this.intervalIds.splice(id, 1);
23+
}
24+
}
25+
};
26+
27+
$(document).on('pjax:complete', function(xhr) {
28+
$.admin.cleanIntervalId();
29+
});
30+
31+
$('.log-refresh').on('click', function() {
32+
$.pjax.reload('#pjax-container');
33+
});
34+
35+
var pos = {{ $end }};
36+
37+
function changePos(offset){
38+
pos = offset;
39+
}
40+
41+
function fetch() {
42+
$.ajax({
43+
url:'{{ $tailPath }}',
44+
method: 'get',
45+
data : {offset : pos},
46+
success:function(data) {
47+
for (var i in data.logs) {
48+
$('table > tbody > tr:first').before(data.logs[i]);
49+
}
50+
changePos(data.pos);
51+
}
52+
});
53+
}
54+
55+
var refreshIntervalId = null;
56+
57+
$('.log-live').click(function() {
58+
$("i", this).toggleClass("fa-play fa-pause");
59+
60+
if (refreshIntervalId) {
61+
$.admin.clearIntervalId(refreshIntervalId);
62+
refreshIntervalId = null;
63+
} else {
64+
refreshIntervalId = setInterval(function() {
65+
fetch();
66+
}, 2000);
67+
$.admin.addIntervalId(refreshIntervalId, false);
68+
}
69+
});
70+
});
71+
72+
73+
</script>
74+
<div class="row">
75+
76+
<!-- /.col -->
77+
<div class="col-md-10">
78+
<div class="box box-primary">
79+
<div class="box-header with-border">
80+
<button type="button" class="btn btn-primary btn-sm log-refresh"><i class="fa fa-refresh"></i> {{ trans('admin.refresh') }}</button>
81+
<button type="button" class="btn btn-default btn-sm log-live"><i class="fa fa-play"></i> </button>
82+
<div class="pull-right">
83+
<div class="btn-group">
84+
@if ($prevUrl)
85+
<a href="{{ $prevUrl }}" class="btn btn-default btn-sm"><i class="fa fa-chevron-left"></i></a>
86+
@endif
87+
@if ($nextUrl)
88+
<a href="{{ $nextUrl }}" class="btn btn-default btn-sm"><i class="fa fa-chevron-right"></i></a>
89+
@endif
90+
</div>
91+
<!-- /.btn-group -->
92+
</div>
93+
<!-- /.box-tools -->
94+
</div>
95+
<!-- /.box-header -->
96+
<div class="box-body no-padding">
97+
98+
<div class="table-responsive">
99+
<table class="table table-hover">
100+
101+
<thead>
102+
<tr>
103+
<th>Level</th>
104+
<th>Env</th>
105+
<th>Time</th>
106+
<th>Message</th>
107+
<th></th>
108+
</tr>
109+
</thead>
110+
111+
<tbody>
112+
113+
@foreach($logs as $index => $log)
114+
115+
<tr>
116+
<td><span class="label bg-{{\Encore\Admin\LogViewer\LogViewer::$levelColors[$log['level']]}}">{{ $log['level'] }}</span></td>
117+
<td><strong>{{ $log['env'] }}</strong></td>
118+
<td style="width:150px;">{{ $log['time'] }}</td>
119+
<td><code>{{ $log['info'] }}</code></td>
120+
<td>
121+
@if(!empty($log['trace']))
122+
<a class="btn btn-primary btn-xs" data-toggle="collapse" data-target=".trace-{{$index}}"><i class="fa fa-info"></i>&nbsp;&nbsp;Exception</a>
123+
@endif
124+
</td>
125+
</tr>
126+
127+
@if (!empty($log['trace']))
128+
<tr class="collapse trace-{{$index}}">
129+
<td colspan="5"><div style="white-space: pre-wrap;background: #333;color: #fff; padding: 10px;">{{ $log['trace'] }}</div></td>
130+
</tr>
131+
@endif
132+
133+
@endforeach
134+
135+
</tbody>
136+
</table>
137+
<!-- /.table -->
138+
</div>
139+
<!-- /.mail-box-messages -->
140+
</div>
141+
<!-- /.box-body -->
142+
</div>
143+
<!-- /. box -->
144+
</div>
145+
146+
<div class="col-md-2">
147+
148+
<div class="box box-solid">
149+
<div class="box-header with-border">
150+
<h3 class="box-title">Files</h3>
151+
</div>
152+
<div class="box-body no-padding">
153+
<ul class="nav nav-pills nav-stacked">
154+
@foreach($logFiles as $logFile)
155+
<li @if($logFile == $fileName)class="active"@endif>
156+
<a href="{{ action('\Encore\Admin\LogViewer\LogController@index', ['file' => $logFile]) }}"><i class="fa fa-{{ ($logFile == $fileName) ? 'folder-open' : 'folder' }}"></i>{{ $logFile }}</a>
157+
</li>
158+
@endforeach
159+
</ul>
160+
</div>
161+
<!-- /.box-body -->
162+
</div>
163+
164+
<div class="box box-solid">
165+
<div class="box-header with-border">
166+
<h3 class="box-title">Info</h3>
167+
</div>
168+
<div class="box-body no-padding">
169+
<ul class="nav nav-pills nav-stacked">
170+
<li class="margin: 10px;">
171+
<a>Size: {{ $size }}</a>
172+
</li>
173+
<li class="margin: 10px;">
174+
<a>Updated at: {{ date('Y-m-d H:i:s', filectime($filePath)) }}</a>
175+
</li>
176+
</ul>
177+
</div>
178+
<!-- /.box-body -->
179+
</div>
180+
181+
<!-- /.box -->
182+
</div>
183+
<!-- /.col -->
184+
</div>

src/BootExtension.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Encore\Admin\LogViewer;
4+
5+
use Encore\Admin\Admin;
6+
7+
trait BootExtension
8+
{
9+
/**
10+
* {@inheritdoc}
11+
*/
12+
public static function boot()
13+
{
14+
static::registerRoutes();
15+
16+
Admin::extend('log-viwer', __CLASS__);
17+
}
18+
19+
/**
20+
* Register routes for laravel-admin.
21+
*
22+
* @return void
23+
*/
24+
protected static function registerRoutes()
25+
{
26+
parent::routes(function ($router) {
27+
/* @var \Illuminate\Routing\Router $router */
28+
$router->get('logs', 'Encore\Admin\LogViewer\LogController@index');
29+
$router->get('logs/{file}', 'Encore\Admin\LogViewer\LogController@index');
30+
$router->get('logs/{file}/tail', 'Encore\Admin\LogViewer\LogController@tail');
31+
});
32+
}
33+
34+
/**
35+
* {@inheritdoc}
36+
*/
37+
public static function import()
38+
{
39+
parent::createMenu('Log viwer', 'logs', 'fa-database');
40+
41+
parent::createPermission('Logs', 'ext.log-viwer', 'logs*');
42+
}
43+
}

src/LogController.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
namespace Encore\Admin\LogViewer;
4+
5+
use Encore\Admin\Facades\Admin;
6+
use Encore\Admin\Layout\Content;
7+
use Illuminate\Http\Request;
8+
use Illuminate\Routing\Controller;
9+
10+
class LogController extends Controller
11+
{
12+
public function index($file = null, Request $request)
13+
{
14+
return Admin::content(function (Content $content) use ($file, $request) {
15+
16+
$offset = $request->get('offset');
17+
18+
$viewer = new LogViewer($file);
19+
20+
$content->body(view('laravel-admin-logs::logs', [
21+
'logs' => $viewer->fetch($offset),
22+
'logFiles' => $viewer->getLogFiles(),
23+
'fileName' => $viewer->file,
24+
'end' => $viewer->getFilesize(),
25+
'tailPath' => action('\Encore\Admin\LogViewer\LogController@tail', ['file' => $viewer->file]),
26+
'prevUrl' => $viewer->getPrevPageUrl(),
27+
'nextUrl' => $viewer->getNextPageUrl(),
28+
'filePath' => $viewer->getFilePath(),
29+
'size' => static::bytesToHuman($viewer->getFilesize())
30+
]));
31+
32+
$content->header($viewer->getFilePath());
33+
34+
});
35+
}
36+
37+
public function tail($file, Request $request)
38+
{
39+
$offset = $request->get('offset');
40+
41+
$viewer = new LogViewer($file);
42+
43+
list($pos, $logs) = $viewer->tail($offset);
44+
45+
return compact('pos', 'logs');
46+
}
47+
48+
protected static function bytesToHuman($bytes)
49+
{
50+
$units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
51+
52+
for ($i = 0; $bytes > 1024; $i++) {
53+
$bytes /= 1024;
54+
}
55+
56+
return round($bytes, 2) . ' ' . $units[$i];
57+
}
58+
}

0 commit comments

Comments
 (0)