From 90682100e0e45cf451494863479291ee5ba4cfd3 Mon Sep 17 00:00:00 2001 From: ephraimduncan Date: Wed, 29 Jul 2026 13:03:34 +0000 Subject: [PATCH] feat(integrations): add telemetry.dev logging integration --- litellm/__init__.py | 1 + .../integrations/telemetry_dev/__init__.py | 3 ++ .../telemetry_dev/telemetry_dev.py | 47 +++++++++++++++++++ .../custom_logger_registry.py | 1 + litellm/litellm_core_utils/litellm_logging.py | 25 ++++++++++ .../test_unit_tests_init_callbacks.py | 1 + 6 files changed, 78 insertions(+) create mode 100644 litellm/integrations/telemetry_dev/__init__.py create mode 100644 litellm/integrations/telemetry_dev/telemetry_dev.py diff --git a/litellm/__init__.py b/litellm/__init__.py index 3f8c742c5a2..8c0f212c5c2 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -128,6 +128,7 @@ _custom_logger_compatible_callbacks_literal = Literal[ "arize", "arize_phoenix", "langtrace", + "telemetry_dev", "gcs_bucket", "azure_storage", "opik", diff --git a/litellm/integrations/telemetry_dev/__init__.py b/litellm/integrations/telemetry_dev/__init__.py new file mode 100644 index 00000000000..60bbd536b5f --- /dev/null +++ b/litellm/integrations/telemetry_dev/__init__.py @@ -0,0 +1,3 @@ +from litellm.integrations.telemetry_dev.telemetry_dev import TelemetryDevLogger + +__all__ = ["TelemetryDevLogger"] diff --git a/litellm/integrations/telemetry_dev/telemetry_dev.py b/litellm/integrations/telemetry_dev/telemetry_dev.py new file mode 100644 index 00000000000..ed92657ae28 --- /dev/null +++ b/litellm/integrations/telemetry_dev/telemetry_dev.py @@ -0,0 +1,47 @@ +import os +from typing import TYPE_CHECKING, Any + +from litellm.integrations.opentelemetry import OpenTelemetry + +if TYPE_CHECKING: + from litellm.integrations.opentelemetry import OpenTelemetryConfig + from litellm.types.integrations.arize import Protocol +else: + Protocol = Any + OpenTelemetryConfig = Any + + +class TelemetryDevConfig: + """Configuration for the telemetry.dev OTLP integration.""" + + def __init__( + self, + otlp_auth_headers: str, + protocol: Protocol, + endpoint: str, + ): + self.otlp_auth_headers = otlp_auth_headers + self.protocol = protocol + self.endpoint = endpoint + + +class TelemetryDevLogger(OpenTelemetry): + """telemetry.dev logger that extends OpenTelemetry for OTLP export.""" + + @staticmethod + def get_telemetry_dev_config() -> TelemetryDevConfig: + api_key = os.environ.get("TELEMETRY_DEV_API_KEY") + if not api_key: + raise ValueError( + "TELEMETRY_DEV_API_KEY environment variable is required for the telemetry.dev integration." + ) + + base_url = os.environ.get( + "TELEMETRY_DEV_BASE_URL", "https://ingest.telemetry.dev" + ).rstrip("/") + + return TelemetryDevConfig( + otlp_auth_headers=f"Authorization=Bearer {api_key}", + protocol="otlp_http", + endpoint=f"{base_url}/v1/traces", + ) diff --git a/litellm/litellm_core_utils/custom_logger_registry.py b/litellm/litellm_core_utils/custom_logger_registry.py index a7fae104c92..67593ea9dbe 100644 --- a/litellm/litellm_core_utils/custom_logger_registry.py +++ b/litellm/litellm_core_utils/custom_logger_registry.py @@ -85,6 +85,7 @@ class CustomLoggerRegistry: "langfuse_otel": OpenTelemetry, "arize_phoenix": OpenTelemetry, "langtrace": OpenTelemetry, + "telemetry_dev": OpenTelemetry, "weave_otel": OpenTelemetry, "levo": OpenTelemetry, "mlflow": MlflowLogger, diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 83d6fcc0bee..39f7b44f759 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -3825,6 +3825,31 @@ def _init_custom_logger_compatible_class( _levo_otel_logger = LevoLogger(config=otel_config, callback_name="levo") _in_memory_loggers.append(_levo_otel_logger) return _levo_otel_logger # type: ignore + elif logging_integration == "telemetry_dev": + from litellm.integrations.opentelemetry import OpenTelemetryConfig + from litellm.integrations.telemetry_dev.telemetry_dev import ( + TelemetryDevLogger, + ) + + telemetry_dev_config = TelemetryDevLogger.get_telemetry_dev_config() + otel_config = OpenTelemetryConfig( + exporter=telemetry_dev_config.protocol, + endpoint=telemetry_dev_config.endpoint, + headers=telemetry_dev_config.otlp_auth_headers, + ) + + for callback in _in_memory_loggers: + if ( + isinstance(callback, TelemetryDevLogger) + and callback.callback_name == "telemetry_dev" + ): + return callback # type: ignore + + telemetry_dev_logger = TelemetryDevLogger( + config=otel_config, callback_name="telemetry_dev" + ) + _in_memory_loggers.append(telemetry_dev_logger) + return telemetry_dev_logger # type: ignore elif logging_integration == "otel": # Gate the new typed V2 adapter behind LITELLM_OTEL_V2. When off, # the legacy 3,227-line god-class is used unchanged. The two are diff --git a/tests/logging_callback_tests/test_unit_tests_init_callbacks.py b/tests/logging_callback_tests/test_unit_tests_init_callbacks.py index b2243eed049..ee0f57efb86 100644 --- a/tests/logging_callback_tests/test_unit_tests_init_callbacks.py +++ b/tests/logging_callback_tests/test_unit_tests_init_callbacks.py @@ -42,6 +42,7 @@ expected_env_vars = { "GOOGLE_APPLICATION_CREDENTIALS": "gcs_credentials", "OPIK_API_KEY": "opik_api_key", "LANGTRACE_API_KEY": "langtrace_api_key", + "TELEMETRY_DEV_API_KEY": "td_live_test", "LOGFIRE_TOKEN": "logfire_token", "ARIZE_SPACE_KEY": "arize_space_key", "ARIZE_API_KEY": "arize_api_key",