Motivations
Nowadays most backend-software is written as a microservice that needs to interact with other internal and possibly external services. Although the benefits are quite obvious (independently deployable and managed, etc) there are some associated challenges.
With this setup it becomes very hard to diagnose what went wrong in the case of failure. We might have isolated logs but how can we tell a call to a database and an external service are associated to a single http request? This is a very common problem with a only a few solutions and not so many standards.
Distributed tracing is still an area of research but some standards have already emerged in the past years, most of them based on the Google Dapper specification:
The solution proposed by these specifications are very broad and their intent is to become a standard no matter what programming language you’re using. But it comes with a price. Setting this up is not a trivial task and the most important feature is to trace distributed calls which, in my opinion, can be simplified quite a lot with a minimalistic solution.
The RIO Monad
The most common approach to carry “context” (this can be a “Trace-Id”) in functional programming is by using the ReaderT
monad transformer or more commonly called Kleisli
.
There are a few blog posts out there recommending the use of the RIO
monad, which is basically ReaderT
+ IO
defined as a newtype
, in production applications. The most remarkable ones and where I’ve got the inspiration to write this library are:
- [Jul 2017] - The RIO Monad by Michael Snoyman
- [Mar 2018] - Haskell modules for masses by Eric Torreborre
Http4s Tracer
The implementation presented by this library follows the same idea but in a minimalistic way: just carrying a TraceId
around instead of a whole application context. And it is mainly adapted to work nicely with Http4s
while abstracting over the effect type using Cats Effect
which are two amazing libraries of the Typelevel ecosystem.
The main type defined as type Trace[F[_], A] = Kleisli[F, TraceId, A]
, a bit simpler than how RIO
is defined because it’s also doing less.