Flask has solid built-in logging built on Python's logging module, but by default that output goes to stderr and disappears once the process is gone. This tutorial shows how to wire BunnyLogs into Flask's logger in a few lines, giving you a persistent live stream URL you can watch from anywhere.
pip install bunnylogs
The simplest integration adds the handler to Flask's app logger:
from flask import Flask
from bunnylogs import BunnyLogsHandler
app = Flask(__name__)
app.logger.addHandler(BunnyLogsHandler("your-uuid-here"))
app.logger.setLevel("INFO")
@app.route("/")
def index():
app.logger.info("Home page hit")
return "Hello, world!"
Start Flask and open bunnylogs.com/live/your-uuid. Make a request to / and the log line appears immediately.
Flask's development server uses Werkzeug, which logs every HTTP request to the werkzeug logger. Add the handler there too to see access logs in your stream:
import logging
from bunnylogs import BunnyLogsHandler
handler = BunnyLogsHandler("your-uuid")
logging.getLogger("werkzeug").addHandler(handler)
app.logger.addHandler(handler)
Now every GET / and POST /api/data appears in the live stream alongside your application logs.
For production, configure logging in the app factory and read the UUID from an environment variable so it doesn't get committed to source control:
import os
import logging
from flask import Flask
from bunnylogs import BunnyLogsHandler
def create_app():
app = Flask(__name__)
uuid = os.environ.get("BUNNYLOGS_UUID")
if uuid:
handler = BunnyLogsHandler(uuid, level=logging.WARNING)
app.logger.addHandler(handler)
logging.getLogger("werkzeug").addHandler(handler)
return app
In development, leave BUNNYLOGS_UUID unset (or use a development stream). In production, set it to your production stream UUID.
Flask's request context is available inside view functions. Log relevant details so the stream tells the full story of each request:
from flask import Flask, request, g
import time
app = Flask(__name__)
@app.before_request
def start_timer():
g.start = time.time()
@app.after_request
def log_request(response):
duration_ms = int((time.time() - g.start) * 1000)
app.logger.info(
f"{request.method} {request.path} → {response.status_code} ({duration_ms}ms)"
)
return response
Your stream now shows every request with method, path, status code, and duration. Slow requests and errors are immediately visible.
Register an error handler that logs the full traceback to the stream:
@app.errorhandler(Exception)
def handle_exception(e):
app.logger.error(f"Unhandled exception: {e}", exc_info=True)
return {"error": "Internal server error"}, 500
Unhandled exceptions now appear in your live stream with the full traceback — visible to you and anyone you've shared the stream URL with.
When running under Gunicorn, Flask's app logger doesn't inherit Gunicorn's logging configuration by default. Configure the handler in your app factory (as shown above) and it'll work correctly with Gunicorn workers.
April 22, 2025
March 4, 2025