Prerequisites
- Basic understanding of programming concepts ๐
- Python installation (3.8+) ๐
- VS Code or preferred IDE ๐ป
What you'll learn
- Understand the concept fundamentals ๐ฏ
- Apply the concept in real projects ๐๏ธ
- Debug common issues ๐
- Write clean, Pythonic code โจ
๐ฏ Introduction
Welcome to this exciting tutorial on Python logging levels! ๐ In this guide, weโll explore how DEBUG, INFO, and WARNING levels can transform your debugging and monitoring experience.
Youโll discover how proper logging can be your secret weapon for understanding whatโs happening inside your applications. Whether youโre building web APIs ๐, automation scripts ๐ค, or data pipelines ๐, mastering logging levels is essential for creating maintainable and debuggable code.
By the end of this tutorial, youโll confidently use logging levels to track your applicationโs behavior like a pro! Letโs dive in! ๐โโ๏ธ
๐ Understanding Logging Levels
๐ค What Are Logging Levels?
Logging levels are like different volumes on your stereo ๐๏ธ. Think of them as filters that control how much information your application shares with you - from whispers (DEBUG) to normal conversation (INFO) to loud warnings (WARNING).
In Python terms, logging levels help you categorize messages by importance and control what gets recorded. This means you can:
- โจ Debug issues with detailed trace information
- ๐ Monitor normal application flow
- ๐ก๏ธ Catch potential problems before they become errors
๐ก Why Use Logging Levels?
Hereโs why developers love logging levels:
- Flexible Debugging ๐: Turn on DEBUG in development, off in production
- Performance Control โก: Less logging = faster execution
- Organized Output ๐: Filter noise, focus on what matters
- Problem Detection ๐ฏ: Spot issues before users do
Real-world example: Imagine running an online store ๐. With logging levels, you can track detailed payment processing in DEBUG mode during development, monitor successful orders with INFO in production, and get alerts for WARNING when inventory runs low!
๐ง Basic Syntax and Usage
๐ Simple Example
Letโs start with a friendly example:
import logging
# ๐ Hello, logging!
logging.basicConfig(level=logging.DEBUG)
# ๐จ Creating different level messages
logging.debug("๐ Detailed information for debugging")
logging.info("๐ General information about program flow")
logging.warning("โ ๏ธ Something unexpected but not critical")
# ๐ฏ Real example with context
user_balance = 50
purchase_amount = 45
logging.info(f"๐ฐ User balance: ${user_balance}")
logging.debug(f"๐ Attempting purchase of ${purchase_amount}")
if purchase_amount > user_balance * 0.9:
logging.warning(f"โ ๏ธ User spending {purchase_amount/user_balance*100:.0f}% of balance!")
๐ก Explanation: Notice how each level serves a different purpose! DEBUG gives us the nitty-gritty details, INFO tracks normal operations, and WARNING alerts us to potential issues.
๐ฏ Common Patterns
Here are patterns youโll use daily:
# ๐๏ธ Pattern 1: Setting up a logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# ๐จ Pattern 2: Conditional logging
def process_order(order_id, amount):
logger.info(f"๐ฆ Processing order {order_id}")
logger.debug(f"๐ณ Payment amount: ${amount}")
if amount > 1000:
logger.warning(f"๐ฐ Large transaction detected: ${amount}")
return f"Order {order_id} processed! โ
"
# ๐ Pattern 3: Dynamic level adjustment
def set_log_level(verbose=False):
level = logging.DEBUG if verbose else logging.INFO
logging.getLogger().setLevel(level)
logger.info(f"๐ Log level set to: {logging.getLevelName(level)}")
๐ก Practical Examples
๐ Example 1: E-Commerce Order Processor
Letโs build something real:
import logging
from datetime import datetime
# ๐๏ธ Set up our logger
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("OrderProcessor")
class OrderProcessor:
def __init__(self):
self.orders = []
self.inventory = {"๐ Pizza": 10, "๐ Burger": 15, "๐ฅค Soda": 20}
# ๐ฆ Process an order
def process_order(self, item, quantity, customer):
logger.info(f"๐ฅ New order from {customer}")
logger.debug(f"๐ Order details: {quantity}x {item}")
# Check inventory
if item not in self.inventory:
logger.warning(f"โ ๏ธ Unknown item requested: {item}")
return False
current_stock = self.inventory[item]
logger.debug(f"๐ Current stock for {item}: {current_stock}")
if current_stock < quantity:
logger.warning(f"๐จ Low inventory! Only {current_stock} {item} left")
return False
# Process the order
self.inventory[item] -= quantity
order_id = len(self.orders) + 1
self.orders.append({
"id": order_id,
"item": item,
"quantity": quantity,
"customer": customer,
"timestamp": datetime.now()
})
logger.info(f"โ
Order #{order_id} completed successfully!")
# Check if we need to restock
if self.inventory[item] < 5:
logger.warning(f"๐ฆ Low stock alert: {item} down to {self.inventory[item]}")
return True
# ๐ Get daily summary
def daily_summary(self):
logger.info("๐ Generating daily summary...")
logger.debug(f"๐ Total orders processed: {len(self.orders)}")
for item, stock in self.inventory.items():
if stock < 5:
logger.warning(f"โ ๏ธ Restock needed: {item} ({stock} remaining)")
else:
logger.debug(f"โ
{item}: {stock} in stock")
# ๐ฎ Let's use it!
processor = OrderProcessor()
# Simulate some orders
processor.process_order("๐ Pizza", 3, "Alice")
processor.process_order("๐ Burger", 20, "Bob") # This will fail
processor.process_order("๐ Pizza", 8, "Charlie") # This will trigger low stock
# Get summary
processor.daily_summary()
๐ฏ Try it yourself: Add a restock_item
method that logs when items are restocked!
๐ฎ Example 2: Game Server Monitor
Letโs make monitoring fun:
import logging
import random
import time
# ๐ Game server monitoring system
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
datefmt='%H:%M:%S'
)
class GameServer:
def __init__(self, name):
self.name = name
self.players = []
self.cpu_usage = 20
self.memory_usage = 30
self.logger = logging.getLogger(f"GameServer-{name}")
# ๐ฎ Player joins
def player_join(self, player_name):
self.players.append(player_name)
self.logger.info(f"๐ฎ {player_name} joined the server!")
self.logger.debug(f"๐ฅ Total players: {len(self.players)}")
# Simulate resource usage increase
self.cpu_usage += random.randint(5, 10)
self.memory_usage += random.randint(3, 7)
self._check_resources()
# ๐ Player leaves
def player_leave(self, player_name):
if player_name in self.players:
self.players.remove(player_name)
self.logger.info(f"๐ {player_name} left the server")
# Resources go down
self.cpu_usage = max(20, self.cpu_usage - random.randint(3, 8))
self.memory_usage = max(30, self.memory_usage - random.randint(2, 5))
# ๐ Check server resources
def _check_resources(self):
self.logger.debug(f"๐ป CPU: {self.cpu_usage}% | RAM: {self.memory_usage}%")
if self.cpu_usage > 80:
self.logger.warning(f"๐ฅ High CPU usage: {self.cpu_usage}%!")
if self.memory_usage > 75:
self.logger.warning(f"๐พ High memory usage: {self.memory_usage}%!")
if len(self.players) > 10:
self.logger.warning(f"๐ฅ Server getting crowded: {len(self.players)} players")
# ๐ Server status
def status_report(self):
self.logger.info(f"๐ === {self.name} Status Report ===")
self.logger.info(f"๐ฅ Players online: {len(self.players)}")
self.logger.debug(f"๐ค Player list: {', '.join(self.players) if self.players else 'None'}")
self.logger.info(f"๐ป CPU: {self.cpu_usage}% | RAM: {self.memory_usage}%")
# Health check
if self.cpu_usage < 60 and self.memory_usage < 60:
self.logger.info("โ
Server health: Excellent")
elif self.cpu_usage < 80 and self.memory_usage < 75:
self.logger.info("โก Server health: Good")
else:
self.logger.warning("โ ๏ธ Server health: Needs attention")
# ๐ฎ Simulate game server activity
server = GameServer("DragonRealm")
# Players joining
players = ["Alice๐ฆ", "Bob๐", "Charlieโ๏ธ", "Diana๐น", "Eve๐งโโ๏ธ"]
for player in players:
server.player_join(player)
time.sleep(0.5) # Small delay for realistic logs
# Status check
server.status_report()
# Some players leave
server.player_leave("Bob๐")
server.player_leave("Diana๐น")
# Final status
server.status_report()
๐ Advanced Concepts
๐งโโ๏ธ Advanced Topic 1: Custom Log Formatting
When youโre ready to level up, try custom formatters:
import logging
# ๐ฏ Create a custom formatter with emojis
class EmojiFormatter(logging.Formatter):
emoji_map = {
logging.DEBUG: "๐",
logging.INFO: "๐",
logging.WARNING: "โ ๏ธ",
logging.ERROR: "โ",
logging.CRITICAL: "๐จ"
}
def format(self, record):
emoji = self.emoji_map.get(record.levelno, "๐")
record.emoji = emoji
return super().format(record)
# ๐ช Set up the magical formatter
handler = logging.StreamHandler()
formatter = EmojiFormatter('%(emoji)s %(levelname)-8s | %(message)s')
handler.setFormatter(formatter)
logger = logging.getLogger("MagicalLogger")
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
# โจ Test our magical logger
logger.debug("Finding the bug...")
logger.info("Process completed successfully")
logger.warning("Memory usage is getting high")
๐๏ธ Advanced Topic 2: Log Level Context Manager
For the brave developers:
import logging
from contextlib import contextmanager
# ๐ Temporary log level changer
@contextmanager
def log_level(logger, level):
"""Temporarily change log level - perfect for debugging! ๐ฏ"""
old_level = logger.level
logger.setLevel(level)
logger.debug(f"๐ Switching log level to {logging.getLevelName(level)}")
try:
yield logger
finally:
logger.setLevel(old_level)
logger.debug(f"๐ Restored log level to {logging.getLevelName(old_level)}")
# ๐ก Usage example
logger = logging.getLogger("SmartLogger")
logger.setLevel(logging.INFO)
logger.info("๐ Normal operation mode")
logger.debug("๐ This won't show (level is INFO)")
# Temporarily enable DEBUG for troubleshooting
with log_level(logger, logging.DEBUG):
logger.debug("๐ Now I can see debug messages!")
logger.info("๐ Info still works too")
logger.debug("๐ Back to being invisible")
โ ๏ธ Common Pitfalls and Solutions
๐ฑ Pitfall 1: Print vs Logging
# โ Wrong way - using print for debugging
def calculate_discount(price, percentage):
print(f"Calculating discount for ${price}") # ๐ฐ No control!
discount = price * (percentage / 100)
print(f"Discount amount: ${discount}")
return price - discount
# โ
Correct way - using proper logging
import logging
logger = logging.getLogger(__name__)
def calculate_discount(price, percentage):
logger.debug(f"๐งฎ Calculating {percentage}% discount for ${price}")
discount = price * (percentage / 100)
logger.info(f"๐ฐ Applied discount: ${discount:.2f}")
return price - discount
๐คฏ Pitfall 2: Wrong Level Selection
# โ Dangerous - using wrong levels
logger.debug("โ User password incorrect!") # Too low level for security!
logger.warning("๐ Processing order #12345") # Not a warning!
logger.info("๐จ SYSTEM CRASH IMMINENT") # Should be CRITICAL!
# โ
Safe - appropriate level usage
logger.warning("โ ๏ธ Failed login attempt for user 'admin'")
logger.info("๐ฆ Processing order #12345")
logger.critical("๐จ Database connection lost!")
logger.debug("๐ Checking cache for user_id=42")
๐ ๏ธ Best Practices
- ๐ฏ Choose Wisely: DEBUG for development details, INFO for flow, WARNING for issues
- ๐ Be Descriptive: Include context in your messages
- ๐ก๏ธ Security First: Never log sensitive data (passwords, tokens)
- ๐จ Stay Consistent: Use the same format across your app
- โจ Performance Aware: DEBUG logging can slow things down
๐งช Hands-On Exercise
๐ฏ Challenge: Build a Weather Station Monitor
Create a logging system for a weather station:
๐ Requirements:
- โ Log temperature readings (INFO level)
- ๐ก๏ธ Warn when temperature exceeds thresholds
- ๐จ Debug wind speed calculations
- ๐ง๏ธ Track rainfall with appropriate levels
- ๐ Generate hourly summaries
๐ Bonus Points:
- Add custom formatter for weather emojis
- Implement log rotation for daily files
- Create alert system for extreme weather
๐ก Solution
๐ Click to see solution
import logging
import random
from datetime import datetime
# ๐ฏ Our weather station monitoring system!
class WeatherStation:
def __init__(self, location):
self.location = location
self.logger = self._setup_logger()
self.readings = []
def _setup_logger(self):
"""Set up logger with custom formatting ๐จ"""
logger = logging.getLogger(f"WeatherStation-{self.location}")
logger.setLevel(logging.DEBUG)
# Console handler with custom format
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s | %(name)s | %(levelname)s | %(message)s',
datefmt='%H:%M:%S'
)
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
def read_temperature(self):
"""Read temperature sensor ๐ก๏ธ"""
temp = random.uniform(15, 35)
self.logger.info(f"๐ก๏ธ Temperature: {temp:.1f}ยฐC")
# Check thresholds
if temp > 30:
self.logger.warning(f"๐ฅ High temperature alert: {temp:.1f}ยฐC")
elif temp < 18:
self.logger.warning(f"โ๏ธ Low temperature alert: {temp:.1f}ยฐC")
return temp
def read_wind_speed(self):
"""Read wind speed sensor ๐จ"""
base_speed = random.uniform(5, 25)
gust_factor = random.uniform(0.8, 1.5)
self.logger.debug(f"๐ Base wind speed: {base_speed:.1f} km/h")
self.logger.debug(f"๐ Gust factor: {gust_factor:.2f}")
actual_speed = base_speed * gust_factor
self.logger.info(f"๐จ Wind speed: {actual_speed:.1f} km/h")
if actual_speed > 30:
self.logger.warning(f"๐ช๏ธ Strong wind warning: {actual_speed:.1f} km/h")
return actual_speed
def read_rainfall(self):
"""Read rainfall sensor ๐ง๏ธ"""
rainfall = random.uniform(0, 10)
self.logger.info(f"๐ง๏ธ Rainfall: {rainfall:.1f} mm")
if rainfall > 5:
self.logger.warning(f"โ Heavy rain alert: {rainfall:.1f} mm")
return rainfall
def collect_reading(self):
"""Collect all sensor readings ๐"""
self.logger.debug("๐ก Starting sensor collection...")
reading = {
"timestamp": datetime.now(),
"temperature": self.read_temperature(),
"wind_speed": self.read_wind_speed(),
"rainfall": self.read_rainfall()
}
self.readings.append(reading)
self.logger.debug("โ
Sensor collection complete")
return reading
def hourly_summary(self):
"""Generate hourly weather summary ๐"""
self.logger.info("๐ === Hourly Weather Summary ===")
if not self.readings:
self.logger.warning("โ ๏ธ No readings available for summary")
return
# Calculate averages
avg_temp = sum(r["temperature"] for r in self.readings) / len(self.readings)
avg_wind = sum(r["wind_speed"] for r in self.readings) / len(self.readings)
total_rain = sum(r["rainfall"] for r in self.readings)
self.logger.info(f"๐ก๏ธ Average temperature: {avg_temp:.1f}ยฐC")
self.logger.info(f"๐จ Average wind speed: {avg_wind:.1f} km/h")
self.logger.info(f"๐ง๏ธ Total rainfall: {total_rain:.1f} mm")
# Weather assessment
if avg_temp > 28 and total_rain < 1:
self.logger.warning("โ๏ธ Hot and dry conditions - fire risk!")
elif avg_wind > 25 and total_rain > 5:
self.logger.warning("โ๏ธ Stormy conditions detected!")
else:
self.logger.info("โ
Weather conditions: Normal")
# ๐ฎ Test our weather station!
station = WeatherStation("Tokyo")
# Simulate hourly readings
for i in range(5):
station.logger.info(f"๐ก Reading #{i+1}")
station.collect_reading()
# Generate summary
station.hourly_summary()
๐ Key Takeaways
Youโve learned so much! Hereโs what you can now do:
- โ Use logging levels appropriately (DEBUG, INFO, WARNING) ๐ช
- โ Replace print statements with proper logging ๐ก๏ธ
- โ Design logging strategies for real applications ๐ฏ
- โ Debug issues with the right level of detail ๐
- โ Monitor applications like a pro! ๐
Remember: Good logging is like having a conversation with your future self - make it clear and helpful! ๐ค
๐ค Next Steps
Congratulations! ๐ Youโve mastered Python logging levels!
Hereโs what to do next:
- ๐ป Practice with the weather station exercise
- ๐๏ธ Add logging to your existing projects
- ๐ Explore ERROR and CRITICAL levels in our next tutorial
- ๐ Share your logging best practices with your team!
Remember: Every debugging session becomes easier with good logging. Keep learning, keep logging, and most importantly, have fun! ๐
Happy coding! ๐๐โจ