Skip to main content
Monitoring Node.js Apps with Prometheus
  1. Blog/

Monitoring Node.js Apps with Prometheus

·6 mins·

In the age of monolithic apps and app servers, monitoring was relatively straightforward. With microservices, you’re dealing with more servers and more services, and monitoring gets complex fast. You have options — Nagios, Zabbix, or Prometheus. My preference goes to the Greek deity that stole fire from Mount Olympus and brought it to us.

Deity? Prometheus
#

From the Prometheus website:

(…) an open-source systems monitoring and alerting toolkit originally built at SoundCloud. Since its inception in 2012, many companies and organizations have adopted Prometheus, and the project has a very active developer and user community.

As SoundCloud moved to microservices they needed to monitor thousands of services, and their existing monitoring had too many limitations. The Prometheus website adds:

It is now a standalone open source project and maintained independently of any company. To emphasize this and clarify the project’s governance structure, Prometheus joined the Cloud Native Computing Foundation in 2016 as the second hosted project after Kubernetes.

Prometheus architecture
#

Prometheus was designed with 4 main requirements:

  • A multi-dimensional data model, so that data can be sliced and diced at will, along dimensions like instance, service, endpoint, and method.
  • Operational simplicity, so that you can spin up a monitoring server where and when you want, even on your local workstation, without setting up a distributed storage backend or reconfiguring the world.
  • Scalable data collection and decentralized architecture, so that you can reliably monitor the many instances of your services, and independent teams can set up independent monitoring servers.
  • Finally, a powerful query language that leverages the data model for meaningful alerting (including easy silencing) and graphing (for dashboards and for ad-hoc exploration).

arch
Image from Prometheus documentation

There are many good explanations of the individual components out there, so I’ll leave that to those resources.

Building a Node.js app
#

Before we get into Prometheus configuration, we need a service to monitor. I’ll walk through what you need to add to your Node.js apps to start monitoring them with Prometheus. I’m assuming you’ve created an API spec in TIBCO Cloud Integration and exported the Node.js code. If not, the tutorial on the TIBCO Community will get you started (or just grab the code from my GitHub repo ;-)).

Adding dependencies to package.json
#

We need a Prometheus client for Node.js that supports histograms, summaries, gauges, and counters. Prometheus recommends prom-client:

npm install --save prom-client

We also want response time tracking, so we need a middleware that records response times for HTTP requests:

npm install --save response-time

Creating a module
#

Following Node.js best practices, we’ll create a separate module for all the Prometheus instrumentation. It’s a fair amount of JavaScript, but I’ve documented it thoroughly.

/**
 * Newly added requires
 */
var Register = require('prom-client').register;
var Counter = require('prom-client').Counter;
var Histogram = require('prom-client').Histogram;
var Summary = require('prom-client').Summary;
var ResponseTime = require('response-time');
var Logger = require('./logger');

/**
 * A Prometheus counter that counts the invocations of the different HTTP verbs
 * e.g. a GET and a POST call will be counted as 2 different calls
 */
module.exports.numOfRequests = numOfRequests = new Counter({
    name: 'numOfRequests',
    help: 'Number of requests made',
    labelNames: ['method']
});

/**
 * A Prometheus counter that counts the invocations with different paths
 * e.g. /foo and /bar will be counted as 2 different paths
 */
module.exports.pathsTaken = pathsTaken = new Counter({
    name: 'pathsTaken',
    help: 'Paths taken in the app',
    labelNames: ['path']
});

/**
 * A Prometheus summary to record the HTTP method, path, response code and response time
 */
module.exports.responses = responses = new Summary({
    name: 'responses',
    help: 'Response time in millis',
    labelNames: ['method', 'path', 'status']
});

/**
 * This funtion will start the collection of metrics and should be called from within in the main js file
 */
module.exports.startCollection = function () {
    Logger.log(Logger.LOG_INFO, `Starting the collection of metrics, the metrics are available on /metrics`);
    require('prom-client').collectDefaultMetrics();
};

/**
 * This function increments the counters that are executed on the request side of an invocation
 * Currently it increments the counters for numOfPaths and pathsTaken
 */
module.exports.requestCounters = function (req, res, next) {
    if (req.path != '/metrics') {
        numOfRequests.inc({ method: req.method });
        pathsTaken.inc({ path: req.path });
    }
    next();
}

/**
 * This function increments the counters that are executed on the response side of an invocation
 * Currently it updates the responses summary
 */
module.exports.responseCounters = ResponseTime(function (req, res, time) {
    if(req.url != '/metrics') {
        responses.labels(req.method, req.url, res.statusCode).observe(time);
    }
})

