Skip to main content

Flask: Setting Up Logging

Flask: Setting Up Logging

Logging is essential for monitoring, debugging, and maintaining Flask applications, providing insights into application behavior, errors, and performance. Flask integrates with Python’s built-in logging module, allowing developers to capture and analyze events effectively. This tutorial explores setting up logging in Flask, covering configuration, log levels, handlers, and best practices for robust application monitoring.


01. Why Use Logging in Flask?

Logging helps track application events, diagnose issues, and audit user actions, making it critical for development and production environments. Flask’s minimalistic design relies on Python’s logging module, which supports customizable log levels, output destinations, and formats. Proper logging ensures proactive issue resolution, security monitoring, and compliance with operational requirements.

Example: Basic Flask Logging

from flask import Flask
import logging

app = Flask(__name__)

# Configure basic logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@app.route('/')
def index():
    logger.info('Homepage accessed')
    return "Welcome to Flask!"

if __name__ == '__main__':
    app.run(debug=True)

Output:

* Running on http://127.0.0.1:5000
INFO:__main__:Homepage accessed

Explanation:

  • logging.basicConfig - Sets up logging with a default console handler.
  • logger.info - Logs an informational message when the homepage is accessed.

02. Key Logging Techniques

Flask logging can be tailored to capture specific events, route logs to various outputs, and integrate with external systems. These techniques ensure comprehensive monitoring and scalability. The table below summarizes key techniques and their applications:

Technique Description Use Case
Log Levels Use DEBUG, INFO, WARNING, ERROR, CRITICAL Control log verbosity
File Handler Save logs to files Persistent error tracking
Rotating File Handler Manage log file size Prevent disk overuse
Custom Formatter Customize log message format Include timestamps, request data
External Integration Send logs to external systems Centralized monitoring (e.g., Sentry)


2.1 Using Log Levels

Example: Different Log Levels

from flask import Flask
import logging

app = Flask(__name__)

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

@app.route('/test')
def test():
    logger.debug('Debugging route access')
    logger.info('Processing request')
    logger.warning('Potential issue detected')
    logger.error('Error occurred')
    logger.critical('Critical failure')
    return "Log levels tested"

if __name__ == '__main__':
    app.run(debug=True)

Output:

* Running on http://127.0.0.1:5000
DEBUG:__main__:Debugging route access
INFO:__main__:Processing request
WARNING:__main__:Potential issue detected
ERROR:__main__:Error occurred
CRITICAL:__main__:Critical failure

Explanation:

  • level=logging.DEBUG - Captures all log levels (DEBUG and above).
  • Different levels help categorize messages by severity.

2.2 Logging to a File

Example: File Handler Setup

from flask import Flask
import logging

app = Flask(__name__)

# Configure file logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.INFO)
logger.addHandler(file_handler)

@app.route('/')
def index():
    logger.info('Homepage accessed')
    return "Logged to file"

if __name__ == '__main__':
    app.run(debug=True)

Output (app.log):

INFO:__main__:Homepage accessed

Explanation:

  • FileHandler - Writes logs to app.log.
  • Persistent storage for later analysis.

2.3 Rotating File Handler for Log Management

Example: Rotating Logs

from flask import Flask
import logging
from logging.handlers import RotatingFileHandler

app = Flask(__name__)

# Configure rotating file logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

rotating_handler = RotatingFileHandler('app.log', maxBytes=10000, backupCount=5)
rotating_handler.setLevel(logging.INFO)
logger.addHandler(rotating_handler)

@app.route('/')
def index():
    logger.info('Homepage accessed')
    return "Logged with rotation"

if __name__ == '__main__':
    app.run(debug=True)

Output (app.log, app.log.1, etc.):

INFO:__main__:Homepage accessed

Explanation:

  • RotatingFileHandler - Rotates logs when maxBytes (10KB) is reached, keeping up to 5 backups.
  • Prevents log files from consuming excessive disk space.

2.4 Custom Log Formatter

Example: Custom Log Format

from flask import Flask, request
import logging

app = Flask(__name__)

# Configure logging with custom format
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s - IP: %(ip)s')
file_handler = logging.FileHandler('custom.log')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)

@app.route('/')
def index():
    logger.info('Homepage accessed', extra={'ip': request.remote_addr})
    return "Custom log format"

if __name__ == '__main__':
    app.run(debug=True)

Output (custom.log):

2025-05-12 10:00:00,123 - INFO - Homepage accessed - IP: 127.0.0.1

Explanation:

  • Formatter - Customizes log output with timestamp, level, message, and client IP.
  • extra - Passes additional data like request.remote_addr.

2.5 External Logging Integration (Sentry)

Example: Logging to Sentry

from flask import Flask
import logging
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration
from sentry_sdk.integrations.logging import LoggingIntegration

app = Flask(__name__)

# Configure Sentry
sentry_sdk.init(
    dsn='your-sentry-dsn',
    integrations=[FlaskIntegration(), LoggingIntegration(level=logging.INFO)]
)

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

@app.route('/')
def index():
    logger.info('Homepage accessed')
    try:
        1 / 0  # Simulate error
    except ZeroDivisionError:
        logger.error('Division by zero', exc_info=True)
    return "Logged to Sentry"

if __name__ == '__main__':
    app.run(debug=True)

Output (Sentry Dashboard):

