diff options
author | Máté Szabó <mszabo@wikimedia.org> | 2024-08-12 03:18:57 +0200 |
---|---|---|
committer | Máté Szabó <mszabo@wikimedia.org> | 2024-10-09 15:55:31 +0200 |
commit | 16ec1a37036a370b0dbf2d3371e03c15a8d3a8cb (patch) | |
tree | c52cb09847dec1b462daf151e8b5154cd77320e2 /tests/phpunit/includes/libs/telemetry/TelemetryIntegrationTest.php | |
parent | e4df7ad756b09a0050bb0f2d0e0a2fb6f9e2622a (diff) | |
download | mediawikicore-16ec1a37036a370b0dbf2d3371e03c15a8d3a8cb.tar.gz mediawikicore-16ec1a37036a370b0dbf2d3371e03c15a8d3a8cb.zip |
Introduce minimal OTEL tracing library
In T340552, the official PHP OpenTelemetry client was effectively
rejected for inclusion in MediaWiki due to its size. Implement a minimal
tracing library instead that eschews conformance with the OTEL client
specification in favor of simplicity, while remaining capable of
emitting trace data in OTLP format and thus retaining compatibility with
any ingestion endpoint capable of handling OTLP.
In its current state, the library supports a basic feature set that
should be sufficient for basic tracing integration:
* Span creation, inclusive span activation and automatic parent span
assignment,
* Span attributes and span kinds,
* Basic resource (process/request)-level metadata generation,
* Data export over OTLP.
Additional functionality, such as trace propagation, can then be
incrementally added to the library.
Bug: T340552
Change-Id: Ibc3910058cd7ed064cad293a3cdc091344e66b86
Diffstat (limited to 'tests/phpunit/includes/libs/telemetry/TelemetryIntegrationTest.php')
-rw-r--r-- | tests/phpunit/includes/libs/telemetry/TelemetryIntegrationTest.php | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/tests/phpunit/includes/libs/telemetry/TelemetryIntegrationTest.php b/tests/phpunit/includes/libs/telemetry/TelemetryIntegrationTest.php new file mode 100644 index 000000000000..d6b9ef3e3861 --- /dev/null +++ b/tests/phpunit/includes/libs/telemetry/TelemetryIntegrationTest.php @@ -0,0 +1,141 @@ +<?php +namespace Wikimedia\Tests\Telemetry; + +use GuzzleHttp\Client; +use GuzzleHttp\Handler\MockHandler; +use GuzzleHttp\Psr7\Response; +use MediaWiki\MainConfigNames; +use MediaWikiIntegrationTestCase; +use Wikimedia\Telemetry\Clock; +use Wikimedia\Telemetry\NoopTracer; +use Wikimedia\Telemetry\SpanInterface; +use Wikimedia\Telemetry\Tracer; +use Wikimedia\Telemetry\TracerState; + +/** + * @covers \Wikimedia\Telemetry\Tracer + * @covers \Wikimedia\Telemetry\OtlpHttpExporter + */ +class TelemetryIntegrationTest extends MediaWikiIntegrationTestCase { + private const EXAMPLE_TRACING_CONFIG = [ + 'serviceName' => 'test-service', + 'samplingProbability' => 100, + 'endpoint' => 'http://198.51.100.42:4318/v1/traces' + ]; + + private MockHandler $handler; + + protected function setUp(): void { + parent::setUp(); + + $this->handler = new MockHandler(); + $this->setService( '_TracerHTTPClient', new Client( [ + 'handler' => $this->handler, + 'http_errors' => false + ] ) ); + } + + protected function tearDown(): void { + parent::tearDown(); + Clock::setMockTime( null ); + TracerState::destroyInstance(); + } + + public function testShouldDoNothingWhenTracingDisabled(): void { + $this->overrideConfigValue( MainConfigNames::OpenTelemetryConfig, null ); + + $tracer = $this->getServiceContainer()->getTracer(); + $span = $tracer->createSpan( 'test' ) + ->start(); + + $span->end(); + + $tracer->shutdown(); + + $this->assertInstanceOf( NoopTracer::class, $tracer ); + $this->assertNull( $this->handler->getLastRequest() ); + } + + public function testShouldNotExportDataWhenNoSpansWereCreated(): void { + $this->overrideConfigValue( MainConfigNames::OpenTelemetryConfig, self::EXAMPLE_TRACING_CONFIG ); + + $tracer = $this->getServiceContainer()->getTracer(); + + $tracer->shutdown(); + + $this->assertInstanceOf( Tracer::class, $tracer ); + $this->assertNull( $this->handler->getLastRequest() ); + } + + public function testShouldNotExportDataWhenTracerWasNotExplicitlyShutdown(): void { + $this->overrideConfigValue( MainConfigNames::OpenTelemetryConfig, self::EXAMPLE_TRACING_CONFIG ); + + $tracer = $this->getServiceContainer()->getTracer(); + $span = $tracer->createRootSpan( 'test' ) + ->start(); + + $span->end(); + + $this->assertInstanceOf( Tracer::class, $tracer ); + $this->assertNull( $this->handler->getLastRequest() ); + } + + public function testShouldExportDataOnShutdownWhenTracingEnabled(): void { + $this->overrideConfigValue( MainConfigNames::OpenTelemetryConfig, self::EXAMPLE_TRACING_CONFIG ); + $this->handler->append( new Response( 200 ) ); + + $mockTime = 5481675965496; + Clock::setMockTime( $mockTime ); + + $tracer = $this->getServiceContainer()->getTracer(); + $span = $tracer->createRootSpan( 'test' ) + ->setSpanKind( SpanInterface::SPAN_KIND_SERVER ) + ->start(); + + $span->activate(); + + $mockTime += 100; + Clock::setMockTime( $mockTime ); + + $childSpan = $tracer->createSpan( 'child' ) + ->setAttributes( [ 'some-key' => 'test', 'ignored' => new \stdClass() ] ) + ->start(); + + $mockTime += 250; + Clock::setMockTime( $mockTime ); + + $childSpan->end(); + + $mockTime += 74; + Clock::setMockTime( $mockTime ); + + $span->end(); + + $this->assertNull( + $this->handler->getLastRequest(), + 'Exporting trace data should be deferred until the tracer is explicitly shut down' + ); + + $tracer->shutdown(); + + $request = $this->handler->getLastRequest(); + + $this->assertInstanceOf( Tracer::class, $tracer ); + $this->assertSame( 'http://198.51.100.42:4318/v1/traces', (string)$request->getUri() ); + $this->assertSame( 'application/json', $request->getHeaderLine( 'Content-Type' ) ); + + $expected = file_get_contents( __DIR__ . '/expected-trace-data.json' ); + + $expected = strtr( $expected, [ + '<TRACE-ID>' => $span->getContext()->getTraceId(), + '<SPAN-1-ID>' => $span->getContext()->getSpanId(), + '<SPAN-2-ID>' => $childSpan->getContext()->getSpanId(), + '<HOST-NAME>' => wfHostname() + ] ); + + $this->assertJsonStringEqualsJsonString( + $expected, + (string)$request->getBody() + ); + } +} |