<dd>{$exception[date]|strtotime|plainTime}</dd>
</dl>
- <dl>
- <dt>{lang}wcf.acp.exceptionLog.exception.file{/lang}</dt>
- <dd>{$exception[file]} ({$exception[line]})</dd>
- </dl>
<dl>
<dt>{lang}wcf.acp.exceptionLog.exception.requestURI{/lang}</dt>
<dd>{$exception[requestURI]}</dd>
<dt>{lang}wcf.acp.exceptionLog.exception.userAgent{/lang}</dt>
<dd>{$exception[userAgent]}</dd>
</dl>
- {if $exception[information]}
- <dl>
- <dt>{lang}wcf.acp.exceptionLog.exception.information{/lang}</dt>
- <dd>{@$exception[information]}</dd>
- </dl>
- {/if}
+ <dl>
+ <dt>{lang}wcf.acp.exceptionLog.exception.memory{/lang}</dt>
+ <dd>{$exception[peakMemory]|filesizeBinary} / {$exception[maxMemory]|filesizeBinary}</dd>
+ </dl>
+ {foreach from=$exception[chain] item=chain}
+ <dl>
+ <dt>{lang}wcf.acp.exceptionLog.exception.message{/lang}</dt>
+ <dd>{$chain[message]}</dd>
+ </dl>
+ <dl>
+ <dt>{lang}wcf.acp.exceptionLog.exception.class{/lang}</dt>
+ <dd>{$chain[class]}</dd>
+ </dl>
+ <dl>
+ <dt>{lang}wcf.acp.exceptionLog.exception.file{/lang}</dt>
+ <dd>{$chain[file]} ({$chain[line]})</dd>
+ </dl>
<dl>
<dt>{lang}wcf.acp.exceptionLog.exception.stacktrace{/lang}</dt>
- <dd class="monospace" style="word-wrap: wrap-all; word-break: break-all;">
- <ul>
- <li>{@"</li><li>"|implode:$exception[stacktrace]}</li>
+ <dd>
+ <ol start="0" class="nativeList">
+ {foreach from=$chain[stack] item=stack}
+ <li>{$stack[file]} ({$stack[line]}): {$stack[class]}{$stack[type]}{$stack[function]}(…)</li>
+ {/foreach}
</ul>
</dd>
</dl>
+ {/foreach}
<dl>
<dt><label for="copyException{$exceptionKey}">{lang}wcf.acp.exceptionLog.exception.copy{/lang}</label></dt>
<dd><textarea id="copyException{$exceptionKey}" rows="5" cols="40" class="jsCopyException" readonly="readonly">{$exception[0]}</textarea></dd>
$i = 0;
// TODO: This needs to be adapted for WCF 2.2
- $exceptionRegex = new Regex('(?P<date>[MTWFS][a-z]{2}, \d{1,2} [JFMASOND][a-z]{2} \d{4} \d{2}:\d{2}:\d{2} [+-]\d{4})
-Message: (?P<message>.*?)
-File: (?P<file>.*?) \((?P<line>\d+)\)
-PHP version: (?P<phpVersion>.*?)
-WCF version: (?P<wcfVersion>.*?)
-Request URI: (?P<requestURI>.*?)
-Referrer: (?P<referrer>.*?)
-User-Agent: (?P<userAgent>.*?)
-Information: (?P<information>.*?)
-Stacktrace:
-(?P<stacktrace>.*)', Regex::DOT_ALL);
+ $exceptionRegex = new Regex('(?P<date>[MTWFS][a-z]{2}, \d{1,2} [JFMASOND][a-z]{2} \d{4} \d{2}:\d{2}:\d{2} [+-]\d{4})\s*
+Message: (?P<message>.*?)\s*
+PHP version: (?P<phpVersion>.*?)\s*
+WCF version: (?P<wcfVersion>.*?)\s*
+Request URI: (?P<requestURI>.*?)\s*
+Referrer: (?P<referrer>.*?)\s*
+User Agent: (?P<userAgent>.*?)\s*
+Peak Memory Usage: (?<peakMemory>\d+)/(?<maxMemory>\d+)\s*
+(?<chain>======
+.*)', Regex::DOT_ALL);
+ $chainRegex = new Regex('======
+Error Class: (?P<class>.*?)\s*
+Error Message: (?P<message>.*?)\s*
+Error Code: (?P<code>\d+)\s*
+File: (?P<file>.*?) \((?P<line>\d+)\)\s*
+Extra Information: (?P<information>(?:-|[a-zA-Z0-9+/]+={0,2}))\s*
+Stack Trace: (?P<stack>[a-zA-Z0-9+/]+={0,2})', Regex::DOT_ALL);
$stackTraceFormatter = new Regex('^\s+(#\d+)', Regex::MULTILINE);
foreach ($this->exceptions as $key => $val) {
$i++;
unset($this->exceptions[$key]);
continue;
}
+ $matches = $exceptionRegex->getMatches();
+ $chainRegex->match($matches['chain'], true, Regex::ORDER_MATCH_BY_SET);
- $this->exceptions[$key] = $exceptionRegex->getMatches();
- $this->exceptions[$key]['stacktrace'] = explode("\n", $stackTraceFormatter->replace(StringUtil::encodeHTML($this->exceptions[$key]['stacktrace']), '<strong>\\1</strong>'));
- $this->exceptions[$key]['information'] = JSON::decode($this->exceptions[$key]['information']);
+ $chainMatches = array_map(function ($item) {
+ if ($item['information'] === '-') $item['information'] = null;
+ else $item['information'] = @unserialize(base64_decode($item['information']));
+
+ $item['stack'] = @unserialize(base64_decode($item['stack']));
+
+ return $item;
+ }, $chainRegex->getMatches());
+
+ $matches['chain'] = $chainMatches;
+ $this->exceptions[$key] = $matches;
}
}
*/
namespace {
use wcf\system\WCF;
-
+
// set exception handler
set_exception_handler([ WCF::class, 'handleException' ]);
// set php error handler
register_shutdown_function([ WCF::class, 'destruct' ]);
// set autoload function
spl_autoload_register([ WCF::class, 'autoload' ]);
-
+
// define escape string shortcut
function escapeString($string) {
return WCF::getDB()->escapeString($string);
}
-
+
// define DOCUMENT_ROOT on IIS if not set
if (PHP_EOL == "\r\n") {
if (!isset($_SERVER['DOCUMENT_ROOT']) && isset($_SERVER['SCRIPT_FILENAME'])) {
if (!isset($_SERVER['DOCUMENT_ROOT']) && isset($_SERVER['PATH_TRANSLATED'])) {
$_SERVER['DOCUMENT_ROOT'] = str_replace( '\\', '/', substr(str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']), 0, 0 - strlen($_SERVER['PHP_SELF'])));
}
-
+
if (!isset($_SERVER['REQUEST_URI'])) {
$_SERVER['REQUEST_URI'] = substr($_SERVER['PHP_SELF'], 1);
if (isset($_SERVER['QUERY_STRING'])) {
use wcf\system\WCF;
use wcf\util\FileUtil;
use wcf\util\StringUtil;
-
+
function logThrowable($e) {
$logFile = WCF_DIR . 'log/' . gmdate('Y-m-d', TIME_NOW) . '.txt';
touch($logFile);
-
+
// don't forget to update ExceptionLogViewPage, when changing the log file format
$message = gmdate('r', TIME_NOW)."\n".
'Message: '.str_replace("\n", ' ', $e->getMessage())."\n".
return $item;
}
}, $item['args']);
-
+
return $item;
}, sanitizeStacktrace($e, true))))."\n";
}
while ($e = $e->getPrevious());
-
+
// calculate Exception-ID
$exceptionID = sha1($message);
- $entry = "<<<<<<<<".$exceptionID."<<<<\n".$message."<<<<\n";
-
+ $entry = "<<<<<<<<".$exceptionID."<<<<\n".$message."<<<<\n\n";
+
file_put_contents($logFile, $entry, FILE_APPEND);
return $exceptionID;
}
-
+
function printThrowable($e) {
$exceptionID = logThrowable($e); // TODO
?><!DOCTYPE html>
$message = str_replace('{$exceptionID}', $exceptionID, WCF::getLanguage()->get('wcf.global.error.exception', true));
}
catch (\Exception $e) {
-
+
}
catch (\Throwable $e) {
-
+
}
echo $message;
?>
ob_start();
$e->show();
ob_end_clean();
-
+
$reflection = new \ReflectionClass($e);
$property = $reflection->getProperty('information');
$property->setAccessible(true);
?>
<dt>Stack Trace</dt>
<dd class="pre"><?php
- $trace = array_map(function ($item) {
- if (!isset($item['file'])) $item['file'] = '[internal function]';
- if (!isset($item['line'])) $item['line'] = '?';
- if (!isset($item['class'])) $item['class'] = '';
- if (!isset($item['type'])) $item['type'] = '';
-
- return $item;
- }, sanitizeStacktrace($e));
+ $trace = sanitizeStacktrace($e);
$pathLength = array_reduce($trace, function ($carry, $item) {
return max($carry, mb_strlen($item['file'].$item['line']));
}, 0) + 3;
function sanitizeStacktrace($e, $ignorePaths = false) {
$trace = $e->getTrace();
-
+
return array_map(function ($item) use ($ignorePaths) {
+ if (!isset($item['file'])) $item['file'] = '[internal function]';
+ if (!isset($item['line'])) $item['line'] = '?';
+ if (!isset($item['class'])) $item['class'] = '';
+ if (!isset($item['type'])) $item['type'] = '';
+
// strip database credentials
- if (isset($item['class'])) {
- if (preg_match('~\\\\?wcf\\\\system\\\\database\\\\[a-zA-Z]*Database~', $item['class']) || $item['class'] === 'PDO') {
- if ($item['function'] === '__construct') {
- $item['args'] = array_map(function () {
- return '[redacted]';
- }, $item['args']);
- }
+ if (preg_match('~\\\\?wcf\\\\system\\\\database\\\\[a-zA-Z]*Database~', $item['class']) || $item['class'] === 'PDO') {
+ if ($item['function'] === '__construct') {
+ $item['args'] = array_map(function () {
+ return '[redacted]';
+ }, $item['args']);
}
}
-
+
if (!$ignorePaths) {
$item['args'] = array_map(function ($item) {
if (!is_string($item)) return $item;
-
+
if (preg_match('~^'.preg_quote($_SERVER['DOCUMENT_ROOT'], '~').'~', $item)) {
$item = sanitizePath($item);
}
-
+
return preg_replace('~^'.preg_quote(WCF_DIR, '~').'~', '*/', $item);
}, $item['args']);
-
- if (isset($item['file'])) $item['file'] = sanitizePath($item['file']);
+
+ $item['file'] = sanitizePath($item['file']);
}
-
+
return $item;
}, $trace);
}
if (WCF::debugModeIsEnabled() && defined('EXCEPTION_PRIVACY') && EXCEPTION_PRIVACY === 'public') {
return $path;
}
-
+
return '*/'.FileUtil::removeTrailingSlash(FileUtil::getRelativePath(WCF_DIR, $path));
}
}
<category name="wcf.acp.exceptionLog">
<item name="wcf.acp.exceptionLog"><![CDATA[Protokollierte Fehler]]></item>
+ <item name="wcf.acp.exceptionLog.exception.message"><![CDATA[Fehlermeldung]]></item>
+ <item name="wcf.acp.exceptionLog.exception.class"><![CDATA[Art]]></item>
<item name="wcf.acp.exceptionLog.exception.file"><![CDATA[Datei (Zeile)]]></item>
<item name="wcf.acp.exceptionLog.exception.requestURI"><![CDATA[Aufgerufene URL]]></item>
<item name="wcf.acp.exceptionLog.exception.referrer"><![CDATA[Referrer]]></item>
- <item name="wcf.acp.exceptionLog.exception.information"><![CDATA[Zusatzinformationen]]></item>
<item name="wcf.acp.exceptionLog.exception.stacktrace"><![CDATA[Stacktrace]]></item>
<item name="wcf.acp.exceptionLog.exception.copy"><![CDATA[Fehlermeldung kopieren]]></item>
<item name="wcf.acp.exceptionLog.exceptionNotFound"><![CDATA[Es wurde kein Fehler mit der ID „{$exceptionID}“ gefunden.]]></item>
<category name="wcf.acp.exceptionLog">
<item name="wcf.acp.exceptionLog"><![CDATA[Logged errors]]></item>
+ <item name="wcf.acp.exceptionLog.exception.message"><![CDATA[Error Message]]></item>
+ <item name="wcf.acp.exceptionLog.exception.class"><![CDATA[Type]]></item>
<item name="wcf.acp.exceptionLog.exception.file"><![CDATA[File (Line)]]></item>
<item name="wcf.acp.exceptionLog.exception.requestURI"><![CDATA[Requested URL]]></item>
<item name="wcf.acp.exceptionLog.exception.referrer"><![CDATA[Referrer]]></item>
- <item name="wcf.acp.exceptionLog.exception.information"><![CDATA[Additional information]]></item>
<item name="wcf.acp.exceptionLog.exception.stacktrace"><![CDATA[Stacktrace]]></item>
<item name="wcf.acp.exceptionLog.exception.copy"><![CDATA[Copy error message]]></item>
<item name="wcf.acp.exceptionLog.exceptionNotFound"><![CDATA[No error with the ID “{$exceptionID}” was found.]]></item>