Introduction
When an exception occurs in a Python application, the traceback — the chain of function calls leading to the error — is essential for debugging. Simply catching and printing the exception message loses this context. Python's logging module provides logger.exception() and exc_info=True to capture the full traceback in log output, making it possible to diagnose errors from log files without a debugger attached.
Method 1: logging.exception()
The simplest and most common approach — call logger.exception() inside an except block:
1import logging
2
3logger = logging.getLogger(__name__)
4logging.basicConfig(level=logging.DEBUG)
5
6def divide(a, b):
7 return a / b
8
9try:
10 result = divide(10, 0)
11except Exception:
12 logger.exception("Division failed")
13
14# Output:
15# ERROR:__main__:Division failed
16# Traceback (most recent call last):
17# File "example.py", line 10, in <module>
18# result = divide(10, 0)
19# File "example.py", line 7, in divide
20# return a / b
21# ZeroDivisionError: division by zero
logger.exception() automatically logs at ERROR level and includes the full traceback. It must be called inside an except block where the exception is active.
Method 2: exc_info=True
Use exc_info=True with any log level — not just ERROR:
1try:
2 result = divide(10, 0)
3except Exception:
4 logger.warning("Division failed, using fallback", exc_info=True)
5 result = 0
6
7# Logs the warning message WITH the full traceback
8# WARNING:__main__:Division failed, using fallback
9# Traceback (most recent call last):
10# ...
11# ZeroDivisionError: division by zero
This is useful when you handle the error gracefully but still want the traceback logged at a non-ERROR level.
Method 3: traceback Module
For more control over traceback formatting, use the traceback module directly:
1import traceback
2
3try:
4 result = divide(10, 0)
5except Exception:
6 # Get traceback as a string
7 tb_str = traceback.format_exc()
8 logger.error(f"Division failed:\n{tb_str}")
9
10 # Or print to stderr without logging
11 traceback.print_exc()
1import traceback
2import sys
3
4try:
5 result = divide(10, 0)
6except Exception:
7 exc_type, exc_value, exc_tb = sys.exc_info()
8
9 # List of FrameSummary objects
10 frames = traceback.extract_tb(exc_tb)
11 for frame in frames:
12 print(f" File {frame.filename}, line {frame.lineno}, in {frame.name}")
13 print(f" {frame.line}")
14
15 # Just the last frame (where the error occurred)
16 last_frame = frames[-1]
17 print(f"Error at {last_frame.filename}:{last_frame.lineno}")
1import logging
2
3# Include timestamp, level, and module in log output
4logging.basicConfig(
5 level=logging.DEBUG,
6 format='%(asctime)s %(levelname)s %(name)s %(message)s',
7 datefmt='%Y-%m-%d %H:%M:%S'
8)
9
10logger = logging.getLogger('myapp')
11
12# Log to file
13file_handler = logging.FileHandler('app.log')
14file_handler.setLevel(logging.ERROR)
15file_handler.setFormatter(logging.Formatter(
16 '%(asctime)s %(levelname)s %(name)s\n%(message)s'
17))
18logger.addHandler(file_handler)
19
20try:
21 result = divide(10, 0)
22except Exception:
23 logger.exception("Division failed")
24 # Written to both console and app.log with full traceback
Logging Exceptions in Async Code
1import asyncio
2import logging
3
4logger = logging.getLogger(__name__)
5
6async def fetch_data(url):
7 raise ConnectionError(f"Failed to connect to {url}")
8
9async def main():
10 try:
11 await fetch_data("https://example.com")
12 except ConnectionError:
13 logger.exception("Connection failed")
14
15asyncio.run(main())
16# The traceback correctly shows the async call stack
Add structured context to exception logs:
1def process_order(order_id, user_id):
2 try:
3 # ... processing logic ...
4 raise ValueError("Invalid quantity")
5 except Exception:
6 logger.exception(
7 "Order processing failed",
8 extra={'order_id': order_id, 'user_id': user_id}
9 )
10
11# With a custom formatter that includes extra fields:
12# 2026-03-01 10:30:00 ERROR Order processing failed [order_id=123, user_id=456]
13# Traceback (most recent call last):
14# ...
Anti-Pattern: Swallowing Exceptions
1# BAD — loses the traceback entirely
2try:
3 result = divide(10, 0)
4except Exception as e:
5 logger.error(f"Error: {e}")
6 # Only logs: "Error: division by zero"
7 # No traceback, no file/line information
8
9# BAD — bare except with pass
10try:
11 result = divide(10, 0)
12except Exception:
13 pass # Silent failure — impossible to debug
14
15# GOOD — always log with traceback
16try:
17 result = divide(10, 0)
18except Exception:
19 logger.exception("Division failed")
20 raise # Re-raise if the caller should handle it
Common Pitfalls
Using logger.error(str(e)) instead of logger.exception(): This logs only the exception message, losing the traceback. Always use logger.exception() or exc_info=True to capture the full stack trace.
Calling logger.exception() outside an except block: It will log NoneType: None as the traceback because there is no active exception. Only call it inside an except clause.
Catching too broadly: except Exception catches everything including programming errors like TypeError and AttributeError. Catch specific exceptions when possible, and use except Exception only as a top-level safety net.
Logging and re-raising without care: If you logger.exception() and then raise, the same exception may be logged multiple times as it propagates up through multiple try/except blocks. Log at the point where you handle the error, not at every level.
Missing logger configuration: If logging.basicConfig() is never called and no handlers are configured, log messages are silently discarded (Python 3.2+ shows a warning). Always configure logging at application startup.
Summary
Use logger.exception("message") inside except blocks to log the full traceback at ERROR level
Use logger.error("message", exc_info=True) or any other level with exc_info=True for non-ERROR traceback logging
Use the traceback module (traceback.format_exc()) when you need the traceback as a string
Never log just str(e) — the traceback is the most valuable debugging information
Configure logging with file handlers and formatters for production applications