Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changelog/5660.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`opentelemetry-sdk`: fix overriding of the service.instance.id which has been populated from the user provided values through the resource detectors
16 changes: 10 additions & 6 deletions opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,16 +550,20 @@ def _build_resource_detectors() -> list["ResourceDetector"]:
Fast path: if no extra detectors are configured, returns only the two
built-in detectors without scanning entry_points.

"service_instance" (ServiceInstanceIdResourceDetector) and "otel"
(OTELResourceDetector) are always appended as defaults. "otel" is last so
that OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME take highest merge
priority, but an explicit position in OTEL_EXPERIMENTAL_RESOURCE_DETECTORS
"service_instance" (ServiceInstanceIdResourceDetector) is prepended. "otel"
(OTELResourceDetector) is last so that OTEL_RESOURCE_ATTRIBUTES and
OTEL_SERVICE_NAME take highest merge priority, but an explicit position in OTEL_EXPERIMENTAL_RESOURCE_DETECTORS
is respected for either name.
"""
detector_names: list[str] = list(
dict.fromkeys(
[name.strip() for name in environ.get(OTEL_EXPERIMENTAL_RESOURCE_DETECTORS, "").split(",") if name.strip()]
+ ["service_instance", "otel"]
["service_instance"]
+ [
name.strip()
for name in environ.get(OTEL_EXPERIMENTAL_RESOURCE_DETECTORS, "").split(",")
if name.strip()
]
+ ["otel"]
)
)

Expand Down
36 changes: 36 additions & 0 deletions opentelemetry-sdk/tests/resources/test_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,42 @@ def tearDown(self) -> None:
def test_is_process_dependent(self):
self.assertTrue(ServiceInstanceIdResourceDetector().is_process_dependent())

@patch.dict(environ, {}, clear=True)
def test_service_instance_detector_sets_id_without_custom_detector(self):
resource = Resource.create()

self.assertEqual(uuid.UUID(resource.attributes[SERVICE_INSTANCE_ID]).version, 4)

@patch.dict(
environ,
{OTEL_EXPERIMENTAL_RESOURCE_DETECTORS: "mock"},
clear=True,
)
def test_configured_detector_overrides_service_instance_id(self):
custom_detector = Mock(spec=ResourceDetector)
custom_detector.detect.return_value = Resource(
{
SERVICE_INSTANCE_ID: "configured-instance-id",
"custom.detector": "value",
}
)
entry_point = Mock(**{"load.return_value": Mock(return_value=custom_detector)})

def side_effect(*args, **kwargs):
if kwargs.get("name") == "mock":
return [entry_point]
return real_entry_points(*args, **kwargs)

with patch(
"opentelemetry.util._importlib_metadata.entry_points",
side_effect=side_effect,
):
resource = Resource.create()

custom_detector.detect.assert_called_once()
self.assertEqual(resource.attributes[SERVICE_INSTANCE_ID], "configured-instance-id")
self.assertEqual(resource.attributes["custom.detector"], "value")

def test_detect_value_is_valid_uuid4(self):
_resources_module._service_instance_id = None
_resources_module._service_instance_id_pid = None
Expand Down
Loading