Repository: open-telemetry/opentelemetry-php
Pull request #1559: Refactor InMemory exporters to use shared storage manager
Author: login: GrzegorzDrozd
Created: 2025-04-08T22:10:10Z | Merged: 2025-04-15T07:02:07Z

Introduced `InMemoryStorageManager` to centralize storage management for spans, metrics, and logs. Updated InMemory exporters and their factory classes to utilize this shared storage.


Diff:
diff --git a/src/SDK/Common/Export/InMemoryStorageManager.php b/src/SDK/Common/Export/InMemoryStorageManager.php
new file mode 100644
index 000000000..23f310666
--- /dev/null
+++ b/src/SDK/Common/Export/InMemoryStorageManager.php
@@ -0,0 +1,32 @@
+<?php
+
+declare(strict_types=1);
+
+namespace OpenTelemetry\SDK\Common\Export;
+
+use ArrayObject;
+
+class InMemoryStorageManager
+{
+    private static ArrayObject $spans;
+    private static ArrayObject $metrics;
+    private static ArrayObject $logs;
+
+    public static function metrics(): ArrayObject
+    {
+        /** @psalm-suppress RedundantPropertyInitializationCheck */
+        return self::$metrics ??= new ArrayObject();
+    }
+
+    public static function logs(): ArrayObject
+    {
+        /** @psalm-suppress RedundantPropertyInitializationCheck */
+        return self::$logs ??= new ArrayObject();
+    }
+
+    public static function spans(): ArrayObject
+    {
+        /** @psalm-suppress RedundantPropertyInitializationCheck */
+        return self::$spans ??= new ArrayObject();
+    }
+}
diff --git a/src/SDK/Logs/Exporter/InMemoryExporterFactory.php b/src/SDK/Logs/Exporter/InMemoryExporterFactory.php
index 6f24defe0..b61a339ce 100644
--- a/src/SDK/Logs/Exporter/InMemoryExporterFactory.php
+++ b/src/SDK/Logs/Exporter/InMemoryExporterFactory.php
@@ -4,6 +4,7 @@
 
 namespace OpenTelemetry\SDK\Logs\Exporter;
 
+use OpenTelemetry\SDK\Common\Export\InMemoryStorageManager;
 use OpenTelemetry\SDK\Logs\LogRecordExporterFactoryInterface;
 use OpenTelemetry\SDK\Logs\LogRecordExporterInterface;
 
@@ -11,6 +12,6 @@ class InMemoryExporterFactory implements LogRecordExporterFactoryInterface
 {
     public function create(): LogRecordExporterInterface
     {
-        return new InMemoryExporter();
+        return new InMemoryExporter(InMemoryStorageManager::logs());
     }
 }
diff --git a/src/SDK/Metrics/MetricExporter/InMemoryExporter.php b/src/SDK/Metrics/MetricExporter/InMemoryExporter.php
index e0d7da4ec..6feaa5b79 100644
--- a/src/SDK/Metrics/MetricExporter/InMemoryExporter.php
+++ b/src/SDK/Metrics/MetricExporter/InMemoryExporter.php
@@ -4,27 +4,30 @@
 
 namespace OpenTelemetry\SDK\Metrics\MetricExporter;
 
-use function array_push;
+use ArrayObject;
 use OpenTelemetry\SDK\Metrics\AggregationTemporalitySelectorInterface;
 use OpenTelemetry\SDK\Metrics\Data\Metric;
 use OpenTelemetry\SDK\Metrics\Data\Temporality;
 use OpenTelemetry\SDK\Metrics\MetricExporterInterface;
 use OpenTelemetry\SDK\Metrics\MetricMetadataInterface;
+use OpenTelemetry\SDK\Metrics\PushMetricExporterInterface;
 
 /**
  * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk_exporters/in-memory.md
  */
