Health checks made simple with Salutem

 • Jonas Svalin

At Atomic we are passionate about building innovative products. When building products and businesses that are non-trivial, we typically integrate with a plethora of interfaces. These can range from simple internal databases to complex third-party APIs. In either case it’s useful to have a way of surfacing the status of these interfaces so that we can proactively identify issues in our services and address them in a timely manner. To ensure that we don’t reinvent the wheel every time we build a new service we decided to create Salutem, a Clojure library designed to abstract away the intricacies of maintaining health checks.

Salutem

In a nutshell, Salutem allows you to define a set of health checks, a registry in which to store them, and a maintainer which ensures that results of health checks are kept up-to-date. Here’s a basic example which monitors www.google.com using an http call:

(require '[salutem.core :as salutem])
(require '[org.httpkit.client :as http])

(defn http-endpoint-check-fn [url]
  (fn [_ callback-fn]
    (http/get url
      (fn [{:keys [status]}]
        (callback-fn
          (if (<= 200 status 399)
            (salutem/healthy)
            (salutem/unhealthy)))))))

(def registry-store
  (atom
    (->
      (salutem/empty-registry)
      (salutem/with-check
        (salutem/background-check :google
          (http-endpoint-check-fn
            "https://www.google.com/"))))))

(def maintenance-pipeline
  (salutem/maintain registry-store))

(salutem/resolve-checks @registry-store)
; => {:google
;     {:status :healthy,
;      :evaluated-at #time/instant"2021-10-05T12:15:52.910167Z"}}

Realtime vs background

You may have noticed in the previous example that we defined the check as a background check. This means that the check is continuously performed in the background. When resolving checks, Salutem serves up the last cached result. Salutem also supports realtime checks which, unlike the background checks, are executed when the checks are being resolved.

With both of these options you are free to configure Salutem to allow the highest degree of optimisation for your use case. The factors to consider when choosing check type are how often checks will be resolved, if you prioritise freshness over resolve time and how much control you want over the frequency of which a dependency is monitored. Keep in mind that you may use different types of checks for different dependencies. To learn more about Salutem, such as how to set timeouts or how to pass results to callback functions, head on over to our Getting Started documentation.