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

View file

@ -61,9 +61,13 @@ class Application extends ApplicationBase
{ {
parent::__construct(); parent::__construct();
$this->debugMode = $debug; $this->debugMode = $debug;
ExceptionHandler::register();
ExceptionHandler::debug($this->debugMode);
$this->configInstance = new ConfigManager($configFile); $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; 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. * Whether debug mode is active.
* If true (or in CLI) a backtrace will be displayed. * If true (or in CLI) a backtrace will be displayed.
@ -25,19 +31,19 @@ class ExceptionHandler
*/ */
private static $debugMode = false; 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. * 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']); self::$debugMode = $debugMode;
set_error_handler([static::class, 'error']); 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(); 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. * The actual handler for rendering and reporting exceptions.
* Checks if the exception is extends on HttpException, * Checks if the exception is extends on HttpException,
@ -66,14 +63,13 @@ class ExceptionHandler
*/ */
public static function exception(Throwable $exception): void public static function exception(Throwable $exception): void
{ {
$is_http = is_subclass_of($exception, HttpException::class); $report = !self::$debugMode && self::$reportUrl !== null;
$report = !static::$debugMode && !$is_http && static::$reportUrl !== null;
if ($report) { 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. * Shoots a POST request to the report URL.
* @todo Implement this. * @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. * Renders exceptions.
* In debug or cli mode a backtrace is displayed. * 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 Throwable $exception
* @param bool $reported * @param bool $reported
*/ */
private static function render(Throwable $exception, bool $reported): void 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()) { if (PHP_SAPI !== 'cli' && !headers_sent()) {
ob_clean();
http_response_code(500); http_response_code(500);
header('Content-Type: text/plain'); header('Content-Type: text/plain');
} }
if (PHP_SAPI === 'cli' || self::$debugMode) {
echo $exception; echo $exception;
return; } else {
} echo 'Something broke!';
$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!";
if ($reported) { 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.';
}
} }
} }
} }