Optimizing Performance Using Prometheus with Node JS for Monitoring
Tags: prometheus, monitoring, node, grafana
Published: 2024-04-30 18:25:35


Application performance monitoring is an essential hack you as a developer should get acquainted with to detect any error and provide a seamless experience to the end user of the service. 
However, to achieve this, it’s quite needed to possess adequate knowledge of some Application monitoring tools. 

In this article, we will be talking about The Prometheus tool, its use cases, and its relevance to backend development. Furthermore, we will be looking into integrating it into our Backend application, via the use of several packages and we will also test its functionality. 
Additionally, we would chip in on how to generate meaningful insights from our scraped metrics with Prometheus using a tool called Grafana. Having laid out our objectives, let’s get started.
Before moving on to this tutorial, here are some prerequisites 
needed to understand this tutorial efficiently. 

- Firstly, you need to have at least basic to intermediate knowledge of Node JS
- Understand and ability to use NPM


## What is Prometheus?
Prometheus is an open-source system monitoring and observability tool invented in the year 2012. It provides monitoring, observability and alerting features for both cloud services and backend applications.  It also works on the principle of querying various endpoints and generating various data matrices which are then stored and can be analyzed to monitor for application and cloud performance.
It provides client libraries across several programming languages providing ease of Application integration.  It also provides an Admin dashboard where various data are queried and scraped from an application and cloud operations are collected. Its Alert features also notify the application developer in case of an occurrence of any anomaly in the application metrics.
Additionally; it possesses an advanced feature known as the PROMQL (Prometheus query language) which allows the developer to use advanced queries to generate data commands for appropriate analysis and the generation of measurable information insights. Right now, we will be integrating Prometheus into our sample backend application which is a Node JS application powered by the Express framework. It is important to have the software already set up before proceeding to the subsequent sections. Here is a link to the Prometheus [tool](https://prometheus.io/download/). 

After completing your installation, initialize a default Node JS application and then install the necessary server dependencies.
## Prometheus Client packages for Node JS
Prometheus as mentioned earlier provides various client packages across various languages and Node JS isn’t left out. Currently, here are some of the most popular Prometheus client packages available for Node JS.

- [Express-prom-bundle](https://npmjs.com/package/express-prom-bundle)
- [Prom-client](https://npmjs.com/package/prom-client)
- [prometheus-api-metrics](https://npmjs.com/package/prometheus-api-metrics)
- [express-prometheus-middleware](https://npmjs.com/package/express-prometheus-middleware)
- [appmetrics](https://npmjs.com/package/appmetrics)
- [metrics](https://npmjs.com/package/metrics)
And others. 

However, for this tutorial, the Prom-client package will be used as it has user-friendly documentation, a large user base and strong community support. Here is a link to its [documentation](https://github.com/siimon/prom-client) and [npm](https://www.npmjs.com/package/prom-client) page. 
## Node JS integration with Prometheus
To integrate the Prometheus metrics tracking feature into our Node JS application, we would have to install the prom-client package
`npm i prom-client`
On successful completion of this step, we will then be initializing the prom client in our code base.
Navigating to the `index.js` page, and prom client can be initialized by

```
const client =require(“prom-client”)
```
With this, we have successfully initialized Prometheus into our Node JS project. Subsequently, we would be setting it up to collect relevant metrics and also learn about some of its specific metric features.
## Tracking metrics using Prometheus             
Prometheus provides the user with the flexibility to collect routine Backend application service metrics via the use of its `CollectDefaultMetrics` feature. It also empowers the user to design unique customized metric collection functions which helps the user assess the application performance more reliably. 
To collect routine application metrics, the code below can be used. 

```
const register = new client.Registry();
const collectDefaultMetrics = client.collectDefaultMetrics;

collectDefaultMetrics({
    register
});

```
The above code initializes the Prometheus registry which serves as a state/container that holds all metrics to be collected from the application. 

With this properly configured, we have successfully implemented the default metrics collection feature. To access the metrics collected, an endpoint would have to be created which when accessed, would provide all metrics scraped per time. This can easily be created below.

```
app.get("/metrics", async (req, res) => {
    res.setHeader("Content-Type", client.register.contentType);
    let metrics = await register.metrics();
    res.send(metrics);
});

```
 
Accessing this endpoint "/metrics",  will reveal all  the scraped metrics collected. We would then go on to create customizable specific application metrics to be scraped by the Prometheus library. 
Here is the code below. on how to create custom metrics. 

```
const http_request_counter = new client.Counter({
    name: 'myapp_http_request_count',
    help: 'Count of HTTP requests',
    labelNames: ['method', 'route', 'statusCode']
});

register.registerMetric(http_request_counter);

app.use("/*", function(req, res, next) {
    http_request_counter.labels({
        method: req.method,
        route: req.originalUrl,
        statusCode: res.statusCode
    }).inc();
    console.log(register.metrics());
    next();
});

 
```

in the code above, we created a request counter metric. This counter will automatically increase by 1 whenever we access any endpoint in our backend application. Also included is the name assigned to the  custom metrics and the parameters to be collected such as the route, the request method and the status code of the request. 



 Here is the final code for the project.

```
// Import required modules
const express = require("express");
const client = require("prom-client");
const bodyParser = require("body-parser");
const cors = require("cors");
const connectDB = require("./Config/db");

// Create an Express application
const app = express();

// Initialize Prometheus registry
const register = new client.Registry();

// Configure default Prometheus labels
register.setDefaultLabels({
    app: "blue",
});

// Define Prometheus metrics
const http_request_counter = new client.Counter({
    name: 'myapp_http_request_count',
    help: 'Count of HTTP requests',
    labelNames: ['method', 'route', 'statusCode']
});
const userCounter = new client.Counter({
    name: "user_counter",
    help: "User counter for my application"
});

// Register Prometheus metrics with the registry
register.registerMetric(http_request_counter);
register.registerMetric(userCounter);

// Middleware to collect and expose Prometheus metrics
app.get("/metrics", async (req, res) => {
    res.setHeader("Content-Type", client.register.contentType);
    let metrics = await register.metrics();
    res.send(metrics);
});

// Middleware to increment user counter for all routes
app.get("/*", (req, res) => {
    userCounter.inc();
    console.log(register.metrics());
    res.send("test");
});

// Middleware to count HTTP requests and log metrics
app.use("/*", function(req, res, next) {
    http_request_counter.labels({
        method: req.method,
        route: req.originalUrl,
        statusCode: res.statusCode
    }).inc();
    console.log(register.metrics());
    next();
});

// Collect default Prometheus metrics (e.g., CPU, memory)
client.collectDefaultMetrics({
    register
});

// Define a Prometheus histogram for response time
const restResponseTimeHistogram = new client.Histogram({
    name: 'rest_response_time_duration_seconds',
    help: 'REST API response time in seconds',
    labelNames: ['method', 'route', 'status_code']
});

// Enable CORS for all routes
app.use(cors({
    origin: '*',
}));

// Parse JSON requests
app.use(express.json());
app.use(bodyParser.json());

// Connect to the database
connectDB();

// Start the server
app.listen(process.env.PORT || 5000, () => {
    console.log("Server is running...");
});

```
Here is the endpoint showing the application metrics scraped by Prom-client. 
![Prometheus scraping](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vknv6vdcj5f5uv0wxh6i.JPG)


With this, we have completed the basics of Prometheus integration with Node JS. Other advanced concepts in Prometheus would further help the developer to create complex relevant metrics registers using the prom client tool. 
## Additional info (Exploring Grafana)

![Grafana dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t9fg0h4amxrt29ovp0wp.JPG)
Obtaining your metrics from your backend service application shouldn’t be all that there is to be. As beautiful as Prometheus is, it is also limited in quite several features of which data visualization is among. Here comes Grafana to the rescue. 
The Grafana tool is a data visualization tool that generates meaningful readable insights from the queried metric raw data obtained from Prometheus. You can check out their documentation [here](https://grafana.com/docs/) and download the tool [here](https://grafana.com/grafana/download) for your respective Operating systems. You can also check out tutorials on how to integrate both Prometheus and Grafana. 



## Conclusion
With this, we have come to the end of the tutorial. We hope you’ve learned essentially about Prometheus, its uses and how to integrate into a backend application for efficient monitoring of application performance.
Feel free to drop comments and questions in the box below, and also check out my other articles [here](linktr.ee/tobilyn77). Till next time, keep on coding!