Scala 3: Using context functions for request context with http4s and log4cats

Introduction

During studying Scala 3 Context Functions first use case I thought about it, was X-Request-ID, X-Correlation-ID or similar headers identifying request context. So in this post, I wanted to show Context Function usage on an example of implementing wiring request context to logs in a simple application with http4s and log4cats.

Alternative approaches

But first, let’s consider alternative approaches to solving this problem:

  • Java’s TheadLocal possible for context propagation only in case of synchronous invocations;

Context function

Dotty’s Context Functions on another hand, allows being independent of the underlying effect library because provides the capability to pass request context as a hidden implicit parameter.

Request context propagation

Fortunately, http4s provides RequestId middleware which does half of the job for us: create random UUID and add it as X-Request-ID for into response. We need retrieve it from Request[F] from attributes and pass it through the application.

First let’s define simple model of our request context:

Since we need to use RequestContext in logging, we need special logger to do this job:

For sake of example, let’s define simple service which contains some dummy business level login of handling abstract request:

In order to isloate logic of retrving requestIdAttrKey and converting it to implicit parameter let's define next helper function:

And use it in HTTP routes service:

Putting all pieces together in final application:

So let’s run our application and test it:

curl -X GET http://localhost:8080/contextual/test -v
Note: Unnecessary use of -X or --request, GET is already inferred.
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /contextual/test HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=UTF-8
< X-Request-ID: f6e8d59e-ddd3-438c-88f6-1136d6838234
< Date: Sun, 30 May 2021 18:21:10 GMT
< Content-Length: 2
<
* Connection #0 to host localhost left intact
OK* Closing connection 0

So you can see X-Request-ID is our request id in response with value: f6e8d59e-ddd3-438c-88f6-1136d6838234. And log line in application logs with same request id (rest of logs ommited):

...
21:21:09.972 [io-compute-7] INFO <empty>.ApplicationService - f6e8d59e-ddd3-438c-88f6-1136d6838234 - Received request to handle: test
....

Pros and cons

From my perspective using context function over other approaches of context propagations (e.g. fiber locals) has next :

Cons:

  • It is an invasive technic: context function type should be used across the whole project codebase, which might be harder to refactor, maintain and compose.

Pros:

  • Contextual function prooves on type-level that certain context was provided.

Further reading

Related blog posts which you might be interested in and inspired this post:

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store