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/5534.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`opentelemetry-configuration`: add missing process executable name to default service name when available in resource attributes
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import fnmatch
import logging
import os
import sys
from collections.abc import Callable
from typing import Any
from urllib import parse
Expand Down Expand Up @@ -101,7 +102,9 @@ def create_resource(config: ResourceConfig | None) -> Resource:
"""
# Spec requires service.name to always be present; detectors and explicit
# config attributes can override this default.
base = _DEFAULT_RESOURCE.merge(Resource({SERVICE_NAME: "unknown_service"}))
executable_name = os.path.basename(sys.executable) if sys.executable else None
default_service_name = f"unknown_service:{executable_name}" if executable_name else "unknown_service"
base = _DEFAULT_RESOURCE.merge(Resource({SERVICE_NAME: default_service_name}))

if config is None:
return base
Expand Down
23 changes: 17 additions & 6 deletions opentelemetry-configuration/tests/test_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,16 @@


class TestCreateResourceDefaults(unittest.TestCase):
@patch("sys.executable", "/usr/bin/python3")
def test_none_config_returns_sdk_defaults(self):
resource = create_resource(None)
self.assertIsInstance(resource, Resource)
self.assertEqual(resource.attributes[TELEMETRY_SDK_LANGUAGE], "python")
self.assertEqual(resource.attributes[TELEMETRY_SDK_NAME], "opentelemetry")
self.assertIn(TELEMETRY_SDK_VERSION, resource.attributes)
self.assertEqual(resource.attributes[SERVICE_NAME], "unknown_service")
self.assertEqual(resource.attributes[SERVICE_NAME], "unknown_service:python3")

@patch("sys.executable", "/usr/bin/python3")
def test_none_config_does_not_read_env_vars(self):
with patch.dict(
os.environ,
Expand All @@ -52,23 +54,30 @@ def test_none_config_does_not_read_env_vars(self):
):
resource = create_resource(None)
self.assertNotIn("foo", resource.attributes)
self.assertEqual(resource.attributes[SERVICE_NAME], "unknown_service")
self.assertEqual(resource.attributes[SERVICE_NAME], "unknown_service:python3")

@patch("sys.executable", "/usr/bin/python3")
def test_empty_resource_config(self):
resource = create_resource(ResourceConfig())
self.assertEqual(resource.attributes[TELEMETRY_SDK_LANGUAGE], "python")
self.assertEqual(resource.attributes[SERVICE_NAME], "unknown_service")
self.assertEqual(resource.attributes[SERVICE_NAME], "unknown_service:python3")

@patch("sys.executable", "/usr/bin/python3")
def test_service_name_default_added_when_missing(self):
config = ResourceConfig(attributes=[AttributeNameValue(name="env", value="staging")])
resource = create_resource(config)
self.assertEqual(resource.attributes[SERVICE_NAME], "unknown_service")
self.assertEqual(resource.attributes[SERVICE_NAME], "unknown_service:python3")

def test_service_name_not_overridden_when_set(self):
config = ResourceConfig(attributes=[AttributeNameValue(name="service.name", value="my-app")])
resource = create_resource(config)
self.assertEqual(resource.attributes[SERVICE_NAME], "my-app")

@patch("sys.executable", None)
def test_default_service_name_without_sys_executable_returns_plain_unkwnon_service(self):
resource = create_resource(None)
self.assertEqual(resource.attributes[SERVICE_NAME], "unknown_service")

def test_env_vars_not_read(self):
"""OTEL_RESOURCE_ATTRIBUTES must not affect declarative config resource."""
with patch.dict(
Expand Down Expand Up @@ -294,10 +303,11 @@ def test_service_detector_reads_otel_service_name_env_var(self):
resource = create_resource(self._config_with_service())
self.assertEqual(resource.attributes[SERVICE_NAME], "my-service")

@patch("sys.executable", "/usr/bin/python3")
def test_service_detector_no_env_var_leaves_default_service_name(self):
with patch.dict(os.environ, {}, clear=True):
resource = create_resource(self._config_with_service())
self.assertEqual(resource.attributes[SERVICE_NAME], "unknown_service")
self.assertEqual(resource.attributes[SERVICE_NAME], "unknown_service:python3")

def test_explicit_service_name_overrides_env_var(self):
"""Config attributes win over the service detector's env-var value."""
Expand Down Expand Up @@ -333,6 +343,7 @@ def test_service_detector_also_includes_sdk_defaults(self):
self.assertEqual(resource.attributes[TELEMETRY_SDK_LANGUAGE], "python")
self.assertIn(TELEMETRY_SDK_VERSION, resource.attributes)

@patch("sys.executable", "/usr/bin/python3")
def test_included_filter_limits_service_attributes(self):
config = ResourceConfig(
detection_development=ExperimentalResourceDetection(
Expand All @@ -345,7 +356,7 @@ def test_included_filter_limits_service_attributes(self):
self.assertIn(SERVICE_INSTANCE_ID, resource.attributes)
# service.name comes from the filter-excluded detector output, but the
# default "unknown_service" is still added by create_resource directly
self.assertEqual(resource.attributes[SERVICE_NAME], "unknown_service")
self.assertEqual(resource.attributes[SERVICE_NAME], "unknown_service:python3")


class TestHostResourceDetector(unittest.TestCase):
Expand Down
Loading