Added error reporting.

This commit is contained in:
flash 2018-07-14 19:57:21 +02:00
parent edc3cb03fc
commit 88dfe7a950
3 changed files with 80 additions and 65 deletions

View file

@ -126,6 +126,8 @@ if (PHP_SAPI === 'cli') {
}
}
} else {
ob_start($app->inDebugMode() ? null : 'ob_gzhandler');
$storage_dir = $app->getStoragePath();
if (!$storage_dir->isReadable()
|| !$storage_dir->isWritable()) {
@ -133,10 +135,6 @@ if (PHP_SAPI === 'cli') {
exit;
}
if (!$app->inDebugMode()) {
ob_start('ob_gzhandler');
}
$app->startTemplating();
$tpl = $app->getTemplating();

View file

@ -61,9 +61,13 @@ class Application extends ApplicationBase
{
parent::__construct();
$this->debugMode = $debug;
ExceptionHandler::register();
ExceptionHandler::debug($this->debugMode);
$this->configInstance = new ConfigManager($configFile);
ExceptionHandler::register(
$debug,
$this->configInstance->get('Exceptions', 'report_url', 'string', null),
$this->configInstance->get('Exceptions', 'hash_key', 'string', null)
);
}
/**

View file

@ -17,6 +17,12 @@ class ExceptionHandler
*/
private static $reportUrl = null;
/**
* HMAC key that will be used to sign the request.
* @var string
*/
private static $reportSign = null;
/**
* Whether debug mode is active.
* If true (or in CLI) a backtrace will be displayed.
@ -25,19 +31,19 @@ class ExceptionHandler
*/
private static $debugMode = false;
/**
* Internal bool used to prevent an infinite loop when the templating engine is not available.
* @var bool
*/
private static $failSafe = false;
/**
* Registers the exception handler and make it so all errors are thrown as ErrorExceptions.
* @param bool $debugMode
* @param string|null $reportUrl
* @param string|null $reportSign
*/
public static function register(): void
public static function register(bool $debugMode, ?string $reportUrl = null, ?string $reportSign = null): void
{
set_exception_handler([static::class, 'exception']);
set_error_handler([static::class, 'error']);
self::$debugMode = $debugMode;
self::$reportUrl = $reportUrl;
self::$reportSign = $reportSign;
set_exception_handler([self::class, 'exception']);
set_error_handler([self::class, 'error']);
}
/**
@ -49,15 +55,6 @@ class ExceptionHandler
restore_error_handler();
}
/**
* Turns debug mode on or off.
* @param bool $mode
*/
public static function debug(bool $mode): void
{
static::$debugMode = $mode;
}
/**
* The actual handler for rendering and reporting exceptions.
* Checks if the exception is extends on HttpException,
@ -66,14 +63,13 @@ class ExceptionHandler
*/
public static function exception(Throwable $exception): void
{
$is_http = is_subclass_of($exception, HttpException::class);
$report = !static::$debugMode && !$is_http && static::$reportUrl !== null;
$report = !self::$debugMode && self::$reportUrl !== null;
if ($report) {
static::report($exception);
self::report($exception);
}
static::render($exception, $report);
self::render($exception, $report);
}
/**
@ -92,59 +88,76 @@ class ExceptionHandler
/**
* Shoots a POST request to the report URL.
* @todo Implement this.
* @param Throwable $exception
* @param Throwable $throwable
*/
private static function report(Throwable $exception): void
private static function report(Throwable $throwable): bool
{
// send POST request with json encoded exception to destination
if (!strlen(self::$reportUrl)) {
return false;
}
if (!($curl = curl_init(self::$reportUrl))) {
return false;
}
$json = json_encode([
'git' => [
'branch' => git_branch(),
'hash' => git_commit_hash(true),
],
'exception' => [
'type' => get_class($throwable),
'message' => $throwable->getMessage(),
'code' => $throwable->getCode(),
'file' => str_replace(dirname(__DIR__, 1), '', $throwable->getFile()),
'line' => $throwable->getLine(),
'trace' => $throwable->getTraceAsString(),
],
]);
$headers = [
'Content-Type: application/json;charset=utf-8',
];
if (strlen(self::$reportSign)) {
$headers[] = 'X-Misuzu-Signature: sha256=' . hash_hmac('sha256', $json, self::$reportSign);
}
$setOpts = curl_setopt_array($curl, [
CURLOPT_TCP_NODELAY => true,
CURLOPT_POSTFIELDS => $json,
CURLOPT_HTTPHEADER => $headers,
]);
if (!$setOpts) {
return false;
}
return curl_exec($curl) !== false;
}
/**
* Renders exceptions.
* In debug or cli mode a backtrace is displayed.
* Otherwise if the error extends on HttpException the respective error code is set.
* If the View alias is still available the script will attempt to render a path 'errors/{error code}.twig'.
* @param Throwable $exception
* @param bool $reported
*/
private static function render(Throwable $exception, bool $reported): void
{
$is_http = false;//$exception instanceof HttpException;
if (PHP_SAPI === 'cli' || (!$is_http && static::$debugMode)) {
if (PHP_SAPI !== 'cli' && !headers_sent()) {
ob_clean();
http_response_code(500);
header('Content-Type: text/plain');
}
if (PHP_SAPI === 'cli' || self::$debugMode) {
echo $exception;
return;
}
$http_code = $is_http ? $exception->httpCode : 500;
http_response_code($http_code);
static::$failSafe = true;
/*if (!static::$failSafe && View::available()) {
static::$failSafe = true;
$template = "errors.{$http_code}";
$namespace = View::findNamespace($template);
if ($namespace !== null) {
echo View::render("@{$namespace}.{$template}", compact('reported'));
return;
}
}*/
if ($is_http) {
echo "Error {$http_code}";
return;
}
echo "Something broke!";
} else {
echo 'Something broke!';
if ($reported) {
echo "<br>The error has been reported to the developers.";
echo PHP_EOL . 'Information about this error has been sent to the devs.';
}
}
}
}