|
21 | 21 | shutdown, |
22 | 22 | _setup_meter_provider, |
23 | 23 | _create_metric_exporter, |
| 24 | + _merge_sdk_resource_into_meter_provider, |
24 | 25 | setup_log_provider, |
25 | 26 | _create_log_exporter, |
26 | 27 | _merge_sdk_resource_into_log_provider, |
@@ -173,32 +174,66 @@ def test_delegates_to_create_metric_exporter(self): |
173 | 174 | mock_exporter = MagicMock() |
174 | 175 | with patch("sap_cloud_sdk.core.telemetry._provider.get_config", return_value=_ENABLED_CONFIG): |
175 | 176 | with patch("sap_cloud_sdk.core.telemetry._provider.Resource"): |
176 | | - with patch("sap_cloud_sdk.core.telemetry._provider._create_metric_exporter", return_value=mock_exporter) as mock_create: |
177 | | - with patch("sap_cloud_sdk.core.telemetry._provider.PeriodicExportingMetricReader") as mock_reader: |
178 | | - with patch("sap_cloud_sdk.core.telemetry._provider.MeterProvider"): |
179 | | - with patch("opentelemetry.metrics.set_meter_provider"): |
| 177 | + with patch("sap_cloud_sdk.core.telemetry._provider.metrics") as mock_metrics: |
| 178 | + mock_metrics.get_meter_provider.return_value = MagicMock() |
| 179 | + with patch("sap_cloud_sdk.core.telemetry._provider._create_metric_exporter", return_value=mock_exporter) as mock_create: |
| 180 | + with patch("sap_cloud_sdk.core.telemetry._provider.PeriodicExportingMetricReader") as mock_reader: |
| 181 | + with patch("sap_cloud_sdk.core.telemetry._provider.MeterProvider"): |
180 | 182 | _setup_meter_provider() |
181 | 183 |
|
182 | | - mock_create.assert_called_once_with() |
183 | | - mock_reader.assert_called_once_with(exporter=mock_exporter) |
| 184 | + mock_create.assert_called_once_with() |
| 185 | + mock_reader.assert_called_once_with(exporter=mock_exporter) |
184 | 186 |
|
185 | 187 | def test_unsupported_protocol_returns_none(self): |
186 | 188 | with patch("sap_cloud_sdk.core.telemetry._provider.get_config", return_value=_ENABLED_CONFIG): |
187 | 189 | with patch("sap_cloud_sdk.core.telemetry._provider.Resource"): |
188 | | - with patch.dict("os.environ", {"OTEL_EXPORTER_OTLP_PROTOCOL": "http/json"}): |
189 | | - assert _setup_meter_provider() is None |
| 190 | + with patch("sap_cloud_sdk.core.telemetry._provider.metrics") as mock_metrics: |
| 191 | + mock_metrics.get_meter_provider.return_value = MagicMock() |
| 192 | + with patch.dict("os.environ", {"OTEL_EXPORTER_OTLP_PROTOCOL": "http/json"}): |
| 193 | + assert _setup_meter_provider() is None |
190 | 194 |
|
191 | 195 | def test_returns_configured_provider(self): |
192 | 196 | mock_provider = MagicMock() |
193 | 197 | with patch("sap_cloud_sdk.core.telemetry._provider.get_config", return_value=_ENABLED_CONFIG): |
194 | 198 | with patch("sap_cloud_sdk.core.telemetry._provider.Resource"): |
195 | | - with patch("sap_cloud_sdk.core.telemetry._provider._create_metric_exporter"): |
196 | | - with patch("sap_cloud_sdk.core.telemetry._provider.PeriodicExportingMetricReader"): |
197 | | - with patch("sap_cloud_sdk.core.telemetry._provider.MeterProvider", return_value=mock_provider): |
198 | | - with patch("opentelemetry.metrics.set_meter_provider"): |
| 199 | + with patch("sap_cloud_sdk.core.telemetry._provider.metrics") as mock_metrics: |
| 200 | + mock_metrics.get_meter_provider.return_value = MagicMock() |
| 201 | + with patch("sap_cloud_sdk.core.telemetry._provider._create_metric_exporter"): |
| 202 | + with patch("sap_cloud_sdk.core.telemetry._provider.PeriodicExportingMetricReader"): |
| 203 | + with patch("sap_cloud_sdk.core.telemetry._provider.MeterProvider", return_value=mock_provider): |
199 | 204 | assert _setup_meter_provider() is mock_provider |
200 | 205 |
|
201 | 206 |
|
| 207 | + def test_reuses_existing_sdk_meter_provider(self): |
| 208 | + """When a MeterProvider is already set, merge into it instead of creating a new one.""" |
| 209 | + from opentelemetry.sdk.metrics import MeterProvider as _MP |
| 210 | + existing = MagicMock(spec=_MP) |
| 211 | + with patch("sap_cloud_sdk.core.telemetry._provider.get_config", return_value=_ENABLED_CONFIG): |
| 212 | + with patch("sap_cloud_sdk.core.telemetry._provider.Resource"): |
| 213 | + with patch("sap_cloud_sdk.core.telemetry._provider.metrics") as mock_metrics: |
| 214 | + mock_metrics.get_meter_provider.return_value = existing |
| 215 | + with patch("sap_cloud_sdk.core.telemetry._provider._merge_sdk_resource_into_meter_provider") as mock_merge: |
| 216 | + with patch("sap_cloud_sdk.core.telemetry._provider._SDKMeterProvider", _MP): |
| 217 | + result = _setup_meter_provider() |
| 218 | + assert result is existing |
| 219 | + mock_merge.assert_called_once() |
| 220 | + mock_metrics.set_meter_provider.assert_not_called() |
| 221 | + |
| 222 | + def test_existing_provider_no_new_reader(self): |
| 223 | + """Reuse path must not create a new PeriodicExportingMetricReader.""" |
| 224 | + from opentelemetry.sdk.metrics import MeterProvider as _MP |
| 225 | + existing = MagicMock(spec=_MP) |
| 226 | + with patch("sap_cloud_sdk.core.telemetry._provider.get_config", return_value=_ENABLED_CONFIG): |
| 227 | + with patch("sap_cloud_sdk.core.telemetry._provider.Resource"): |
| 228 | + with patch("sap_cloud_sdk.core.telemetry._provider.metrics") as mock_metrics: |
| 229 | + mock_metrics.get_meter_provider.return_value = existing |
| 230 | + with patch("sap_cloud_sdk.core.telemetry._provider._merge_sdk_resource_into_meter_provider"): |
| 231 | + with patch("sap_cloud_sdk.core.telemetry._provider._SDKMeterProvider", _MP): |
| 232 | + with patch("sap_cloud_sdk.core.telemetry._provider.PeriodicExportingMetricReader") as mock_reader: |
| 233 | + _setup_meter_provider() |
| 234 | + mock_reader.assert_not_called() |
| 235 | + |
| 236 | + |
202 | 237 | _LOGGING_HANDLER = "sap_cloud_sdk.core.telemetry._provider.LoggingHandler" |
203 | 238 | _GRPC_LOG_EXPORTER = "sap_cloud_sdk.core.telemetry._provider.GRPCLogExporter" |
204 | 239 | _HTTP_LOG_EXPORTER = "sap_cloud_sdk.core.telemetry._provider.HTTPLogExporter" |
@@ -330,6 +365,44 @@ def test_platform_path_adds_handler_when_none_present(self): |
330 | 365 | mock_handler_cls.assert_called_once_with(logger_provider=external) |
331 | 366 |
|
332 | 367 |
|
| 368 | +class TestMergeSdkResourceIntoMeterProvider: |
| 369 | + def test_updates_sdk_config_resource(self): |
| 370 | + from opentelemetry.sdk.metrics import MeterProvider as _MP |
| 371 | + from opentelemetry.sdk.resources import Resource as _R |
| 372 | + |
| 373 | + sdk_resource = _R({"sap.cloud_sdk.language": "python"}) |
| 374 | + existing_resource = _R({"service.name": "svc"}) |
| 375 | + provider = _MP(resource=existing_resource) |
| 376 | + |
| 377 | + _merge_sdk_resource_into_meter_provider(provider, sdk_resource) |
| 378 | + |
| 379 | + assert provider._sdk_config.resource.attributes["sap.cloud_sdk.language"] == "python" |
| 380 | + assert provider._sdk_config.resource.attributes["service.name"] == "svc" |
| 381 | + |
| 382 | + def test_measurement_consumer_sees_update(self): |
| 383 | + """_measurement_consumer shares the same SdkConfiguration object.""" |
| 384 | + from opentelemetry.sdk.metrics import MeterProvider as _MP |
| 385 | + from opentelemetry.sdk.resources import Resource as _R |
| 386 | + |
| 387 | + sdk_resource = _R({"sap.cloud_sdk.language": "python"}) |
| 388 | + provider = _MP(resource=_R({"service.name": "svc"})) |
| 389 | + |
| 390 | + _merge_sdk_resource_into_meter_provider(provider, sdk_resource) |
| 391 | + |
| 392 | + assert provider._measurement_consumer._sdk_config.resource is provider._sdk_config.resource |
| 393 | + |
| 394 | + def test_sdk_attrs_win_on_collision(self): |
| 395 | + from opentelemetry.sdk.metrics import MeterProvider as _MP |
| 396 | + from opentelemetry.sdk.resources import Resource as _R |
| 397 | + |
| 398 | + sdk_resource = _R({"service.name": "sdk-name"}) |
| 399 | + provider = _MP(resource=_R({"service.name": "platform-name"})) |
| 400 | + |
| 401 | + _merge_sdk_resource_into_meter_provider(provider, sdk_resource) |
| 402 | + |
| 403 | + assert provider._sdk_config.resource.attributes["service.name"] == "sdk-name" |
| 404 | + |
| 405 | + |
333 | 406 | class TestMergeSdkResourceIntoLogProvider: |
334 | 407 | def test_updates_provider_resource(self): |
335 | 408 | from opentelemetry.sdk._logs import LoggerProvider as _LP |
|
0 commit comments