FEATURE: move profiler into own repository

This commit is contained in:
Michal Kváček
2017-09-23 00:51:24 +02:00
parent 680edb14c1
commit b99f70c269
29 changed files with 0 additions and 935 deletions
-12
View File
@@ -1,12 +0,0 @@
<?php
return [
'name' => 'Profiler',
'translatedName' => 'profiler::module.name',
'translatedDescription' => 'profiler::module.description',
'alias' => 'profiler',
'handler' => \Modules\Profiler\Handlers\ProfileHandler::class,
'providers' => [\Modules\Profiler\Providers\ProfilerServiceProvider::class],
'files' => ['start.php'],
'order' => -100,
];
@@ -1,33 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateQueriesLogTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('queries_log', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->text('query');
$table->text('bindings');
$table->double('time');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('queries_log');
}
}
@@ -1,35 +0,0 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateHttpLogTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('http_log', function(Blueprint $table)
{
$table->increments('id');
$table->timestamps();
$table->string('method');
$table->text('url');
$table->double('time');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('http_log');
}
}
@@ -1,35 +0,0 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class UpdateHttpLogAddLoadAvg extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('http_log', function (Blueprint $table) {
$table->double('load_1')->nullable();
$table->double('load_5')->nullable();
$table->double('load_15')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('http_log', function (Blueprint $table) {
$table->dropColumn('load_1');
$table->dropColumn('load_5');
$table->dropColumn('load_15');
});
}
}
@@ -1,43 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class UpdateQueryLogAddRequestId extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('queries_log', function (Blueprint $table) {
$table->integer('http_log_id')
->nullable()
->unsigned()
->after('id')
->comment('FK to http_log');
$table->foreign('http_log_id')
->references('id')
->on('http_log')
->onDelete('cascade')
->onUpdate('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('queries_log', function (Blueprint $table) {
// $table->dropForeign('');
$table->dropColumn('http_log_id');
});
}
}
@@ -1,20 +0,0 @@
<?php namespace Modules\Profiler\Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
class ProfilerDatabaseSeeder extends Seeder {
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Model::unguard();
// $this->call("OthersTableSeeder");
}
}
-22
View File
@@ -1,22 +0,0 @@
<?php
namespace Modules\Profiler\Entities;
use Illuminate\Database\Eloquent\Model;
class HttpLog extends Model
{
public $table = 'http_log';
protected $fillable = [
'method',
'url',
'time',
'load_1',
'load_5',
'load_15'
];
public function queries()
{
return $this->hasMany(QueryLog::class);
}
}
-23
View File
@@ -1,23 +0,0 @@
<?php
namespace Modules\Profiler\Entities;
use Illuminate\Database\Eloquent\Model;
class QueryLog extends Model
{
public $table = 'queries_log';
protected $fillable = [
'query',
'bindings',
'time',
'http_log_id'
];
protected $casts = [
'bindings' => 'array'
];
public function httpLog()
{
return $this->belongsTo(HttpLog::class);
}
}
@@ -1,25 +0,0 @@
<?php
namespace Modules\Profiler\Handlers;
use App\CMS\Contracts\Modules\BaseModuleHandler;
use App\Models\User;
class ProfileHandler implements BaseModuleHandler
{
/**
* Register admin menu (or multiple ones)
*
* This cannot be in ServiceProvider because Session is started at middleware. And ServiceProvider is called before
* starting any middleware..
*
* @param User $user model of logged user
*/
public static function registerAdminMenu(User $user)
{
// register links in admin menu
$menu = \Menu::instance('mainMenu');
$submenu = $menu->whereTitle(trans('admin/mainMenu.system'));
$submenu->route('profiler.index', trans('profiler::module.name'), [], 10, ['icon' => 'fa fa-dashboard']);
}
}
@@ -1,67 +0,0 @@
<?php namespace Modules\Profiler\Http\Controllers;
use App\Http\Controllers\Admin\Controller;
use Carbon\Carbon;
use Illuminate\Support\Facades\Input;
use Modules\Profiler\Entities\HttpLog;
use Modules\Profiler\Entities\QueryLog;
class ProfilerController extends Controller
{
public function index()
{
$filters = [];
$pastDays = Input::get('pastDays', session('profilerPastDays', 0));
$httpLog = HttpLog::query();
if (is_numeric($pastDays) && $pastDays >= 0) {
$httpLog = $httpLog->where('created_at', '>=', Carbon::today()
->subDays($pastDays));
}
session(['profilerPastDays' => $pastDays]);
if (Input::get('method')) {
$filters[] = 'method';
$httpLog = $httpLog->where('method', Input::get('method'));
}
$displayGrouped = false;
if (Input::get('url')) {
$httpLog = $httpLog->where('url', Input::get('url'))
->orderBy('created_at', 'DESC')
->with('queries');
} else {
$displayGrouped = true;
$httpLog = $httpLog->groupBy('url', 'method')
->select(\DB::raw('*'), \DB::raw('COUNT(url) as total'), \DB::raw('AVG(time) as time_avg'))
->orderBy('total', 'DESC');
}
$httpLog = $httpLog->paginate(25);
$filter = Input::all();
return view('profiler::index', compact('httpLog', 'displayGrouped', 'filter', 'pastDays'));
}
public function destroy()
{
$older = Input::get('olderThan');
if (is_null($older)) {
\DB::statement("SET foreign_key_checks=0");
QueryLog::truncate();
HttpLog::truncate();
\DB::statement("SET foreign_key_checks=1");
} else {
HttpLog::where('created_at', '<=', Carbon::now()
->subDays($older))
->delete();
}
return redirect()
->back()
->with('success', trans('profiler::module.succesfullyDeleted'));
}
}
@@ -1,13 +0,0 @@
<?php
namespace Modules\Profiler\Http\Controllers;
use App\Http\Controllers\Admin\Controller;
use Modules\Profiler\Entities\HttpLog;
class QueryController extends Controller {
public function index(HttpLog $httpLog) {
$queries = $httpLog->queries;
return view('profiler::queries.index', compact('queries'));
}
}
@@ -1,37 +0,0 @@
<?php
namespace Modules\Profiler\Http\Controllers;
use App\Http\Controllers\Admin\Controller;
use Carbon\Carbon;
use Illuminate\Support\Facades\Input;
use Modules\Profiler\Entities\HttpLog;
class RequestController extends Controller
{
public function detail(HttpLog $httpLog)
{
$sameRequests = HttpLog::where('url', $httpLog->url)
->where('method', $httpLog->method);
$pastDays = Input::get('pastDays', session('profilerPastDays', 0));
if (is_numeric($pastDays) && $pastDays >= 0) {
$sameRequests = $sameRequests->where('created_at', '>=', Carbon::today()
->subDays($pastDays));
}
session(['profilerPastDays' => $pastDays]);
$sameRequests = $sameRequests->get();
$times = [
'avg' => $sameRequests->avg('time'),
'min' => $sameRequests->min('time'),
'max' => $sameRequests->max('time')
];
$filter = Input::all();
return view('profiler::request.detail', compact('httpLog', 'sameRequests', 'times', 'pastDays', 'filter'));
}
}
-23
View File
@@ -1,23 +0,0 @@
<?php
Route::group([
'prefix' => LaravelLocalization::setLocale(),
'namespace' => 'Modules\Profiler\Http\Controllers'
], function () {
Route::group([
'middleware' => ['admin'],
'prefix' => 'admin/profiler'
], function () {
Route::get('/', 'ProfilerController@index')
->name('profiler.index');
Route::get('{httpLog}', 'RequestController@detail')
->name('request.detail');
Route::get('{httpLog}/queries', 'QueryController@index')
->name('queries.index');
Route::get('delete/all', 'ProfilerController@destroy')
->name('profiler.deleteAll');
});
});
-64
View File
@@ -1,64 +0,0 @@
<?php
namespace Modules\Profiler\Middleware;
use Illuminate\Http\Request;
use Modules\Profiler\Entities\HttpLog;
use Modules\Profiler\Entities\QueryLog;
class Profiler
{
private $queries = [];
public function handle(Request $request, $next)
{
\DB::enableQueryLog();
return $next($request);
}
public function terminate(Request $request, $response)
{
$this->queries = \DB::getQueryLog();
$requestId = $this->logRequest($request);
$this->logQueries($requestId);
}
/**
*
*
* @param Request $request
*
* @return mixed
*/
protected function logRequest(Request $request)
{
$url = $request->fullUrl();
$method = $request->getMethod();
$time = (microtime(true) - LARAVEL_START) * 1000;
$data = ['url' => $url, 'method' => $method, 'time' => $time];
if (function_exists('sys_getloadavg')) {
$load = sys_getloadavg();
$data['load_1'] = $load[0];
$data['load_5'] = $load[1];
$data['load_15'] = $load[2];
}
$http = HttpLog::create($data);
return $http->id;
}
protected function logQueries($httpLogId = null)
{
foreach ($this->queries as $query) {
QueryLog::create([
'query' => $query['query'],
'bindings' => $query['bindings'],
'time' => $query['time'],
'http_log_id' => $httpLogId
]);
}
}
}
@@ -1,8 +0,0 @@
<?php
namespace Modules\Profiler\Providers;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class ProfilerEventServiceProvider extends ServiceProvider
{
}
@@ -1,49 +0,0 @@
<?php
namespace Modules\Profiler\Providers;
use App\Providers\ModuleServiceProvider;
use Modules\Profiler\Middleware\Profiler;
class ProfilerServiceProvider extends ModuleServiceProvider
{
protected function setModuleAlias()
{
$this->alias = 'profiler';
}
/**
* Boot the application events.
*
* @return void
*/
public function boot()
{
parent::boot();
$this->app->register(ProfilerEventServiceProvider::class);
}
public function register()
{
parent::register();
// register settings form
$registrator = $this->app->make('registerSettingsExtension');
$registrator->register('profiler', 'profiler::module.name', 'profiler::settings.profiler');
// turn on profiler for visitor area (middleware group "web")
if (setting('useProfilerInVisitorArea', true)) {
$this->app['router']->prependMiddlewareToGroup('web', Profiler::class);
}
// use profiler also in admin?
if (setting('useProfilerInAdmin', true)) {
$this->app['router']->prependMiddlewareToGroup('admin', Profiler::class);
}
}
protected function setModulePath()
{
$this->path = __DIR__;
}
}
@@ -1,44 +0,0 @@
<?php
return [
'name' => 'Profiler',
'index' => [
'createdAt' => 'Datum',
'httpMethod' => 'HTTP metoda',
'url' => 'URL',
'time' => 'Čas',
'timeAvg' => 'Průměrný čas',
'timeMin' => 'Minimální čas',
'timeMax' => 'Maximální čas',
'systemLoad' => 'Load serveru',
'calledTotal' => 'Volání',
'pastDays' => [
'today' => 'Dnes',
'overall' => 'Vše',
'days' => '1 den|:count dny|:count dní'
]
],
'settings' => [
'useProfilerInVisitorArea' => 'Zapnout měření ve veřejné části?',
'useProfilerInAdmin' => 'Zapnout měření v administraci?'
],
'request' => [
'info' => 'Detaily requestu',
'sql' => 'SQL dotazy',
'detail' => 'Detail požadavku',
'sameRequests' => 'Ostatní volání'
],
'graphs' => [
'responseTime' => 'Doba odezvy'
],
'queries' => [
'sql' => 'SQL dotaz',
'time' => 'Čas dotazu'
],
'listDescription' => 'Seznam HTTP požadavků na tuto aplikaci.',
'groupedListDescription' => 'Seznam HTTP požadavků na tuto aplikaci.',
'requestDetail' => 'Detail HTTP požadavku',
'deleteAllData' => 'Smazat všechna data',
'deleteOld' => 'Smazat data starší než 30 dní',
'succesfullyDeleted' => 'Úspěšně vymazána data z profileru.',
];
@@ -1,31 +0,0 @@
@if(count($requests) > 0)
<table class="item-list content-overview full-width table striped">
<thead>
<tr>
<td>@lang('profiler::module.index.calledTotal')</td>
<td>@lang('profiler::module.index.httpMethod')</td>
<td>@lang('profiler::module.index.url')</td>
<td>@lang('profiler::module.index.timeAvg')</td>
<td></td>
</tr>
</thead>
<tbody>
@foreach($requests as $request)
<tr>
<td>{{$request->total}}</td>
<td><a href="{{route('profiler.index', ['method' => $request->method])}}">{{$request->method}}</a></td>
<td><a href="{{route('profiler.index', ['url' => $request->url])}}">{{$request->url}}</a></td>
<td>{{number_format($request->time_avg, 2)}} ms</td>
<td>
<a href="{{route('request.detail', ['httpLog' => $request])}}" data-toggle="tooltip"
title="@lang('profiler::module.request.info')">
<i class="fa fa-info"></i>
</a>
</td>
</tr>
@endforeach
</tbody>
</table>
@else
@lang('admin/common.empty')
@endif
@@ -1,60 +0,0 @@
@if(count($requests) > 0)
<table class="item-list content-overview full-width table striped">
<thead>
<tr>
<td>@lang('profiler::module.index.createdAt')</td>
<td>@lang('profiler::module.index.httpMethod')</td>
<td>@lang('profiler::module.index.url')</td>
<td>@lang('profiler::module.index.time')</td>
<td>@lang('profiler::module.index.systemLoad')</td>
<td></td>
</tr>
</thead>
<tbody>
@foreach($requests as $request)
<tr>
<td>{{$request->created_at->format('d.m.Y G:i')}}</td>
<td><a href="{{route('profiler.index', ['method' => $request->method])}}">{{$request->method}}</a></td>
<td><a href="{{route('profiler.index', ['url' => $request->url])}}">{{$request->url}}</a></td>
<td>{{number_format($request->time, 3)}} ms</td>
<td>{{$request->load_1}}, {{$request->load_5}}, {{$request->load_15}}</td>
<td>
<div class="item-actions-dropdown">
<a class="item-actions-toggle-btn">
<span class="inactive">
<i class="fa fa-cog"></i>
</span>
<span class="active">
<i class="fa fa-chevron-circle-right"></i>
</span>
</a>
<div class="item-actions-block">
<ul class="item-actions-list">
<li>
<a href="{{route('request.detail', ['httpLog' => $request])}}" data-toggle="tooltip"
title="@lang('profiler::module.request.info')">
<i class="fa fa-info"></i>
</a>
</li>
<li>
<a href="{{route('queries.index', ['httpLog' => $request])}}" data-toggle="tooltip"
title="@lang('profiler::module.request.sql')">
<i class="fa fa-database"></i></a>
</li>
</ul>
</div>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
@else
@lang('admin/common.empty')
@endif
@@ -1,44 +0,0 @@
@extends('admin.layoutWithNavigation')
@section('pageHeading', trans('profiler::module.name'))
@section('headerButtons')
@if($displayGrouped == false)
<a class="btn btn-sm rounded btn-primary" href="{{route('profiler.index')}}"><i class="fa fa-list"></i></a>
@endif
<div class="pull-right">
@foreach(['0' => 'today', '7' => 'days', '30' => 'days', 'inf' => 'overall'] as $pastDay => $name)
@php $filter['pastDays'] = $pastDay; @endphp
<a class="btn btn-sm rounded @if($pastDays === (string)$pastDay) btn-primary @else btn-secondary @endif"
href="{{route('profiler.index', $filter)}}">{{trans_choice('profiler::module.index.pastDays.'.$name, $pastDay)}}</a>
@endforeach
</div>
@endsection
@if($displayGrouped == false)
@section('pageDescription', trans('profiler::module.listDescription'))
@else
@section('pageDescription', trans('profiler::module.groupedListDescription'))
@endif
@section('content')
<div class="card">
<div class="card-block">
@if ($displayGrouped == true)
@include('profiler::_partials.groupedRequestList', ['requests' => $httpLog])
@else
@include('profiler::_partials.requestList', ['requests' => $httpLog])
@endif
</div>
<div class="card-footer">
{!! $httpLog->render() !!}
</div>
</div>
@endsection
@@ -1,41 +0,0 @@
@extends('admin.layoutWithNavigation')
@section('pageHeading', trans('profiler::module.name'))
@section('content')
<div class="card">
<div class="card-block">
<table class="table stripped">
<thead>
<tr>
<td>@lang('profiler::module.queries.sql')</td>
<td>@lang('profiler::module.queries.time')</td>
<td></td>
</tr>
</thead>
<tbody>
@foreach($queries as $query)
<tr>
<td>
@php $sql = $query->query; @endphp
@foreach($query->bindings as $value)
@php
if (is_array($value) && isset($value['date']))
{$value = \Carbon\Carbon::parse($value['date'])->toDateTimeString();}
$sql = preg_replace('/\?/', $value, $sql, 1);
@endphp
@endforeach
{{$sql}}
</td>
<td>{{number_format($query->time, 3)}} ms</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endsection
@@ -1,111 +0,0 @@
@extends('admin.layoutWithNavigation')
@section('pageHeading', trans('profiler::module.requestDetail'))
@section('headerButtons')
<a class="btn btn-sm rounded btn-primary" href="{{route('profiler.index')}}"><i class="fa fa-list"></i></a>
<div class="pull-right">
@foreach(['0' => 'today', '7' => 'days', '30' => 'days', 'inf' => 'overall'] as $pastDay => $name)
@php $filter['httpLog'] = $httpLog ;$filter['pastDays'] = $pastDay; @endphp
<a class="btn btn-sm rounded @if($pastDays === (string)$pastDay) btn-primary @else btn-secondary @endif"
href="{{route('request.detail', $filter)}}">{{trans_choice('profiler::module.index.pastDays.'.$name, $pastDay)}}</a>
@endforeach
</div>
@endsection
@section('content')
<div class="row">
<div class="col-md-5">
<div class="card">
<div class="card-header">
<div class="header-block">
<p class="title">@lang('profiler::module.request.detail')</p>
</div>
</div>
<div class="card-block">
<div class="row">
<ul>
<li>@lang('profiler::module.index.httpMethod'): {{$httpLog->method}}</li>
<li>@lang('profiler::module.index.url'): <a
href="{{route('profiler.index', ['url' => $httpLog->url])}}">{{$httpLog->url}}</a>
</li>
<li>@lang('profiler::module.index.time'): {{number_format($httpLog->time, 2)}} ms</li>
<li>@lang('profiler::module.index.systemLoad'): {{$httpLog->load_1}}, {{$httpLog->load_5}}
, {{$httpLog->load_15}}</li>
</ul>
</div>
<div class="row text-center">
<div class="col-sm-3"><h5>
{{count($sameRequests)}} &times;</h5> @lang('profiler::module.index.calledTotal')</div>
<div class="col-sm-3"><h5>{{number_format($times['min'], 2)}}
ms</h5> @lang('profiler::module.index.timeMin')</div>
<div class="col-sm-3"><h5>{{number_format($times['max'], 2)}}
ms</h5> @lang('profiler::module.index.timeMax')</div>
<div class="col-sm-3"><h5>{{number_format($times['avg'], 2)}}
ms</h5> @lang('profiler::module.index.timeAvg')</div>
</div>
</div>
</div>
</div>
<div class="col-md-7">
<div class="card">
<div class="card-header">
<div class="header-block">
<p class="title">@lang('profiler::module.graphs.responseTime')</p>
</div>
</div>
<div class="card-block">
<canvas id="responseTimeHistory" height="100"></canvas>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<div class="header-block">
<p class="title">@lang('profiler::module.request.sameRequests')</p>
</div>
</div>
<div class="card-block">
@include('profiler::_partials.requestList', ['requests' => $sameRequests])
</div>
</div>
@endsection
@section('javascripts')
<script>
var ctx = $ ("#responseTimeHistory");
var myChart = new Chart (ctx, {
type: 'bubble',
data: {
datasets: [
{
label: '@lang('profiler::module.graphs.responseTime')',
data: [
@foreach($sameRequests as $i => $request)
{
x: '{{$i}}',
y: '{{number_format($request->time, 2)}}',
r: 5
},
@endforeach
],
backgroundColor: "#413e39",
hoverBackgroundColor: "#515151",
}]
}
});
</script>
@endsection
@@ -1,65 +0,0 @@
<div class="row">
<div class="col-lg-9">
@php $profileVisitorArea = old('useProfilerInVisitorArea', isset($settings->useProfilerInVisitorArea) ? $settings->useProfilerInVisitorArea['val'] : null); @endphp
<span class="control-label">@lang('profiler::module.settings.useProfilerInVisitorArea')</span>
<label>
<input class="radio" type="radio" name="useProfilerInVisitorArea"
value="1"
@if($profileVisitorArea === "1") checked @endif />
<span>@lang('common.yes')</span>
</label>
<label>
<input class="radio" type="radio" name="useProfilerInVisitorArea"
value="0"
@if($profileVisitorArea === "0") checked @endif />
<span>@lang('common.no')</span>
</label>
</div>
<div class="col-lg-3">
@include('admin.settings.snippets.commonSettings', ['field' => 'useProfilerInVisitorArea'])
</div>
</div>
<div class="row">
<div class="col-lg-9">
@php $profileAdmin = old('useProfilerInAdmin', isset($settings->useProfilerInAdmin) ? $settings->useProfilerInAdmin['val'] : false); @endphp
<span class="control-label">@lang('profiler::module.settings.useProfilerInAdmin')</span>
<label>
<input class="radio" type="radio" name="useProfilerInAdmin"
value="1"
@if($profileAdmin) checked @endif />
<span>@lang('common.yes')</span>
</label>
<label>
<input class="radio" type="radio" name="useProfilerInAdmin"
value="0"
@if(!$profileAdmin) checked @endif />
<span>@lang('common.no')</span>
</label>
</div>
<div class="col-lg-3">
@include('admin.settings.snippets.commonSettings', ['field' => 'useProfilerInAdmin'])
</div>
</div>
<div class="row text-center">
<a href="{{route('profiler.deleteAll')}}" class="btn btn-danger remove confirm">
<i class="fa fa-trash-o"></i> @lang('profiler::module.deleteAllData')
</a>
<a href="{{route('profiler.deleteAll', ['olderThan' => 30])}}" class="btn btn-danger remove confirm">
<i class="fa fa-trash-o"></i> @lang('profiler::module.deleteOld')
</a>
</div>
-15
View File
@@ -1,15 +0,0 @@
{
"name": "pingpong-modules/profiler",
"description": "",
"authors": [
{
"name": "Pingpong Labs",
"email": "pingpong.labs@gmail.com"
}
],
"autoload": {
"psr-4": {
"Modules\\Profiler\\": ""
}
}
}
-15
View File
@@ -1,15 +0,0 @@
<?php
/*
|--------------------------------------------------------------------------
| Register Namespaces And Routes
|--------------------------------------------------------------------------
|
| When a module starting, this file will executed automatically. This helps
| to register some namespaces like translator or view. Also this file
| will load the routes file for each module. You may also modify
| this file as you want.
|
*/
require __DIR__ . '/Http/routes.php';