-final class InMemoryExporter implements MetricExporterInterface, AggregationTemporalitySelectorInterface
+final class InMemoryExporter implements MetricExporterInterface, AggregationTemporalitySelectorInterface, PushMetricExporterInterface
 {
-    /**
-     * @var list<Metric>
-     */
-    private array $metrics = [];
-
     private bool $closed = false;
 
-    public function __construct(private readonly string|Temporality|null $temporality = null)
-    {
+    /**
+     * @template-implements ArrayObject<Metric> $storage
+     * @param ArrayObject $storage
+     * @param string|Temporality|null $temporality
+     */
+    public function __construct(
+        private ArrayObject $storage = new ArrayObject(),
+        private readonly string|Temporality|null $temporality = null,
+    ) {
     }
 
     public function temporality(MetricMetadataInterface $metric): string|Temporality|null
@@ -33,13 +36,13 @@ public function temporality(MetricMetadataInterface $metric): string|Temporality
     }
 
     /**
-     * @return list<Metric>
+     * @return Metric[]
      */
     public function collect(bool $reset = false): array
     {
-        $metrics = $this->metrics;
+        $metrics = $this->storage->getArrayCopy();
         if ($reset) {
-            $this->metrics = [];
+            $this->storage = new ArrayObject();
         }
 
         return $metrics;
@@ -51,8 +54,9 @@ public function export(iterable $batch): bool
             return false;
         }
 
-        /** @psalm-suppress InvalidPropertyAssignmentValue */
-        array_push($this->metrics, ...$batch);
+        foreach ($batch as $metric) {
+            $this->storage->append($metric);
+        }
 
         return true;
     }
@@ -67,4 +71,9 @@ public function shutdown(): bool
 
         return true;
     }
+
+    public function forceFlush(): bool
+    {
+        return true;
+    }
 }
diff --git a/src/SDK/Metrics/MetricExporter/InMemoryExporterFactory.php b/src/SDK/Metrics/MetricExporter/InMemoryExporterFactory.php
index c72c7b169..d2cea7298 100644
--- a/src/SDK/Metrics/MetricExporter/InMemoryExporterFactory.php
+++ b/src/SDK/Metrics/MetricExporter/InMemoryExporterFactory.php
@@ -4,6 +4,7 @@
 
 namespace OpenTelemetry\SDK\Metrics\MetricExporter;
 
+use OpenTelemetry\SDK\Common\Export\InMemoryStorageManager;
 use OpenTelemetry\SDK\Metrics\MetricExporterFactoryInterface;
 use OpenTelemetry\SDK\Metrics\MetricExporterInterface;
 
@@ -11,6 +12,6 @@ class InMemoryExporterFactory implements MetricExporterFactoryInterface
 {
     public function create(): MetricExporterInterface
     {
-        return new InMemoryExporter();
+        return new InMemoryExporter(InMemoryStorageManager::metrics());
     }
 }
diff --git a/src/SDK/Trace/SpanExporter/InMemorySpanExporterFactory.php b/src/SDK/Trace/SpanExporter/InMemorySpanExporterFactory.php
index c19686fac..d47398901 100644
--- a/src/SDK/Trace/SpanExporter/InMemorySpanExporterFactory.php
+++ b/src/SDK/Trace/SpanExporter/InMemorySpanExporterFactory.php
@@ -4,12 +4,13 @@
 
 namespace OpenTelemetry\SDK\Trace\SpanExporter;
 
+use OpenTelemetry\SDK\Common\Export\InMemoryStorageManager;
 use OpenTelemetry\SDK\Trace\SpanExporterInterface;
 
 class InMemorySpanExporterFactory implements SpanExporterFactoryInterface
 {
     public function create(): SpanExporterInterface
     {
-        return new InMemoryExporter();
+        return new InMemoryExporter(InMemoryStorageManager::spans());
     }
 }
