From 978912ef3265b9b023ea79d466ba034a5e477be6 Mon Sep 17 00:00:00 2001 From: alisalim17 Date: Sat, 4 May 2024 17:40:23 +0400 Subject: [PATCH] feat: add failure handler for logfire --- litellm/integrations/logfire_logger.py | 50 +++++++++++++++++++++----- litellm/utils.py | 44 +++++++++++++++++------ 2 files changed, 76 insertions(+), 18 deletions(-) diff --git a/litellm/integrations/logfire_logger.py b/litellm/integrations/logfire_logger.py index edc83a35e12..fbe390b52ca 100644 --- a/litellm/integrations/logfire_logger.py +++ b/litellm/integrations/logfire_logger.py @@ -8,6 +8,7 @@ import traceback import uuid from litellm._logging import print_verbose, verbose_logger +from enum import Enum from typing import Any, Dict, NamedTuple from typing_extensions import LiteralString @@ -17,6 +18,11 @@ class SpanConfig(NamedTuple): span_data: Dict[str, Any] +class LogfireLevel(str, Enum): + INFO = "info" + ERROR = "error" + + class LogfireLogger: # Class variables or attributes def __init__(self): @@ -63,12 +69,31 @@ class LogfireLogger: ) async def _async_log_event( - self, kwargs, response_obj, start_time, end_time, print_verbose, user_id + self, + kwargs, + response_obj, + start_time, + end_time, + print_verbose, + level: LogfireLevel, ): - self.log_event(kwargs, response_obj, start_time, end_time, print_verbose) + self.log_event( + kwargs=kwargs, + response_obj=response_obj, + start_time=start_time, + end_time=end_time, + print_verbose=print_verbose, + level=level, + ) def log_event( - self, kwargs, response_obj, start_time, end_time, user_id, print_verbose + self, + kwargs, + start_time, + end_time, + print_verbose, + level: LogfireLevel, + response_obj, ): try: import logfire @@ -76,6 +101,9 @@ class LogfireLogger: verbose_logger.debug( f"logfire Logging - Enters logging function for model {kwargs}" ) + + if not response_obj: + response_obj = {} litellm_params = kwargs.get("litellm_params", {}) metadata = ( litellm_params.get("metadata", {}) or {} @@ -127,11 +155,17 @@ class LogfireLogger: logfire_openai = logfire.with_settings(custom_scope_suffix="openai") message_template, span_data = self._get_span_config(payload) - with logfire_openai.span( - message_template, - **span_data, - ): - pass + if level == LogfireLevel.INFO: + logfire_openai.info( + message_template, + **span_data, + ) + elif level == LogfireLevel.ERROR: + logfire_openai.error( + message_template, + **span_data, + exc_info=str(kwargs.get("exception", None)), + ) print_verbose(f"\ndd Logger - Logging payload = {payload}") print_verbose( diff --git a/litellm/utils.py b/litellm/utils.py index 1a0310928e8..0cd0b4c6c5b 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -66,7 +66,7 @@ from .integrations.supabase import Supabase from .integrations.lunary import LunaryLogger from .integrations.prompt_layer import PromptLayerLogger from .integrations.langsmith import LangsmithLogger -from .integrations.logfire_logger import LogfireLogger +from .integrations.logfire_logger import LogfireLogger, LogfireLevel from .integrations.weights_biases import WeightsBiasesLogger from .integrations.custom_logger import CustomLogger from .integrations.langfuse import LangFuseLogger @@ -1646,14 +1646,14 @@ class Logging: else: print_verbose("reaches logfire for streaming logging!") result = kwargs["complete_streaming_response"] - + logfireLogger.log_event( kwargs=self.model_call_details, response_obj=result, start_time=start_time, end_time=end_time, print_verbose=print_verbose, - user_id=kwargs.get("user", None), + level=LogfireLevel.INFO.value, ) if callback == "lunary": @@ -2346,7 +2346,9 @@ class Logging: def failure_handler( self, exception, traceback_exception, start_time=None, end_time=None ): - print_verbose(f"Logging Details LiteLLM-Failure Call") + print_verbose( + f"eLogging Details LiteLLM-Failure Call: {litellm.failure_callback}" + ) try: start_time, end_time = self._failure_handler_helper_fn( exception=exception, @@ -2359,6 +2361,7 @@ class Logging: self.redact_message_input_output_from_logging(result=result) for callback in litellm.failure_callback: + print_verbose(f"failure_callback: {callback}") try: if callback == "lite_debugger": print_verbose("reaches lite_debugger for logging!") @@ -2386,7 +2389,7 @@ class Logging: call_type=self.call_type, stream=self.stream, ) - elif callback == "lunary": + if callback == "lunary": print_verbose("reaches lunary for logging error!") model = self.model @@ -2411,7 +2414,7 @@ class Logging: end_time=end_time, print_verbose=print_verbose, ) - elif callback == "sentry": + if callback == "sentry": print_verbose("sending exception to sentry") if capture_exception: capture_exception(exception) @@ -2419,7 +2422,7 @@ class Logging: print_verbose( f"capture exception not initialized: {capture_exception}" ) - elif callable(callback): # custom logger functions + if callable(callback): # custom logger functions customLogger.log_event( kwargs=self.model_call_details, response_obj=result, @@ -2428,7 +2431,7 @@ class Logging: print_verbose=print_verbose, callback_func=callback, ) - elif ( + if ( isinstance(callback, CustomLogger) and self.model_call_details.get("litellm_params", {}).get( "acompletion", False @@ -2445,7 +2448,7 @@ class Logging: response_obj=result, kwargs=self.model_call_details, ) - elif callback == "langfuse": + if callback == "langfuse": global langFuseLogger verbose_logger.debug("reaches langfuse for logging!") kwargs = {} @@ -2473,7 +2476,7 @@ class Logging: level="ERROR", kwargs=self.model_call_details, ) - elif callback == "prometheus": + if callback == "prometheus": global prometheusLogger verbose_logger.debug("reaches prometheus for success logging!") kwargs = {} @@ -2491,6 +2494,26 @@ class Logging: user_id=kwargs.get("user", None), print_verbose=print_verbose, ) + + if callback == "logfire": + global logfireLogger + verbose_logger.debug("reaches logfire for failure logging!") + kwargs = {} + for k, v in self.model_call_details.items(): + if ( + k != "original_response" + ): # copy.deepcopy raises errors as this could be a coroutine + kwargs[k] = v + kwargs["exception"] = exception + + logfireLogger.log_event( + kwargs=kwargs, + response_obj=result, + start_time=start_time, + end_time=end_time, + level=LogfireLevel.ERROR.value, + print_verbose=print_verbose, + ) except Exception as e: print_verbose( f"LiteLLM.LoggingError: [Non-Blocking] Exception occurred while failure logging with integrations {str(e)}" @@ -3227,6 +3250,7 @@ def client(original_function): return original_function(*args, **kwargs) traceback_exception = traceback.format_exc() end_time = datetime.datetime.now() + # LOG FAILURE - handle streaming failure logging in the _next_ object, remove `handle_failure` once it's deprecated if logging_obj: logging_obj.failure_handler(