Repository: open-telemetry/opentelemetry-php (php)

Issue:
Request to the matrices are send even when no payload
When calling `shutdown` on `MeterProvider` after creating it with purely default providers using `(new MeterProviderFactory())->create()` it will send a request to the matrices endpoint, even though there has been no meters registered.

This can of course be solved in the application by not calling `shutdown` when the class haven't been used, however this is not the same way `TracerProvider` works. 
That one only sends a request if there actually is something to send.


Pull request description:
do not export empty metrics
If no metrics have been generated, do not send an empty export request (except via forceFlush())

Closes #905

Patch:
diff --git a/src/SDK/Metrics/MetricReader/ExportingReader.php b/src/SDK/Metrics/MetricReader/ExportingReader.php
index c56bf6f9..8bc849e4 100644
--- a/src/SDK/Metrics/MetricReader/ExportingReader.php
+++ b/src/SDK/Metrics/MetricReader/ExportingReader.php
@@ -70,6 +70,10 @@ final class ExportingReader implements MetricReaderInterface, MetricSourceRegist
             $metrics[] = $source->collect($timestamp);
         }
 
+        if ($metrics === []) {
+            return true;
+        }
+
         return $this->exporter->export($metrics);
     }
 


Test patch:
diff --git a/tests/Unit/SDK/Metrics/MeterProviderTest.php b/tests/Unit/SDK/Metrics/MeterProviderTest.php
index 7615771f..ac6370aa 100644
--- a/tests/Unit/SDK/Metrics/MeterProviderTest.php
+++ b/tests/Unit/SDK/Metrics/MeterProviderTest.php
@@ -94,7 +94,6 @@ final class MeterProviderTest extends TestCase
             ClockFactory::getDefault(),
             Attributes::factory(),
             new InstrumentationScopeFactory(Attributes::factory()),
-            /** @phpstan-ignore-next-line */
             [$metricReader->reveal()],
             new CriteriaViewRegistry(),
             null,
@@ -123,7 +122,6 @@ final class MeterProviderTest extends TestCase
             ClockFactory::getDefault(),
             Attributes::factory(),
             new InstrumentationScopeFactory(Attributes::factory()),
-            /** @phpstan-ignore-next-line */
             [$metricReader->reveal()],
             new CriteriaViewRegistry(),
             null,
diff --git a/tests/Unit/SDK/Metrics/MetricReader/ExportingReaderTest.php b/tests/Unit/SDK/Metrics/MetricReader/ExportingReaderTest.php
index d6ca5642..12df7211 100644
--- a/tests/Unit/SDK/Metrics/MetricReader/ExportingReaderTest.php
+++ b/tests/Unit/SDK/Metrics/MetricReader/ExportingReaderTest.php
@@ -157,10 +157,43 @@ final class ExportingReaderTest extends TestCase
     public function test_shutdown_calls_exporter_shutdown(): void
     {
         $exporter = $this->createMock(MetricExporterInterface::class);
+        $exporter->expects($this->once())->method('shutdown')->willReturn(true);
+        $clock = new TestClock();
+        $reader = new ExportingReader($exporter, $clock);
+
+        $this->assertTrue($reader->shutdown());
+    }
+
+    public function test_shutdown_does_not_export_empty_metrics(): void
+    {
+        $exporter = $this->createMock(MetricExporterInterface::class);
+        $exporter->expects($this->never())->method('export');
+        $exporter->expects($this->once())->method('shutdown')->willReturn(true);
+
+        $clock = new TestClock();
+        $reader = new ExportingReader($exporter, $clock);
+
+        $reader->shutdown();
+    }
+
+    public function test_shutdown_exports_metrics(): void
+    {
+        $exporter = $this->createMock(MetricExporterInterface::class);
+        $provider = $this->createMock(MetricSourceProviderInterface::class);
+        $source = $this->createMock(MetricSourceInterface::class);
+        $source->method('collect')->willReturn($this->createMock(Metric::class));
+        $provider->method('create')->willReturn($source);
+        $exporter->method('temporality')->willReturn('foo');
         $exporter->expects($this->once())->method('export')->willReturn(true);
         $exporter->expects($this->once())->method('shutdown')->willReturn(true);
+
         $clock = new TestClock();
         $reader = new ExportingReader($exporter, $clock);
+        $reader->add(
+            $provider,
+            $this->createMock(MetricMetadataInterface::class),
+            $this->createMock(StalenessHandlerInterface::class)
+        );
 
         $this->assertTrue($reader->shutdown());
     }
@@ -168,7 +201,6 @@ final class ExportingReaderTest extends TestCase
     public function test_force_flush_calls_exporter_force_flush(): void
     {
         $exporter = $this->createMock(MetricExporterInterface::class);
-        $exporter->expects($this->once())->method('export')->willReturn(true);
         $exporter->expects($this->once())->method('forceFlush')->willReturn(true);
         $clock = new TestClock();
         $reader = new ExportingReader($exporter, $clock);