From faca8d5210eafbd35ef986640173fe0f74261ea3 Mon Sep 17 00:00:00 2001 From: Oli Salisbury <12654315+ol1s@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:43:02 +0100 Subject: [PATCH 1/3] Update LogWatcher.php --- .../Laravel/src/Watchers/LogWatcher.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Instrumentation/Laravel/src/Watchers/LogWatcher.php b/src/Instrumentation/Laravel/src/Watchers/LogWatcher.php index 65b5751ac..e74387f50 100644 --- a/src/Instrumentation/Laravel/src/Watchers/LogWatcher.php +++ b/src/Instrumentation/Laravel/src/Watchers/LogWatcher.php @@ -10,6 +10,8 @@ use OpenTelemetry\API\Instrumentation\CachedInstrumentation; use OpenTelemetry\API\Logs\LogRecord; use OpenTelemetry\API\Logs\Severity; +use OpenTelemetry\SDK\Common\Exception\StackTraceFormatter; +use Throwable; use TypeError; class LogWatcher extends Watcher @@ -54,6 +56,7 @@ public function recordLog(MessageLogged $log): void $attributes = [ 'context' => json_encode(array_filter($log->context)), + ...$this->parseExceptionFromContext($log->context), ]; $logger = $this->instrumentation->logger(); @@ -65,4 +68,22 @@ public function recordLog(MessageLogged $log): void $logger->emit($record); } + + private function parseExceptionFromContext(array $context): array + { + if ( + ! isset($context['exception']) || + ! $context['exception'] instanceof Throwable + ) { + return []; + } + + $exception = $context['exception']; + + return [ + 'exception.type' => $exception::class, + 'exception.message' => $exception->getMessage(), + 'exception.stacktrace' => StackTraceFormatter::format($exception), + ]; + } } From 6f4230d393ccd7cd70e63982dfc9f3ad50fce10d Mon Sep 17 00:00:00 2001 From: ol1s <12654315+ol1s@users.noreply.github.com> Date: Thu, 24 Jul 2025 17:26:05 +0100 Subject: [PATCH 2/3] add test coverage --- .../tests/Integration/LaravelInstrumentationTest.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Instrumentation/Laravel/tests/Integration/LaravelInstrumentationTest.php b/src/Instrumentation/Laravel/tests/Integration/LaravelInstrumentationTest.php index a4dfe7c00..b15c6edb7 100644 --- a/src/Instrumentation/Laravel/tests/Integration/LaravelInstrumentationTest.php +++ b/src/Instrumentation/Laravel/tests/Integration/LaravelInstrumentationTest.php @@ -4,6 +4,7 @@ namespace OpenTelemetry\Tests\Contrib\Instrumentation\Laravel\Integration; +use Exception; use Illuminate\Routing\Router; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Http; @@ -106,6 +107,16 @@ public function test_route_span_name_if_not_found(): void $this->assertSame('GET', $span->getName()); } + public function test_records_exception_in_logs(): void + { + $this->router()->get('/exception', fn () => throw new Exception('Test exception')); + $this->call('GET', '/exception'); + $logRecord = $this->storage[0]; + $this->assertEquals(Exception::class, $logRecord->getAttributes()->get(TraceAttributes::EXCEPTION_TYPE)); + $this->assertEquals('Test exception', $logRecord->getAttributes()->get(TraceAttributes::EXCEPTION_MESSAGE)); + $this->assertNotNull($logRecord->getAttributes()->get(TraceAttributes::EXCEPTION_STACKTRACE)); + } + private function router(): Router { /** @psalm-suppress PossiblyNullReference */ From a5d9cc4d942e4cd7c7169ce2aa7d302817c9442d Mon Sep 17 00:00:00 2001 From: ol1s <12654315+ol1s@users.noreply.github.com> Date: Thu, 24 Jul 2025 17:37:06 +0100 Subject: [PATCH 3/3] removes exception from context --- .../Laravel/src/Watchers/LogWatcher.php | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/Instrumentation/Laravel/src/Watchers/LogWatcher.php b/src/Instrumentation/Laravel/src/Watchers/LogWatcher.php index e74387f50..169921235 100644 --- a/src/Instrumentation/Laravel/src/Watchers/LogWatcher.php +++ b/src/Instrumentation/Laravel/src/Watchers/LogWatcher.php @@ -54,9 +54,21 @@ public function recordLog(MessageLogged $log): void // Should this fail, we should continue to emit the LogRecord. } + $contextToEncode = array_filter($log->context); + + $exception = $this->getExceptionFromContext($log->context); + + if ($exception !== null) { + unset($contextToEncode['exception']); + } + $attributes = [ - 'context' => json_encode(array_filter($log->context)), - ...$this->parseExceptionFromContext($log->context), + 'context' => json_encode($contextToEncode), + ...$exception !== null ? [ + 'exception.type' => $exception::class, + 'exception.message' => $exception->getMessage(), + 'exception.stacktrace' => StackTraceFormatter::format($exception), + ] : [], ]; $logger = $this->instrumentation->logger(); @@ -69,21 +81,15 @@ public function recordLog(MessageLogged $log): void $logger->emit($record); } - private function parseExceptionFromContext(array $context): array + private function getExceptionFromContext(array $context): ?Throwable { if ( ! isset($context['exception']) || ! $context['exception'] instanceof Throwable ) { - return []; + return null; } - $exception = $context['exception']; - - return [ - 'exception.type' => $exception::class, - 'exception.message' => $exception->getMessage(), - 'exception.stacktrace' => StackTraceFormatter::format($exception), - ]; + return $context['exception']; } }