Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 40 additions & 34 deletions components/ILIAS/Init/classes/class.ilErrorHandling.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

declare(strict_types=1);

use ILIAS\Init\ErrorHandling\Infrastructure\Whoops as ErrorHandlers;
use ILIAS\Init\ErrorHandling\Infrastructure\Logging as ErrorLogging;
use ILIAS\Init\ErrorHandling;
use Whoops\Run;
use Whoops\RunInterface;
use Whoops\Handler\PrettyPageHandler;
Expand All @@ -34,7 +37,7 @@
* @todo when an error occured and clicking the back button to return to previous page the referer-var in session is deleted -> server error
* @todo This class is a candidate for a singleton. initHandlers could only be called once per process anyways, as it checks for static $handlers_registered.
*/
class ilErrorHandling
class ilErrorHandling implements ErrorHandling\Application\ContextErrorHandlerProvider
{
/** @var list<string> */
private const array SENSTIVE_PARAMETER_NAMES = [
Expand All @@ -55,6 +58,8 @@ class ilErrorHandling

protected ?RunInterface $whoops;
protected string $message;
protected ErrorHandling\Incident\ErrorIncidentRegistry $error_incident_registry;
protected ErrorHandling\Application\DevmodeState $devmode_state;
/** Error level 1: exit application immedietly */
public int $FATAL = 1;
/** Error level 2: show warning page */
Expand All @@ -67,6 +72,8 @@ public function __construct()
$this->FATAL = 1;
$this->WARNING = 2;
$this->MESSAGE = 3;
$this->error_incident_registry = new ErrorHandling\Incident\InMemoryErrorIncidentRegistry();
$this->devmode_state = new ErrorHandling\Infrastructure\Environment\RuntimeDevmodeState();

$this->initWhoopsHandlers();

Expand All @@ -89,10 +96,26 @@ protected function initWhoopsHandlers(): void

$runtime = $this->getRuntime();
$this->whoops = $this->getWhoops();
$this->whoops->pushHandler(new ilDelegatingHandler($this, self::SENSTIVE_PARAMETER_NAMES));
$this->whoops->pushHandler(
new ErrorHandlers\DelegatingHandler($this, self::SENSTIVE_PARAMETER_NAMES)
);
if ($runtime->shouldLogErrors()) {
$this->whoops->pushHandler($this->loggingHandler());
}
$this->whoops->pushHandler(
new ErrorHandlers\RecordErrorIncidentHandler(
new ErrorHandling\Application\ProductionOnlyErrorIncidentReporting(
new ErrorHandling\Application\ReportErrorIncident(
new ErrorLogging\LoggingErrorLogDirectory(),
new ErrorLogging\LoggingErrorFileStorageAdapter(),
new ErrorHandling\Incident\SessionPrefixedErrorIncidentFactory(),
$this->error_incident_registry,
self::SENSTIVE_PARAMETER_NAMES
),
$this->devmode_state
)
)
);
$this->whoops->register();

self::$whoops_handlers_registered = true;
Expand All @@ -106,7 +129,10 @@ public function getHandler(): HandlerInterface
{
if (ilContext::getType() === ilContext::CONTEXT_SOAP &&
strcasecmp($_SERVER['REQUEST_METHOD'] ?? '', 'post') === 0) {
return new ilSoapExceptionHandler();
return new ErrorHandlers\SoapExceptionHandler(
$this->error_incident_registry,
$this->devmode_state
);
}

// TODO: There might be more specific execution contexts (WebDAV, REST, etc.) that need specific error handling.
Expand Down Expand Up @@ -222,48 +248,26 @@ protected function getWhoops(): RunInterface

protected function isDevmodeActive(): bool
{
return defined('DEVMODE') && (int) DEVMODE === 1;
return $this->devmode_state->isActive();
}

protected function defaultHandler(): HandlerInterface
{
return new CallbackHandler(function ($exception, Inspector $inspector, Run $run) {
global $DIC;

$logger = ilLoggingErrorSettings::getInstance();

$message = 'Sorry, an error occured.';
if ($DIC->isDependencyAvailable('language')) {
$DIC->language()->loadLanguageModule('logging');
$message = $DIC->language()->txt('error_sry_error');
}

if (!empty($logger->folder())) {
$session_id = substr(session_id(), 0, 5);
$r = new \Random\Randomizer();
$err_num = $r->getInt(1, 9999);
$file_name = $session_id . '_' . $err_num;

$lwriter = new ilLoggingErrorFileStorage($inspector, $logger->folder(), $file_name);
$lwriter = $lwriter->withExclusionList(self::SENSTIVE_PARAMETER_NAMES);
$lwriter->write();

if ($DIC->isDependencyAvailable('language')) {
$message = sprintf($DIC->language()->txt('log_error_message'), $file_name);
if ($logger->mail()) {
$message .= ' ' . sprintf(
$DIC->language()->txt('log_error_message_send_mail'),
$logger->mail(),
$file_name,
$logger->mail()
);
}
} else {
$message = 'Sorry, an error occured. A logfile has been created which can be identified via the code "' . $file_name . '"';
if ($logger->mail()) {
$message .= ' ' . 'Please send a mail to <a href="mailto:' . $logger->mail() . '?subject=code: ' . $file_name . '">' . $logger->mail() . '</a>';
}
}
$incident = $this->error_incident_registry->current();
if ($incident !== null) {
$language = $DIC->isDependencyAvailable('language') ? $DIC->language() : null;
$message = new ErrorHandling\Notification\ErrorIncidentUserMessage(
ilLoggingErrorSettings::getInstance()
)->format($incident, $language);
}

if ($DIC->isDependencyAvailable('ui') && isset($DIC['tpl']) && $DIC->isDependencyAvailable('ctrl')) {
Expand All @@ -283,10 +287,12 @@ protected function devmodeHandler(): HandlerInterface

switch (ERROR_HANDLER) {
case 'TESTING':
return (new ilTestingHandler())->withExclusionList(self::SENSTIVE_PARAMETER_NAMES);
return new ErrorHandlers\TestingHandler()
->withExclusionList(self::SENSTIVE_PARAMETER_NAMES);

case 'PLAIN_TEXT':
return (new ilPlainTextHandler())->withExclusionList(self::SENSTIVE_PARAMETER_NAMES);
return new ErrorHandlers\PlainTextHandler()
->withExclusionList(self::SENSTIVE_PARAMETER_NAMES);

case 'PRETTY_PAGE':
// fallthrough
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

/**
* This file is part of ILIAS, a powerful learning management system
* published by ILIAS open source e-Learning e.V.
*
* ILIAS is licensed with the GPL-3.0,
* see https://www.gnu.org/licenses/gpl-3.0.en.html
* You should have received a copy of said license along with the
* source code, too.
*
* If this is not the case or you just want to try ILIAS, you'll find
* us at:
* https://www.ilias.de
* https://github.com/ILIAS-eLearning
*
*********************************************************************/

declare(strict_types=1);

namespace ILIAS\Init\ErrorHandling\Application;

use Whoops\Handler\HandlerInterface;

/**
* Provides the context-specific Whoops handler for the current request.
*/
interface ContextErrorHandlerProvider
{
public function getHandler(): HandlerInterface;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

/**
* This file is part of ILIAS, a powerful learning management system
* published by ILIAS open source e-Learning e.V.
*
* ILIAS is licensed with the GPL-3.0,
* see https://www.gnu.org/licenses/gpl-3.0.en.html
* You should have received a copy of said license along with the
* source code, too.
*
* If this is not the case or you just want to try ILIAS, you'll find
* us at:
* https://www.ilias.de
* https://github.com/ILIAS-eLearning
*
*********************************************************************/

declare(strict_types=1);

namespace ILIAS\Init\ErrorHandling\Application;

/**
* Whether ILIAS runs in developer mode.
*
* Implementations determine the state lazily (on each call), because consumers may be
* wired before the runtime has decided whether devmode is active.
*/
interface DevmodeState
{
public function isActive(): bool;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

/**
* This file is part of ILIAS, a powerful learning management system
* published by ILIAS open source e-Learning e.V.
*
* ILIAS is licensed with the GPL-3.0,
* see https://www.gnu.org/licenses/gpl-3.0.en.html
* You should have received a copy of said license along with the
* source code, too.
*
* If this is not the case or you just want to try ILIAS, you'll find
* us at:
* https://www.ilias.de
* https://github.com/ILIAS-eLearning
*
*********************************************************************/

declare(strict_types=1);

namespace ILIAS\Init\ErrorHandling\Application;

use ILIAS\Init\ErrorHandling\Incident\ErrorIncident;
use Whoops\Exception\Inspector;

/**
* Application port for reporting an exception as a logged error incident.
*/
interface ErrorIncidentReporting
{
public function report(Inspector $inspector): ?ErrorIncident;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

/**
* This file is part of ILIAS, a powerful learning management system
* published by ILIAS open source e-Learning e.V.
*
* ILIAS is licensed with the GPL-3.0,
* see https://www.gnu.org/licenses/gpl-3.0.en.html
* You should have received a copy of said license along with the
* source code, too.
*
* If this is not the case or you just want to try ILIAS, you'll find
* us at:
* https://www.ilias.de
* https://github.com/ILIAS-eLearning
*
*********************************************************************/

declare(strict_types=1);

namespace ILIAS\Init\ErrorHandling\Application;

/**
* Resolves the configured directory for dedicated error log files.
*/
interface ErrorLogDirectory
{
public function path(): string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

/**
* This file is part of ILIAS, a powerful learning management system
* published by ILIAS open source e-Learning e.V.
*
* ILIAS is licensed with the GPL-3.0,
* see https://www.gnu.org/licenses/gpl-3.0.en.html
* You should have received a copy of said license along with the
* source code, too.
*
* If this is not the case or you just want to try ILIAS, you'll find
* us at:
* https://www.ilias.de
* https://github.com/ILIAS-eLearning
*
*********************************************************************/

declare(strict_types=1);

namespace ILIAS\Init\ErrorHandling\Application;

use Whoops\Exception\Inspector;

/**
* Outbound application port for persisting exception details to a dedicated log file.
*/
interface ErrorLogFileStorage
{
/**
* @param list<string> $sensitive_parameter_names
*/
public function write(
Inspector $inspector,
string $directory,
string $file_name,
array $sensitive_parameter_names
): void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

/**
* This file is part of ILIAS, a powerful learning management system
* published by ILIAS open source e-Learning e.V.
*
* ILIAS is licensed with the GPL-3.0,
* see https://www.gnu.org/licenses/gpl-3.0.en.html
* You should have received a copy of said license along with the
* source code, too.
*
* If this is not the case or you just want to try ILIAS, you'll find
* us at:
* https://www.ilias.de
* https://github.com/ILIAS-eLearning
*
*********************************************************************/

declare(strict_types=1);

namespace ILIAS\Init\ErrorHandling\Application;

use ILIAS\Init\ErrorHandling\Incident\ErrorIncident;
use Whoops\Exception\Inspector;

/**
* Suppresses incident reporting (and thus log file writing) while devmode is active,
* so that error log files are only created in production.
*/
final readonly class ProductionOnlyErrorIncidentReporting implements ErrorIncidentReporting
{
public function __construct(
private ErrorIncidentReporting $reporting,
private DevmodeState $devmode_state
) {
}

public function report(Inspector $inspector): ?ErrorIncident
{
if ($this->devmode_state->isActive()) {
return null;
}

return $this->reporting->report($inspector);
}
}
Loading
Loading