Skip to content

Commit a80c64f

Browse files
committed
wire process resource detector in declarative config
When `resource.detection_development.detectors[].process` is set in the config file, run `ProcessResourceDetector` and merge its output into the resource. Explicit config attributes take priority over detector-provided values. Assisted-by: Claude Sonnet 4.6
1 parent 8fde77a commit a80c64f

2 files changed

Lines changed: 75 additions & 1 deletion

File tree

opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_resource.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from opentelemetry.sdk.resources import (
3030
_DEFAULT_RESOURCE,
3131
SERVICE_NAME,
32+
ProcessResourceDetector,
3233
Resource,
3334
)
3435

@@ -45,7 +46,7 @@ def _array(coerce: Callable) -> Callable:
4546
return lambda value: [coerce(item) for item in value]
4647

4748

48-
# Unified dispatch table for all attribute type coercions
49+
# Dispatch table mapping AttributeType to its coercion callable
4950
_COERCIONS = {
5051
AttributeType.string: str,
5152
AttributeType.int: int,
@@ -152,6 +153,8 @@ def _run_detectors(
152153
is updated in-place; later detectors overwrite earlier ones for the
153154
same key.
154155
"""
156+
if detector_config.process is not None:
157+
detected_attrs.update(ProcessResourceDetector().detect().attributes)
155158

156159

157160
def _filter_attributes(

opentelemetry-sdk/tests/_configuration/test_resource.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,21 @@
1313
# limitations under the License.
1414

1515
import os
16+
import sys
1617
import unittest
1718
from unittest.mock import patch
1819

1920
from opentelemetry.sdk._configuration._resource import create_resource
2021
from opentelemetry.sdk._configuration.models import (
2122
AttributeNameValue,
2223
AttributeType,
24+
ExperimentalResourceDetection,
25+
ExperimentalResourceDetector,
2326
)
2427
from opentelemetry.sdk._configuration.models import Resource as ResourceConfig
2528
from opentelemetry.sdk.resources import (
29+
PROCESS_PID,
30+
PROCESS_RUNTIME_NAME,
2631
SERVICE_NAME,
2732
TELEMETRY_SDK_LANGUAGE,
2833
TELEMETRY_SDK_NAME,
@@ -295,3 +300,69 @@ def test_attributes_list_invalid_pair_skipped(self):
295300
self.assertEqual(resource.attributes["foo"], "bar")
296301
self.assertNotIn("no-equals", resource.attributes)
297302
self.assertTrue(any("no-equals" in msg for msg in cm.output))
303+
304+
305+
class TestProcessResourceDetector(unittest.TestCase):
306+
def _config_with_process(self) -> ResourceConfig:
307+
return ResourceConfig(
308+
detection_development=ExperimentalResourceDetection(
309+
detectors=[ExperimentalResourceDetector(process={})]
310+
)
311+
)
312+
313+
def test_process_detector_adds_process_attributes(self):
314+
resource = create_resource(self._config_with_process())
315+
self.assertIn(PROCESS_PID, resource.attributes)
316+
self.assertEqual(resource.attributes[PROCESS_PID], os.getpid())
317+
self.assertEqual(
318+
resource.attributes[PROCESS_RUNTIME_NAME],
319+
sys.implementation.name,
320+
)
321+
322+
def test_process_detector_also_includes_sdk_defaults(self):
323+
resource = create_resource(self._config_with_process())
324+
self.assertEqual(resource.attributes[TELEMETRY_SDK_LANGUAGE], "python")
325+
self.assertIn(TELEMETRY_SDK_VERSION, resource.attributes)
326+
327+
def test_process_detector_not_run_when_absent(self):
328+
resource = create_resource(ResourceConfig())
329+
self.assertNotIn(PROCESS_PID, resource.attributes)
330+
331+
def test_process_detector_not_run_when_detection_development_is_none(self):
332+
resource = create_resource(ResourceConfig(detection_development=None))
333+
self.assertNotIn(PROCESS_PID, resource.attributes)
334+
335+
def test_process_detector_not_run_when_detectors_list_empty(self):
336+
config = ResourceConfig(
337+
detection_development=ExperimentalResourceDetection(detectors=[])
338+
)
339+
resource = create_resource(config)
340+
self.assertNotIn(PROCESS_PID, resource.attributes)
341+
342+
def test_explicit_attributes_override_process_detector(self):
343+
"""Config attributes win over detector-provided values."""
344+
config = ResourceConfig(
345+
attributes=[
346+
AttributeNameValue(
347+
name="process.pid", value=99999, type=AttributeType.int
348+
)
349+
],
350+
detection_development=ExperimentalResourceDetection(
351+
detectors=[ExperimentalResourceDetector(process={})]
352+
),
353+
)
354+
resource = create_resource(config)
355+
self.assertEqual(resource.attributes[PROCESS_PID], 99999)
356+
357+
def test_multiple_detector_entries_run_process_once(self):
358+
"""Multiple detector list entries each with process={} should still work."""
359+
config = ResourceConfig(
360+
detection_development=ExperimentalResourceDetection(
361+
detectors=[
362+
ExperimentalResourceDetector(process={}),
363+
ExperimentalResourceDetector(process={}),
364+
]
365+
)
366+
)
367+
resource = create_resource(config)
368+
self.assertEqual(resource.attributes[PROCESS_PID], os.getpid())

0 commit comments

Comments
 (0)