diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/in_memory_metrics_exporter.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/in_memory_metrics_exporter.py new file mode 100644 index 00000000000..a5df2bb1d83 --- /dev/null +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/in_memory_metrics_exporter.py @@ -0,0 +1,58 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import threading +from typing import Sequence + +from . import MetricRecord, MetricsExporter, MetricsExportResult + + +class InMemoryMetricsExporter(MetricsExporter): + """Implementation of `MetricsExporter` that stores metrics in memory. + + This class can be used for testing purposes. It stores exported metrics + in a list in memory that can be retrieved using the + :func:`.get_exported_metrics` method. + """ + + def __init__(self): + self._exported_metrics = [] + self._stopped = False + self._lock = threading.Lock() + + def clear(self): + """Clear list of collected metrics.""" + with self._lock: + self._exported_metrics.clear() + + def export( + self, metric_records: Sequence[MetricRecord] + ) -> MetricsExportResult: + if self._stopped: + return MetricsExportResult.FAILURE + with self._lock: + self._exported_metrics.extend(metric_records) + return MetricsExportResult.SUCCESS + + def get_exported_metrics(self): + """Get list of collected metrics.""" + with self._lock: + return tuple(self._exported_metrics) + + def shutdown(self) -> None: + """Shuts down the exporter. + + Called when the SDK is shut down. + """ + self._stopped = True diff --git a/tests/util/src/opentelemetry/test/test_base.py b/tests/util/src/opentelemetry/test/test_base.py index ca015ff0110..4b40d52ccbc 100644 --- a/tests/util/src/opentelemetry/test/test_base.py +++ b/tests/util/src/opentelemetry/test/test_base.py @@ -16,7 +16,12 @@ import unittest from contextlib import contextmanager +from opentelemetry import metrics as metrics_api from opentelemetry import trace as trace_api +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.metrics.export.in_memory_metrics_exporter import ( + InMemoryMetricsExporter, +) from opentelemetry.sdk.trace import TracerProvider, export from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( InMemorySpanExporter, @@ -26,14 +31,19 @@ class TestBase(unittest.TestCase): @classmethod def setUpClass(cls): - cls.original_provider = trace_api.get_tracer_provider() + cls.original_tracer_provider = trace_api.get_tracer_provider() result = cls.create_tracer_provider() cls.tracer_provider, cls.memory_exporter = result trace_api.set_tracer_provider(cls.tracer_provider) + cls.original_meter_provider = metrics_api.get_meter_provider() + result = cls.create_meter_provider() + cls.meter_provider, cls.memory_metrics_exporter = result + metrics_api.set_meter_provider(cls.meter_provider) @classmethod def tearDownClass(cls): - trace_api.set_tracer_provider(cls.original_provider) + trace_api.set_tracer_provider(cls.original_tracer_provider) + metrics_api.set_meter_provider(cls.original_meter_provider) def setUp(self): self.memory_exporter.clear() @@ -53,7 +63,7 @@ def create_tracer_provider(**kwargs): Returns: A list with the tracer provider in the first element and the - memory exporter in the second. + in-memory span exporter in the second. """ tracer_provider = TracerProvider(**kwargs) memory_exporter = InMemorySpanExporter() @@ -62,6 +72,20 @@ def create_tracer_provider(**kwargs): return tracer_provider, memory_exporter + @staticmethod + def create_meter_provider(**kwargs): + """Helper to create a configured meter provider + + Creates a `MeterProvider` and an `InMemoryMetricsExporter`. + + Returns: + A list with the meter provider in the first element and the + in-memory metrics exporter in the second + """ + meter_provider = MeterProvider(**kwargs) + memory_exporter = InMemoryMetricsExporter() + return meter_provider, memory_exporter + @staticmethod @contextmanager def disable_logging(highest_level=logging.CRITICAL):