Skip to content

Commit

Permalink
call stack and others
Browse files Browse the repository at this point in the history
  • Loading branch information
andrepereiradasilva committed Jan 13, 2016
1 parent 30ecd5e commit 1b84f01
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 39 deletions.
6 changes: 6 additions & 0 deletions administrator/language/en-GB/en-GB.plg_system_debug.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

PLG_DEBUG_BYTES="Bytes"
PLG_DEBUG_CALL_STACK="Call Stack"
PLG_DEBUG_CALL_STACK_CALLER="Caller"
PLG_DEBUG_CALL_STACK_FILE_AND_LINE="File and line number"
PLG_DEBUG_CALL_STACK_SAME_FILE="<em>Same as call in the line below.</em>"
PLG_DEBUG_ERRORS="Errors"
PLG_DEBUG_EXPLAIN="Explain"
PLG_DEBUG_FIELD_ALLOWED_GROUPS_DESC="Optionally restrict users that can see debug information to those in the selected user groups. If none selected, all users will see the debug information."
Expand Down Expand Up @@ -62,6 +65,9 @@ PLG_DEBUG_LANG_NOT_LOADED="Not loaded"
PLG_DEBUG_LINK_FORMAT="Add xdebug.file_link_format directive to your php.ini file to have links for files."
PLG_DEBUG_LOGGING_FIELDSET_LABEL="Logging"
PLG_DEBUG_LOGS="Log Messages"
PLG_DEBUG_LOGS_DEPRECATED_FOUND_TEXT="The code that is marked as deprecated will not work in upcoming Joomla versions, please review it."
PLG_DEBUG_LOGS_DEPRECATED_FOUND_TITLE="%s deprecated messages logged!"
PLG_DEBUG_LOGS_LOGGED="%s messages logged"
PLG_DEBUG_MEMORY="Memory"
PLG_DEBUG_MEMORY_USED_FOR_QUERY="Query memory: %s Memory before query: %s"
PLG_DEBUG_MEMORY_USAGE="Memory Usage"
Expand Down
10 changes: 10 additions & 0 deletions libraries/joomla/log/entry.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ class JLogEntry
JLog::DEBUG
);

/**
* Call stack and back trace of the logged call.
* @var string
* @since 12.3
*/
public $callStack = [];