diff --git a/tests/Integration/SDK/Common/InMemoryStorageManagerWithSKDAutoload.php b/tests/Integration/SDK/Common/InMemoryStorageManagerWithSKDAutoload.php
new file mode 100644
index 000000000..8828b6936
--- /dev/null
+++ b/tests/Integration/SDK/Common/InMemoryStorageManagerWithSKDAutoload.php
@@ -0,0 +1,95 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Integration\SDK\Common;
+
+use OpenTelemetry\API\Globals;
+use OpenTelemetry\API\LoggerHolder;
+use OpenTelemetry\API\Logs\LogRecord;
+use OpenTelemetry\SDK\Common\Configuration\Variables;
+use OpenTelemetry\SDK\Common\Export\InMemoryStorageManager;
+use OpenTelemetry\SDK\Logs\LoggerProvider;
+use OpenTelemetry\SDK\Metrics\MeterProvider;
+use OpenTelemetry\SDK\SdkAutoloader;
+use OpenTelemetry\SDK\Trace\TracerProvider;
+use OpenTelemetry\Tests\TestState;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+
+class InMemoryStorageManagerWithSKDAutoload extends TestCase
+{
+    use TestState;
+
+    protected LoggerInterface&MockObject $logger;
+
+    public function setUp(): void
+    {
+        $this->logger = $this->createMock(LoggerInterface::class);
+        LoggerHolder::set($this->logger);
+    }
+
+    public function test_in_memory_storage_manager_for_metrics_with_sdk_autoload_enabled(): void
+    {
+        $this->setEnvironmentVariable('OTEL_METRICS_EXPORTER', 'memory');
+        $this->setEnvironmentVariable('OTEL_METRICS_EXEMPLAR_FILTER', 'all');
+        $this->setEnvironmentVariable('OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE', 'cumulative');
+        $this->setEnvironmentVariable(Variables::OTEL_PHP_AUTOLOAD_ENABLED, 'true');
+        SdkAutoloader::autoload();
+
+        $meterProvider = Globals::meterProvider();
+
+        $m = $meterProvider->getMeter('some_meter');
+        $counter = $m->createCounter('test_value');
+        $counter->add(1);
+        $counter->add(5);
+
+        if ($meterProvider instanceof MeterProvider) {
+            $meterProvider->forceFlush();
+        }
+
+        $storage = InMemoryStorageManager::metrics();
+        $this->assertEquals(1, $storage->count());
+        $this->assertEquals('test_value', $storage[0]->name);
+        $this->assertEquals(6, $storage[0]->data->dataPoints[0]->value);
+    }
+
+    public function test_in_memory_storage_manager_for_logs_with_sdk_autoload_enabled(): void
+    {
+        $this->setEnvironmentVariable('OTEL_LOGS_EXPORTER', 'memory');
+        $this->setEnvironmentVariable(Variables::OTEL_PHP_AUTOLOAD_ENABLED, 'true');
+        SdkAutoloader::autoload();
+
+        $loggerProvider = Globals::loggerProvider();
+        $log = new LogRecord('some body');
+        $loggerProvider->getLogger('test_logger')->emit($log);
+        if ($loggerProvider instanceof LoggerProvider) {
+            $loggerProvider->forceFlush();
+        }
+
+        $storage = InMemoryStorageManager::logs();
+        $this->assertEquals(1, $storage->count());
+        $this->assertEquals('some body', $storage[0]->getBody());
+    }
+
+    public function test_in_memory_storage_manager_for_traces_with_sdk_autoload_enabled(): void
+    {
+        $this->setEnvironmentVariable('OTEL_TRACES_EXPORTER', 'memory');
+        $this->setEnvironmentVariable(Variables::OTEL_PHP_AUTOLOAD_ENABLED, 'true');
+        SdkAutoloader::autoload();
+
+        $tracerProvider = Globals::tracerProvider();
+        $tracer = $tracerProvider->getTracer('test_tracer');
+        $tracer->spanBuilder('test_span_1')->startSpan()->end();
+        $tracer->spanBuilder('test_span_2')->startSpan()->end();
+        if ($tracerProvider instanceof TracerProvider) {
+            $tracerProvider->forceFlush();
+        }
+
+        $storage = InMemoryStorageManager::spans();
+        $this->assertEquals(2, $storage->count());
+        $this->assertEquals('test_span_1', $storage[0]->getName());
+        $this->assertEquals('test_span_2', $storage[1]->getName());
+    }
+}
diff --git a/tests/Integration/SDK/Metrics/MeterConfigTest.php b/tests/Integration/SDK/Metrics/MeterConfigTest.php
index 439b9fea7..d744f5031 100644
--- a/tests/Integration/SDK/Metrics/MeterConfigTest.php
+++ b/tests/Integration/SDK/Metrics/MeterConfigTest.php
@@ -105,7 +105,7 @@ public function test_streams_recreated_on_enable(): void
         $clock = new TestClock(self::T0);
         $disabledConfigurator = Configurator::meter()
             ->with(static fn (MeterConfig $config) => $config->setDisabled(true), name: '*');
