Lines 100.00% 13 / 13
Functions and Methods 100.00% 1 / 1
Classes and Traits 100.00% 1 / 1
Name Lines Functions and Methods CRAP Classes and Traits
ExceptionContextProcessor 100.00% 13 / 13 100.00% 1 / 1 6 100.00% 1 / 1
 __invoke 100.00% 13 / 13 100.00% 1 / 1 6
1<?php
2
3declare(strict_types=1);
4
5namespace Stewart\Runtime\Logging;
6
7use Monolog\LogRecord;
8use Monolog\Processor\ProcessorInterface;
9use Stewart\Runtime\Model\ExceptionDetails;
10use Throwable;
11
12final class ExceptionContextProcessor implements ProcessorInterface
13{
14    public const string TRACE_KEY = 'trace';
15
16    public function __invoke(LogRecord $record): LogRecord
17    {
18        $context = $record->context;
19        $forwarded = $context[ExceptionDetails::CONTEXT_KEY] ?? null;
20        $wasForwarded = \array_key_exists(ExceptionDetails::CONTEXT_KEY, $context);
21        unset($context[ExceptionDetails::CONTEXT_KEY]);
22
23        $exception = $context['exception'] ?? null;
24        $details = $forwarded instanceof ExceptionDetails
25            ? $forwarded
26            : ($exception instanceof Throwable ? ExceptionDetails::fromThrowable($exception) : null);
27
28        if ($details !== null) {
29            return $record->with(context: $context, extra: [...$record->extra, 'error' => $details->toArray()]);
30        }
31
32        // A Stewart exception is an expected failure; anything else is a bug that needs its trace.
33        if ($exception instanceof Throwable) {
34            return $record->with(context: $context, extra: [...$record->extra, self::TRACE_KEY => $exception->getTraceAsString()]);
35        }
36
37        return $wasForwarded ? $record->with(context: $context) : $record;
38    }
39}