update 1.0.8.0
Commits for version update
This commit is contained in:
195
app/FaveoLog/LaravelLogViewer.php
Normal file
195
app/FaveoLog/LaravelLogViewer.php
Normal file
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
namespace App\FaveoLog;
|
||||
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Psr\Log\LogLevel;
|
||||
use ReflectionClass;
|
||||
use UTC;
|
||||
|
||||
/**
|
||||
* Class LaravelLogViewer.
|
||||
*/
|
||||
class LaravelLogViewer
|
||||
{
|
||||
/**
|
||||
* @var string file
|
||||
*/
|
||||
private static $file;
|
||||
private static $levels_classes = [
|
||||
'debug' => 'info',
|
||||
'info' => 'info',
|
||||
'notice' => 'info',
|
||||
'warning' => 'warning',
|
||||
'error' => 'danger',
|
||||
'critical' => 'danger',
|
||||
'alert' => 'danger',
|
||||
'emergency' => 'danger',
|
||||
];
|
||||
private static $levels_imgs = [
|
||||
'debug' => 'info',
|
||||
'info' => 'info',
|
||||
'notice' => 'info',
|
||||
'warning' => 'warning',
|
||||
'error' => 'warning',
|
||||
'critical' => 'warning',
|
||||
'alert' => 'warning',
|
||||
'emergency' => 'warning',
|
||||
];
|
||||
|
||||
const MAX_FILE_SIZE = 52428800; // Why? Uh... Sorry
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
*/
|
||||
public static function setFile($file)
|
||||
{
|
||||
$file = self::pathToLogFile($file);
|
||||
|
||||
if (File::exists($file)) {
|
||||
self::$file = $file;
|
||||
}
|
||||
}
|
||||
|
||||
public static function pathToLogFile($file)
|
||||
{
|
||||
$logsPath = storage_path('logs');
|
||||
|
||||
if (File::exists($file)) { // try the absolute path
|
||||
return $file;
|
||||
}
|
||||
|
||||
$file = $logsPath.'/'.$file;
|
||||
|
||||
// check if requested file is really in the logs directory
|
||||
if (dirname($file) !== $logsPath) {
|
||||
throw new \Exception('No such log file');
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function getFileName()
|
||||
{
|
||||
return basename(self::$file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function all()
|
||||
{
|
||||
$log = [];
|
||||
|
||||
$log_levels = self::getLogLevels();
|
||||
|
||||
$pattern = '/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\].*/';
|
||||
|
||||
if (!self::$file) {
|
||||
$log_file = self::getFiles();
|
||||
if (!count($log_file)) {
|
||||
return [];
|
||||
}
|
||||
self::$file = $log_file[0];
|
||||
}
|
||||
|
||||
if (File::size(self::$file) > self::MAX_FILE_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
$file = File::get(self::$file);
|
||||
|
||||
preg_match_all($pattern, $file, $headings);
|
||||
|
||||
if (!is_array($headings)) {
|
||||
return $log;
|
||||
}
|
||||
|
||||
$log_data = preg_split($pattern, $file);
|
||||
|
||||
if ($log_data[0] < 1) {
|
||||
array_shift($log_data);
|
||||
}
|
||||
|
||||
foreach ($headings as $h) {
|
||||
for ($i = 0, $j = count($h); $i < $j; $i++) {
|
||||
foreach ($log_levels as $level_key => $level_value) {
|
||||
if (strpos(strtolower($h[$i]), '.'.$level_value)) {
|
||||
preg_match('/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\].*?(\w+)\.'.$level_key.': (.*?)( in .*?:[0-9]+)?$/', $h[$i], $current);
|
||||
|
||||
if (!isset($current[3])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$array = explode(':-:-:-', $current[3]);
|
||||
$message = $current[3];
|
||||
$context = $current[2];
|
||||
|
||||
if (is_array($array)) {
|
||||
if (array_key_exists(0, $array)) {
|
||||
$message = $array[0];
|
||||
}
|
||||
if (array_key_exists(1, $array)) {
|
||||
$context = $array[1];
|
||||
} else {
|
||||
$context = $current[2];
|
||||
}
|
||||
}
|
||||
//dd($current);
|
||||
$log[] = [
|
||||
'context' => $context,
|
||||
'level' => $level_value,
|
||||
'level_class' => self::$levels_classes[$level_value],
|
||||
'level_img' => self::$levels_imgs[$level_value],
|
||||
'date' => self::date($current[1]),
|
||||
'text' => $message,
|
||||
'in_file' => isset($current[4]) ? $current[4] : null,
|
||||
'stack' => preg_replace("/^\n*/", '', $log_data[$i]),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_reverse($log);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $basename
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getFiles($basename = false)
|
||||
{
|
||||
$files = glob(storage_path().'/logs/*');
|
||||
$files = array_reverse($files);
|
||||
$files = array_filter($files, 'is_file');
|
||||
if ($basename && is_array($files)) {
|
||||
foreach ($files as $k => $file) {
|
||||
$files[$k] = basename($file);
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($files);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private static function getLogLevels()
|
||||
{
|
||||
$class = new ReflectionClass(new LogLevel());
|
||||
|
||||
return $class->getConstants();
|
||||
}
|
||||
|
||||
public static function date($utc)
|
||||
{
|
||||
$system_date = UTC::usertimezone($utc);
|
||||
|
||||
return $system_date;
|
||||
}
|
||||
}
|
58
app/FaveoLog/LaravelLogViewerServiceProvider.php
Normal file
58
app/FaveoLog/LaravelLogViewerServiceProvider.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\FaveoLog;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class LaravelLogViewerServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Indicates if loading of the provider is deferred.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $defer = false;
|
||||
|
||||
/**
|
||||
* Bootstrap the application events.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
if (method_exists($this, 'package')) {
|
||||
$this->package('rap2hpoutre/laravel-log-viewer', 'laravel-log-viewer', __DIR__.'/../../');
|
||||
}
|
||||
|
||||
|
||||
$view_path = app_path().DIRECTORY_SEPARATOR.'FaveoLog'.DIRECTORY_SEPARATOR.'views';
|
||||
$this->loadViewsFrom($view_path, 'log');
|
||||
|
||||
$lang_path = app_path().DIRECTORY_SEPARATOR.'FaveoLog'.DIRECTORY_SEPARATOR.'lang';
|
||||
$this->loadTranslationsFrom($lang_path, 'log');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Add routes
|
||||
$routes = app_path('/FaveoLog/routes.php');
|
||||
if (file_exists($routes)) {
|
||||
require $routes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provides()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
0
app/FaveoLog/config/.gitkeep
Normal file
0
app/FaveoLog/config/.gitkeep
Normal file
0
app/FaveoLog/controllers/.gitkeep
Normal file
0
app/FaveoLog/controllers/.gitkeep
Normal file
38
app/FaveoLog/controllers/LogViewerController.php
Normal file
38
app/FaveoLog/controllers/LogViewerController.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\FaveoLog\controllers;
|
||||
|
||||
use App\FaveoLog\LaravelLogViewer;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\Support\Facades\Request;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
use Illuminate\Support\Facades\View;
|
||||
|
||||
class LogViewerController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
if (Request::input('l')) {
|
||||
//dd(base64_decode(Request::input('l')));
|
||||
LaravelLogViewer::setFile(base64_decode(Request::input('l')));
|
||||
}
|
||||
|
||||
if (Request::input('dl')) {
|
||||
return Response::download(LaravelLogViewer::pathToLogFile(base64_decode(Request::input('dl'))));
|
||||
} elseif (Request::has('del')) {
|
||||
File::delete(LaravelLogViewer::pathToLogFile(base64_decode(Request::input('del'))));
|
||||
|
||||
return Redirect::to(Request::url());
|
||||
}
|
||||
|
||||
$logs = LaravelLogViewer::all();
|
||||
|
||||
return View::make('log::log', [
|
||||
'logs' => $logs,
|
||||
'files' => LaravelLogViewer::getFiles(true),
|
||||
'current_file' => LaravelLogViewer::getFileName(),
|
||||
]);
|
||||
}
|
||||
}
|
0
app/FaveoLog/lang/.gitkeep
Normal file
0
app/FaveoLog/lang/.gitkeep
Normal file
5
app/FaveoLog/lang/en/lang.php
Normal file
5
app/FaveoLog/lang/en/lang.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'logs' => 'Logs',
|
||||
];
|
9
app/FaveoLog/routes.php
Normal file
9
app/FaveoLog/routes.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
Breadcrumbs::register('logs', function ($breadcrumbs) {
|
||||
$breadcrumbs->parent('setting');
|
||||
$breadcrumbs->push('System Logs', route('logs'));
|
||||
});
|
||||
Route::group(['middleware' => ['web', 'auth', 'roles']], function () {
|
||||
Route::get('logs', ['as' => 'logs', 'uses' => 'App\FaveoLog\controllers\LogViewerController@index']);
|
||||
});
|
0
app/FaveoLog/views/.gitkeep
Normal file
0
app/FaveoLog/views/.gitkeep
Normal file
132
app/FaveoLog/views/log.blade.php
Normal file
132
app/FaveoLog/views/log.blade.php
Normal file
@@ -0,0 +1,132 @@
|
||||
@extends('themes.default1.admin.layout.admin')
|
||||
@section('Log')
|
||||
active
|
||||
@stop
|
||||
|
||||
@section('logs')
|
||||
class="active"
|
||||
@stop
|
||||
|
||||
@section('HeadInclude')
|
||||
@stop
|
||||
<!-- header -->
|
||||
@section('PageHeader')
|
||||
<h1>{{Lang::get('log::lang.logs')}}</h1>
|
||||
@stop
|
||||
<!-- /header -->
|
||||
<!-- breadcrumbs -->
|
||||
@section('breadcrumbs')
|
||||
<ol class="breadcrumb">
|
||||
</ol>
|
||||
@stop
|
||||
@section('content')
|
||||
<style>
|
||||
|
||||
|
||||
.stack {
|
||||
font-size: 0.85em;
|
||||
}
|
||||
.date {
|
||||
min-width: 75px;
|
||||
}
|
||||
.text {
|
||||
word-break: break-all;
|
||||
}
|
||||
a.llv-active {
|
||||
z-index: 2;
|
||||
background-color: #f5f5f5;
|
||||
border-color: #777;
|
||||
}
|
||||
</style>
|
||||
<div class="container-fluid">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h4>System Logs</h4>
|
||||
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<div class="list-group">
|
||||
@foreach($files as $file)
|
||||
<a href="?l={{ base64_encode($file) }}" class="list-group-item @if ($current_file == $file) llv-active @endif">
|
||||
{{$file}}
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-10 table-container">
|
||||
@if ($logs === null)
|
||||
<div>
|
||||
Log file >50M, please download it.
|
||||
</div>
|
||||
@else
|
||||
<table id="table-log" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:10%">Level</th>
|
||||
<th style="width:12%">Context</th>
|
||||
<th>Date</th>
|
||||
<th>Content</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@foreach($logs as $key => $log)
|
||||
<tr>
|
||||
<td class="text-{{{$log['level_class']}}}"><span class="glyphicon glyphicon-{{{$log['level_img']}}}-sign" aria-hidden="true"></span> {{ucfirst($log['level'])}}</td>
|
||||
<td class="text">{{ucfirst($log['context'])}}</td>
|
||||
<td class="date">{{{$log['date']}}}</td>
|
||||
<td class="text">
|
||||
@if ($log['stack']) <a class="pull-right expand btn btn-default btn-xs" data-display="stack{{{$key}}}"><span class="glyphicon glyphicon-search"></span></a>@endif
|
||||
{{{$log['text']}}}
|
||||
@if (isset($log['in_file'])) <br />{{{$log['in_file']}}}@endif
|
||||
@if ($log['stack']) <div class="stack" id="stack{{{$key}}}" style="display: none; white-space: pre-wrap;">{{{ trim($log['stack']) }}}</div>@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
@endif
|
||||
<div>
|
||||
<a href="?dl={{ base64_encode($current_file) }}"><span class="glyphicon glyphicon-download-alt"></span> Download file</a>
|
||||
-
|
||||
<a id="delete-log" href="?del={{ base64_encode($current_file) }}"><span class="glyphicon glyphicon-trash"></span> Delete file</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@stop
|
||||
@section('FooterInclude')
|
||||
<!--<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>-->
|
||||
<!--<script src="https://cdn.datatables.net/1.10.4/js/jquery.dataTables.min.js"></script>-->
|
||||
<script src="https://cdn.datatables.net/plug-ins/9dcbecd42ad/integration/bootstrap/3/dataTables.bootstrap.js"></script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('#table-log').DataTable({
|
||||
"order": [1, 'desc'],
|
||||
"stateSave": true,
|
||||
"stateSaveCallback": function (settings, data) {
|
||||
window.localStorage.setItem("datatable", JSON.stringify(data));
|
||||
},
|
||||
"stateLoadCallback": function (settings) {
|
||||
var data = JSON.parse(window.localStorage.getItem("datatable"));
|
||||
if (data)
|
||||
data.start = 0;
|
||||
return data;
|
||||
}
|
||||
});
|
||||
$('.table-container').on('click', '.expand', function () {
|
||||
$('#' + $(this).data('display')).toggle();
|
||||
});
|
||||
$('#delete-log').click(function () {
|
||||
return confirm('Are you sure?');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@stop
|
106
app/FaveoLog/views/logold.blade.php
Normal file
106
app/FaveoLog/views/logold.blade.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Laravel log viewer</title>
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/plug-ins/9dcbecd42ad/integration/bootstrap/3/dataTables.bootstrap.css">
|
||||
|
||||
|
||||
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-3 col-md-2 sidebar">
|
||||
<h1><span class="glyphicon glyphicon-calendar" aria-hidden="true"></span> Laravel Log Viewer</h1>
|
||||
<p class="text-muted"><i>by Rap2h</i></p>
|
||||
<div class="list-group">
|
||||
@foreach($files as $file)
|
||||
<a href="?l={{ base64_encode($file) }}" class="list-group-item @if ($current_file == $file) llv-active @endif">
|
||||
{{$file}}
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-9 col-md-10 table-container">
|
||||
@if ($logs === null)
|
||||
<div>
|
||||
Log file >50M, please download it.
|
||||
</div>
|
||||
@else
|
||||
<table id="table-log" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Level</th>
|
||||
<th>Context</th>
|
||||
<th>Date</th>
|
||||
<th>Content</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@foreach($logs as $key => $log)
|
||||
<tr>
|
||||
<td class="text-{{{$log['level_class']}}}"><span class="glyphicon glyphicon-{{{$log['level_img']}}}-sign" aria-hidden="true"></span> {{$log['level']}}</td>
|
||||
<td class="text">{{$log['context']}}</td>
|
||||
<td class="date">{{{$log['date']}}}</td>
|
||||
<td class="text">
|
||||
@if ($log['stack']) <a class="pull-right expand btn btn-default btn-xs" data-display="stack{{{$key}}}"><span class="glyphicon glyphicon-search"></span></a>@endif
|
||||
{{{$log['text']}}}
|
||||
@if (isset($log['in_file'])) <br />{{{$log['in_file']}}}@endif
|
||||
@if ($log['stack']) <div class="stack" id="stack{{{$key}}}" style="display: none; white-space: pre-wrap;">{{{ trim($log['stack']) }}}</div>@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
@endif
|
||||
<div>
|
||||
<a href="?dl={{ base64_encode($current_file) }}"><span class="glyphicon glyphicon-download-alt"></span> Download file</a>
|
||||
-
|
||||
<a id="delete-log" href="?del={{ base64_encode($current_file) }}"><span class="glyphicon glyphicon-trash"></span> Delete file</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.10.4/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/plug-ins/9dcbecd42ad/integration/bootstrap/3/dataTables.bootstrap.js"></script>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
$('#table-log').DataTable({
|
||||
"order": [ 1, 'desc' ],
|
||||
"stateSave": true,
|
||||
"stateSaveCallback": function (settings, data) {
|
||||
window.localStorage.setItem("datatable", JSON.stringify(data));
|
||||
},
|
||||
"stateLoadCallback": function (settings) {
|
||||
var data = JSON.parse(window.localStorage.getItem("datatable"));
|
||||
if (data) data.start = 0;
|
||||
return data;
|
||||
}
|
||||
});
|
||||
$('.table-container').on('click', '.expand', function(){
|
||||
$('#' + $(this).data('display')).toggle();
|
||||
});
|
||||
$('#delete-log').click(function(){
|
||||
return confirm('Are you sure?');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user