/**
* Constructor
*
Expand Down Expand Up @@ -92,6 +99,9 @@ public function __construct($message, $priority = JLog::INFO, $category = '', $d
$this->category = (string) strtolower(preg_replace('/[^A-Z0-9_\.-]/i', '', $category));
}

// Get the current call stack and back trace (without args to save memory).
$this->callStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);

// Get the date as a JDate object.
$this->date = new JDate($date ? $date : 'now');
}
Expand Down
186 changes: 147 additions & 39 deletions plugins/system/debug/debug.php
Original file line number Diff line number Diff line change
Expand Up @@ -1008,32 +1008,6 @@ protected function displayQueries()

$htmlProfile = ($info[$id]->profile ? $info[$id]->profile : JText::_('PLG_DEBUG_NO_PROFILE'));

// Backtrace and call stack.
$htmlCallStack = '';

if (isset($callStacks[$id]))
{
$htmlCallStackElements = array();

foreach ($callStacks[$id] as $functionCall)
{
if (isset($functionCall['file']) && isset($functionCall['line']) && (strpos($functionCall['file'], '/libraries/joomla/database/') === false))
{
$htmlFile = htmlspecialchars($functionCall['file']);
$htmlLine = htmlspecialchars($functionCall['line']);
$htmlCallStackElements[] = '<span class="dbg-log-called-from">' . $this->formatLink($htmlFile, $htmlLine) . '</span>';
}
}

$htmlCallStack = '<div class="dbg-query-table"><div>' . implode('</div><div>', $htmlCallStackElements) . '</div></div>';

if (!$this->linkFormat)
{
$htmlCallStack .= '<div>[<a href="http://xdebug.org/docs/all_settings#file_link_format" target="_blank">';
$htmlCallStack .= JText::_('PLG_DEBUG_LINK_FORMAT') . '</a>]</div>';
}
}

$htmlAccordions = JHtml::_(
'bootstrap.startAccordion', 'dbg_query_' . $id, array(
'active' => ($info[$id]->hasWarnings ? ('dbg_query_explain_' . $id) : '')
Expand All @@ -1048,10 +1022,11 @@ protected function displayQueries()
. $htmlProfile
. JHtml::_('bootstrap.endSlide');

if ($htmlCallStack)
// Call stack and back trace.
if (isset($callStacks[$id]))
{
$htmlAccordions .= JHtml::_('bootstrap.addSlide', 'dbg_query_' . $id, JText::_('PLG_DEBUG_CALL_STACK'), 'dbg_query_callstack_' . $id)
. $htmlCallStack
. $this->renderCallStack($callStacks[$id])
. JHtml::_('bootstrap.endSlide');
}

Expand Down Expand Up @@ -1759,24 +1734,157 @@ public function logger(JLogEntry $entry)
protected function displayLogs()
{
$priorities = array(
JLog::EMERGENCY => 'EMERGENCY',
JLog::ALERT => 'ALERT',
JLog::CRITICAL => 'CRITICAL',
JLog::ERROR => 'ERROR',
JLog::WARNING => 'WARNING',
JLog::NOTICE => 'NOTICE',
JLog::INFO => 'INFO',
JLog::DEBUG => 'DEBUG'
JLog::EMERGENCY => '<span class="badge badge-important">EMERGENCY</span>',
JLog::ALERT => '<span class="badge badge-important">ALERT</span>',
JLog::CRITICAL => '<span class="badge badge-important">CRITICAL</span>',
JLog::ERROR => '<span class="badge badge-important">ERROR</span>',
JLog::WARNING => '<span class="badge badge-warning">WARNING</span>',
JLog::NOTICE => '<span class="badge badge-info">NOTICE</span>',
JLog::INFO => '<span class="badge badge-info">INFO</span>',
JLog::DEBUG => '<span class="badge">DEBUG</span>'
);

$out = array();
$databasequeryTotal = count(array_filter($this->logEntries, function($logEntry) {
return $logEntry->category == 'databasequery';
}));

$deprecatedTotal = count(array_filter($this->logEntries, function($logEntry) {
return $logEntry->category == 'deprecated';
}));

$logsTotal = count($this->logEntries);

$out = '';

$out .= '<h4>' . JText::sprintf(JText::_('PLG_DEBUG_LOGS_LOGGED'), $logsTotal - $databasequeryTotal) . '</h4><br />';
if ($deprecatedTotal > 0)
{
$out .= '
<div class="alert alert-warning">
<h4>' . sprintf(JText::_('PLG_DEBUG_LOGS_DEPRECATED_FOUND_TITLE'), $deprecatedTotal) . '</h4>
<div>' . JText::_('PLG_DEBUG_LOGS_DEPRECATED_FOUND_TEXT') . '</div>
</div>
<br />';
}

$out .= '<ol>';
$count = 1;
foreach ($this->logEntries as $entry)
{
$out[] = '<h5>' . $priorities[$entry->priority] . ' - ' . $entry->category . ' </h5><code>' . $entry->message . '</code>';
// Don't show database queries since there is slider just for that.
if ($entry->category === 'databasequery')
{
continue;
}

$out .= '<li id="dbg_logs_' . $count . '">';
$out .= '<h5>' . $priorities[$entry->priority] . ' ' . $entry->category . '</h5><br />
<pre>' . $entry->message . '</pre>';

if ($entry->callStack)
{
$out .= JHtml::_('bootstrap.startAccordion', 'dbg_logs_' . $count, array('active' => ''));
$out .= JHtml::_('bootstrap.addSlide', 'dbg_logs_' . $count, JText::_('PLG_DEBUG_CALL_STACK'), 'dbg_logs_backtrace_' . $count);
$out .= $this->renderCallStack($entry->callStack);
$out .= JHtml::_('bootstrap.endSlide');
$out .= JHtml::_('bootstrap.endAccordion');
}
$out .= '<hr /></li>';
$count++;
}
$out .= '</ol>';

return $out;
}

/**
* Renders call stack and back trace in HTML.
*
* @param string $callStack The call stack and back trace array.
*
* @return string The call stack and back trace in HMTL format.
*
* @since 3.6
*/
protected function renderCallStack(array $callStack = array())
{
$htmlCallStack = '';

if (isset($callStack))
{
$htmlCallStack = '<div>';
$htmlCallStack .= '<table class="table table-striped dbg-query-table">';
$htmlCallStack .= '<thead>';
$htmlCallStack .= '<th>#</th>';
$htmlCallStack .= '<th>' . JText::_('PLG_DEBUG_CALL_STACK_CALLER') . '</th>';
$htmlCallStack .= '<th>' . JText::_('PLG_DEBUG_CALL_STACK_FILE_AND_LINE') . '</th>';
$htmlCallStack .= '</tr>';
$htmlCallStack .= '</thead>';
$htmlCallStack .= '<tbody>';

$count = count($callStack);

foreach ($callStack as $call)
{
// Dont' back trace log classes.
if (isset($call['class']) && strpos($call['class'], 'JLog') !== false)
{
$count--;
continue;
}

$htmlCallStack .= '<tr>';

$htmlCallStack .= '<td>' . $count .'</td>';

$htmlCallStack .= '<td>';
if (isset($call['class']))
{
// If entry has Class/Method print it.
$htmlCallStack .= htmlspecialchars($call['class'] . $call['type'] . $call['function']) . '()';
}
else
{
if (isset($call['args']))
{
// If entry has args is a require/include.
$htmlCallStack .= htmlspecialchars($call['function']) . ' ' . $this->formatLink($call['args'][0]);
}
else
{
// It's a function.
$htmlCallStack .= htmlspecialchars($call['function']) . '()';
}
}
$htmlCallStack .= '</td>';

$htmlCallStack .= '<td>';
// If entry doesn't have line and number the next is a call_user_func.
if (!isset($call['file']) && !isset($call['line']))
{
$htmlCallStack .= JText::_('PLG_DEBUG_CALL_STACK_SAME_FILE');
}
// If entry has file and line print it.
else
{
$htmlCallStack .= $this->formatLink(htmlspecialchars($call['file']), htmlspecialchars($call['line']));
}
$htmlCallStack .= '</td>';

$htmlCallStack .= '</tr>';
$count--;
}
$htmlCallStack .= '</tbody>';
$htmlCallStack .= '</table>';
$htmlCallStack .= '</div>';

if (!$this->linkFormat)
{
$htmlCallStack .= '<div>[<a href="http://xdebug.org/docs/all_settings#file_link_format" target="_blank">' . JText::_('PLG_DEBUG_LINK_FORMAT') . '</a>]</div>';
}
}

return implode('<br /><br />', $out);
return $htmlCallStack;
}

/**
Expand Down

0 comments on commit 1b84f01

Please sign in to comment.