📝 Using Logging

Logging is a better alternative to print statements for tracking application behavior. Python's logging module provides structured, configurable output that's essential for debugging and monitoring applications.

import logging

# Basic logging setup
logging.basicConfig(level=logging.INFO)

# Create log messages
logging.debug("This is a debug message")
logging.info("Application started")
logging.warning("This is a warning")
logging.error("An error occurred")
logging.critical("Critical system failure")

🎯 Basic Logging

Logging provides different levels of messages for various situations.

Simple Logging Examples

import logging

# Configure logging to show all levels
logging.basicConfig(level=logging.DEBUG)

def divide_numbers(a, b):
    logging.info(f"Dividing {a} by {b}")
    
    if b == 0:
        logging.error("Division by zero attempted")
        return None
    
    result = a / b
    logging.info(f"Division result: {result}")
    return result

# Test the function
result1 = divide_numbers(10, 2)
result2 = divide_numbers(10, 0)

Logging with Different Levels

import logging

# Set up logging
logging.basicConfig(level=logging.DEBUG)

def process_user_data(user_data):
    logging.debug(f"Processing user data: {user_data}")
    
    if not user_data:
        logging.warning("Empty user data received")
        return {}
    
    if "email" not in user_data:
        logging.error("Missing required field: email")
        return None
    
    logging.info("User data processed successfully")
    return {"status": "processed"}

# Test with different scenarios
process_user_data({"name": "Alice", "email": "alice@example.com"})
process_user_data({"name": "Bob"})  # Missing email
process_user_data({})  # Empty data

Logging Exceptions

import logging

logging.basicConfig(level=logging.INFO)

def safe_file_operation(filename):
    try:
        logging.info(f"Attempting to read file: {filename}")
        with open(filename, 'r') as file:
            content = file.read()
            logging.info("File read successfully")
            return content
    except FileNotFoundError:
        logging.error(f"File not found: {filename}")
        return None
    except Exception as e:
        logging.exception("Unexpected error occurred")  # Includes traceback
        return None

# Test file operations
content = safe_file_operation("existing_file.txt")
content = safe_file_operation("missing_file.txt")

📋 Log Level Reference

LevelNumeric ValueWhen to Use
DEBUG10Detailed diagnostic info
INFO20General information
WARNING30Something unexpected
ERROR40Serious problem
CRITICAL50Very serious error

🔧 Logging Configuration

Basic Configuration

import logging

# Configure logging format
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

def user_login(username):
    logging.info(f"User login attempt: {username}")
    
    if username == "admin":
        logging.info("Admin user logged in successfully")
        return True
    else:
        logging.warning(f"Unknown user login attempt: {username}")
        return False

# Test user login
user_login("admin")
user_login("hacker")

Logging to File

import logging

# Configure logging to file
logging.basicConfig(
    filename='application.log',
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

def database_operation(operation, table):
    logging.info(f"Database operation: {operation} on table {table}")
    
    try:
        # Simulate database operation
        if table == "users":
            logging.info("Operation completed successfully")
            return True
        else:
            logging.error(f"Table '{table}' not found")
            return False
    except Exception as e:
        logging.exception("Database operation failed")
        return False

# Test database operations
database_operation("SELECT", "users")
database_operation("INSERT", "invalid_table")

Custom Logger

import logging

# Create custom logger
logger = logging.getLogger('my_application')
logger.setLevel(logging.DEBUG)

# Create handler for console output
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)

# Create formatter
formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)

# Add handler to logger
logger.addHandler(console_handler)

def api_request(endpoint):
    logger.debug(f"API request details: {endpoint}")
    logger.info(f"Making API request to: {endpoint}")
    
    if endpoint.startswith("/api/"):
        logger.info("API request successful")
        return {"status": "success"}
    else:
        logger.error("Invalid API endpoint")
        return {"status": "error"}

# Test API requests
api_request("/api/users")
api_request("/invalid/endpoint")

📊 Logging Configuration Options

Basic Configuration Parameters

ParameterPurposeExample
levelMinimum log levellogging.INFO
formatMessage format'%(levelname)s - %(message)s'
filenameLog to file'app.log'
filemodeFile mode'a' (append) or 'w' (write)

Format Specifiers

SpecifierMeaningExample
%(asctime)sTimestamp2024-03-15 14:30:45,123
%(levelname)sLog levelINFO
%(message)sLog messageUser logged in
%(name)sLogger namemy_app
%(filename)sSource filenamemain.py
%(lineno)dLine number42

🛠️ Practical Logging Examples

Application Startup Logging

import logging

# Configure application logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

def initialize_application():
    logging.info("Application starting up")
    
    # Initialize components
    components = ["database", "cache", "api_server"]
    
    for component in components:
        logging.info(f"Initializing {component}")
        
        # Simulate initialization
        if component == "database":
            logging.info("Database connection established")
        elif component == "cache":
            logging.warning("Cache server running on backup port")
        elif component == "api_server":
            logging.info("API server started on port 8080")
    
    logging.info("Application startup completed")

initialize_application()

Error Tracking

import logging

logging.basicConfig(level=logging.INFO)

def process_orders(orders):
    logging.info(f"Processing {len(orders)} orders")
    
    processed = 0
    errors = 0
    
    for order_id, order in enumerate(orders, 1):
        try:
            logging.debug(f"Processing order {order_id}: {order}")
            
            if "total" not in order:
                raise ValueError("Missing total amount")
            
            if order["total"] <= 0:
                raise ValueError("Invalid total amount")
            
            processed += 1
            logging.info(f"Order {order_id} processed successfully")
            
        except ValueError as e:
            errors += 1
            logging.error(f"Order {order_id} failed: {e}")
        except Exception as e:
            errors += 1
            logging.exception(f"Unexpected error in order {order_id}")
    
    logging.info(f"Order processing complete: {processed} successful, {errors} errors")

# Test order processing
orders = [
    {"id": 1, "total": 25.99},
    {"id": 2, "total": -5.00},  # Invalid amount
    {"id": 3, "product": "item"}  # Missing total
]

process_orders(orders)

🎯 Key Takeaways

🚀 What's Next?

Learn how to implement retry logic to make your applications more resilient to temporary failures.

Continue to: Implement Retry Logic

Was this helpful?

😔Poor
🙁Fair
😊Good
😄Great
🤩Excellent