From 6f206e64e3b4534b841b6dbebcb13f7a81f0ffcc Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 16 Sep 2026 09:32:37 -0700 Subject: [PATCH] Render log context in Front error comments --- src/Logging/LogHandler.php | 21 +++++++++---- tests/Logging/LogHandlerTest.php | 51 ++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 tests/Logging/LogHandlerTest.php diff --git a/src/Logging/LogHandler.php b/src/Logging/LogHandler.php index d644ad5..961b66a 100644 --- a/src/Logging/LogHandler.php +++ b/src/Logging/LogHandler.php @@ -29,7 +29,7 @@ public function write(array|LogRecord $record): void $record = $record->toArray(); } - if (! Arr::get($record, 'context.exception')) { + if (! $error = Arr::get($record, 'context.exception')) { $errors = collect([ 'Request URL: '.request()->fullUrl(), 'Request data: '.json_encode(request()->input()), @@ -48,23 +48,32 @@ public function write(array|LogRecord $record): void front() ->post( "/conversations/$conversation/comments", - $this->convertErrorToFrontMessage(Arr::get($record, 'context.exception')) + $this->convertErrorToFrontMessage($error, Arr::except(Arr::get($record, 'context', []), 'exception')) )->throw(); } - private function convertErrorToFrontMessage(Throwable $error): array + private function convertErrorToFrontMessage(Throwable $error, array $context): array { - return ['body' => $this->formatErrorLines($error)->implode(PHP_EOL)]; + return ['body' => $this->formatErrorLines($error, $context)->implode(PHP_EOL)]; } - private function formatErrorLines(Throwable $error): Collection + private function formatContext(array $context): Collection + { + return collect($context) + ->map(fn ($value, string $key) => '* '.$key.': '.(is_string($value) ? $value : json_encode($value))) + ->values(); + } + + private function formatErrorLines(Throwable $error, array $context): Collection { return collect([ 'Request URL: '.request()->fullUrl(), 'Request data: '.json_encode(request()->input()), '**'.$error->getMessage().'**', '* '.$error->getFile().' ('.$error->getLine().')', - ])->merge($this->formatStackTrace($error)); + ]) + ->merge($this->formatContext($context)) + ->merge($this->formatStackTrace($error)); } private function formatStackTrace(Throwable $error): Collection diff --git a/tests/Logging/LogHandlerTest.php b/tests/Logging/LogHandlerTest.php new file mode 100644 index 0000000..4ff29d3 --- /dev/null +++ b/tests/Logging/LogHandlerTest.php @@ -0,0 +1,51 @@ +set('front.logging.conversation_id', 'cnv_id'); + Http::preventStrayRequests(); + Http::fake(['https://api2.frontapp.com/conversations/cnv_id/comments' => Http::response([], 200)]); +}); + +it('adds the log context to the comment', function () { + (new LogHandler(['level' => 'error']))->write(record(context: [ + 'exception' => new Exception('boom'), + 'redis' => ['loading' => 1, 'uptime_in_seconds' => 12], + 'userId' => 7, + ])); + + Http::assertSent(function ($request) { + expect($request['body']) + ->toContain('* redis: {"loading":1,"uptime_in_seconds":12}') + ->toContain('* userId: 7'); + + return true; + }); +}); + +it('leaves the exception out of the rendered context', function () { + (new LogHandler(['level' => 'error']))->write(record(context: ['exception' => new Exception('boom')])); + + Http::assertSent(function ($request) { + expect($request['body']) + ->toContain('**boom**') + ->not->toContain('* exception:'); + + return true; + }); +}); + +function record(array $context): LogRecord +{ + return new LogRecord( + datetime: new DateTimeImmutable, + channel: 'testing', + level: Level::Error, + message: 'boom', + context: $context, + ); +}