For the times when you just want those exception to be delivered to your inbox with as much information as possible.
Converting over some smaller apps to use monolog I found my favourite snippet for emailing the critical exceptions which bubble to the top of the app wasn't that easy to replicate.
Update January 2020: A few years back I swapped to using a dedicated online error tracker sentry.io (no affiliation) personally and professionally and now would now not advocate trying to roll your own error tracking if you can help it.
}catch (Exception $e) {
mail(
'[email protected]',
__FILE__,
print_r( $e, true ).print_r( $_SERVER, true )
);
}
### Output ###
Exception Object
(
[message:protected] =>
[string:Exception:private] =>
[code:protected] => 0
[file:protected] => /foo/bar/index.php
[line:protected] => 19
[trace:Exception:private] => Array
(
[0] => Array
(
[file] => /foo/bar/RubbishCode.php
[line] => 23
[function] => __construct
[class] => RubbishCode
[type] => ->
[args] => Array
(
)
)
)
[previous:Exception:private] =>
)
Array
(
[SERVER_PROTOCOL] => HTTP/1.1
[REQUEST_METHOD] => GET
...
)
monolog emailing out of the box
use Monolog\Logger;
use Monolog\Handler\NativeMailerHandler;
$log = new Logger('foo');
$handler = new NativeMailerHandler(
'[email protected]',
'Subject',
'From'
);
$log->pushHandler($handler);
### Output ###
[YYYY-MM-DD HH:MM:SS] foo.ERROR: Exception {"exception":"[object]
(Exception(code: 0): at
/foo/bar/RubbishCode.php:29)"} []
monolog LineFormatter->includeStacktraces()
LineFormatter does have a property which can be set which will cause exceptions to output the $exception->getTrace() however the output is still going to be missing the args array etc.
$lineFormatter = new LineFormatter;
$lineFormatter-> includeStacktraces();
$handler->setFormatter($lineFormatter);
$log->pushHandler($handler);
### Output ###
[YYYY-MM-DD HH:MM:SS] foo.ERROR: Exception {"exception":"[object]
(Exception(code: 0): at
/foo/bar/index.php:19)\n[stacktrace]\n#0
/foo/bar/RubbishCode.php(23):
RubbishCode->__construct()\n#1 {main}"} []
Extending LineFormatter to use print_r()
Personally I find print_r($foo)
the easiest to read, with json_encode($foo, JSON_PRETTY_PRINT)
a close second.
class PrintRLineFormatter extends Monolog\Formatter\LineFormatter
{
/**
* {@inheritdoc}
*/
public function format(array $record)
{
return print_r( $record, true );
}
}
### Output ###
Array
(
[message] => Exception
[context] => Array
(
[exception] => Exception Object
(
[message:protected] =>
[string:Exception:private] =>
[code:protected] => 0
[file:protected] =>/foo/bar/index.php
[line:protected] => 19
[trace:Exception:private] => Array
(
[0] => Array
(
[file] =>/foo/bar/RubbishCode.php
[line] => 23
[function] => __construct
[class] => RubbishCode
[type] => ->
[args] => Array
(
)
)
)
[previous:Exception:private] =>
)
)
[level] => 400
[level_name] => ERROR
[channel] => foo
[datetime] => DateTime Object
(
[date] => YYYY-MM-DD HH:MM:SS
[timezone_type] => 3
[timezone] => Europe/London
)
...
Adding in global variables with a Processor
Most of the time you can't get away from the fact that the global PHP variables are going to give a good place to start debugging the state of the script at the time of failure:
$log->pushProcessor(function ($record) {
if( !empty( $_SERVER ) ){
$record['extra']['_SERVER'] = $_SERVER;
}
if( !empty( $_SESSION ) ){
$record['extra']['_SESSION'] = $_SESSION;
}
if( !empty( $_POST ) ){
$record['extra']['_POST'] = $_POST;
}
return $record;
});
All together
use Monolog\Logger;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\NativeMailerHandler;
class PrintRLineFormatter extends LineFormatter
{
/**
* {@inheritdoc}
*/
public function format(array $record)
{
return print_r( $record, true );
}
}
$log = new Logger('foo');
$handler = new NativeMailerHandler(
'[email protected]',
'Subject',
'From'
);
$handler->setFormatter(new PrintRLineFormatter);
$log->pushHandler($handler);
$log->pushProcessor(function ($record) {
if( !empty( $_SERVER ) ){
$record['extra']['_SERVER'] = $_SERVER;
}
if( !empty( $_SESSION ) ){
$record['extra']['_SESSION'] = $_SESSION;
}
if( !empty( $_POST ) ){
$record['extra']['_POST'] = $_POST;
}
return $record;
});
### Output ###
Array
(
[message] => Exception
[context] => Array
(
[exception] => Exception Object
(
[message:protected] =>
[string:Exception:private] =>
[code:protected] => 0
[file:protected] =>/foo/bar/index.php
[line:protected] => 19
[trace:Exception:private] => Array
(
[0] => Array
(
[file] =>/foo/bar/RubbishCode.php
[line] => 23
[function] => __construct
[class] => RubbishCode
[type] => ->
[args] => Array
(
)
)
)
[previous:Exception:private] =>
)
)
[level] => 400
[level_name] => ERROR
[channel] => foo
[datetime] => DateTime Object
(
[date] => YYYY-MM-DD HH:MM:SS
[timezone_type] => 3
[timezone] => Europe/London
)
[extra] => Array
(
[_SERVER] => Array
(
...
[SERVER_PROTOCOL] => HTTP/1.1
[REQUEST_METHOD] => GET
...
)
)
)