Skip to main content

Measure the execution time of a function or endpoint in Flask

ยท 4 min read
Serhii Hrekov
software engineer, creator, artist, programmer, projects founder

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โ€‹

MethodProsConsBest For
DecoratorFine-grained control, targets specific functions.Requires manual application to each function.Measuring specific, critical endpoints.
before/after_requestCentralized, 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.