Initial implementation of debug panel on web page

This commit is contained in:
Kijin Sung 2016-02-13 01:33:06 +09:00
parent e956ee88d7
commit 373305ab6b
8 changed files with 287 additions and 2 deletions

View file

@ -176,16 +176,34 @@ class DisplayHandler extends Handler
{
case 'panel':
$data = Rhymix\Framework\Debug::getDebugData();
if ($data->entries)
{
foreach ($data->entries as &$entry)
{
if (is_scalar($entry->message))
{
$entry->message = var_export($entry->message, true);
}
else
{
$entry->message = trim(print_r($entry->message, true));
}
}
}
switch (Context::getResponseMethod())
{
case 'HTML':
$json_options = defined('JSON_PRETTY_PRINT') ? (JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) : 0;
$panel_script = "<script>\nvar rhymix_debug_content = " . json_encode($data, $json_options) . ";\n</script>";
$panel_script = sprintf('<script src="%s%s?%s"></script>', RX_BASEURL, 'common/js/debug.js', filemtime(RX_BASEDIR . 'common/js/debug.js'));
$panel_script .= "\n<script>\nvar rhymix_debug_content = " . json_encode($data, $json_options) . ";\n</script>";
$body_end_position = strrpos($output, '</body>') ?: strlen($output);
$output = substr($output, 0, $body_end_position) . "\n$panel_script\n" . substr($output, $body_end_position);
return;
case 'JSON':
$output = preg_replace('/\}$/', ',"_rx_debug":' . json_encode($data) . '}', $output);
if (preg_match('/^(.+)\}$/', $output, $matches))
{
$output = $matches[1] . ',"_rx_debug":' . json_encode($data) . '}';
}
return;
default:
return;

View file

@ -290,3 +290,122 @@ button.btn {
.btn-group>.btn.active {
z-index: 2;
}
/* Debug */
#rhymix_debug_button {
position: fixed;
left: 0; bottom: 40px;
background: #eeeeee;
border: 1px solid #ccc; border-left: 0;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
box-shadow: 0px 0px 3px 0px rgba(0, 0, 0, 0.18), 0px 0px 6px 0px rgba(0, 0, 0, 0.12);
z-index: 1073741824;
}
#rhymix_debug_button:hover {
background: #444444;
}
#rhymix_debug_button a {
display: block;
font: bold 12px/14px Arial, sans-serif;
color: #444;
text-decoration: none;
padding: 4px 8px;
}
#rhymix_debug_button a:hover {
color: #eeeeee;
}
#rhymix_debug_panel {
display: none;
position: absolute;
left: 0; top: 0;
max-width: 96%;
min-height: 100%;
background: #fcfcfc;
border-right: 1px solid #ccc;
box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.18), 0px 0px 8px 0px rgba(0, 0, 0, 0.12);
z-index: 1073741824;
}
#rhymix_debug_panel .debug_header {
clear: both;
width: 100%;
height: 36px;
background: #444444;
background: linear-gradient(to right, #222222 0%, #444444 40%, #eeeeee 100%);
position: relative;
}
#rhymix_debug_panel .debug_header h2 {
font: bold 16px/20px Arial, sans-serif;
color: #fcfcfc;
position: absolute;
left: 10px; top: 10px;
}
#rhymix_debug_panel .debug_header .debug_maximize {
font: normal 20px/24px Arial, sans-serif;
color: #444444;
position: absolute;
right: 32px; top: 7px;
}
#rhymix_debug_panel .debug_header .debug_close {
font: normal 28px/28px Arial, sans-serif;
color: #444444;
position: absolute;
right: 10px; top: 4px;
}
#rhymix_debug_panel .debug_header .debug_close:hover {
color: #f44336;
}
#rhymix_debug_panel .debug_page {
clear: both;
margin: 12px 10px;
font: normal 12px/16px Arial, NanumBarunGothic, NanumGothic, "Malgun Gothic", sans-serif;
}
#rhymix_debug_panel .debug_page .debug_page_header {
padding-bottom: 8px;
border-bottom: 1px solid #ddd;
position: relative;
}
#rhymix_debug_panel .debug_page .debug_page_header h3 {
color: #444;
font: inherit;
font-size: 14px;
font-weight: bold;
margin: 0;
padding: 0;
}
#rhymix_debug_panel .debug_page .debug_page_collapse {
display: block;
position: absolute;
right: 0; top: 0;
color: #999;
font-size: 10px;
line-height: 12px;
text-decoration: none;
padding: 2px 2px;
}
#rhymix_debug_panel .debug_page .debug_page_body {
margin: 8px 4px 8px 10px;
}
#rhymix_debug_panel .debug_page .debug_page_body h4 {
color: #444;
font: inherit;
font-size: 13px;
font-weight: bold;
margin: 0 0 8px 0;
padding: 0;
}
#rhymix_debug_panel .debug_page .debug_entry {
font-family: Consolas, "Courier New", monospace;
color: #666;
margin-left: 38px;
margin-bottom: 8px;
text-indent: -28px;
white-space: pre-wrap;
}
#rhymix_debug_panel .debug_page .debug_entry.no_indentation {
margin-left: 10px;
text-indent: 0;
}
#rhymix_debug_panel .debug_page .debug_entry.collapse_spaces {
white-space: pre-line;
}

