Sunny

Logging in Cloudflare Workers with Sentry

Atlas uses Cloudflare Workers to build the routing layer, a simple API app (like DNS) to redirect requests to the correct destinations. It’s a critical piece of our infrastructure. We already have around five routers mapping requests from different services we use for the platform. I previously discussed how we developed with Workers here.

Workers provide us with some basic logging but have some limitations:

  • Workers platform will capture all console.log ‘s, and that’s all. It’s inefficient but straightforward, which makes it hard to debug.
  • Logs are not persistent, they are not stored, and we can start and stop the stream at any time to view them.
  • Logs will not display if the Worker’s requests per second are over 200 for the last 5 minutes.

Insufficient logging and tracing have always been a problem we faced with Workers. Working with external systems like payment processors and other integrations made this problem more severe and forced us to solve it. I recently got my hands on solving this problem and thought to share some of our findings and solution with you, hoping this will benefit some of you.

. . .


Logging tools

There are a ton of logging tools out in the market that support Javascript logging, and a few of them support logging on Workers platform. 1

We picked Sentry because it has excellent Javascript support, and we are already using it for most of our front-end applications. Sentry Javascript doesn’t support out-of-the-box, and we ended up using Robert Cepa’s toucan-js; it’s a very reliable Sentry client for Workers platform.

. . .


Developing the application

A quick yarn add toucan-js and a few lines of code we can get started with logging. Here’s how we did it.


Generate the application

wrangler generate <app-name> https://github.com/cloudflare/worker-template

Add Webpack to support modules modules.

yarn add --dev webpack

Add Sentry with Toucan

yarn add toucan-js

Setup the library

let sentry = new Toucan({
  dsn: SENTRY_DSN,
  context: CF_EVENT,
  environment: "workbox",
});


This gets us to a stage where we can start logging anything from the application.

sentry.captureException # To capture errors
sentry.captureMessage   # To capture messages
sentry.addBreadcrumb    # To record breadcrumbs


A captured message on Sentry


. . .


But what we needed was more:

  1. We wanted to tag each release to segregate errors and logs easily
  2. We wanted to upload source maps for each release, so we can easily debug errors
  3. We wanted to repeat the above steps on our CI/CD pipelines

Both 1 and 2 are configurable on Sentry. We can use Sentry’s webpack plugin to upload source maps and set release and environment details when initialising the client.

We need to provide authentication token, organisation, and project in the webpack plugin config. We also set release information, ideally accessible as host environment variables. But environment variables work differently in Cloudflare workers; we can declare variables on the Workers toml file but cannot override when dynamically deploying the application from our CI/CD executor (and we also don’t want to hardcode secrets).

The hack we found here was to use Webpack’s environment-plugin and access process.env variables in the application. CI/CD executor and Workers platform can read these environment variables. Here’s how we did it.


Add Sentry webpack plugin

yarn add --dev @sentry/webpack-plugin

Update webpack config and add necessary environment variables

const { EnvironmentPlugin } = require("webpack");
const SentryWebpackPlugin = require("@sentry/webpack-plugin");

module.exports = {
  target: "webworker",
  entry: "./index.js",
  devtool: "hidden-source-map",
  plugins: [
    new EnvironmentPlugin(["ENVIRONMENT", "SENTRY_DSN", "SENTRY_RELEASE"]),
    new SentryWebpackPlugin({
      authToken: process.env.SENTRY_AUTH_TOKEN,
      org: process.env.SENTRY_ORG,
      project: process.env.SENTRY_PROJECT,
      release: process.env.SENTRY_RELEASE,
      include: "./dist",
    }),
  ],
};

Update Sentry client

let sentry = new Toucan({
  dsn: process.env.SENTRY_DSN,
  context: CF_EVENT,
  environment: process.env.ENVIRONMENT,
  release: process.env.SENTRY_RELEASE,
});


Sentry release


Sourcemap for a release


This setup allowed us to set environment variables on the host, automatically tag releases, and upload source maps for any machine – local and CI/CD executors.

Once we had it up, we got a much clearer view of the errors occurring on the Worker.


Error with source map


We implemented this solution in one of our routers built using Workers and will extend it to all of our routers soon.

I published a template here with the same setup if you want to use it.

. . .


Thanks for reading. Please feel free to reach out to me (email) if you have any questions and ideas, I’ll be happy to take your feedback, help, and discuss it further.


Notes

  1. https://blog.cloudflare.com/observability-ecosystem/