After years of building and maintaining applications, I've realized that proper logging isn't just a nice to have – it's crucial for keeping things running smoothly.
When I first started out, I used to rely on scattered print statements, but over time, I learned how to implement logging that works efficiently from development to production. Let me show you how to set it up the right way.
Understanding Flask Logging Fundamentals
What Makes Flask Logging Special?
Flask integrates with Python's built-in logging module but adds some special sauce. The app.logger
is actually an extension of standard Python logging that's aware of your web application's context.
Here's a basic setup I use for every new project:
from flask import Flask
import logging
app = Flask(__name__)
# Basic logging setup
app.logger.setLevel(logging.INFO)
@app.route('/')
def home_page():
app.logger.info('Home page accessed')
return 'Hello World'
Where Are Flask Logs Stored?
By default, Flask logs to stderr, but in production, you'll want to configure proper file handling:
- Development: Console output (when
debug=True
) - Production: Log files (typically in
/var/log/
or a dedicated logs directory) - Cloud: Often forwarded to services like CloudWatch or Stackdriver
Real-World Logging Configuration
Let's look at a production-ready setup I recently implemented for a client's API service:
def configure_logging():
# Create formatters and handlers
formatter = logging.Formatter('[%(asctime)s] %(levelname)s in %(module)s: %(message)s')
# Ensure logs directory exists
os.makedirs('logs', exist_ok=True)
file_handler = logging.FileHandler('logs/app.log')
file_handler.setFormatter(formatter)
file_handler.setLevel(logging.INFO)
return file_handler
Handling Different Log Levels
In my experience, proper log levels are crucial for production debugging:
- DEBUG: Detailed information for debugging
- INFO: General operational entries
- WARNING: Something unexpected, but not critical
- ERROR: Something failed, but the application continues
- CRITICAL: The application cannot continue
Structured Logging for Modern Applications
When working with microservices, I always implement structured logging using JSON:
class JSONFormatter(logging.Formatter):
def format(self, record):
log_record = {
'timestamp': self.formatTime(record),
'level': record.levelname,
'message': record.getMessage(),
'module': record.module
}
return json.dumps(log_record)
Request Logging and HTTP Context
One of the most useful patterns I've developed is logging request context:
@app.before_request
def log_request_info():
app.logger.info('Headers: %s', request.headers)
app.logger.info('Body: %s', request.get_data())
Flask Logging Architecture
The Logging System
A Flask logger is built on top of Python's logging system. When you create an app.py
, Flask automatically configures a basic logger. Here's how the components work together:
- Werkzeug: Flask's underlying WSGI library
- Application Logger: Your custom logging configuration
- Request Logger: HTTP request/response logging
Basic Setup
Let's start with a simple tutorial on setting up logging in a web app:
# app.py
from flask import Flask
from logging.config import dictConfig
def init_app():
app = Flask(__name__)
# Basic logging setup
dictConfig({
'version': 1,
'handlers': {
'wsgi': {
'class': 'logging.StreamHandler',
'stream': 'ext://flask.logging.wsgi_errors_stream',
'formatter': 'default'
}
},
'formatters': {
'default': {
'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s',
}
},
'root': {
'level': 'INFO',
'handlers': ['wsgi']
}
})
return app
Understanding Logger Components
- getLogger: Function to create or retrieve a logger
- addHandler: Method to attach logging handlers
- logging.StreamHandler: Handler for streaming logs to console
- filename: Parameter for
FileHandler
to specify log file location
Template Integration
When working with HTML templates, you might want to log render events:
@app.route('/')
def home():
app.logger.info('Rendering home template')
return render_template('home.html')
Using Decorators
Decorator patterns are common in Flask logging:
def log_route():
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
app.logger.info(f'Route {f.__name__} called')
return f(*args, **kwargs)
return decorated_function
return decorator
@app.route('/api/data')
@log_route()
def get_data():
return {'data': 'success'}
Log Levels and Messages
Understanding log messages and their appropriate levels is crucial:
Info Level: Regular application eventspythonCopy code
app.logger.info('Application started')
Debug Level: Detailed information for debuggingpythonCopy code
app.logger.debug('Query parameters: %s', request.args)
Error Level: Application errors
app.logger.error('Database connection failed')
Running Flask Applications
When you execute flask run
, several logging components activate:
- Werkzeug logger for request/response
- Application logger for your code
- Extension loggers (if any)
$ export FLASK_APP=app.py
$ export FLASK_ENV=development
$ flask run
Advanced Configuration
Custom Log Handlers
class CustomHandler(logging.Handler):
def emit(self, record):
# Custom logging logic
log_entry = self.format(record)
# Store or forward log entry
Request Context Logging
class RequestFormatter(logging.Formatter):
def format(self, record):
record.url = request.url
record.method = request.method
return super().format(record)
Production Best Practices
After dealing with numerous production issues, here are my top logging recommendations:
Log Rotation: Implement it from day one
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler('app.log', maxBytes=10000, backupCount=3)
Structured Data: Always include key context
app.logger.info('User action', extra={
'user_id': current_user.id,
'action': 'purchase',
'amount': 99.99
})
Error Tracking: Integrate with error tracking services
@app.errorhandler(Exception)
def handle_exception(e):
app.logger.error(f'Unhandled exception: {e}', exc_info=True)
return 'Internal Server Error', 500
Debugging with Flask Logs
Here's my debugging workflow:
Enable debug mode temporarily:
app.config['DEBUG'] = True
Set up detailed logging:
app.logger.setLevel(logging.DEBUG)
Add context-specific logging:
@app.route('/api/data')
def get_data():
app.logger.debug(f'Query parameters: {request.args}')
# ... rest of the view code
Testing Your Logging Setup
Here's how I test logging configurations:
def test_logging_configuration():
with app.test_client() as client:
response = client.get('/')
assert 'Home page accessed' in open('logs/app.log').read()
Monitoring and Alerts
For production applications, I recommend:
- Setting up log aggregation (ELK Stack or similar)
- Implementing log-based alerts
- Regular log analysis for patterns
Conclusion
Proper logging in Flask is a journey, not a destination. Start simple, then gradually enhance your logging as your application grows.
Additional Resources
FAQs
Q: Where can I see Flask logs?
A: By default, they appear in your terminal. In production, check your configured log files or logging service.
Q: Do I need to run Flask in debug mode?
A: Only during development. Never in production!
Q: How does logging work in Flask?
A: Flask uses Python's logging module but provides its own logger instance (app.logger
) that's aware of the web application context.
Q: How do I enable logging in views.py
?
A: Import the logger from your app instance or use Flask's current_app
: python from flask import current_app current_app.logger.info('Message from view')