Monitoring and Logging

To keep an eye on the performance of his application and troubleshoot issues, Batman wanted a proper access log — one line per request showing the method, path, status code, and how long the request took.

Robyn builds on Python's standard logging module, so you can assemble a request/access log from two middlewares: a before_request hook that starts a timer, and an after_request hook that emits the log line once the response is ready. The after_request hook can receive both the request and the response, which is exactly what you need to log the path and the status code together. And because Robyn shares a contextvars context across the before_request, handler, and after_request hooks, a ContextVar is a tidy place to stash the start time.

Request / access logging

import logging
import time
from contextvars import ContextVar

from robyn import Request, Response

# A dedicated logger for access logs. It inherits Robyn's logging configuration,
# so keep the level at INFO or below (running with `--log-level WARN` would hide
# these lines).
logging.basicConfig(level=logging.INFO)
access_logger = logging.getLogger("robyn.access")

_request_start: ContextVar[float] = ContextVar("request_start")


@app.before_request()
def start_timer(request: Request):
    _request_start.set(time.perf_counter())
    return request


@app.after_request()
def log_request(request: Request, response: Response):
    start = _request_start.get(None)
    duration_ms = (time.perf_counter() - start) * 1000 if start is not None else 0.0
    access_logger.info(
        "%s %s -> %s (%.2fms)",
        request.method,
        request.url.path,
        response.status_code,
        duration_ms,
    )
    return response

With those two hooks registered globally, every request produces a line such as:

INFO:robyn.access:GET /crimes -> 200 (1.83ms)

A couple of things worth knowing:

  • The two-argument after_request(request, response) signature is what gives you access to the request alongside the response. If you only need the response, a single-argument after_request(response) works too — Robyn picks the calling convention based on your function's parameters.
  • after_request hooks always run, even when a before_request hook short-circuits the request, so your access log captures every response.
  • Prefer %s-style logging arguments (as above) over f-strings so the message is only formatted when the log level is actually enabled.

With monitoring and logging in place, Batman could now easily detect issues and analyze the performance of his web application, ensuring that it was always running optimally and ready to assist him in his fight against crime.