/**
 * In order to have Prometheus get the data from this app a specific URL is registered
 */
module.exports.injectMetricsRoute = function (App) {
    App.get('/metrics', (req, res) => {
        res.set('Content-Type', Register.contentType);
        res.end(Register.metrics());
    });
};

Adding code to server.js
#

In server.js you only need a few lines:

'use strict';

...
/**
 * This creates the module that we created in the step before.
 * In my case it is stored in the util folder.
 */
var Prometheus = require('./util/prometheus');
...

/**
 * The below arguments start the counter functions
 */
App.use(Prometheus.requestCounters);
App.use(Prometheus.responseCounters);

/**
 * Enable metrics endpoint
 */
Prometheus.injectMetricsRoute(App);

/**
 * Enable collection of default metrics
 */
Prometheus.startCollection();
...

Five lines of code in server.js and your app is instrumented.

Deploy
#

Push the app to TIBCO Cloud Integration using either the tibcli utility or the Visual Studio Code extension. More details in the TCI docs or in this post on the TIBCO Community. After deploying, grab the URL of your app — you’ll need it for Prometheus. For example: https://integration.cloud.tibcoapps.com/ijdc72jg2ugg2dikkkl236f2rhma6qaz.

Running Prometheus
#

There are many ways to run Prometheus, but Docker is probably the easiest. No installation headaches, no dependency management. You just need to tell Prometheus which endpoint to monitor via a prometheus.yml file that you bind-mount from the host.

Prometheus needs a hostname and port for its targets, which makes monitoring apps on iPaaS/PaaS platforms a bit trickier. The metrics_path parameter per job handles this by telling Prometheus to hit a specific path on the server.

A basic but functional prometheus.yml:

global:
  scrape_interval: 1m
  scrape_timeout: 10s
  evaluation_interval: 1m
rule_files:
- /etc/prometheus/alert.rules
scrape_configs:
- job_name: PrometheusApp
  scrape_interval: 5s
  scrape_timeout: 5s
  metrics_path: /ijdc72jg2ugg2dikkkl236f2rhma6qaz/metrics
  scheme: https
  static_configs:
  - targets:
    - integration.cloud.tibcoapps.com
    labels:
      app: PrometheusApp
      sandbox: MyDefaultSandbox

I’ve set the job_name and the label app to match my app name in TCI for easy correlation. The metrics_path contains the app URL path plus /metrics.

Start the Docker container:

docker run -p 9090:9090 -v /path/to/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus

Looking at Prometheus data
#

Open Prometheus at http://localhost:9090/graph and enter the expression up:

pic1

That checks whether your app is running. sum(numOfRequests) gives you the total request count:

pic2

Without sum() you get a breakdown by HTTP verb:

pic3

Reporting with Grafana
#

Grafana can take the data from Prometheus and present it in dashboards. You can build views showing app status, request counts, and request type breakdowns:

pic4
A sample dashboard that shows the status, the number of requests and what kind of requests were served

Wrapping up
#

Prometheus lets you monitor the health and status of your Node.js apps on TIBCO Cloud Integration (or anywhere else), and Grafana gives you custom dashboards on top of that data. The combination works well for any app that can expose or push metrics to Prometheus.

Related

How To Combine APIs With Flogo Apps In TIBCO Cloud Integration

·4 mins
In 2002 Jeff Bezos issued a mandate that would change the world forever. At the very least it brought a massive change to how data is reused on the Internet: All teams will henceforth expose their data and functionality through service interfaces. Teams must communicate with each other through these interfaces. There will be no other form of inter-process communication allowed: no direct linking, no direct reads of another team’s data store, no shared-memory model, no back-doors whatsoever. The only communication allowed is via service interface calls over the network. It doesn’t matter what technology they use. All service interfaces, without exception, must be designed from the ground up to be externalizable. That is to say, the team must plan and design to be able to expose the interface to developers in the outside world. No exceptions. Anyone who doesn’t do this will be fired. Thank you; have a nice day! That mandate kicked off a lot of what we now call the API economy. Many enterprises have APIs that deliver data so you can focus on building value rather than figuring out how to get the data. That said, most APIs out there are documented but don’t have a swagger.json you can import directly. The Web Integrator in TIBCO Cloud Integration lets you paste sample messages from API docs and use those as the basis for invoking REST APIs.

How To Connect Google Forms to APIs

·4 mins
Ever wanted to capture data from a form and send it somewhere useful? Google Forms handles the collection side well, but what about routing that data to an API? That’s where TIBCO Cloud Integration comes in.