From 3d639c1c6cf733bd5eaa1fb346ac05f9d3f5580a Mon Sep 17 00:00:00 2001 From: Hendrik Heil Date: Fri, 30 Aug 2024 00:51:18 +0200 Subject: [PATCH 1/3] feat(laravel-auto-instrumentation): use logger provider in logwatcher (#286) * feat(laravel-auto-instrumentation): use logger provider in logwatcher * style: correctly order import statements * test: update test offsets for logging storage --- .../Illuminate/Foundation/Application.php | 2 +- src/Watchers/LogWatcher.php | 26 ++++++++------ .../LaravelInstrumentationTest.php | 25 ++++++++----- tests/Integration/Queue/QueueTest.php | 35 ++++++++++++------- tests/Integration/TestCase.php | 19 ++++++++-- 5 files changed, 71 insertions(+), 36 deletions(-) diff --git a/src/Hooks/Illuminate/Foundation/Application.php b/src/Hooks/Illuminate/Foundation/Application.php index f76b298..ebae628 100644 --- a/src/Hooks/Illuminate/Foundation/Application.php +++ b/src/Hooks/Illuminate/Foundation/Application.php @@ -30,7 +30,7 @@ public function instrument(): void $this->registerWatchers($application, new CacheWatcher()); $this->registerWatchers($application, new ClientRequestWatcher($this->instrumentation)); $this->registerWatchers($application, new ExceptionWatcher()); - $this->registerWatchers($application, new LogWatcher()); + $this->registerWatchers($application, new LogWatcher($this->instrumentation)); $this->registerWatchers($application, new QueryWatcher($this->instrumentation)); }, ); diff --git a/src/Watchers/LogWatcher.php b/src/Watchers/LogWatcher.php index f887da8..a5163c6 100644 --- a/src/Watchers/LogWatcher.php +++ b/src/Watchers/LogWatcher.php @@ -6,11 +6,17 @@ use Illuminate\Contracts\Foundation\Application; use Illuminate\Log\Events\MessageLogged; -use OpenTelemetry\API\Trace\Span; -use OpenTelemetry\Context\Context; +use OpenTelemetry\API\Instrumentation\CachedInstrumentation; +use OpenTelemetry\API\Logs\LogRecord; +use OpenTelemetry\API\Logs\Map\Psr3; class LogWatcher extends Watcher { + public function __construct( + private CachedInstrumentation $instrumentation, + ) { + } + /** @psalm-suppress UndefinedInterfaceMethod */ public function register(Application $app): void { @@ -24,18 +30,16 @@ public function register(Application $app): void public function recordLog(MessageLogged $log): void { $attributes = [ - 'level' => $log->level, + 'context' => json_encode(array_filter($log->context)), ]; - $attributes['context'] = json_encode(array_filter($log->context)); + $logger = $this->instrumentation->logger(); - $message = $log->message; + $record = (new LogRecord($log->message)) + ->setSeverityText($log->level) + ->setSeverityNumber(Psr3::severityNumber($log->level)) + ->setAttributes($attributes); - $scope = Context::storage()->scope(); - if (!$scope) { - return; - } - $span = Span::fromContext($scope->context()); - $span->addEvent($message, $attributes); + $logger->emit($record); } } diff --git a/tests/Integration/LaravelInstrumentationTest.php b/tests/Integration/LaravelInstrumentationTest.php index 5dd520b..c5096ff 100644 --- a/tests/Integration/LaravelInstrumentationTest.php +++ b/tests/Integration/LaravelInstrumentationTest.php @@ -34,7 +34,7 @@ public function test_cache_log_db(): void $this->router()->get('/hello', function () { $text = 'Hello Cruel World'; cache()->forever('opentelemetry', 'opentelemetry'); - Log::info('Log info'); + Log::info('Log info', ['test' => true]); cache()->get('opentelemetry.io', 'php'); cache()->get('opentelemetry', 'php'); cache()->forget('opentelemetry'); @@ -46,23 +46,30 @@ public function test_cache_log_db(): void $this->assertCount(0, $this->storage); $response = $this->call('GET', '/hello'); $this->assertEquals(200, $response->status()); - $this->assertCount(2, $this->storage); - $span = $this->storage[1]; + $this->assertCount(3, $this->storage); + $span = $this->storage[2]; $this->assertSame('GET /hello', $span->getName()); $this->assertSame('http://localhost/hello', $span->getAttributes()->get(TraceAttributes::URL_FULL)); - $this->assertCount(5, $span->getEvents()); + $this->assertCount(4, $span->getEvents()); $this->assertSame('cache set', $span->getEvents()[0]->getName()); - $this->assertSame('Log info', $span->getEvents()[1]->getName()); - $this->assertSame('cache miss', $span->getEvents()[2]->getName()); - $this->assertSame('cache hit', $span->getEvents()[3]->getName()); - $this->assertSame('cache forget', $span->getEvents()[4]->getName()); + $this->assertSame('cache miss', $span->getEvents()[1]->getName()); + $this->assertSame('cache hit', $span->getEvents()[2]->getName()); + $this->assertSame('cache forget', $span->getEvents()[3]->getName()); - $span = $this->storage[0]; + $span = $this->storage[1]; $this->assertSame('sql SELECT', $span->getName()); $this->assertSame('SELECT', $span->getAttributes()->get('db.operation')); $this->assertSame(':memory:', $span->getAttributes()->get('db.name')); $this->assertSame('select 1', $span->getAttributes()->get('db.statement')); $this->assertSame('sqlite', $span->getAttributes()->get('db.system')); + + /** @var \OpenTelemetry\SDK\Logs\ReadWriteLogRecord $logRecord */ + $logRecord = $this->storage[0]; + $this->assertSame('Log info', $logRecord->getBody()); + $this->assertSame('info', $logRecord->getSeverityText()); + $this->assertSame(9, $logRecord->getSeverityNumber()); + $this->assertArrayHasKey('context', $logRecord->getAttributes()->toArray()); + $this->assertSame(json_encode(['test' => true]), $logRecord->getAttributes()->toArray()['context']); } public function test_low_cardinality_route_span_name(): void diff --git a/tests/Integration/Queue/QueueTest.php b/tests/Integration/Queue/QueueTest.php index 5ad620e..1d902e0 100644 --- a/tests/Integration/Queue/QueueTest.php +++ b/tests/Integration/Queue/QueueTest.php @@ -38,11 +38,15 @@ public function test_it_handles_pushing_to_a_queue(): void $logger->info('Logged from closure'); }); - $this->assertEquals('sync process', $this->storage[0]->getName()); - $this->assertEquals('Task: A', $this->storage[0]->getEvents()[0]->getName()); - + /** @var \OpenTelemetry\SDK\Logs\ReadWriteLogRecord $logRecord0 */ + $logRecord0 = $this->storage[0]; + $this->assertEquals('Task: A', $logRecord0->getBody()); $this->assertEquals('sync process', $this->storage[1]->getName()); - $this->assertEquals('Logged from closure', $this->storage[1]->getEvents()[0]->getName()); + + /** @var \OpenTelemetry\SDK\Logs\ReadWriteLogRecord $logRecord2 */ + $logRecord2 = $this->storage[2]; + $this->assertEquals('Logged from closure', $logRecord2->getBody()); + $this->assertEquals('sync process', $this->storage[3]->getName()); } public function test_it_can_push_a_message_with_a_delay(): void @@ -51,19 +55,19 @@ public function test_it_can_push_a_message_with_a_delay(): void $this->queue->later(new DateInterval('PT10M'), new DummyJob('DateInterval')); $this->queue->later(new DateTimeImmutable('2024-04-15 22:29:00.123Z'), new DummyJob('DateTime')); - $this->assertEquals('sync create', $this->storage[1]->getName()); + $this->assertEquals('sync create', $this->storage[2]->getName()); $this->assertIsInt( - $this->storage[1]->getAttributes()->get('messaging.message.delivery_timestamp'), + $this->storage[2]->getAttributes()->get('messaging.message.delivery_timestamp'), ); - $this->assertEquals('sync create', $this->storage[3]->getName()); + $this->assertEquals('sync create', $this->storage[5]->getName()); $this->assertIsInt( - $this->storage[3]->getAttributes()->get('messaging.message.delivery_timestamp'), + $this->storage[5]->getAttributes()->get('messaging.message.delivery_timestamp'), ); - $this->assertEquals('sync create', $this->storage[5]->getName()); + $this->assertEquals('sync create', $this->storage[8]->getName()); $this->assertIsInt( - $this->storage[5]->getAttributes()->get('messaging.message.delivery_timestamp'), + $this->storage[8]->getAttributes()->get('messaging.message.delivery_timestamp'), ); } @@ -141,9 +145,14 @@ public function test_it_drops_empty_receives(): void } /** @psalm-suppress PossiblyInvalidMethodCall */ - $this->assertEquals(102, $this->storage->count()); + $this->assertEquals(204, $this->storage->count()); + + /** @var \OpenTelemetry\SDK\Logs\ReadWriteLogRecord $logRecord100 */ + $logRecord100 = $this->storage[100]; + $this->assertEquals('Task: 500', $logRecord100->getBody()); - $this->assertEquals('Task: 500', $this->storage[50]->getEvents()[0]->getName()); - $this->assertEquals('Task: More work', $this->storage[100]->getEvents()[0]->getName()); + /** @var \OpenTelemetry\SDK\Logs\ReadWriteLogRecord $logRecord200 */ + $logRecord200 = $this->storage[200]; + $this->assertEquals('Task: More work', $logRecord200->getBody()); } } diff --git a/tests/Integration/TestCase.php b/tests/Integration/TestCase.php index 48be746..56c1abb 100644 --- a/tests/Integration/TestCase.php +++ b/tests/Integration/TestCase.php @@ -7,8 +7,13 @@ use ArrayObject; use OpenTelemetry\API\Instrumentation\Configurator; use OpenTelemetry\Context\ScopeInterface; +use OpenTelemetry\SDK\Common\Attribute\Attributes; +use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScopeFactory; +use OpenTelemetry\SDK\Logs\Exporter\InMemoryExporter as LogInMemoryExporter; +use OpenTelemetry\SDK\Logs\LoggerProvider; +use OpenTelemetry\SDK\Logs\Processor\SimpleLogRecordProcessor; use OpenTelemetry\SDK\Trace\ImmutableSpan; -use OpenTelemetry\SDK\Trace\SpanExporter\InMemoryExporter; +use OpenTelemetry\SDK\Trace\SpanExporter\InMemoryExporter as SpanInMemoryExporter; use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor; use OpenTelemetry\SDK\Trace\TracerProvider; use Orchestra\Testbench\TestCase as BaseTestCase; @@ -18,7 +23,9 @@ abstract class TestCase extends BaseTestCase protected ScopeInterface $scope; /** @var ArrayObject|ImmutableSpan[] $storage */ protected ArrayObject $storage; + protected ArrayObject $loggerStorage; protected TracerProvider $tracerProvider; + protected LoggerProvider $loggerProvider; public function setUp(): void { @@ -27,12 +34,20 @@ public function setUp(): void $this->storage = new ArrayObject(); $this->tracerProvider = new TracerProvider( new SimpleSpanProcessor( - new InMemoryExporter($this->storage), + new SpanInMemoryExporter($this->storage), ), ); + $this->loggerProvider = new LoggerProvider( + new SimpleLogRecordProcessor( + new LogInMemoryExporter($this->storage), + ), + new InstrumentationScopeFactory(Attributes::factory()) + ); + $this->scope = Configurator::create() ->withTracerProvider($this->tracerProvider) + ->withLoggerProvider($this->loggerProvider) ->activate(); } From d702db45fc4203a81d10ea5fd0a30b7d677122cd Mon Sep 17 00:00:00 2001 From: Alec Sammon Date: Wed, 4 Sep 2024 13:09:50 +0100 Subject: [PATCH 2/3] Fix span status for 4XX responses (#290) --- src/Hooks/Illuminate/Contracts/Http/Kernel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Hooks/Illuminate/Contracts/Http/Kernel.php b/src/Hooks/Illuminate/Contracts/Http/Kernel.php index e9e3535..b5415cc 100644 --- a/src/Hooks/Illuminate/Contracts/Http/Kernel.php +++ b/src/Hooks/Illuminate/Contracts/Http/Kernel.php @@ -91,7 +91,7 @@ protected function hookHandle(): bool } if ($response) { - if ($response->getStatusCode() >= 400) { + if ($response->getStatusCode() >= 500) { $span->setStatus(StatusCode::STATUS_ERROR); } $span->setAttribute(TraceAttributes::HTTP_RESPONSE_STATUS_CODE, $response->getStatusCode()); From 785e7978b407766a97747fc1f5bba5dfe4bf1b33 Mon Sep 17 00:00:00 2001 From: MilesChou Date: Thu, 5 Sep 2024 06:36:27 +0800 Subject: [PATCH 3/3] Fix: ignore redirection error (#291) --- src/Watchers/ClientRequestWatcher.php | 6 ++++++ tests/Integration/Http/ClientTest.php | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/Watchers/ClientRequestWatcher.php b/src/Watchers/ClientRequestWatcher.php index 855c9f5..cc6b3d5 100644 --- a/src/Watchers/ClientRequestWatcher.php +++ b/src/Watchers/ClientRequestWatcher.php @@ -112,6 +112,12 @@ private function maybeRecordError(SpanInterface $span, Response $response): void return; } + // HTTP status code 3xx is not really error + // See https://www.rfc-editor.org/rfc/rfc9110.html#name-redirection-3xx + if ($response->redirect()) { + return; + } + $span->setStatus( StatusCode::STATUS_ERROR, HttpResponse::$statusTexts[$response->status()] ?? (string) $response->status() diff --git a/tests/Integration/Http/ClientTest.php b/tests/Integration/Http/ClientTest.php index 268379c..1fd528a 100644 --- a/tests/Integration/Http/ClientTest.php +++ b/tests/Integration/Http/ClientTest.php @@ -20,6 +20,7 @@ public function test_it_records_requests(): void Http::fake([ 'ok.opentelemetry.io/*' => Http::response(status: 201), 'missing.opentelemetry.io' => Http::response(status: 404), + 'redirect.opentelemetry.io' => Http::response(status: 302), ]); $response = Http::get('missing.opentelemetry.io'); @@ -27,12 +28,21 @@ public function test_it_records_requests(): void self::assertEquals(404, $response->status()); self::assertEquals('GET', $span->getName()); self::assertEquals('missing.opentelemetry.io', $span->getAttributes()->get(TraceAttributes::URL_PATH)); + self::assertEquals(StatusCode::STATUS_ERROR, $span->getStatus()->getCode()); $response = Http::post('ok.opentelemetry.io/foo?param=bar'); $span = $this->storage[1]; self::assertEquals(201, $response->status()); self::assertEquals('POST', $span->getName()); self::assertEquals('ok.opentelemetry.io/foo', $span->getAttributes()->get(TraceAttributes::URL_PATH)); + self::assertEquals(StatusCode::STATUS_UNSET, $span->getStatus()->getCode()); + + $response = Http::get('redirect.opentelemetry.io'); + $span = $this->storage[2]; + self::assertEquals(302, $response->status()); + self::assertEquals('GET', $span->getName()); + self::assertEquals('redirect.opentelemetry.io', $span->getAttributes()->get(TraceAttributes::URL_PATH)); + self::assertEquals(StatusCode::STATUS_UNSET, $span->getStatus()->getCode()); } public function test_it_records_connection_failures(): void pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy