From 47800757fdef71392dc286798d1e69c7aec31253 Mon Sep 17 00:00:00 2001 From: tyler-liner Date: Tue, 23 Sep 2025 15:53:41 +0900 Subject: [PATCH] feat(opentelemetry): use generation_name for span naming in logging method --- litellm/integrations/opentelemetry.py | 16 +++++- .../integrations/test_opentelemetry.py | 55 +++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index e6f265ded58..d6cd0531318 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -575,9 +575,16 @@ class OpenTelemetry(CustomLogger): if litellm.turn_off_message_logging or not self.message_logging: return + litellm_params = kwargs.get("litellm_params", {}) + metadata = litellm_params.get("metadata", {}) + generation_name = metadata.get("generation_name") + + raw_span_name = generation_name if generation_name else RAW_REQUEST_SPAN_NAME + + otel_tracer: Tracer = self.get_tracer_to_use_for_request(kwargs) raw_span = otel_tracer.start_span( - name=RAW_REQUEST_SPAN_NAME, + name=raw_span_name, start_time=self._to_ns(start_time), context=trace.set_span_in_context(parent_span), ) @@ -1165,6 +1172,13 @@ class OpenTelemetry(CustomLogger): return int(dt.timestamp() * 1e9) def _get_span_name(self, kwargs): + litellm_params = kwargs.get("litellm_params", {}) + metadata = litellm_params.get("metadata", {}) + generation_name = metadata.get("generation_name") + + if generation_name: + return generation_name + return LITELLM_REQUEST_SPAN_NAME def get_traceparent_from_header(self, headers): diff --git a/tests/test_litellm/integrations/test_opentelemetry.py b/tests/test_litellm/integrations/test_opentelemetry.py index 7fb91f274d0..e605718d29f 100644 --- a/tests/test_litellm/integrations/test_opentelemetry.py +++ b/tests/test_litellm/integrations/test_opentelemetry.py @@ -751,3 +751,58 @@ class TestOpenTelemetry(unittest.TestCase): # ─── no events when only metrics enabled ───────────────────────────────── logs = log_exporter.get_finished_logs() self.assertFalse(logs, "Did not expect any logs") + + def test_get_span_name_with_generation_name(self): + """Test _get_span_name returns generation_name when present""" + otel = OpenTelemetry() + kwargs = { + "litellm_params": { + "metadata": { + "generation_name": "custom_span" + } + } + } + result = otel._get_span_name(kwargs) + self.assertEqual(result, "custom_span") + + def test_get_span_name_without_generation_name(self): + """Test _get_span_name returns default when generation_name missing""" + from litellm.integrations.opentelemetry import LITELLM_REQUEST_SPAN_NAME + + otel = OpenTelemetry() + kwargs = {"litellm_params": {"metadata": {}}} + result = otel._get_span_name(kwargs) + self.assertEqual(result, LITELLM_REQUEST_SPAN_NAME) + + @patch('litellm.turn_off_message_logging', False) + def test_maybe_log_raw_request_creates_span(self): + """Test _maybe_log_raw_request creates span when logging enabled""" + from litellm.integrations.opentelemetry import RAW_REQUEST_SPAN_NAME + + otel = OpenTelemetry() + otel.message_logging = True + + mock_tracer = MagicMock() + mock_span = MagicMock() + mock_tracer.start_span.return_value = mock_span + otel.get_tracer_to_use_for_request = MagicMock(return_value=mock_tracer) + otel.set_raw_request_attributes = MagicMock() + otel._to_ns = MagicMock(return_value=1234567890) + + kwargs = {"litellm_params": {"metadata": {}}} + otel._maybe_log_raw_request(kwargs, {}, datetime.now(), datetime.now(), MagicMock()) + + mock_tracer.start_span.assert_called_once() + self.assertEqual(mock_tracer.start_span.call_args[1]['name'], RAW_REQUEST_SPAN_NAME) + + @patch('litellm.turn_off_message_logging', True) + def test_maybe_log_raw_request_skips_when_logging_disabled(self): + """Test _maybe_log_raw_request skips when logging disabled""" + otel = OpenTelemetry() + mock_tracer = MagicMock() + otel.get_tracer_to_use_for_request = MagicMock(return_value=mock_tracer) + + kwargs = {"litellm_params": {"metadata": {}}} + otel._maybe_log_raw_request(kwargs, {}, datetime.now(), datetime.now(), MagicMock()) + + mock_tracer.start_span.assert_not_called()