Basic Logging
When your application runs on a server, you can't open a debugger and step through the code. Logging is how you see what's happening inside your application — both during normal operation and when things go wrong.
Why Logging Matters
Print statements work during development, but they're not enough for production:
- You need to see logs without being connected to the server
- You need timestamps to understand when things happened
- You need severity levels to filter important messages
- You need structured data for searching and analysis
Proper logging gives you visibility into your running application.
Log Levels
Logging libraries provide severity levels:
- DEBUG: Detailed information for diagnosing problems
- INFO: Confirmation that things are working as expected
- WARNING: Something unexpected happened, but the application continues
- ERROR: A serious problem prevented something from working
- CRITICAL: The application may not be able to continue
In production, you typically show INFO and above. During debugging, you might enable DEBUG.
Setting Up Logging
Python's built-in logging module is straightforward:
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
Now you can log throughout your application:
logger.info("Application started")
logger.warning("Rate limit approaching")
logger.error("Database connection failed")
What to Log
Log events that help you understand your application's behavior:
- Application startup and shutdown
- Incoming requests (method, path, user)
- Important business events (user created, order placed)
- Errors and exceptions with context
- Performance data (slow queries, high memory)
What NOT to Log
Some data should never appear in logs:
- Passwords and authentication tokens
- API keys and secrets
- Credit card numbers
- Personal data (depending on regulations)
- Full request bodies that might contain sensitive data
If you accidentally log secrets, they end up in log files, log aggregation services, and potentially backups — all places attackers might look.
Logging in Practice
Here's a realistic example:
@app.route('/todos', methods=['POST'])
def create_todo():
logger.info(f"Creating todo for user {current_user.id}")
try:
todo = save_todo(request.json['text'])
logger.info(f"Created todo {todo.id}")
return jsonify(todo.to_dict()), 201
except Exception as e:
logger.error(f"Failed to create todo: {e}", exc_info=True)
return jsonify({'error': 'Failed to create todo'}), 500
The exc_info=True parameter includes the full stack trace in the log.