View file

@ -379,6 +379,10 @@ class Debug
// Clean up the backtrace.
foreach (array('entries', 'errors', 'queries') as $key)
{
if (!$data->$key)
{
continue;
}
foreach ($data->$key as &$entry)
{
if (isset($entry->file))

View file

@ -28,6 +28,9 @@
}
});
/* Array for pending debug data */
window.rhymix_debug_pending_data = [];
/**
* @brief XE 공용 유틸리티 함수
* @namespace XE

119
common/js/debug.js Normal file
View file

@ -0,0 +1,119 @@
/**
* Client-side script for manipulating the debug panel on Rhymix.
*
* @file debug.js
* @author Kijin Sung <kijin@kijinsung.com>
*/
jQuery(function() {
// Find debug panel elements.
var panel = $("#rhymix_debug_panel");
var button = $("#rhymix_debug_button");
// Initialize the debug button.
$('<a href="#"></a>').text("DEBUG").appendTo(button).click(function(event) {
event.preventDefault();
panel.css({ width: 0 }).show().animate({ width: 640 }, 200);
button.hide();
});
// Initialize the debug panel.
var header = $('<div class="debug_header"></div>').appendTo(panel);
header.append('<h2>RHYMIX DEBUG</h2>');
header.append($('<a class="debug_maximize" href="#">+</a>').click(function(event) {
panel.animate({ width: "95%" }, 300);
}));
header.append($('<a class="debug_close" href="#">&times;</a>').click(function(event) {
event.preventDefault();
panel.animate({ width: 0 }, 200, function() {
panel.hide();
button.show();
});
}));
// Define a function for adding debug data to the panel.
window.rhymix_debug_add_data = function(data) {
// Create the page.
var page = $('<div class="debug_page"></div>').appendTo(panel);
var page_body = $('<div class="debug_page_body"></div>').appendTo(page);
// Create the page header.
var page_header = $('<div class="debug_page_header"></div>').prependTo(page);
page_header.append($('<h3></h3>').text(data.page_title).attr("title", data.url));
page_header.append($('<a class="debug_page_collapse" href="#"></a>').text("▲").click(function(event) {
event.preventDefault();
if (page_body.is(":visible")) {
page_body.slideUp(200);
$(this).text("▼");
} else {
page_body.slideDown(200);
$(this).text("▲");
}
}));
// Add general information.
page_body.append($('<h4></h4>').text('General Information'));
page_body.append($('<div class="debug_entry no_indentation"></div>').text(
'Request: ' + data.request.method + ' (' + data.request.size + ' bytes)' + "\n" +
'Response: ' + data.response.method + ' (' + data.response.size + ' bytes)' + "\n" +
'Time: ' + data.timing.total));
// Add debug entries.
if (data.entries && data.entries.length) {
page_body.append($('<h4></h4>').text('Debug Entries (' + data.entries.length + ')'));
for (var i in data.entries) {
var entry = $('<div class="debug_entry"></div>').appendTo(page_body);
var num = parseInt(i) + 1; if (num < 10) num = "0" + num;
var backtrace = "";
for (var j in data.entries[i].backtrace) {
backtrace += "\n- " + data.entries[i].backtrace[j].file + ":" + data.entries[i].backtrace[j].line;
}
entry.text(num + ". " + data.entries[i].message + backtrace);
}
}
// Add errors.
if (data.errors && data.errors.length) {
page_body.append($('<h4></h4>').text('Errors (' + data.errors.length + ')'));
for (var i in data.errors) {
var entry = $('<div class="debug_entry"></div>').appendTo(page_body);
var num = parseInt(i) + 1; if (num < 10) num = "0" + num;
var backtrace = "";
for (var j in data.errors[i].backtrace) {
backtrace += "\n- " + data.errors[i].backtrace[j].file + ":" + data.errors[i].backtrace[j].line;
}
entry.text(num + ". " + data.errors[i].type + ": " + data.errors[i].message + backtrace);
}
}
// Add queries.
if (data.queries && data.queries.length) {
page_body.append($('<h4></h4>').text('Queries (' + data.queries.length + ')'));
for (var i in data.queries) {
var entry = $('<div class="debug_entry collapse_spaces"></div>').appendTo(page_body);
var num = parseInt(i) + 1; if (num < 10) num = "0" + num;
var description = "";
description += "\nCaller: " + data.queries[i].file + ":" + data.queries[i].line + " (" + data.queries[i].method + ")";
description += "\nConnection: " + data.queries[i].query_connection;
description += "\nQuery Time: " + data.queries[i].query_time.toFixed(4) + " sec";
description += "\nResult: " + ((data.queries[i].message === "success") ? "success" : ("error " + data.queries[i].error_code + " " + data.queries[i].message));
entry.text(num + ". " + data.queries[i].query_string + description);
}
}
};
// Add debug data from the current page.
if (rhymix_debug_content) {
rhymix_debug_content.page_title = 'MAIN PAGE';
rhymix_debug_add_data(rhymix_debug_content);
}
// Add debug data from pending AJAX requests.
if (rhymix_debug_pending_data) {
while (rhymix_debug_pending_data.length) {
rhymix_debug_add_data(rhymix_debug_pending_data.shift());
}
}
});

