Skip to content

Commit

Permalink
metrics: added noop exporter
Browse files Browse the repository at this point in the history
  • Loading branch information
nosahama committed Jan 22, 2025
1 parent 618b21f commit 84f5293
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 9 deletions.
41 changes: 37 additions & 4 deletions src/schema_registry/telemetry/meter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"""

from dependency_injector.wiring import inject, Provide
from karapace.config import Config
from karapace.config import Config, KarapaceTelemetryOTelExporter
from karapace.container import KarapaceContainer
from opentelemetry import metrics
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
Expand All @@ -13,7 +13,32 @@
MetricExporter,
MetricReader,
PeriodicExportingMetricReader,
MetricsData,
MetricExportResult,
)
from typing import Any


class NOOPMetricExporter(MetricExporter):
"""Implementation of :class:`MetricExporter` that does nothing.
This class is intended to be used when metrics exporting to an OTel backend is disabled
and the ConsoleExporter is too verbose to be used.
"""

def export(
self,
metrics_data: MetricsData,
timeout_millis: float = 0,
**kwargs: Any,
) -> MetricExportResult:
return MetricExportResult.SUCCESS

def shutdown(self, timeout_millis: float = 0, **kwargs: Any) -> None:
pass

def force_flush(self, _: float = 0) -> bool:
return True


class Meter:
Expand All @@ -22,12 +47,20 @@ class Meter:
def get_meter(config: Config = Provide[KarapaceContainer.config]) -> metrics.Meter:
return metrics.get_meter_provider().get_meter(f"{config.tags.app}.meter")

@staticmethod
def get_metric_exporter(config: Config) -> MetricExporter:
match config.telemetry.otel_exporter:
case KarapaceTelemetryOTelExporter.NOOP:
return NOOPMetricExporter()
case KarapaceTelemetryOTelExporter.CONSOLE:
return ConsoleMetricExporter()
case KarapaceTelemetryOTelExporter.OTLP:
return OTLPMetricExporter(endpoint=config.telemetry.otel_endpoint_url)

@staticmethod
@inject
def get_metric_reader(config: Config = Provide[KarapaceContainer.config]) -> MetricReader:
exporter: MetricExporter = ConsoleMetricExporter()
if config.telemetry.otel_endpoint_url:
exporter = OTLPMetricExporter(endpoint=config.telemetry.otel_endpoint_url)
exporter: MetricExporter = Meter.get_metric_exporter(config)
return PeriodicExportingMetricReader(
exporter=exporter,
export_interval_millis=config.telemetry.metrics_export_interval_milliseconds,
Expand Down
57 changes: 52 additions & 5 deletions tests/unit/schema_registry/telemetry/test_meter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@

from karapace.config import KarapaceTelemetry
from karapace.container import KarapaceContainer
from schema_registry.telemetry.meter import Meter
from schema_registry.telemetry.meter import Meter, NOOPMetricExporter
from unittest.mock import patch
from opentelemetry.sdk.metrics.export import ConsoleMetricExporter, MetricExporter


def test_meter(karapace_container: KarapaceContainer):
Expand All @@ -17,26 +18,72 @@ def test_meter(karapace_container: KarapaceContainer):
mock_metrics.get_meter_provider.return_value.get_meter.assert_called_once_with("Karapace.meter")


def test_get_metric_exporter_noop(karapace_container: KarapaceContainer) -> None:
config = karapace_container.config().set_config_defaults(
new_config={
"telemetry": KarapaceTelemetry(
otel_endpoint_url="http://otel:4317",
otel_exporter="NOOP",
)
}
)
exporter: MetricExporter = Meter.get_metric_exporter(config=config)
assert isinstance(exporter, NOOPMetricExporter)


def test_get_metric_exporter_console(karapace_container: KarapaceContainer) -> None:
config = karapace_container.config().set_config_defaults(
new_config={
"telemetry": KarapaceTelemetry(
otel_endpoint_url="http://otel:4317",
otel_exporter="CONSOLE",
)
}
)
exporter: MetricExporter = Meter.get_metric_exporter(config=config)
assert isinstance(exporter, ConsoleMetricExporter)


def test_get_metric_exporter_otlp(karapace_container: KarapaceContainer) -> None:
config = karapace_container.config().set_config_defaults(
new_config={
"telemetry": KarapaceTelemetry(
otel_endpoint_url="http://otel:4317",
otel_exporter="OTLP",
)
}
)
with patch("schema_registry.telemetry.meter.OTLPMetricExporter") as mock_otlp_exporter:
exporter: MetricExporter = Meter.get_metric_exporter(config=config)
mock_otlp_exporter.assert_called_once_with(endpoint="http://otel:4317")
assert exporter is mock_otlp_exporter.return_value


def test_get_metric_reader_without_otel_endpoint(karapace_container: KarapaceContainer) -> None:
config = karapace_container.config().set_config_defaults(
new_config={"telemetry": KarapaceTelemetry(otel_endpoint_url=None)}
)
with (
patch("schema_registry.telemetry.meter.ConsoleMetricExporter") as mock_console_exporter,
patch("schema_registry.telemetry.meter.NOOPMetricExporter") as mock_noop_exporter,
patch("schema_registry.telemetry.meter.PeriodicExportingMetricReader") as mock_periodic_exporting_metric_reader,
):
reader = Meter.get_metric_reader(config=config)
mock_console_exporter.assert_called_once()
mock_noop_exporter.assert_called_once()
mock_periodic_exporting_metric_reader.assert_called_once_with(
exporter=mock_console_exporter.return_value,
exporter=mock_noop_exporter.return_value,
export_interval_millis=10000,
)
assert reader is mock_periodic_exporting_metric_reader.return_value


def test_get_metric_reader_with_otel_endpoint(karapace_container: KarapaceContainer) -> None:
config = karapace_container.config().set_config_defaults(
new_config={"telemetry": KarapaceTelemetry(otel_endpoint_url="http://otel:4317")}
new_config={
"telemetry": KarapaceTelemetry(
otel_endpoint_url="http://otel:4317",
otel_exporter="OTLP",
)
}
)
with (
patch("schema_registry.telemetry.meter.OTLPMetricExporter") as mock_otlp_exporter,
Expand Down

0 comments on commit 84f5293

Please sign in to comment.