Logging readable ClojureScript errors to Sentry

Frankline Apiyo
March 24, 2022

An essential part of a running a production web application is having deep visibility into what is causing errors and how code traces underlying them are propagating through your code base. When using a compiled (or technically, transpiled) language, there is the additional challenge of understanding how an error in the target language translates back to the source language.

I recently had the pleasure of setting up front-end error reporting for a web application at ona.io that helps us track down errors in our source code. We use Sentry to log and report errors in our web apps. Here’s a step by step tutorial on how to set up ClojureScript error reporting with Sentry.

Step 1: Install and configure Sentry browser SDK

The first step in using Sentry with source maps is to install Sentry browser SDK and let it know what part of your web application to monitor. Sentry uses the error boundary component to subscribe Sentry to exceptions that are raised by your web application.

The example below uses re-frame:

(ns your.namespace
  (:require ["@sentry/browser" :as Sentry]
            ["@sentry/tracing" :refer (Integrations)]))

(defn error-boundary
  [comp]
  (let [error (r/atom nil)]
    (r/create-class
      {:component-did-catch
        (fn [_this e _info]
          (let [{:keys [sentry-dsn app-version env]}
                @(re-frame/subscribe [::your-configuration-sub])]
            (.init Sentry
                   #js
                    {:environment env,
                     :dsn sentry-dsn,
                     :release app-version,
                     :integrations #js [(new (.-BrowserTracing Integrations))],
                     :tracesSampleRate 1.0})
            (.captureException Sentry e))),
       :get-derived-state-from-error (fn [e] (reset! error e) #js {}),
       :reagent-render (fn [comp]
                         (if @error
                           [:div.fallback-error-ui
                            [:p.no-saved-charts-message.alert-danger
                             [:span.tooltip [:span.tip-info "Reload page"]
                              [:i.fa.fa-refresh
                               {:aria-hidden true,
                                :on-click (click-fn #(.reload (.-location
                                                                js/window)))}]]
                             "An error

Step 2: Set up source maps

2.1 Why we need source maps

ClojureScript is a compiler for Clojure that targets JavaScript. ClojureScript compiles to JavaScript that is then minified and loaded by the browser. By mapping the ClojureScript code to the minified and translated JavaScript, source maps keep client-side code readable and more importantly debuggable even after you’ve combined and minified it, without impacting performance.

2.2 How to generate source maps

This depends on how you are compiling your ClojureScript. If you are using shadow-cljs, like me, this process is simple, you can use the following shadow-cljs configs, shown truncated here:

...
:release
         {:build-options
            {:ns-aliases {day8.re-frame.tracing day8.re-frame.tracing-stubs}},
          :compiler-options {:source-map true}},
...

2.3 How to use source maps

If you are using Sentry for error reporting, it provides functionality that will use your source maps to make error messages clearer. To upload your source maps to Sentry, follow these steps:

1. Create a sentry release, for example with the below bash command:

sentry-cli releases new

2. Make sure that sourceMappingURL is set correctly. From my personal experience, even after uploading source maps to Sentry, your error messages may not look correct. The issue may result from an incorrect sourceMappingURL value in your generated .js files. You can fix this with sed:

# ensure sourceMappingURL is set correctly
sed -i "s/sourceMappingURL=/sourceMappingURL=$1:\/\/$2\/js\/path\/to\/your\//g" -r resources/public/js/path/to/your/*.js

3. Push your source maps to sentry

sentry-cli --url https://sentry.io releases --org  --project  files  upload-sourcemaps resources/public/js// --url-prefix :///js//

4. Finalize the release

sentry-cli releases finalize

The output with Sentry source maps working

Here’s an example stack trace in Sentry with source maps working:

your sentry stack trace

Source maps are helpful during both development and production. They give us a clearer view of the code and through this accelerate debugging. This helps us get fixes and new features out faster.

Tags