diff --git a/litellm/__init__.py b/litellm/__init__.py index b85d582606..dbdb2222d7 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -34,7 +34,7 @@ input_callback: List[Union[str, Callable]] = [] success_callback: List[Union[str, Callable]] = [] failure_callback: List[Union[str, Callable]] = [] service_callback: List[Union[str, Callable]] = [] -_custom_logger_compatible_callbacks_literal = Literal["lago", "openmeter"] +_custom_logger_compatible_callbacks_literal = Literal["lago", "openmeter", "logfire"] callbacks: List[Union[Callable, _custom_logger_compatible_callbacks_literal]] = [] _langfuse_default_tags: Optional[ List[ diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index a66d978e62..b72098fbe7 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -10,7 +10,7 @@ import sys import time import traceback import uuid -from typing import Callable, Optional +from typing import Any, Callable, Dict, List, Optional import litellm from litellm import ( @@ -73,6 +73,8 @@ from ..integrations.supabase import Supabase from ..integrations.traceloop import TraceloopLogger from ..integrations.weights_biases import WeightsBiasesLogger +_in_memory_loggers: List[Any] = [] + class Logging: global supabaseClient, liteDebuggerClient, promptLayerLogger, weightsBiasesLogger, langsmithLogger, logfireLogger, capture_exception, add_breadcrumb, lunaryLogger, logfireLogger, prometheusLogger, slack_app @@ -1741,13 +1743,33 @@ def _init_custom_logger_compatible_class( logging_integration: litellm._custom_logger_compatible_callbacks_literal, ) -> Callable: if logging_integration == "lago": - return LagoLogger() # type: ignore + for callback in _in_memory_loggers: + if isinstance(callback, LagoLogger): + return callback # type: ignore + + lago_logger = LagoLogger() + _in_memory_loggers.append(lago_logger) + return lago_logger # type: ignore elif logging_integration == "openmeter": - return OpenMeterLogger() # type: ignore + for callback in _in_memory_loggers: + if isinstance(callback, OpenMeterLogger): + return callback # type: ignore + + _openmeter_logger = OpenMeterLogger() + _in_memory_loggers.append(_openmeter_logger) + return _openmeter_logger # type: ignore elif logging_integration == "logfire": + if "LOGFIRE_TOKEN" not in os.environ: + raise ValueError("LOGFIRE_TOKEN not found in environment variables") + otel_config = OpenTelemetryConfig( exporter="otlp_http", endpoint="https://logfire-api.pydantic.dev/v1/traces", headers=f"Authorization={os.getenv('LOGFIRE_TOKEN')}", ) - return OpenTelemetry(config=otel_config) # type: ignore + for callback in _in_memory_loggers: + if isinstance(callback, OpenTelemetry): + return callback # type: ignore + _otel_logger = OpenTelemetry(config=otel_config) + _in_memory_loggers.append(_otel_logger) + return _otel_logger # type: ignore diff --git a/litellm/utils.py b/litellm/utils.py index 7fbb337d60..e84a262afa 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -340,6 +340,15 @@ def function_setup( try: global callback_list, add_breadcrumb, user_logger_fn, Logging function_id = kwargs["id"] if "id" in kwargs else None + + # Note: maintain backwards compatibility with logfire. some users use litellm.success_callback=["logfire"] + if ( + litellm.success_callback is not None + and "logfire" in litellm.success_callback + ): + litellm.success_callback.remove("logfire") + litellm.callbacks.append("logfire") + if len(litellm.callbacks) > 0: for callback in litellm.callbacks: # check if callback is a string - e.g. "lago", "openmeter" @@ -7243,7 +7252,7 @@ def get_secret( else: raise ValueError( f"Google KMS requires the encrypted secret to be encoded in base64" - )#fix for this vulnerability https://huntr.com/bounties/ae623c2f-b64b-4245-9ed4-f13a0a5824ce + ) # fix for this vulnerability https://huntr.com/bounties/ae623c2f-b64b-4245-9ed4-f13a0a5824ce response = client.decrypt( request={ "name": litellm._google_kms_resource_name,