🔍 Error Handling: Debugging and Logging
The Detective Story of Your Code
Imagine you’re a detective. Your code is the mystery. Something went wrong, but what? Where? Why?
Debugging is like following clues to solve the mystery. Logging is like writing down everything that happens—so you can look back later.
Let’s learn how to become a code detective! 🕵️
🖨️ Debugging with Print
The Flashlight in the Dark
Think of your code as a dark room. You can’t see what’s happening inside. But what if you had a flashlight?
print() is your flashlight. It shows you what’s happening at any moment.
def add_numbers(a, b):
print(f"a = {a}")
print(f"b = {b}")
result = a + b
print(f"result = {result}")
return result
add_numbers(5, 3)
Output:
a = 5
b = 3
result = 8
Now you can see exactly what values your code is using!
When to Use Print Debugging
- Quick checks while writing code
- Understanding the flow of your program
- Seeing values at specific points
Pro Tip: Use F-Strings! 🚀
name = "Luna"
age = 7
# Easy to read!
print(f"Name: {name}, Age: {age}")
⏸️ The Breakpoint Function
The Pause Button
What if you could freeze time in your code? Look around. Check everything. Then continue?
breakpoint() does exactly that!
def calculate_total(items):
total = 0
for item in items:
total += item
breakpoint() # Code freezes here!
return total
calculate_total([10, 20, 30])
When Python hits breakpoint(), it stops and lets you explore:
- What are the current values?
- What happened so far?
- What will happen next?
Magic Words in Breakpoint
When code stops, you can type commands:
| Command | What It Does |
|---|---|
n |
Go to next line |
c |
Continue running |
p variable |
Print a variable |
q |
Quit debugger |
🐛 Using the pdb Debugger
The Full Detective Toolkit
pdb stands for Python DeBugger. It’s like having a complete detective office!
Starting pdb
Method 1: Import and set trace
import pdb
def find_bug():
x = 10
pdb.set_trace() # Stops here
y = x * 2
return y
Method 2: Run from command line
python -m pdb my_script.py
pdb Commands
graph TD A["Start Debugging"] --> B{At Breakpoint} B --> C["n: Next Line"] B --> D["s: Step Into"] B --> E["c: Continue"] B --> F["l: List Code"] B --> G["p: Print Value"] B --> H["q: Quit"]
Real Example
import pdb
def divide(a, b):
pdb.set_trace()
result = a / b
return result
divide(10, 0) # Bug! Division by zero
When it stops, type:
p a→ Shows10p b→ Shows0(Aha! That’s the bug!)
✅ Assert Statements
The Guard at the Gate
Imagine a guard who checks: “Are you supposed to be here?”
assert is that guard. It checks if something is true. If not? It raises an alarm!
def calculate_age(birth_year, current_year):
assert birth_year > 0, "Birth year must be positive!"
assert current_year >= birth_year, "Can't be born in future!"
return current_year - birth_year
# This works
age = calculate_age(2010, 2024) # Returns 14
# This raises AssertionError!
age = calculate_age(-100, 2024)
How Assert Works
assert condition, "Error message"
- If
conditionis True → Code continues - If
conditionis False → AssertionError happens!
When to Use Assert
✅ Checking function inputs ✅ Verifying assumptions ✅ During development and testing
⚠️ Warning: Don’t use assert for user input validation in production!
📝 Logging Configuration
The Diary of Your Code
print() is temporary. Logging is permanent.
Think of logging as writing in a diary. You can look back and see everything that happened!
Setting Up Logging
import logging
# Basic setup
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
# Now you can log!
logging.debug("This is a debug message")
logging.info("App started successfully")
Logging to a File
import logging
logging.basicConfig(
filename='my_app.log',
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logging.info("This goes into the file!")
Format Options
| Code | Meaning |
|---|---|
%(asctime)s |
When it happened |
%(levelname)s |
Type of message |
%(message)s |
Your message |
%(name)s |
Logger name |
%(lineno)d |
Line number |
📊 Log Levels and Formatting
The Importance Scale
Not all messages are equal. Some are casual. Some are emergencies!
graph TD A["DEBUG 🔍"] --> B["INFO ℹ️"] B --> C["WARNING ⚠️"] C --> D["ERROR ❌"] D --> E["CRITICAL 🔥"] style A fill:#e8f5e9 style B fill:#e3f2fd style C fill:#fff3e0 style D fill:#ffebee style E fill:#f3e5f5
The Five Levels
| Level | Number | When to Use |
|---|---|---|
| DEBUG | 10 | Detailed info for fixing bugs |
| INFO | 20 | Normal operations |
| WARNING | 30 | Something unexpected happened |
| ERROR | 40 | Something failed |
| CRITICAL | 50 | App might crash! |
Using Each Level
import logging
logging.basicConfig(level=logging.DEBUG)
logging.debug("Checking database connection...")
logging.info("User logged in: Alice")
logging.warning("Memory usage is high: 85%")
logging.error("Failed to save file!")
logging.critical("Database is down!")
Setting the Level Filter
# Only show WARNING and above
logging.basicConfig(level=logging.WARNING)
logging.debug("Won't show")
logging.info("Won't show")
logging.warning("This shows!")
logging.error("This shows!")
Custom Formatting
import logging
logging.basicConfig(
level=logging.DEBUG,
format='[%(levelname)s] %(asctime)s | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logging.info("Server started")
# Output: [INFO] 2024-01-15 10:30:45 | Server started
🎯 Quick Summary
| Tool | Best For |
|---|---|
print() |
Quick checks |
breakpoint() |
Pausing to explore |
pdb |
Full debugging |
assert |
Checking conditions |
logging |
Permanent records |
The Detective’s Toolkit
- See values → Use
print() - Pause and explore → Use
breakpoint() - Deep investigation → Use
pdb - Guard your code → Use
assert - Keep records → Use
logging
🌟 You’re Now a Code Detective!
You have the tools. You know the techniques.
When bugs appear, you won’t panic. You’ll investigate. You’ll find them. You’ll fix them.
Happy debugging! 🔍🐛✨