View file

@ -77,6 +77,16 @@
}
});
// Add debug information.
if (data._rx_debug) {
data._rx_debug.page_title = "AJAX : " + params.module + "." + params.act;
if (window.rhymix_debug_add_data) {
window.rhymix_debug_add_data(data._rx_debug);
} else {
window.rhymix_debug_pending_data.push(data._rx_debug);
}
}
// If the response contains an error, display the error message.
if (data.error != "0") {
// This way of calling an error handler is deprecated. Do not use it.
@ -176,6 +186,16 @@
clearTimeout(wfsr_timeout);
waiting_obj.hide().trigger("cancel_confirm");
// Add debug information.
if (data._rx_debug) {
data._rx_debug.page_title = "AJAX : " + params.module + "." + params.act;
if (window.rhymix_debug_add_data) {
window.rhymix_debug_add_data(data._rx_debug);
} else {
window.rhymix_debug_pending_data.push(data._rx_debug);
}
}
// If the response contains an error, display the error message.
if(data.error != "0" && data.error > -1000) {
if(data.error == -1 && data.message == "msg_is_not_administrator") {

View file

@ -73,6 +73,7 @@ xe.msg_select_menu = "{$lang->msg_select_menu}";
<!-- ETC -->
<div id="rhymix_waiting" class="wfsr"></div>
<div id="rhymix_debug_panel"></div>
<div id="rhymix_debug_button"></div>
{@ $js_body_files = Context::getJsFile('body') }
<block loop="$js_body_files => $key, $js_file">
<block cond="$js_file['targetie']"><!--[if {$js_file['targetie']}]></block><script src="{$js_file['file']}"></script><block cond="$js_file['targetie']"><![endif]--></block>

View file

@ -61,6 +61,7 @@ var default_url = "{Context::getDefaultUrl()}";
{$content}
{Context::getHtmlFooter()}
<div id="rhymix_debug_panel"></div>
<div id="rhymix_debug_button"></div>
<!--// ETC -->
{@ $js_body_files = Context::getJsFile('body') }
<!--@foreach($js_body_files as $key => $js_file)-->