report()? コンテキスト?
Laravelが用意しているreportヘルパは、例外をロギングしてくれます。
report(new \Exception('sample exception'));
[2019-07-30 16:15:55] local.ERROR: sample exception {"exception":"[object] (Exception(code: 0): sample exception at /var/www/routes/web.php:29)
[stacktrace]
...
ログファイルにある{"exception":"[object] ...
がコンテキストで、ロギングする情報のことです。デフォルトでは例外情報とログインしていればそのユーザーIDになります。以下はデフォルトLaravelの実装です。
/**
* Report or log an exception.
*
* @param \Exception $e
* @return mixed
*
* @throws \Exception
*/
public function report(Exception $e)
{
//略
$logger->error(
$e->getMessage(),
array_merge($this->context(), ['exception' => $e] // ここでコンテキストをロギング
));
}
/**
* Get the default context variables for logging.
*
* @return array
*/
protected function context()
{
try {
return array_filter([
'userId' => Auth::id(),
// 'email' => optional(Auth::user())->email,
]);
} catch (Throwable $e) {
return [];
}
}
上記のcontextメソッドをオーバーライドすればコンテキストを自由に編集できますが、全てのreportヘルパに影響するので、reportするときにコンテキストを渡せるようにしました。
ただ、本当は以下のようにしたかったのですが
report($e); // デフォルトのコンテキストでロギング
report($e, ['hoge' => 'fuga']); // コンテキストに追加してロギング
後述の理由で難しそうだったので以下で妥協しました。
use Illuminate\Contracts\Debug\ExceptionHandler;
report($e); // デフォルトのコンテキストでロギング
app(ExceptionHandler::class)->report($e, ['hoge' => 'fuga']); // コンテキストに追加してロギング
実装
app/Exceptions/Handler.php
でreportメソッドをオーバーライドします。
/**
* Report or log an exception.
*
* @param \Exception $e
* @return mixed
*
* @throws \Exception
*/
public function report(Exception $e, array $context = []) // $contextを引数に追加
{
if ($this->shouldntReport($e)) {
return;
}
if (is_callable($reportCallable = [$e, 'report'])) {
return $this->container->call($reportCallable);
}
try {
$logger = $this->container->make(LoggerInterface::class);
} catch (Exception $ex) {
throw $e;
}
$logger->error(
$e->getMessage(),
array_merge($this->context(), $context, ['exception' => $e] // $contextをマージするようにする
));
}
使用するときは、サービスコンテナからExceptionHandlerを受け取りreportしてください。
use Illuminate\Contracts\Debug\ExceptionHandler;
app(ExceptionHandler::class)->report(new \Exception('sample exception'), ['hoge' => 'fuga']);
[2019-07-30 16:34:17] local.ERROR: sample exception {"hoge":"fuga","exception":"[object] (Exception(code: 0): sample exception at /var/www/routes/web.php:25)
[stacktrace]
...
reportヘルパにコンテキストを渡せるようにできないか?
上記はreportヘルパを通さず、直接ExceptionHandlerのreportメソッドを呼んでいます。reportヘルパを使ってreport($e, $context)
とするには、reportヘルパをオーバーライドする必要があります。
reportヘルパの実装は以下になっています。
if (! function_exists('report')) {
/**
* Report an exception.
*
* @param \Throwable $exception
* @return void
*/
function report($exception)
{
if ($exception instanceof Throwable &&
! $exception instanceof Exception) {
$exception = new FatalThrowableError($exception);
}
app(ExceptionHandler::class)->report($exception);
}
}
ヘルパのオーバーライドは、
https://qiita.com/kd9951/items/46ef3559009ee575ea7d
の記事が詳しいです。ただヘルパの引数を追加したいので、この記事のベストな方法(サービスコンテナで入れ替える)は使えません。
以下のようなapp/helpers.php
を作り、vendor/laravel/framework/src/Illuminate/Foundation/helpers.php
よりも早くapp/helpers.php
を読み込むようにすればオーバーライドできるようですが、良さそうな方法がないので辞めました。
<?php
if (! function_exists('report')) {
/**
* Report an exception.
*
* @param \Throwable $exception
* @param array $context
* @return void
*/
function report($exception, $context = []) // 引数に$context追加
{
if ($exception instanceof Throwable &&
! $exception instanceof Exception) {
$exception = new FatalThrowableError($exception);
}
app(ExceptionHandler::class)->report($exception, $context); // $contextを渡す
}
}