
Python
Learn to use Python's logging module instead of print statements for production-ready error tracking.
Print statements have problems:
# ❌ Problems with print
print("User login attempted") # Always shows, no way to turn off
print("ERROR: Database connection failed") # No timestamp
print("Debug info:", user_data) # Pollutes output with debug info
# ✅ Better: use logging
import logging
logging.info("User login attempted")
logging.error("Database connection failed")
logging.debug("Debug info: %s", user_data) # Can disable debug messagesLogging advantages:
import logging
# Simple setup
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Now use logging
logging.debug("Detailed debug information")
logging.info("General informational messages")
logging.warning("Warning messages for potential issues")
logging.error("Error messages for serious problems")
logging.critical("Critical messages for emergency situations")
# Output:
# 2024-02-23 10:30:45,123 - root - DEBUG - Detailed debug information
# 2024-02-23 10:30:45,124 - root - INFO - General informational messages
# 2024-02-23 10:30:45,125 - root - WARNING - Warning messages for potential issues
# 2024-02-23 10:30:45,126 - root - ERROR - Error messages for serious problems
# 2024-02-23 10:30:45,127 - root - CRITICAL - Critical messages for emergency situations| Level | When to Use | Example |
|---|---|---|
| DEBUG | Development details | Variable values, loop iterations |
| INFO | Important events | "User logged in", "File processed" |
| WARNING | Potential issues | "API rate limit approaching" |
| ERROR | Something failed | "Connection timeout", "Invalid input" |
| CRITICAL | System failure | "Out of disk space", "Database down" |
import logging
# Different severity levels
def process_user_registration(username, email):
logging.debug(f"Processing registration: username={username}")
if not validate_email(email):
logging.warning(f"Invalid email format: {email}")
return False
try:
save_to_database(username, email)
logging.info(f"User registered successfully: {username}")
return True
except DatabaseError as e:
logging.error(f"Database error during registration: {e}")
return False
except Exception as e:
logging.critical(f"Unexpected error during registration: {e}")
raiseimport logging
# Log to both console and file
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('app.log'), # File handler
logging.StreamHandler() # Console handler
]
)
logging.info("This goes to both file and console")
# Only important messages to console, everything to file
import logging
# Setup logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# File handler (gets all messages)
file_handler = logging.FileHandler('debug.log')
file_handler.setLevel(logging.DEBUG)
# Console handler (only warnings and above)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING)
# Format
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# Add handlers
logger.addHandler(file_handler)
logger.addHandler(console_handler)
# Now use
logger.debug("Debug info (only in file)")
logger.warning("Warning (console and file)")
logger.error("Error (console and file)")import logging
import sys
# Create logger for your module
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# Create handlers
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.INFO)
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)
# Create formatter
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# Add handlers
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# Now use in your code
def fetch_api_data(endpoint):
logger.debug(f"Fetching from endpoint: {endpoint}")
try:
response = requests.get(endpoint, timeout=5)
logger.info(f"Successfully fetched data from {endpoint}")
return response.json()
except requests.Timeout:
logger.warning(f"Request timeout for {endpoint}")
return None
except requests.ConnectionError as e:
logger.error(f"Connection error: {e}")
return None
except Exception as e:
logger.critical(f"Unexpected error: {e}")
raiseimport logging
logger = logging.getLogger(__name__)
# Example 1: File operations with logging
def read_config_file(filename):
logger.info(f"Reading configuration from {filename}")
try:
with open(filename) as f:
config = json.load(f)
logger.debug(f"Successfully loaded config with {len(config)} keys")
return config
except FileNotFoundError:
logger.error(f"Configuration file not found: {filename}")
logger.info("Using default configuration")
return {}
except json.JSONDecodeError as e:
logger.error(f"Invalid JSON in {filename}: {e}")
return {}
# Example 2: Database operations with logging
class UserDatabase:
def __init__(self, connection_string):
self.connection_string = connection_string
logger.info(f"Initializing database: {connection_string}")
def get_user(self, user_id):
logger.debug(f"Fetching user {user_id}")
try:
# Simulate database query
user = self._query(f"SELECT * FROM users WHERE id = {user_id}")
logger.debug(f"Found user: {user['name']}")
return user
except DatabaseError as e:
logger.error(f"Database error fetching user {user_id}: {e}")
raise
except Exception as e:
logger.critical(f"Unexpected error fetching user {user_id}: {e}")
raise
def _query(self, query):
pass
# Example 3: API request logging
def call_external_api(endpoint, params):
logger.info(f"Calling external API: {endpoint}")
logger.debug(f"Parameters: {params}")
try:
response = requests.post(endpoint, json=params, timeout=5)
response.raise_for_status()
logger.info(f"API call successful: {endpoint}")
logger.debug(f"Response: {response.text[:200]}") # Log first 200 chars
return response.json()
except requests.Timeout:
logger.warning(f"API timeout: {endpoint}")
return None
except requests.HTTPError as e:
logger.error(f"API error {response.status_code}: {e}")
return None
except Exception as e:
logger.critical(f"Unexpected API error: {e}")
raiseimport logging
# 1. Include exception info
logger = logging.getLogger(__name__)
def risky_operation():
try:
result = 10 / 0
except ZeroDivisionError:
# Log with exception info
logger.error("Division by zero", exc_info=True)
# Output includes full traceback!
# 2. Variable substitution (safer than f-strings for logging)
logger.info("Processing file: %s with size %d bytes", filename, file_size)
# 3. Multiple loggers for different modules
api_logger = logging.getLogger('myapp.api')
db_logger = logging.getLogger('myapp.database')
auth_logger = logging.getLogger('myapp.auth')
# Each can have different configuration
# 4. Logger hierarchy
root_logger = logging.getLogger()
app_logger = logging.getLogger('myapp')
auth_logger = logging.getLogger('myapp.auth')
# auth_logger inherits from app_logger which inherits from root_logger# ❌ Mistake 1: Logging personal data
logger.info(f"User created: {email}, password: {password}")
# ✅ Better: don't log sensitive data
logger.info(f"User created: {email}")
# ❌ Mistake 2: Using print in production
print("Error occurred!") # Hard to control
# ✅ Better: use logging
logger.error("Error occurred!") # Can disable or redirect
# ❌ Mistake 3: Not setting up logging properly
# Just using logging.info() without configuration
# ✅ Better: configure at application start
if __name__ == '__main__':
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('app.log'),
logging.StreamHandler()
]
)
main()
# ❌ Mistake 4: Logging too much
for item in millions_of_items:
logger.debug(f"Processing {item}") # Creates massive log file!
# ✅ Better: log summaries
logger.info(f"Processing {len(items)} items")
# Then log details only for errorsimport logging
import logging.config
# Use a config dictionary for complex setups
LOGGING_CONFIG = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
},
'detailed': {
'format': '%(asctime)s [%(levelname)s] %(filename)s:%(lineno)d - %(funcName)s() - %(message)s'
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'INFO',
'formatter': 'standard',
'stream': 'ext://sys.stdout'
},
'file': {
'class': 'logging.FileHandler',
'level': 'DEBUG',
'formatter': 'detailed',
'filename': 'debug.log'
},
},
'loggers': {
'': {
'handlers': ['console', 'file'],
'level': 'DEBUG',
}
}
}
logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger(__name__)
# Now all loggers are configured consistently
logger.info("Application started")Completed: Error Handling Fundamentals
Resources
Ojasa Mirai
Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.
Learn Deep • Build Real • Verify Skills • Launch Forward