From 4c772c80c3bff06e46bcafdece10e63a03b2e61c Mon Sep 17 00:00:00 2001 From: Sam Minnee Date: Mon, 24 Apr 2017 16:38:13 +1200 Subject: [PATCH] FIX: Show detailed errors on CLI for live environments API: Add HTTPOutputHandler::setCLIFormatter Fixes https://github.com/silverstripe/silverstripe-framework/issues/6835 This provides detailed errors (but not warnings or notices) in CLI calls on live environments. It does this by adding a 2nd argument to our output handler, CliFormatter. This formatter will be used when Director::is_cli() is true. --- _config/logging.yml | 5 +- .../07_Debugging/01_Error_Handling.md | 29 +++++------ src/Logging/HTTPOutputHandler.php | 48 ++++++++++++++++++- 3 files changed, 65 insertions(+), 17 deletions(-) diff --git a/_config/logging.yml b/_config/logging.yml index 675a305b6..6bfe98007 100644 --- a/_config/logging.yml +++ b/_config/logging.yml @@ -37,8 +37,9 @@ SilverStripe\Core\Injector\Injector: constructor: - "error" properties: - Formatter: %$FriendlyErrorFormatter - FriendlyErrorFormatter: + Formatter: %$SilverStripe\Logging\DebugViewFriendlyErrorFormatter + CLIFormatter: %$SilverStripe\Logging\DetailedErrorFormatter + SilverStripe\Logging\DebugViewFriendlyErrorFormatter: class: SilverStripe\Logging\DebugViewFriendlyErrorFormatter properties: Title: "There has been an error" diff --git a/docs/en/02_Developer_Guides/07_Debugging/01_Error_Handling.md b/docs/en/02_Developer_Guides/07_Debugging/01_Error_Handling.md index a31cc6d87..50ea2a76d 100644 --- a/docs/en/02_Developer_Guides/07_Debugging/01_Error_Handling.md +++ b/docs/en/02_Developer_Guides/07_Debugging/01_Error_Handling.md @@ -107,7 +107,7 @@ To send emails, you can use Monolog's [NativeMailerHandler](https://github.com/S - error properties: ContentType: text/html - Formatter: %$SilverStripe\Framework\Logging\DetailedErrorFormatter + Formatter: %$SilverStripe\Logging\DetailedErrorFormatter The first section 4 lines passes a new handler to `Logger::pushHandler()` from the named service `MailHandler`. The next 10 lines define what the service is. @@ -157,11 +157,12 @@ non-dev. calls: - [ pushHandler, [ %$DisplayErrorHandler ]] DisplayErrorHandler: - class: SilverStripe\Framework\Logging\HTTPOutputHandler + class: SilverStripe\Logging\HTTPOutputHandler constructor: - "notice" properties: - Formatter: %$SilverStripe\Framework\Logging\DetailedErrorFormatter + Formatter: %$SilverStripe\Logging\DetailedErrorFormatter + CLIFormatter: %$SilverStripe\Logging\DetailedErrorFormatter --- Name: live-errors Except: @@ -181,13 +182,13 @@ non-dev. Formatter: %$Monolog\Formatter\HtmlFormatter ContentType: text/html DisplayErrorHandler: - class: SilverStripe\Framework\Logging\HTTPOutputHandler + class: SilverStripe\Logging\HTTPOutputHandler constructor: - "error" properties: - Formatter: %$FriendlyErrorFormatter - FriendlyErrorFormatter: - class: SilverStripe\Framework\Logging\DebugViewFriendlyErrorFormatter + Formatter: %$SilverStripe\Logging\DebugViewFriendlyErrorFormatter + SilverStripe\Logging\DebugViewFriendlyErrorFormatter: + class: SilverStripe\Logging\DebugViewFriendlyErrorFormatter properties: Title: "There has been an error" Body: "The website server has not been able to respond to your request" @@ -236,21 +237,21 @@ be ignored. ### Replacing the error handler -The class `SilverStripe\Framework\Logging\MonologLoader` is responsible for loading performing Monolog-specific -configuration. It does a number of things: +The Injector service `ErrorHandler` is responsible for initialising the error handler. By default it - * Create a `Monolog\ErrorHandler` object. - * Register the registered service `Logger` against it, to start the error handler. - * If `Logger` has a `pushHandler()` method, pass every object defined by `ErrorHandler.handlers` into it, one at a time. + * Create a `SilverStripe\Logging\MonologErrorHandler` object. + * Attach the registered service `Psr\Log\LoggerInterface` to it, to start the error handler. + +Core.php will call `start()` on this method, to start the error handler. This error handler is flexible enough to work with any PSR-3 logging implementation, but sometimes you will want to use another. To replace this, you should registered a new service, `ErrorHandlerLoader`. For example: SilverStripe\Core\Injector\Injector: - ErrorHandlerLoader: + ErrorHandler: class: MyApp\CustomErrorHandlerLoader -You should register something `Callable`, for example a class with an `__invoke()` method. +You should register something with a `start()` method. ## Differences from SilverStripe 3 diff --git a/src/Logging/HTTPOutputHandler.php b/src/Logging/HTTPOutputHandler.php index 505417bb9..c0f61ca16 100644 --- a/src/Logging/HTTPOutputHandler.php +++ b/src/Logging/HTTPOutputHandler.php @@ -3,8 +3,10 @@ namespace SilverStripe\Logging; use SilverStripe\Control\Controller; +use SilverStripe\Control\Director; use SilverStripe\Control\HTTPResponse; use Monolog\Handler\AbstractProcessingHandler; +use Monolog\Formatter\FormatterInterface; /** * Output the error to the browser, with the given HTTP status code. @@ -23,6 +25,11 @@ class HTTPOutputHandler extends AbstractProcessingHandler */ private $statusCode = 500; + /** + * @var FormatterInterface + */ + private $cliFormatter = null; + /** * Get the mime type to use when displaying this error. * @@ -38,7 +45,7 @@ class HTTPOutputHandler extends AbstractProcessingHandler * Default text/html * * @param string $contentType - * @return $this + * @return HTTPOutputHandler Return $this to allow chainable calls */ public function setContentType($contentType) { @@ -69,6 +76,45 @@ class HTTPOutputHandler extends AbstractProcessingHandler return $this; } + /** + * Set a formatter to use if Director::is_cli() is true + * + * @param $cliFormatter + * @return HTTPOutputHandler Return $this to allow chainable calls + */ + public function setCLIFormatter(FormatterInterface $cliFormatter) + { + $this->cliFormatter = $cliFormatter; + + return $this; + } + + /** + * Return the formatter use if Director::is_cli() is true + * If none has been set, null is returned, and the getFormatter() result will be used instead + * + * @return FormatterInterface + */ + public function getCLIFormatter() + { + return $this->cliFormatter; + } + + /** + * Return the formatter to use in this case. + * May be the getCliFormatter() value if one is provided and Director::is_cli() is true. + * + * @return FormatterInterface + */ + public function getFormatter() + { + if (Director::is_cli() && ($cliFormatter = $this->getCLIFormatter())) { + return $cliFormatter; + } + + return parent::getFormatter(); + } + /** * @param array $record * @return bool