Recently I faced a seemingly simple task of measuring how long a request takes and reporting this metric via Prometheus. After a quick google I’ve found that it’s not the first time I’ve encountered this problem.

Here’s the solution I used two years ago:

This looks fine at first sight: we capture the start time when the requests starts (using the extractRequestContext directive), and then map over the response, to capture the moment when the request is done.

However, there’s an important problem here: we only observe when the HttpResponse instance is created, not when the response has been sent! Meanwhile, a response can contain a stream of data, which may take a long time to produce and send.

A simple test reveals that indeed the implementation of logDuration is flawed:

Our logDuration directive will happily report that the request has finished within tens of milliseconds, while in fact it takes a little over 4 seconds.

(EDIT: as Joshua Junqueira noted on Twitter, a similar directive is described in the akka-http docs, however it has the same flaw as described above).

How to fix this problem? We have to extend the reponse body stream with a stage which will invoke our code when the whole stream completes:

(EDIT: changed the code above with Alan Johnson’s fix for websocket endpoints)

(EDIT: changed the code again with Krzysztof Ciesielski’s fix to handle timeouts)

The function provided to the aroundRequest directive is first called with the RequestContext when a request starts, yielding another function (called onDone in the code). When the request completes, onDone is called with the final result, allowing to log and report metrics.

There are three possible outcomes of a request:

the request completes successfuly, Success(Complete(response)) is passed to onDone

is passed to the request is rejected (e.g. because of a non-matching inner route), then Success(Rejected(rejections)) is passed

is passed producing the response body fails, and hence the request fails as well: Failure is passed to onDone

How to use this directive to log response times?

Integration with Prometheus is easy as well (using the Prometheus java client):

As reporting the request metrics is a common task useful both in production and development, I hope you’ll find the above useful!