2019-11-18 05:58:33 +01:00
---
2015-07-24 00:53:41 +02:00
title: Logging and Error Handling
summary: Trap, fire and report diagnostic logs, user exceptions, warnings and errors.
2019-11-18 05:58:33 +01:00
icon: exclamation-circle
---
2014-10-13 10:52:19 +02:00
2015-07-24 00:53:41 +02:00
# Logging and Error Handling
2011-02-07 07:48:44 +01:00
2015-07-24 00:53:41 +02:00
SilverStripe uses Monolog for both error handling and logging. It comes with two default configurations: one for
2018-07-04 01:56:06 +02:00
logging, and another for core error handling. The core error handling implementation also comes with two default
configurations: one for development environments, and another for test or live environments. On development
environments, SilverStripe will deal harshly with any warnings or errors: a full call-stack is shown and execution
stops for anything, giving you early warning of a potential issue to handle.
2011-02-07 07:48:44 +01:00
2015-07-24 00:53:41 +02:00
## Raising errors and logging diagnostic information.
2011-02-07 07:48:44 +01:00
2018-07-04 01:56:06 +02:00
For general purpose logging, you can use the Logger directly. The Logger is a PSR-3 compatible LoggerInterface and
2015-07-24 00:53:41 +02:00
can be accessed via the `Injector` :
2011-02-07 07:48:44 +01:00
2017-07-10 01:00:25 +02:00
```php
2017-10-26 02:22:02 +02:00
use SilverStripe\Core\Injector\Injector;
2018-02-26 20:31:44 +01:00
use Psr\Log\LoggerInterface;
use SilverStripe\Security\Security;
2017-10-26 02:22:02 +02:00
2017-08-07 02:09:39 +02:00
Injector::inst()->get(LoggerInterface::class)->info('User has logged in: ID #' . Security::getCurrentUser()->ID);
2017-07-10 01:00:25 +02:00
Injector::inst()->get(LoggerInterface::class)->debug('Query executed: ' . $sql);
2018-07-04 01:56:06 +02:00
Injector::inst()->get(LoggerInterface::class)->error('Something went wrong, but let\'s continue on...');
2017-07-10 01:00:25 +02:00
```
2015-07-24 00:53:41 +02:00
Although you can raise more important levels of alerts in this way, we recommend using PHP's native error systems for
these instead.
2018-07-04 01:56:06 +02:00
For notice-level and warning-level issues, you can also use [user_error ](http://www.php.net/user_error ) to throw errors
where appropriate. As with the default Logger implementation these will not halt execution, but will send a message
to the PHP error log.
2015-07-24 00:53:41 +02:00
2017-07-10 01:00:25 +02:00
```php
public function delete()
{
if ($this->alreadyDelete) {
user_error("Delete called on already deleted object", E_USER_NOTICE);
return;
}
// ...
}
public function getRelatedObject()
{
if (!$this->RelatedObjectID) {
user_error("Can't find a related object", E_USER_WARNING);
return;
}
// ...
}
```
2015-07-24 00:53:41 +02:00
2018-07-04 01:56:06 +02:00
For errors that should halt execution, you should use Exceptions. Normally, Exceptions will halt the flow of execution,
2015-07-24 00:53:41 +02:00
but they can be caught with a try/catch clause.
2017-07-10 01:00:25 +02:00
```php
throw new \LogicException("Query failed: " . $sql);
```
2015-07-24 00:53:41 +02:00
2018-07-04 01:56:06 +02:00
### Accessing the logger via dependency injection
2015-07-24 00:53:41 +02:00
2018-02-26 20:31:44 +01:00
It can be quite verbose to call `Injector::inst()->get(LoggerInterface::class)` all the time. More importantly,
2017-03-09 23:20:58 +01:00
it also means that you're coupling your code to global state, which is a bad design practise. A better
2018-07-04 01:56:06 +02:00
approach is to use dependency injection to pass the logger in for you. The [Injector ](../extending/Injector )
2017-03-09 23:20:58 +01:00
can help with this. The most straightforward is to specify a `dependencies` config setting, like this:
2015-07-24 00:53:41 +02:00
2017-07-10 01:00:25 +02:00
```php
2018-07-04 01:56:06 +02:00
use Psr\Log\LoggerInterface;
2017-08-05 00:45:24 +02:00
use SilverStripe\Control\Controller;
2017-07-10 01:00:25 +02:00
class MyController extends Controller
{
private static $dependencies = [
2018-07-04 01:56:06 +02:00
'Logger' => '%$' . LoggerInterface::class,
2017-07-10 01:00:25 +02:00
];
2015-07-24 00:53:41 +02:00
2018-07-04 01:56:06 +02:00
/**
* This will be set automatically, as long as MyController is instantiated via Injector
*
* @var LoggerInterface
*/
protected $logger;
2015-07-24 00:53:41 +02:00
2017-07-10 01:00:25 +02:00
protected function init()
{
$this->logger->debug("MyController::init() called");
parent::init();
}
2018-07-04 01:56:06 +02:00
/**
* @param LoggerInterface $logger
* @return $this
*/
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
return $this;
}
2017-07-10 01:00:25 +02:00
}
```
2011-02-07 07:48:44 +01:00
2015-07-24 00:53:41 +02:00
In other contexts, such as testing or batch processing, logger can be set to a different value by the code calling
MyController.
### Error Levels
2011-02-07 07:48:44 +01:00
2014-10-13 10:52:19 +02:00
* **E_USER_WARNING:** Err on the side of over-reporting warnings. Throwing warnings provides a means of ensuring that
developers know:
2011-02-07 07:48:44 +01:00
* Deprecated functions / usage patterns
* Strange data formats
* Things that will prevent an internal function from continuing. Throw a warning and return null.
* **E_USER_ERROR:** Throwing one of these errors is going to take down the production site. So you should only throw
2020-09-25 02:09:37 +02:00
E_USER_ERROR if it's going to be **dangerous** or **impossible** to continue with the request. Note that it is
preferable to now throw exceptions instead of `E_USER_ERROR` .
2011-02-07 07:48:44 +01:00
2015-07-24 00:53:41 +02:00
## Configuring error logging
2011-02-07 07:48:44 +01:00
2018-07-04 01:56:06 +02:00
You can configure your logging using Monolog handlers. The handlers should be provided in the `Logger.handlers`
2015-07-24 00:53:41 +02:00
configuration setting. Below we have a couple of common examples, but Monolog comes with [many different handlers ](https://github.com/Seldaek/monolog/blob/master/doc/02-handlers-formatters-processors.md#handlers )
for you to try.
2011-02-07 07:48:44 +01:00
2015-07-24 00:53:41 +02:00
### Sending emails
2011-02-07 07:48:44 +01:00
2015-07-24 00:53:41 +02:00
To send emails, you can use Monolog's [NativeMailerHandler ](https://github.com/Seldaek/monolog/blob/master/src/Monolog/Handler/NativeMailerHandler.php#L74 ), like this:
2011-02-07 07:48:44 +01:00
2017-07-10 01:00:25 +02:00
```yaml
SilverStripe\Core\Injector\Injector:
Psr\Log\LoggerInterface:
calls:
2019-06-19 08:21:31 +02:00
MailHandler: [ pushHandler, [ '%$MailHandler' ] ]
2017-07-10 01:00:25 +02:00
MailHandler:
class: Monolog\Handler\NativeMailerHandler
constructor:
- me@example.com
- There was an error on your test site
- me@example.com
- error
properties:
ContentType: text/html
2020-11-06 17:08:53 +01:00
Formatter: '%$SilverStripe\Logging\DetailedErrorFormatter'
2017-07-10 01:00:25 +02:00
```
2015-07-24 00:53:41 +02:00
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.
The calls key, `MailHandler` , can be anything you like: its main purpose is to let other configuration disable it
(see below).
### Logging to a file
To log to a file, you can use Monolog's [StreamHandler ](https://github.com/Seldaek/monolog/blob/master/src/Monolog/Handler/StreamHandler.php#L74 ), like this:
2017-07-10 01:00:25 +02:00
```yaml
SilverStripe\Core\Injector\Injector:
Psr\Log\LoggerInterface:
calls:
2019-06-19 08:21:31 +02:00
LogFileHandler: [ pushHandler, [ '%$LogFileHandler' ] ]
2017-07-10 01:00:25 +02:00
LogFileHandler:
class: Monolog\Handler\StreamHandler
constructor:
- "../silverstripe.log"
- "info"
```
2015-07-24 00:53:41 +02:00
2018-07-04 02:26:47 +02:00
The log file will be relative to the main index.php file path (default: inside public/), so "../silverstripe.log" will
create a file in your project root.
2018-07-04 02:11:56 +02:00
The `info` argument provides the minimum level to start logging at.
2015-07-24 00:53:41 +02:00
### Disabling the default handler
You can disable a handler by removing its pushHandlers call from the calls option of the Logger service definition.
2018-07-04 01:56:06 +02:00
The handler key of the default handler is `pushDisplayErrorHandler` , so you can disable it like this:
2015-07-24 00:53:41 +02:00
2017-07-10 01:00:25 +02:00
```yaml
SilverStripe\Core\Injector\Injector:
2018-07-04 01:56:06 +02:00
Psr\Log\LoggerInterface.errorhandler:
2017-07-10 01:00:25 +02:00
calls:
2020-11-06 17:08:53 +01:00
pushDisplayErrorHandler: '%%remove%%'
2017-07-10 01:00:25 +02:00
```
2015-07-24 00:53:41 +02:00
### Setting a different configuration for dev
In order to set different logging configuration on different environment types, we rely on the environment-specific
2018-07-04 01:56:06 +02:00
configuration features that the config system providers. For example, here we have different configuration for dev and
2015-07-24 00:53:41 +02:00
non-dev.
2017-07-10 01:00:25 +02:00
```yaml
---
Name: dev-errors
Only:
environment: dev
---
SilverStripe\Core\Injector\Injector:
2018-07-04 01:56:06 +02:00
Psr\Log\LoggerInterface.errorhandler:
2017-07-10 01:00:25 +02:00
calls:
2019-06-19 08:21:31 +02:00
pushMyDisplayErrorHandler: [ pushHandler, [ '%$DisplayErrorHandler' ]]
2017-07-10 01:00:25 +02:00
DisplayErrorHandler:
class: SilverStripe\Logging\HTTPOutputHandler
constructor:
- "notice"
properties:
2020-11-06 17:08:53 +01:00
Formatter: '%$SilverStripe\Logging\DetailedErrorFormatter'
CLIFormatter: '%$SilverStripe\Logging\DetailedErrorFormatter'
2017-07-10 01:00:25 +02:00
---
Name: live-errors
Except:
environment: dev
---
SilverStripe\Core\Injector\Injector:
2018-07-04 01:56:06 +02:00
# Default logger implementation for general purpose use
2017-07-10 01:00:25 +02:00
Psr\Log\LoggerInterface:
calls:
2018-07-04 01:56:06 +02:00
# Save system logs to file
2019-06-19 08:21:31 +02:00
pushFileLogHandler: [ pushHandler, [ '%$LogFileHandler' ]]
2018-07-04 01:56:06 +02:00
# Core error handler for system use
Psr\Log\LoggerInterface.errorhandler:
calls:
# Save errors to file
2019-06-19 08:21:31 +02:00
pushFileLogHandler: [ pushHandler, [ '%$LogFileHandler' ]]
2018-07-04 01:56:06 +02:00
# Format and display errors in the browser/CLI
2019-06-19 08:21:31 +02:00
pushMyDisplayErrorHandler: [ pushHandler, [ '%$DisplayErrorHandler' ]]
2018-07-04 01:56:06 +02:00
# Custom handler to log to a file
2017-07-10 01:00:25 +02:00
LogFileHandler:
class: Monolog\Handler\StreamHandler
constructor:
- "../silverstripe.log"
- "notice"
properties:
2020-11-06 17:08:53 +01:00
Formatter: '%$Monolog\Formatter\HtmlFormatter'
2017-07-10 01:00:25 +02:00
ContentType: text/html
2018-07-04 01:56:06 +02:00
# Handler for displaying errors in the browser or CLI
2017-07-10 01:00:25 +02:00
DisplayErrorHandler:
class: SilverStripe\Logging\HTTPOutputHandler
constructor:
- "error"
properties:
2020-11-06 17:08:53 +01:00
Formatter: '%$SilverStripe\Logging\DebugViewFriendlyErrorFormatter'
2018-07-04 01:56:06 +02:00
# Configuration for the "friendly" error formatter
2017-07-10 01:00:25 +02:00
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"
```
2011-02-07 07:48:44 +01:00
2019-11-18 05:58:33 +01:00
[info]
2018-07-04 01:56:06 +02:00
In addition to SilverStripe-integrated logging, it is advisable to fall back to PHP's native logging functionality. A
2014-10-13 10:52:19 +02:00
script might terminate before it reaches the SilverStripe error handling, for example in the case of a fatal error. Make
sure `log_errors` and `error_log` in your PHP ini file are configured.
2019-11-18 05:58:33 +01:00
[/info]
2011-02-07 07:48:44 +01:00
2015-07-24 00:53:41 +02:00
## Replacing default implementations
For most application, Monolog and its default error handler should be fine, as you can get a lot of flexibility simply
by changing that handlers that are used. However, some situations will call for replacing the default components with
others.
### Replacing the logger
Monolog comes by default with SilverStripe, but you may use another PSR-3 compliant logger, if you wish. To do this,
2018-07-04 01:56:06 +02:00
set the `SilverStripe\Core\Injector\Injector.Monolog\Logger` configuration parameter, providing a new injector
definition. For example:
2015-07-24 00:53:41 +02:00
2017-07-10 01:00:25 +02:00
```yaml
SilverStripe\Core\Injector\Injector:
2018-07-04 02:28:50 +02:00
SilverStripe\Logging\ErrorHandler:
2017-07-10 01:00:25 +02:00
class: Logging\Logger
constructor:
- 'alternative-logger'
```
2015-07-24 00:53:41 +02:00
If you do this, you will need to supply your own handlers, and the `Logger.handlers` configuration parameter will
be ignored.
### Replacing the error handler
2018-07-04 01:56:06 +02:00
The Injector service `SilverStripe\Logging\ErrorHandler` is responsible for initialising the error handler. By default
it:
2015-07-24 00:53:41 +02:00
2017-04-24 06:38:13 +02:00
* 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.
2015-07-24 00:53:41 +02:00
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:
2017-07-10 01:00:25 +02:00
```yaml
SilverStripe\Core\Injector\Injector:
2018-07-04 01:56:06 +02:00
SilverStripe\Logging\ErrorHandler:
2017-07-10 01:00:25 +02:00
class: MyApp\CustomErrorHandlerLoader
```
2015-07-24 00:53:41 +02:00
2017-04-24 06:38:13 +02:00
You should register something with a `start()` method.
2015-07-24 00:53:41 +02:00
## Differences from SilverStripe 3
In SilverStripe 3, logging was based on the Zend Log module. Customisations were added using `SS_Log::add_writer()` .
This function no longer works, and any Zend Log writers will need to be replaced with Monolog handlers. Fortunately,
a range of handlers are available, both in the core package and in add-ons. See the
[Monolog documentation ](https://github.com/Seldaek/monolog/blob/master/doc/01-usage.md ) for more information.
2017-11-27 04:39:17 +01:00
## Related Lessons
* [Advanced environment configuration ](https://www.silverstripe.org/learn/lessons/v4/advanced-environment-configuration-1 )