aboutsummaryrefslogtreecommitdiffstats
path: root/tests/phpunit/includes/libs/telemetry/TelemetryIntegrationTest.php
diff options
context:
space:
mode:
authorMáté Szabó <mszabo@wikimedia.org>2024-08-12 03:18:57 +0200
committerMáté Szabó <mszabo@wikimedia.org>2024-10-09 15:55:31 +0200
commit16ec1a37036a370b0dbf2d3371e03c15a8d3a8cb (patch)
treec52cb09847dec1b462daf151e8b5154cd77320e2 /tests/phpunit/includes/libs/telemetry/TelemetryIntegrationTest.php
parente4df7ad756b09a0050bb0f2d0e0a2fb6f9e2622a (diff)
downloadmediawikicore-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.php141
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()
+ );
+ }
+}