Implemented DatabaseTracyBridge

- Query information and profiling is now availble in the Tracy Bar.
This commit is contained in:
Abel Hoogeveen 2017-07-14 16:13:21 +02:00
parent d086923f83
commit e08765246b
3 changed files with 212 additions and 0 deletions

View File

@ -0,0 +1,142 @@
<?php
/**
* FuzeWorks.
*
* The FuzeWorks MVC PHP FrameWork
*
* Copyright (C) 2017 TechFuze
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2017, Techfuze. (http://techfuze.net)
* @copyright Copyright (c) 1996 - 2015, Free Software Foundation, Inc. (http://www.fsf.org/)
* @license http://opensource.org/licenses/GPL-3.0 GPLv3 License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.0.4
*
* @version Version 1.0.4
*/
namespace FuzeWorks;
use Tracy\IBarPanel;
use Tracy\Debugger;
/**
* DatabaseTracyBridge Class.
*
* This class provides a bridge between FuzeWorks\Database and Tracy Debugging tool.
*
* This class registers in Tracy, and creates a Bar object which contains information about database sessions.
* It hooks into database usage and provides the information on the Tracy Bar panel.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2017, Techfuze. (http://techfuze.net)
*/
class DatabaseTracyBridge implements IBarPanel
{
public static $databases = array();
protected $results = array();
public static function register()
{
$class = new self();
$bar = Debugger::getBar();
$bar->addPanel($class);
}
public static function registerDatabase($database)
{
self::$databases[] = $database;
}
protected function getResults()
{
if (!empty($this->results))
{
return $this->results;
}
// First prepare global variables
$results = array();
$results['dbCount'] = 0;
$results['queryCount'] = 0;
$results['queryTimings'] = 0.0;
$results['errorsFound'] = false;
// Go through all databases
foreach (self::$databases as $database) {
// Increase total databases
$results['dbCount']++;
// First determine the ID
if (!empty($database->dsn))
{
$databaseId = $database->dsn;
}
elseif (!empty($database->username) && !empty($database->database) && !empty($database->hostname))
{
$databaseId = $database->username . '@' . $database->hostname . '/' . $database->database;
}
else
{
$databaseId = spl_object_hash($database);
}
// Go through all queries
foreach ($database->queries as $key => $query) {
$results['queryCount']++;
$results['queryTimings'] += $database->query_times[$key];
$results['queries'][$databaseId][$key]['query'] = $query;
$results['queries'][$databaseId][$key]['timings'] = $database->query_times[$key];
$results['queries'][$databaseId][$key]['data'] = $database->query_data[$key];
// If errors are found, set this at the top of the array
if ($database->query_data[$key]['error']['code'] != 0)
{
$results['errorsFound'] = true;
}
}
}
// Limit the amount in order to keep things readable
$results['queryCountProvided'] = 0;
foreach ($results['queries'] as $id => $database) {
$results['queries'][$id] = array_reverse(array_slice($database, -10));
$results['queryCountProvided'] += count($results['queries'][$id]);
}
$results = array_slice($results, -10);
return $this->results = $results;
}
public function getTab()
{
$results = $this->getResults();
ob_start(function () {});
require dirname(__DIR__) . '/Views/view.tracydatabasetab.php';
return ob_get_clean();
}
public function getPanel()
{
// Parse the panel
$results = $this->getResults();
ob_start(function () {});
require dirname(__DIR__) . '/Views/view.tracydatabasepanel.php';
return ob_get_clean();
}
}

View File

@ -0,0 +1,51 @@
<style class="tracy-debug">
#tracy-debug td.nette-DbConnectionPanel-sql { background: white !important }
#tracy-debug .nette-DbConnectionPanel-source { color: #BBB !important }
#tracy-debug .nette-DbConnectionPanel-explain td { white-space: pre }
#tracy-debug .fuzeworks-DbDescriptor th { background: #FDF5CE !important }
</style>
<h1 title="Database">Queries: <?php
echo $results['queryCount'], ($results['queryTimings'] ? sprintf(', time: %0.3f ms', $results['queryTimings'] * 1000) : ''); ?></h1>
<div class="tracy-inner">
<table>
<?php
foreach ($results['queries'] as $database => $queries): ?>
<tr class='fuzeworks-DbDescriptor'>
<th>Database:</th>
<th><?= htmlSpecialChars($database, ENT_QUOTES, 'UTF-8') ?></th>
<th>#</th>
</tr>
<tr><th>Time&nbsp;ms</th><th>SQL Query</th><th>Rows</th></tr>
<?php foreach ($queries as $query): ?>
<tr>
<td>
<?php if ($query['data']['error']['code'] != 0): ?>
<span title="<?= htmlSpecialChars($query['data']['error']['message'], ENT_IGNORE | ENT_QUOTES, 'UTF-8') ?>">ERROR</span>
<br /><a class="tracy-toggle tracy-collapsed" data-tracy-ref="^tr .nette-DbConnectionPanel-explain">explain</a>
<?php elseif ($query['timings'] !== 0): echo sprintf('%0.3f', $query['timings'] * 1000); endif ?>
</td>
<td class="nette-DbConnectionPanel-sql"><?= htmlSpecialChars($query['query'], ENT_QUOTES, 'UTF-8') ?>
<?php if ($query['data']['error']['code'] !== 0): ?>
<table class="tracy-collapsed nette-DbConnectionPanel-explain">
<tr>
<th>Code</th>
<th>Message</th>
</tr>
<tr>
<td><?= htmlSpecialChars($query['data']['error']['code'], ENT_NOQUOTES, 'UTF-8') ?></td>
<td><?= htmlSpecialChars($query['data']['error']['message'], ENT_NOQUOTES, 'UTF-8') ?></td>
</tr>
</table>
<?php endif ?>
</td>
<td> <?= htmlSpecialChars($query['data']['rows'], ENT_QUOTES, 'UTF-8') ?> </td>
</tr>
<?php endforeach;
endforeach; ?>
</table>
<?php if ($results['queryCountProvided'] < $results['queryCount']): ?><p>...and more</p><?php endif ?>
</div>

View File

@ -0,0 +1,19 @@
<?php
if ($results['queryCount'] && !$results['errorsFound'])
{
$color = "#6ba9e6";
}
elseif ($results['queryCount'] && $results['errorsFound'])
{
$color = "#990000";
}
else
{
$color = "#aaa";
}
?>
<span title="Database">
<svg viewBox="0 0 2048 2048"><path fill="<?= $color ?>" d="M1024 896q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0 768q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-384q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-1152q208 0 385 34.5t280 93.5 103 128v128q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-128q0-69 103-128t280-93.5 385-34.5z"/>
</svg><span class="tracy-label"><?= ($results['queryTimings'] ? sprintf('%0.1fms/', $results['queryTimings'] * 1000) : '') . $results['queryCount'] ?></span>
</span>