Measure the execution time of a function or endpoint in Flask
The best way to precisely measure the execution time of a function or endpoint in Flask is by using a decorator or middleware. This approach allows you to wrap your functions with timing logic without directly altering the original function's code, which keeps your application clean and maintainable.
Using a Decorator for a Specific Function​
A decorator is ideal for measuring the execution time of a few specific endpoints or functions. It's a simple, reusable way to add timing functionality.
The Decorator​
import time
import functools
def measure_execution_time(func):
"""A decorator to measure and print the execution time of a function."""
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
execution_time_ms = (end_time - start_time) * 1000
print(f"'{func.__name__}' executed in {execution_time_ms:.4f} ms")
return result
return wrapper
We use time.perf_counter() because it's the most precise timer available for performance measurement and is not affected by system clock adjustments. The @functools.wraps decorator is used to preserve the original function's metadata, such as its name and docstrings.
Applying the Decorator to an Endpoint​
You can now apply this decorator to any of your Flask endpoints.
from flask import Flask
app = Flask(__name__)
@app.route("/items/<int:item_id>")
@measure_execution_time
def get_item(item_id):
# Simulate a long-running operation
time.sleep(0.1)
return {"item_id": item_id}
if __name__ == '__main__':
app.run(debug=True)
Using a before_request and after_request for All Endpoints​
For measuring the execution time of all requests to your application, a better approach is to use Flask's before_request and after_request decorators. This is essentially a form of middleware that centralizes your timing logic.
from flask import Flask, g, request
app = Flask(__name__)
@app.before_request
def start_timer():
"""Starts a timer before a request is processed."""
g.start_time = time.perf_counter()
@app.after_request
def log_request_time(response):
"""Calculates and logs the request processing time."""
end_time = time.perf_counter()
execution_time_ms = (end_time - g.start_time) * 1000
# You can log the time
print(f"Request to '{request.path}' took {execution_time_ms:.4f} ms")
# Or add it to a response header
response.headers["X-Process-Time-Ms"] = str(execution_time_ms)
return response
@app.route("/hello")
def hello():
return "Hello, World!"
The g object is a special context-local object in Flask used to store data for a single request. We store the start time on g in the before_request function and then retrieve it in the after_request function to calculate the duration. This ensures the timer is isolated to each individual request.
Summary of Approaches​
| Method | Pros | Cons | Best For |
|---|---|---|---|
| Decorator | Fine-grained control, targets specific functions. | Requires manual application to each function. | Measuring specific, critical endpoints. |
before/after_request | Centralized, measures all requests automatically. | Less granular, includes overhead from other functions. | Measuring overall API performance. |
For precise, global timing, using before_request and after_request is the most robust solution. For micro-optimizing a few specific functions, a decorator is the best choice.