Event: Homepage accessed (INFO)
Event: Division by zero (ERROR with stack trace)

Explanation:

  • sentry_sdk - Sends logs and errors to Sentry for centralized monitoring.
  • exc_info=True - Includes stack traces for exceptions.

2.6 Incorrect Logging Setup

Example: Misconfigured Logging

from flask import Flask
import logging

app = Flask(__name__)

# Incorrect: No handler configured
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

@app.route('/')
def index():
    logger.info('This will not be logged')
    return "No logging output"

if __name__ == '__main__':
    app.run(debug=True)

Output:

* Running on http://127.0.0.1:5000
(No log output due to missing handler)

Explanation:

  • Logger requires a handler (e.g., FileHandler, StreamHandler) to output logs.
  • Solution: Add a handler like logging.StreamHandler() or logging.basicConfig().

03. Effective Usage

3.1 Recommended Practices

  • Use rotating file handlers and custom formatters for production.

Example: Comprehensive Logging Setup

from flask import Flask, request
import logging
from logging.handlers import RotatingFileHandler

app = Flask(__name__)

# Configure logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# Console handler for development
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

# Rotating file handler for production
file_handler = RotatingFileHandler('app.log', maxBytes=1000000, backupCount=10)
file_handler.setLevel(logging.INFO)

# Custom formatter
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s - IP: %(ip)s - URL: %(url)s')
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)

logger.addHandler(console_handler)
logger.addHandler(file_handler)

@app.route('/')
def index():
    logger.info('Homepage accessed', extra={'ip': request.remote_addr, 'url': request.url})
    return "Comprehensive logging"

@app.route('/error')
def error():
    try:
        1 / 0
    except ZeroDivisionError:
        logger.error('Division by zero error', extra={'ip': request.remote_addr, 'url': request.url}, exc_info=True)
        return "Error logged", 500

if __name__ == '__main__':
    app.run(debug=True)

Output (Console and app.log):

2025-05-12 10:00:00,123 - INFO - Homepage accessed - IP: 127.0.тик0.1 - URL: http://127.0.0.1:5000/
2025-05-12 10:00:01,456 - ERROR - Division by zero error - IP: 127.0.0.1 - URL: http://127.0.0.1:5000/error
Traceback (most recent call last):
  ...
ZeroDivisionError: division by zero
  • Combines console and rotating file handlers with custom formatting.
  • Logs request metadata (IP, URL) and exceptions with stack traces.
  • Scalable for production with log rotation.

3.2 Practices to Avoid

  • Avoid excessive DEBUG logging in production to prevent performance issues.

Example: Excessive Debug Logging

from flask import Flask
import logging

app = Flask(__name__)

# Incorrect: DEBUG level in production
logging.basicConfig(level=logging.DEBUG, filename='app.log')
logger = logging.getLogger(__name__)

@app.route('/')
def index():
    logger.debug('Every request logged with excessive detail')
    return "Overloaded log file"

if __name__ == '__main__':
    app.run(debug=True)

Output (app.log):

DEBUG:__main__:Every request logged with excessive detail
(Fills log file rapidly with verbose output)
  • DEBUG logs generate excessive data, slowing performance and filling storage.
  • Solution: Use INFO or higher in production, reserve DEBUG for development.

04. Common Use Cases

4.1 Debugging Application Errors

Log errors with stack traces to diagnose issues.

Example: Error Logging

from flask import Flask
import logging

app = Flask(__name__)

logging.basicConfig(level=logging.ERROR, filename='errors.log')
logger = logging.getLogger(__name__)

@app.route('/divide/<int:num>')
def divide(num):
    try:
        result = 100 / num
        return f"Result: {result}"
    except ZeroDivisionError:
        logger.error('Division by zero', exc_info=True)
        return "Error: Division by zero", 500

if __name__ == '__main__':
    app.run(debug=True)

Output (errors.log):

ERROR:__main__:Division by zero
Traceback (most recent call last):
  ...
ZeroDivisionError: division by zero

Explanation:

  • Logs exceptions with stack traces for debugging.
  • Isolates error logs to a dedicated file.

4.2 Monitoring User Activity

Log user actions for auditing and analytics.

Example: User Activity Logging

from flask import Flask, request
import logging
from logging.handlers import RotatingFileHandler

app = Flask(__name__)

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

handler = RotatingFileHandler('activity.log', maxBytes=1000000, backupCount=5)
handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s - IP: %(ip)s'))
logger.addHandler(handler)

@app.route('/login', methods=['POST'])
def login():
    username = request.form.get('username', 'anonymous')
    logger.info(f'User {username} logged in', extra={'ip': request.remote_addr})
    return "Login logged"

if __name__ == '__main__':
    app.run(debug=True)

Output (activity.log):

2025-05-12 10:00:00,123 - INFO - User alice logged in - IP: 127.0.0.1

Explanation:

  • Logs user login events with IP addresses for auditing.
  • Rotating handler manages log file size.

Conclusion

Setting up logging in Flask enhances application monitoring, debugging, and security. Key takeaways:

  • Use appropriate log levels (DEBUG for development, INFO/ERROR for production).
  • Implement file and rotating handlers for persistent, manageable logs.
  • Customize formats to include context like IP or request URLs.
  • Integrate with external tools like Sentry for centralized monitoring.

With these practices, you can build Flask applications with effective logging for reliable operation and troubleshooting!

Comments