-        $exporter = new InMemoryExporter(Temporality::CUMULATIVE);
+        $exporter = new InMemoryExporter(temporality: Temporality::CUMULATIVE);
         $reader = new ExportingReader($exporter);
         $meterProvider = MeterProvider::builder()
             ->addReader($reader)
diff --git a/tests/Unit/SDK/Common/Export/InMemoryStorageManagerTest.php b/tests/Unit/SDK/Common/Export/InMemoryStorageManagerTest.php
new file mode 100644
index 000000000..60ba1d1de
--- /dev/null
+++ b/tests/Unit/SDK/Common/Export/InMemoryStorageManagerTest.php
@@ -0,0 +1,53 @@
+<?php
+
+declare(strict_types=1);
+
+namespace OpenTelemetry\Tests\Unit\SDK\Common\Export;
+
+use ArrayObject;
+use OpenTelemetry\SDK\Common\Export\InMemoryStorageManager;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\DataProvider;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Test class for InMemoryStorageManager.
+ *
+ * This class focuses on testing the `getStorageForMetrics` method.
+ */
+#[CoversClass(\OpenTelemetry\SDK\Common\Export\InMemoryStorageManager::class)]
+class InMemoryStorageManagerTest extends TestCase
+{
+    public static function getStorageName(): array
+    {
+        return [
+            ['metrics'],
+            ['logs'],
+            ['spans'],
+        ];
+    }
+
+    public function test_get_storage_for_metrics_returns_same_instance(): void
+    {
+        $storageFirstCall = InMemoryStorageManager::metrics();
+        $storageSecondCall = InMemoryStorageManager::metrics();
+        $this->assertSame($storageFirstCall, $storageSecondCall);
+    }
+
+    #[DataProvider('getStorageName')]
+    public function test_get_storage_for_metrics_operates_properly($method): void
+    {
+        /** @var ArrayObject $storage */
+        $storage = call_user_func(InMemoryStorageManager::class . '::' . $method);
+        $storage->append('test_metric');
+
+        $this->assertCount(1, $storage);
+        $this->assertEquals('test_metric', $storage[0]);
+
+        // test the similar when getting storage again
+        /** @var ArrayObject $storage */
+        $storage = call_user_func(InMemoryStorageManager::class . '::' . $method);
+        $this->assertCount(1, $storage);
+        $this->assertEquals('test_metric', $storage[0]);
+    }
+}
diff --git a/tests/Unit/SDK/Metrics/MetricReader/ExportingReaderTest.php b/tests/Unit/SDK/Metrics/MetricReader/ExportingReaderTest.php
index d9bb3e05a..dfe9a218f 100644
--- a/tests/Unit/SDK/Metrics/MetricReader/ExportingReaderTest.php
+++ b/tests/Unit/SDK/Metrics/MetricReader/ExportingReaderTest.php
@@ -75,7 +75,7 @@ public function test_default_aggregation_returns_exporter_aggregation_if_default
 
     public function test_add_creates_metric_source_with_exporter_temporality(): void
     {
-        $exporter = new InMemoryExporter(Temporality::CUMULATIVE);
+        $exporter = new InMemoryExporter(temporality: Temporality::CUMULATIVE);
         $reader = new ExportingReader($exporter);
 
         $provider = $this->createMock(MetricSourceProviderInterface::class);
@@ -103,7 +103,7 @@ public function test_add_does_not_create_metric_source_if_exporter_temporality_n
 
     public function test_add_does_not_create_metric_source_if_reader_closed(): void
     {
-        $exporter = new InMemoryExporter(Temporality::CUMULATIVE);
+        $exporter = new InMemoryExporter(temporality: Temporality::CUMULATIVE);
         $reader = new ExportingReader($exporter);
 
         $provider = $this->createMock(MetricSourceProviderInterface::class);
@@ -118,7 +118,7 @@ public function test_add_does_not_create_metric_source_if_reader_closed(): void
 
     public function test_staleness_handler_clears_source(): void
     {
-        $exporter = new InMemoryExporter(Temporality::CUMULATIVE);
+        $exporter = new InMemoryExporter(temporality: Temporality::CUMULATIVE);
         $reader = new ExportingReader($exporter);
 
         $provider = $this->createMock(MetricSourceProviderInterface::class);
@@ -134,7 +134,7 @@ public function test_staleness_handler_clears_source(): void
 
     public function test_collect_collects_sources_with_current_timestamp(): void
     {
-        $exporter = new InMemoryExporter(Temporality::CUMULATIVE);
+        $exporter = new InMemoryExporter(temporality: Temporality::CUMULATIVE);
         $reader = new ExportingReader($exporter);
 
         $metric = new Metric(


Reviews:
user: login: brettmc
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T13:11:30Z
user: login: brettmc
body: This looks great! A couple of minor changes and I think we're good to go.
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T00:28:46Z
user: login: brettmc
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-15T06:59:58Z

Review comments:
diff_hunk: @@ -0,0 +1,96 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Integration\SDK\Common;
+
+use OpenTelemetry\API\Globals;
+use OpenTelemetry\API\LoggerHolder;
+use OpenTelemetry\API\Logs\LogRecord;
+use OpenTelemetry\SDK\Common\Configuration\Variables;
+use OpenTelemetry\SDK\Common\Export\InMemoryStorageManager;
+use OpenTelemetry\SDK\Logs\LoggerProvider;
+use OpenTelemetry\SDK\Metrics\MeterProvider;
+use OpenTelemetry\SDK\SdkAutoloader;
+use OpenTelemetry\SDK\Trace\TracerProvider;
+use OpenTelemetry\Tests\TestState;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+
+class InMemoryStorageManagerWithSKDAutoload extends TestCase
+{
+    use TestState;
+
+    protected LoggerInterface&MockObject $logger;
+
+    public function setUp(): void
+    {
+        $this->logger = $this->createMock(LoggerInterface::class);
+        LoggerHolder::set($this->logger);
+        Globals::reset();
path: tests/Integration/SDK/Common/InMemoryStorageManagerWithSKDAutoload.php
user: login: brettmc
body: this is called in `TestState::tearDown`, so it should work with this line removed...I think :)
created_at: 2025-04-14T00:27:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,32 @@
+<?php
+
+declare(strict_types=1);
+
+namespace OpenTelemetry\SDK\Common\Export;
+
+use ArrayObject;
+
+class InMemoryStorageManager
+{
+    private static ArrayObject $spans;
+    private static ArrayObject $metrics;
+    private static ArrayObject $logs;
+
+    public static function getStorageForMetrics(): ArrayObject
path: src/SDK/Common/Export/InMemoryStorageManager.php
user: login: brettmc
body: Stylistic change: I think these method names could be shortened to `metrics`, `logs`, and `spans`
created_at: 2025-04-14T00:28:20Z
author_association: CONTRIBUTOR

PR comments:
user: login: codecov[bot]
created_at: 2025-04-08T22:12:15Z
body: ## [Codecov](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) Report
Attention: Patch coverage is `87.50000%` with `2 lines` in your changes missing coverage. Please review.
> Project coverage is 70.81%. Comparing base [(`fadec4c`)](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/commit/fadec4c8da016b9f0ac1145817797e1c7e628221?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) to head [(`038bc8f`)](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/commit/038bc8f786230ca54319901a613714e19938f58c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry).
> Report is 3 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) | Patch % | Lines |
|---|---|---|
| [...rc/SDK/Metrics/MetricExporter/InMemoryExporter.php](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559?src=pr&el=tree&filepath=src%2FSDK%2FMetrics%2FMetricExporter%2FInMemoryExporter.php&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-c3JjL1NESy9NZXRyaWNzL01ldHJpY0V4cG9ydGVyL0luTWVtb3J5RXhwb3J0ZXIucGhw) | 71.42% | [2 Missing :warning: ](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) |

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559/graphs/tree.svg?width=650&height=150&src=pr&token=OE661REHqm&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry)](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry)

```diff
@@             Coverage Diff              @@
##               main    #1559      +/-   ##
============================================
- Coverage     70.84%   70.81%   -0.04%     
- Complexity     2766     2771       +5     
============================================
  Files           408      409       +1     
  Lines          8356     8363       +7     
============================================
+ Hits           5920     5922       +2     
- Misses         2436     2441       +5     
```

| [Flag](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) | Coverage Δ | |
|---|---|---|
| [8.1](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) | `70.46% <87.50%> (-0.08%)` | :arrow_down: |
| [8.2](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) | `70.74% <87.50%> (+0.06%)` | :arrow_up: |
| [8.3](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) | `70.75% <87.50%> (+0.01%)` | :arrow_up: |
| [8.4](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) | `70.74% <87.50%> (+0.03%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Files with missing lines](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) | Coverage Δ | |
|---|---|---|
| [src/SDK/Common/Export/InMemoryStorageManager.php](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559?src=pr&el=tree&filepath=src%2FSDK%2FCommon%2FExport%2FInMemoryStorageManager.php&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-c3JjL1NESy9Db21tb24vRXhwb3J0L0luTWVtb3J5U3RvcmFnZU1hbmFnZXIucGhw) | `100.00% <100.00%> (ø)` | |
| [src/SDK/Logs/Exporter/InMemoryExporterFactory.php](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559?src=pr&el=tree&filepath=src%2FSDK%2FLogs%2FExporter%2FInMemoryExporterFactory.php&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-c3JjL1NESy9Mb2dzL0V4cG9ydGVyL0luTWVtb3J5RXhwb3J0ZXJGYWN0b3J5LnBocA==) | `100.00% <100.00%> (ø)` | |
| [...Metrics/MetricExporter/InMemoryExporterFactory.php](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559?src=pr&el=tree&filepath=src%2FSDK%2FMetrics%2FMetricExporter%2FInMemoryExporterFactory.php&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-c3JjL1NESy9NZXRyaWNzL01ldHJpY0V4cG9ydGVyL0luTWVtb3J5RXhwb3J0ZXJGYWN0b3J5LnBocA==) | `100.00% <100.00%> (ø)` | |
| [...Trace/SpanExporter/InMemorySpanExporterFactory.php](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559?src=pr&el=tree&filepath=src%2FSDK%2FTrace%2FSpanExporter%2FInMemorySpanExporterFactory.php&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-c3JjL1NESy9UcmFjZS9TcGFuRXhwb3J0ZXIvSW5NZW1vcnlTcGFuRXhwb3J0ZXJGYWN0b3J5LnBocA==) | `100.00% <100.00%> (ø)` | |
| [...rc/SDK/Metrics/MetricExporter/InMemoryExporter.php](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559?src=pr&el=tree&filepath=src%2FSDK%2FMetrics%2FMetricExporter%2FInMemoryExporter.php&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-c3JjL1NESy9NZXRyaWNzL01ldHJpY0V4cG9ydGVyL0luTWVtb3J5RXhwb3J0ZXIucGhw) | `54.54% <71.42%> (-3.35%)` | :arrow_down: |

... and [8 files with indirect coverage changes](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry)

------

[Continue to review full report in Codecov by Sentry](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559?dropdown=coverage&src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry). Last update [fadec4c...038bc8f](https://app.codecov.io/gh/open-telemetry/opentelemetry-php/pull/1559?dropdown=coverage&src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry).

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: brettmc
created_at: 2025-04-08T22:59:29Z
body: Could a test or example be added that shows it working when combined with autoloading?
author_association: CONTRIBUTOR
user: login: GrzegorzDrozd
created_at: 2025-04-13T10:01:34Z
body: Hi @brettmc .

Because of this line: `src/SDK/Metrics/MetricReader/ExportingReader.php:148` I had to add `PushMetricExporterInterface` to `InMemoryExporter`, otherwise storage was actually never appended. I hope this is ok.
I will add similar integration tests for logs and spans.
author_association: CONTRIBUTOR
user: login: GrzegorzDrozd
created_at: 2025-04-13T10:17:55Z
body: @brettmc Done. All tests added. Please take a look when you have a chance.
author_association: CONTRIBUTOR
user: login: GrzegorzDrozd
created_at: 2025-04-14T07:27:50Z
body: @brettmc Done :)
author_association: CONTRIBUTOR
user: login: brettmc
created_at: 2025-04-14T22:59:24Z
body: looking good. Can you fix the test failures? It looks like a couple of renames were missed.
author_association: CONTRIBUTOR