Nov 11th, ‘24/6 min read

Flask Logging Made Simple for Developers

Learn how to implement proper logging in Flask, from development to production, and avoid the pitfalls of scattered print statements.

Flask Logging Made Simple for Developers

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:

  1. Development: Console output (when debug=True)
  2. Production: Log files (typically in /var/log/ or a dedicated logs directory)
  3. 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:

  1. DEBUG: Detailed information for debugging
  2. INFO: General operational entries
  3. WARNING: Something unexpected, but not critical
  4. ERROR: Something failed, but the application continues
  5. CRITICAL: The application cannot continue
Microservices Monitoring with the RED Method | Last9
This blog introduces the RED method—an approach that simplifies microservices monitoring by honing in on requests, errors, and latency.

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())
Golang Logging: A Comprehensive Guide for Developers | Last9
Our blog covers practical insights into Golang logging, including how to use the log package, popular third-party libraries, and tips for structured logging

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:

  1. Werkzeug: Flask's underlying WSGI library
  2. Application Logger: Your custom logging configuration
  3. Request Logger: HTTP request/response logging
Flask Logging Architecture
Flask Logging Architecture

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

  1. getLogger: Function to create or retrieve a logger
  2. addHandler: Method to attach logging handlers
  3. logging.StreamHandler: Handler for streaming logs to console
  4. 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')
Understanding Docker Logs: A Quick Guide for Developers | Last9
Learn how to access and use Docker logs to monitor, troubleshoot, and improve your containerized apps in this simple guide for developers.

Running Flask Applications

When you execute flask run, several logging components activate:

  1. Werkzeug logger for request/response
  2. Application logger for your code
  3. 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
OTEL Collector Monitoring: Best Practices & Guide | Last9
Learn how to effectively monitor the OTEL Collector with best practices and implementation strategies for improved system performance.

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:

  1. Setting up log aggregation (ELK Stack or similar)
  2. Implementing log-based alerts
  3. 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.

🤝
Got more questions? Come chat with us on Discord! We have a dedicated channel where you can connect with other developers and discuss your use case.

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')

Newsletter

Stay updated on the latest from Last9.

Authors

Prathamesh Sonpatki

Prathamesh works as an evangelist at Last9, runs SRE stories - where SRE and DevOps folks share their stories, and maintains o11y.wiki - a glossary of all terms related to observability.

Topics

Handcrafted Related Posts