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.