Python Loguru Exception Handling and Tracebacks
python loguru exception handling and tracebacks: Learn how Loguru captures exceptions, formats tracebacks, and how to control depth, diagnose mode, and production beha...
Loguru changes how Python exception tracebacks appear in your logs. When an unhandled exception reaches the interpreter, Loguru's default handler prints the traceback with the same level of detail as the standard library, but with its own formatting. This article covers python loguru exception handling and tracebacks: how to capture exceptions, control traceback depth, and avoid common pitfalls.
How Loguru Handles Unhandled Exceptions by Default
By default, Loguru installs a handler that catches unhandled exceptions and logs them at ERROR level. The traceback is formatted using the {exception} placeholder in the format string. If you add a handler with logger.add("app.log"), an unhandled exception will produce a log entry like this:
from loguru import logger logger.add("app.log", level="ERROR") def divide(a, b): return a / b divide(1, 0)
When this script runs, Loguru logs the ZeroDivisionError with the full traceback. The default format includes the exception type, message, and stack frames. This behavior is useful for quick debugging, but you may need to customize it for production.
Capturing Exceptions with @logger.catch
The @logger.catch decorator is the simplest way to ensure an exception is logged with its traceback without letting it propagate. It wraps a function and logs any exception that occurs, then re-raises it by default. You can change this behavior with the reraise parameter.
from loguru import logger @logger.catch def risky_operation(): return 1 / 0 risky_operation()
The traceback is logged at ERROR level, and the exception is re-raised. If you want to swallow the exception and return a default value, set reraise=False:
@logger.catch(reraise=False, default=None) def safe_divide(a, b): return a / b
The decorator also accepts level, message, and exception parameters to control the log entry. For instance, you can log a custom message while preserving the traceback.
Logging Exception Objects with logger.exception()
Inside an except block, logger.exception() logs the current exception with its traceback. This method is equivalent to logger.error("...", exc_info=True) in the standard logging module. It is the most direct way to record an exception that you have already caught.
try: result = int("not a number") except ValueError as e: logger.exception("Failed to parse input")
The output includes the exception type, message, and the full stack trace at the point where the exception was raised. If you need to log an exception object that is not currently being handled, use logger.opt(exception=e):
try: result = int("not a number") except ValueError as e: logger.opt(exception=e).error("Custom message")
This allows you to log an arbitrary exception with its traceback, even outside an except block.
Customizing Traceback Formatting and Depth
Loguru's traceback formatting is controlled by two options in logger.add(): backtrace and diagnose. The backtrace option (default True) controls whether the full stack trace is shown, including frames outside the current exception context. Setting backtrace=False limits the traceback to the frames between the try block and the exception, which can reduce noise.
The diagnose option (default True) includes variable values in the traceback. This is extremely helpful during development but can leak sensitive information in production. Set diagnose=False to hide variable values.
logger.add("app.log", backtrace=False, diagnose=False)
You can also customize the traceback format using the {exception} placeholder in the format string. For example, to show only the exception type and message without the stack, use:
logger.add("app.log", format="{time} | {level} | {message} | {exception}")
If you need to control the number of stack frames shown, you can use Loguru's traceback module directly, but that is rarely necessary. The backtrace and diagnose options cover most use cases.
Using Exception Groups and Chained Exceptions
Python 3.11 introduced exception groups and the except* syntax. Loguru handles these naturally because it uses the standard traceback module. When an exception group is logged, the traceback includes the nested exceptions. For chained exceptions (using raise ... from ...), Loguru shows the cause chain.
try: raise ValueError("original") from RuntimeError("cause") except Exception: logger.exception("Chained exception")
The log entry will include both the cause and the original exception. This behavior is consistent with the standard library's traceback.format_exception, so you can rely on it for complex error scenarios.
Performance and Production Considerations for Traceback Logging
Formatting a traceback is CPU-intensive because it involves walking the stack and formatting each frame. In high-throughput applications, logging a traceback for every error can become a bottleneck. Use the backtrace=False option to reduce the amount of work, and consider logging only the exception message in hot paths.
The diagnose option also has a performance cost because it inspects variable values. In production, set diagnose=False to avoid the overhead and to prevent sensitive data from appearing in logs. If you need tracebacks for debugging, enable them only in a development environment or via a runtime flag.
Another production concern is log volume. Tracebacks can be large, especially with deep call stacks. Use log rotation and retention policies to manage disk usage. Loguru's rotation and retention parameters in logger.add() help with this.
Common Pitfalls When Combining Loguru with Other Logging Handlers
Loguru does not use the standard logging module by default. If your application also uses libraries that write to the standard logging system, you need to route those messages to Loguru. The logging module's LoguruHandler can be added to a standard logger to forward records.
import logging from loguru import logger class InterceptHandler(logging.Handler): def emit(self, record): logger_opt = logger.opt(depth=6, exception=record.exc_info) logger_opt.log(record.levelno, record.getMessage()) logging.basicConfig(handlers=[InterceptHandler()], level=0)
This ensures that tracebacks from third-party libraries appear in Loguru's output with the same formatting. Without this, you may see duplicate or missing logs.
Another pitfall is double logging. If you have both a standard logging handler and Loguru's default handler, an exception might be logged twice. Configure one system as the primary sink and use the other only for forwarding.