neurons firing from a keyboard

thoughts about devops, technology, and faster business from a random guy from dallas.

How to SRE-ify your React app with Prometheus

Reading Time: Approximately 3 minutes.
View this post on GitHub.

I am not a JavaScript developer. However, I was given a task at work recently that forced me to enter the abyss and get good at keeping my Promises.

I was asked to create a webinar on helping developers become better SREs through observability and instrumentation. The objective was to take a broken web app and add enough monitoring and logging to it to make troubleshooting its brokenness easier. (I’ll update this post with a link when we broadcast it on April 22nd!)

The web app under repair was a React app with a Rails backend. JavaScript time.

While getting Prometheus wired up with the Rails backend was pretty easy, I had a shockingly difficult time getting it working with Node and React. The web app was created with Create React App, which makes it stupidly easy to get started with React with the help of thousands of lines of black magic (no, seriously; look at the codebase if you don’t believe me). While Create React App handles starting the Express web server for you, it doesn’t provide a whole lot in the way of configuring that server.

I spent several hours figuring out how to get React and Prom talking to each other (and finding surprisingly little in instrumenting a React app with Prom). I succeeded! It was WAY easier than I thought.

I hope this blog post saves you hours of pain. Apologies for any JavaScript errors or misgivings; JS isn’t my bag!

Assumptions

I’m going to assume that you have a Prometheus server already configured, so I won’t cover getting started with Prometheus. Read the excellent configuration documentation if you’re interested in learning more.

I’m also going to assume that your app was created with Create React App and is using the built-in Express server that comes with react-scripts.

Just when I thought I could write code.

How to Avoid Pain and Suffering

  1. Add a Prometheus target to prometheus.yml for the metrics that you’re about to expose:
---
global:
  # rest of the damn owl

  - job_name: frontend
    scrape_interval: 5s
    scrape_timeout: 2s
    honor_labels: true
    static_configs:
      - targets: ['frontend:5000'] # Change to your app's URL
  1. Restart your Prom server. The target should register and be down.

Result after adding Prom target.

  1. In your React app’s repository, add express-prom-bundle and prom-client to your dependencies node in package.json and change your start script to node server.js:
{
  "name": "project_organizer_front_end",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "prom-client": "12.0.0",
    "express-prom-bundle": "6.0.0",
    ... rest of dependencies
  },
  "scripts": {
    "start": "node server.js", <-- change this
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  1. At the root of your React app’s repository, create a new file called server.js: touch server.js

  2. In your editor of choice, copy and paste the following:

const express = require('express');
const favicon = require('express-favicon');
const path = require('path');
const port = process.env.PORT || 8080;
const prometheus = require('express-prom-bundle')

// This will create the /metrics endpoint for you and expose Node default
// metrics.
const metricsMiddleware = prometheus({
  includeMethod: true,
  includePath: true,
  promClient: { collectDefaultMetrics: {} }
})
const app = express();
app.use(favicon(__dirname + '/build/favicon.ico'));
// the __dirname is the current directory from where the script is running
app.use(express.static(__dirname));
app.use(express.static(path.join(__dirname, 'build')));
app.use(metricsMiddleware);
app.get('/*', function (req, res) {
  res.sendFile(path.join(__dirname, 'build', 'index.html')); // <-- change if not using index.html
});
app.listen(port);
  1. Restart Node but make sure that you build your app first: npm build && npm start

  2. Go back into Prometheus. Within a few seconds, the target should be up:

Result after adding Prom target and exporter.

  1. You’re done!