- What is logging — understanding the logging module
- Why log exceptions — benefits of logging errors
- Basic logging setup — configuring the logger
- Logging levels — DEBUG, INFO, WARNING, ERROR, CRITICAL
- Logging exceptions — using logging.exception()
- Logging to files — saving logs to files
- Formatting — customizing log messages
- Best practices — writing effective logs
What is Logging?
Logging is the process of recording events, errors, and information that occur during a program's execution. Python's built-in logging module provides a flexible and powerful way to add logging to your applications.
Definition: Logging is a systematic way to record information about a program's execution, including errors, warnings, and informational messages, for debugging, monitoring, and analysis purposes.
Unlike print() statements, logging:
- Allows different severity levels
- Can be directed to files and console
- Provides timestamps and context
- Can be configured without changing code
- Is production-ready
💡 Key concept: Logging is essential for production applications. It helps you understand what happened when something goes wrong.
Why Log Exceptions?
Benefits of Exception Logging
# The problem with print()
def process_data(data):
try:
result = complex_operation(data)
return result
except Exception as e:
print(f"Error: {e}") # Error is printed but lost
# With logging
import logging
logging.basicConfig(level=logging.INFO)
def process_data(data):
try:
result = complex_operation(data)
return result
except Exception as e:
logging.error(f"Failed to process data: {e}")
# Error is logged with timestamp and context
# Can be sent to files, monitoring systems, etc.
# Benefits of logging:
# 1. Persistent records
# 2. Timestamps for debugging
# 3. Different severity levels
# 4. Can be filtered and searched
# 5. Production-ready
Key benefits:
- Persistence — logs remain after program ends
- Context — timestamps, module names, line numbers
- Organization — different levels for different severity
- Analysis — search, filter, and analyze errors
- Monitoring — integrate with monitoring tools
Quick Check: Why is logging better than print() for errors? (Answer: Logs are persistent, have timestamps, and can be filtered)
Basic Logging Setup
Setting Up the Logger
import logging
# Basic setup
logging.basicConfig(level=logging.INFO)
# Logging messages
logging.debug("This is a debug message")
logging.info("This is an info message")
logging.warning("This is a warning message")
logging.error("This is an error message")
logging.critical("This is a critical message")
# Output:
# INFO:root:This is an info message
# WARNING:root:This is a warning message
# ERROR:root:This is an error message
# CRITICAL:root:This is a critical message
# Note: DEBUG messages are not shown because level is INFO
Key points:
basicConfig()sets up the logging systemlevelcontrols which messages are shown- Default format:
LEVEL:logger_name:message - Root logger name is
root
Quick Check: What function is used to configure logging? (Answer: logging.basicConfig())
Logging Levels
Understanding Severity Levels
import logging
logging.basicConfig(level=logging.DEBUG)
# All levels
logging.debug("Debug - detailed information for debugging")
logging.info("Info - confirmation that things are working")
logging.warning("Warning - something unexpected happened")
logging.error("Error - a serious problem occurred")
logging.critical("Critical - a fatal error occurred")
# Severity levels (increasing severity):
# DEBUG = 10
# INFO = 20
# WARNING = 30
# ERROR = 40
# CRITICAL = 50
# Setting the level
logging.basicConfig(level=logging.WARNING)
# Only WARNING, ERROR, and CRITICAL will be shown
# Programmatic levels
logging.log(logging.DEBUG, "Debug message")
When to use each level:
- DEBUG — detailed information for developers
- INFO — confirming things are working as expected
- WARNING — something unexpected but not fatal
- ERROR — a serious problem, operation failed
- CRITICAL — a fatal error, program may stop
Quick Check: Which logging level is the most severe? (Answer: CRITICAL)
Logging Exceptions
Using logging.exception()
import logging
logging.basicConfig(level=logging.ERROR)
def divide(a, b):
try:
return a / b
except ZeroDivisionError as e:
# logging.exception() automatically includes traceback
logging.exception("Division by zero occurred")
return None
result = divide(10, 0)
# Output:
# ERROR:root:Division by zero occurred
# Traceback (most recent call last):
# File "", line 3, in divide
# ZeroDivisionError: division by zero
# Using error() with exc_info=True
def process_data(data):
try:
# Some operation
result = risky_operation(data)
return result
except Exception as e:
# This also logs the traceback
logging.error(f"Error processing data: {e}", exc_info=True)
return None
Key points:
logging.exception()logs at ERROR level- It automatically includes the traceback
- Use
exc_info=Truewith other logging methods - This is the preferred way to log exceptions
Quick Check: What method automatically includes the traceback when logging? (Answer: logging.exception())
Logging Exception Details
Capturing Error Information
import logging
import traceback
logging.basicConfig(level=logging.ERROR)
def log_error_details():
try:
risky_operation()
except Exception as e:
# 1. Log the exception with traceback
logging.exception("An error occurred")
# 2. Log specific details
logging.error(f"Error type: {type(e).__name__}")
logging.error(f"Error message: {str(e)}")
# 3. Log the stack trace
tb = traceback.format_exc()
logging.error(f"Stack trace:\n{tb}")
# 4. Log custom context
logging.error(f"Context: user_id=123, operation='calculate'")
# Example output:
# ERROR:root:An error occurred
# Traceback (most recent call last):
# ...
# ERROR:root:Error type: ValueError
# ERROR:root:Error message: invalid value
# ERROR:root:Stack trace: ...
# ERROR:root:Context: user_id=123, operation='calculate'
Best practices for exception logging:
- Log the exception type and message
- Include context (user, operation, data)
- Use traceback for debugging
- Log at the appropriate level
Logging to Files
Saving Logs to Files
import logging
# Configure logging to write to a file
logging.basicConfig(
level=logging.INFO,
filename='app.log',
filemode='a', # 'a' for append, 'w' for overwrite
format='%(asctime)s - %(levelname)s - %(message)s'
)
# Log messages
logging.info("Application started")
logging.warning("Low disk space warning")
try:
result = 10 / 0
except ZeroDivisionError:
logging.exception("Division error occurred")
# The logs will be written to app.log
# Creating a custom logger with multiple handlers
logger = logging.getLogger(__name__)
# Console handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# File handler
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.ERROR)
# Add handlers
logger.addHandler(console_handler)
logger.addHandler(file_handler)
Key points:
filename— specify the log file namefilemode— 'a' (append) or 'w' (overwrite)- Use handlers for multiple destinations
- Different handlers can have different levels
Quick Check: What parameter specifies the log file name? (Answer: filename)
Formatting Log Messages
Customizing Log Format
import logging
# Basic format
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Available format attributes:
# %(asctime)s - timestamp
# %(name)s - logger name
# %(levelname)s - severity level
# %(message)s - the log message
# %(pathname)s - file path
# %(filename)s - file name
# %(lineno)d - line number
# %(funcName)s - function name
# %(thread)d - thread ID
# %(process)d - process ID
# Advanced format with all details
logging.basicConfig(
format='%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(funcName)s() - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# Custom formatter
formatter = logging.Formatter(
'%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s'
)
# Applying formatter to a handler
handler = logging.StreamHandler()
handler.setFormatter(formatter)
Key format attributes:
- %(asctime)s — timestamp
- %(levelname)s — severity level
- %(filename)s — source file name
- %(lineno)d — line number
- %(funcName)s — function name
Quick Check: What attribute gives you the line number? (Answer: %(lineno)d)
Best Practices
Writing Effective Logs
import logging
# 1. Use a named logger
logger = logging.getLogger(__name__)
# 2. Configure once at module level
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# 3. Log at appropriate levels
def process_user(user_id):
logger.info(f"Processing user: {user_id}")
try:
# Operation
pass
except Exception as e:
logger.exception(f"Failed to process user {user_id}")
# Don't just log the error message, include context
# 4. Include context in logs
logger.info(f"User {user_id} - Action completed in {duration:.2f}s")
# 5. Avoid logging sensitive information
# WRONG: logger.info(f"User password: {password}")
# CORRECT: logger.info(f"User login attempt: {username}")
# 6. Use structured logging for better searching
logger.info(f"Event: user_login, user: {user_id}, status: success")
# 7. Configure logging in a central place
def setup_logging():
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('app.log'),
logging.StreamHandler()
]
)
Best practices summary:
- Use named loggers —
logging.getLogger(__name__) - Log at appropriate levels — INFO for normal flow, ERROR for exceptions
- Include context — user IDs, operation names, timings
- Avoid sensitive data — no passwords, personal info
- Use structured format — for better searching
- Configure centrally — one place for logging setup
Common Mistakes
Things to Watch Out For
Using print() Instead of logging
# WRONG — print() is temporary
print("Error occurred")
# CORRECT — logging is permanent
import logging
logging.error("Error occurred")
Not Including Exception Context
# WRONG — no context
try:
result = divide(a, b)
except ZeroDivisionError:
logging.error("Division error")
# CORRECT — include context
try:
result = divide(a, b)
except ZeroDivisionError:
logging.error(f"Division error: {a}/{b}")
Logging at the Wrong Level
# WRONG — using INFO for errors
logging.info("Database connection failed")
# CORRECT — use ERROR for errors
logging.error("Database connection failed")
# WRONG — using ERROR for normal operations
logging.error("User logged in")
# CORRECT — use INFO for normal operations
logging.info("User logged in")
Quick Check: What is the most common mistake with logging? (Answer: Using print() instead of logging)
Try It Yourself
Experiment with logging exceptions in the editor below. Modify the code and see what happens.
LOGGING EXCEPTION PRACTICE
========================================
Logging levels:
INFO message
WARNING message
ERROR message
1. LOGGING EXCEPTION
ERROR:root:Division by zero occurred
Traceback (most recent call last):
File "<stdin>", line 4, in divide
ZeroDivisionError: division by zero
2. LOGGING WITH CONTEXT
ERROR:root:Division error: 10/0
3. DIFFERENT LOGGING LEVELS
INFO:root:This is an informational message
WARNING:root:This is a warning
ERROR:root:This is an error
Logging exception practice complete!
You've Got It!
You now understand logging exceptions in Python — setting up logging, logging levels, logging.exception(), logging to files, formatting, and best practices. This is a professional skill for production applications!
Quick Quiz
Test what you've learned:
Frequently Asked Questions
What is the difference between logging and print()?
logging provides timestamps, severity levels, persistence to files, and can be configured without changing code. print() is for simple debugging and is not suitable for production.
What is the difference between logging.error() and logging.exception()?
logging.exception() automatically includes the traceback. logging.error() just logs the message. Use exception() when logging exceptions, error() for other errors.
How do I log to a file?
logging.basicConfig(filename='app.log') or add a FileHandler. This will write all logs to the specified file.
What are the different logging levels?
How do I format log messages?
format parameter in basicConfig(). Example: format='%(asctime)s - %(levelname)s - %(message)s'.
What's a common interview question about logging?
Where to Go From Here
Now that you understand logging exceptions, check out these related topics:
Exception Assignments
Practice what you've learned with assignments.
Learn More →User Defined Exception
Create your own custom exceptions.
Learn More →Exception Handling
Review the basics of exception handling.
Learn More →