Files
letta-server/letta/exceptions/logging.py
Kian Jones 4acda9c80f feat: global exception middleware (#6017)
* global exception middleware

* redo both logging middlewares as one

* remove extra middleware files
2025-11-13 15:36:55 -08:00

138 lines
3.7 KiB
Python

"""
Helper utilities for structured exception logging.
Use these when you need to add context to exceptions before raising them.
"""
from typing import Any, Dict, Optional
from letta.log import get_logger
logger = get_logger(__name__)
def log_and_raise(
exception: Exception,
message: str,
context: Optional[Dict[str, Any]] = None,
level: str = "error",
) -> None:
"""
Log an exception with structured context and then raise it.
This is useful when you want to ensure an exception is logged with
full context before raising it.
Args:
exception: The exception to log and raise
message: Human-readable message to log
context: Additional context to include in logs (dict)
level: Log level (default: "error")
Example:
try:
result = some_operation()
except ValueError as e:
log_and_raise(
e,
"Failed to process operation",
context={
"user_id": user.id,
"operation": "some_operation",
"input": input_data,
}
)
"""
extra = {
"exception_type": exception.__class__.__name__,
"exception_message": str(exception),
"exception_module": exception.__class__.__module__,
}
if context:
extra.update(context)
log_method = getattr(logger, level.lower())
log_method(
f"{message}: {exception.__class__.__name__}: {str(exception)}",
extra=extra,
exc_info=exception,
)
raise exception
def log_exception(
exception: Exception,
message: str,
context: Optional[Dict[str, Any]] = None,
level: str = "error",
) -> None:
"""
Log an exception with structured context without raising it.
Use this when you want to log an exception but handle it gracefully.
Args:
exception: The exception to log
message: Human-readable message to log
context: Additional context to include in logs (dict)
level: Log level (default: "error")
Example:
try:
result = some_operation()
except ValueError as e:
log_exception(
e,
"Operation failed, using fallback",
context={"user_id": user.id}
)
result = fallback_operation()
"""
extra = {
"exception_type": exception.__class__.__name__,
"exception_message": str(exception),
"exception_module": exception.__class__.__module__,
}
if context:
extra.update(context)
log_method = getattr(logger, level.lower())
log_method(
f"{message}: {exception.__class__.__name__}: {str(exception)}",
extra=extra,
exc_info=exception,
)
def add_exception_context(exception: Exception, **context) -> Exception:
"""
Add context to an exception that will be picked up by the global exception handler.
This attaches a __letta_context__ attribute to the exception with structured data.
The global exception handler will automatically include this context in logs.
Args:
exception: The exception to add context to
**context: Key-value pairs to add as context
Returns:
The same exception with context attached
Example:
try:
result = operation()
except ValueError as e:
raise add_exception_context(
e,
user_id=user.id,
operation="do_thing",
input_data=data,
)
"""
if not hasattr(exception, "__letta_context__"):
exception.__letta_context__ = {}
exception.__letta_context__.update(context)
return exception