From 713777669bd1ee0d9c9d0fb2427b65e1d16c235f Mon Sep 17 00:00:00 2001 From: Artur Shiriev Date: Sat, 2 May 2026 18:36:10 +0300 Subject: [PATCH] refactor: declare opentelemetry_service_name/namespace on PyroscopeConfig Replace two getattr(..., None) calls in PyroscopeInstrument.bootstrap() with direct attribute access. The fields are added to PyroscopeConfig with default None; in framework configs MRO causes OpentelemetryConfig's declarations to win, so behavior is unchanged. Standalone PyroscopeConfig users can now set these fields directly without the cross-class coupling. Co-Authored-By: Claude Opus 4.7 --- lite_bootstrap/instruments/pyroscope_instrument.py | 7 ++++--- tests/instruments/test_pyroscope_instrument.py | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/lite_bootstrap/instruments/pyroscope_instrument.py b/lite_bootstrap/instruments/pyroscope_instrument.py index 933fbfb..d6bf89e 100644 --- a/lite_bootstrap/instruments/pyroscope_instrument.py +++ b/lite_bootstrap/instruments/pyroscope_instrument.py @@ -15,6 +15,8 @@ class PyroscopeConfig(BaseConfig): pyroscope_sample_rate: int = 100 pyroscope_tags: dict[str, str] = dataclasses.field(default_factory=dict) pyroscope_additional_params: dict[str, typing.Any] = dataclasses.field(default_factory=dict) + opentelemetry_service_name: str | None = None + opentelemetry_namespace: str | None = None @dataclasses.dataclass(kw_only=True, slots=True, frozen=True) @@ -31,11 +33,10 @@ def check_dependencies() -> bool: return import_checker.is_pyroscope_installed def bootstrap(self) -> None: - namespace: str | None = getattr(self.bootstrap_config, "opentelemetry_namespace", None) + namespace = self.bootstrap_config.opentelemetry_namespace tags = ({"service_namespace": namespace} if namespace else {}) | self.bootstrap_config.pyroscope_tags pyroscope.configure( - application_name=getattr(self.bootstrap_config, "opentelemetry_service_name", None) - or self.bootstrap_config.service_name, + application_name=self.bootstrap_config.opentelemetry_service_name or self.bootstrap_config.service_name, server_address=self.bootstrap_config.pyroscope_endpoint, sample_rate=self.bootstrap_config.pyroscope_sample_rate, tags=tags, diff --git a/tests/instruments/test_pyroscope_instrument.py b/tests/instruments/test_pyroscope_instrument.py index ebc2e41..b347052 100644 --- a/tests/instruments/test_pyroscope_instrument.py +++ b/tests/instruments/test_pyroscope_instrument.py @@ -63,6 +63,20 @@ def test_pyroscope_bootstrap_uses_opentelemetry_service_name() -> None: assert mock_pyroscope.configure.call_args.kwargs["application_name"] == "otel-name" +def test_pyroscope_standalone_config_accepts_otel_fields() -> None: + config = PyroscopeConfig( + service_name="fallback", + pyroscope_endpoint="http://pyroscope:4040", + opentelemetry_service_name="otel-name", + opentelemetry_namespace="my-ns", + ) + with patch(_PYROSCOPE_PYROSCOPE) as mock_pyroscope: + PyroscopeInstrument(bootstrap_config=config).bootstrap() + kwargs = mock_pyroscope.configure.call_args.kwargs + assert kwargs["application_name"] == "otel-name" + assert kwargs["tags"] == {"service_namespace": "my-ns"} + + def test_pyroscope_bootstrap_merges_namespace_tag() -> None: config = FreeBootstrapperConfig( service_name="svc",