Application Monitoring with Prometheus

Hands-On Lab

 

Photo of Elle Krout

Elle Krout

Content Team Lead in Content

Length

00:30:00

Difficulty

Intermediate

When it comes to monitoring, keeping track of our CPU, memory, and other infrastructure metrics often isn't enough. Beyond the common infrastructure-based metrics we expose via tools such as the Node Exporter, we also want to instrument our application to send out metrics related to the app itself. This is done by using the Prometheus client library available to our application's language. Once we have the client library set up in our application, we can then define any metrics we wish to collect and write the code to support them. We can also have the Prometheus client record a number of default metrics already included in the library and available to us. Once finished, we'll have added metrics for our to-do application that track added and removed tasks, as well as response times across our app.

What are Hands-On Labs?

Hands-On Labs are scenario-based learning environments where learners can practice without consequences. Don't compromise a system or waste money on expensive downloads. Practice real-world skills without the real-world risk, no assembly required.

Application Monitoring with Prometheus

In this hands-on lab, we're going to create the metrics needed to monitor Forethought, a task-tracking application written in Node.js.

Start a new terminal session and log in to the Application server using the provided credentials. Open a second terminal and log in to the Monitoring server, too, using the provided credentials.

Add the Prometheus Client Library and Collect Default Metrics

  1. In the Application server terminal, move into the forethought directory:

    cd forethought
  2. Install the prom-client via npm, Node.js's package manager:

    npm install prom-client --save
  3. Open the index.js file, where we'll be adding all of our metrics code:

    $EDITOR index.js
  4. Require the use of the prom-client by adding it to our variable list:

    var express = require('express');
    var bodyParser = require('body-parser');
    var app = express();
    const prom = require('prom-client');

    With prom being the name we'll use when calling the client library.

  5. Enable default metrics scraping:

    const collectDefaultMetrics = prom.collectDefaultMetrics;
    collectDefaultMetrics({ prefix: 'forethought_' });
  6. Use Express to create the /metrics endpoint and call in the Prometheus data:

    app.get('/metrics', function (req, res) {
      res.set('Content-Type', prom.register.contentType);
      res.end(prom.register.metrics());
    });

Add the forethought_tasks_added Metric

  1. Define the metric:

    // Metric Definitions
    
    const tasksadded = new prom.Counter({
      name: 'forethought_tasks_added',
      help: 'The number of items added to the to-do list, total'
    });
  2. Call the new metric in the addtask post function so it increases by one every time the function is called while adding a task:

    // add a task
    app.post("/addtask", function(req, res) {
      var newTask = req.body.newtask;
      task.push(newTask);
      res.redirect("/");
      tasksadded.inc();
    });

Add the forethought_tasks_complete Metric

  1. Define the metric:

    const tasksdone = new prom.Counter({
      name: 'forethought_tasks_complete',
      help: 'The number of items completed'
    });
  2. Call the new metric in the /removetask post function so it increases by one every time the function is called:

    // remove a task
    app.post("/removetask", function(req, res) {
      var completeTask = req.body.check;
      if (typeof completeTask === "string") {
        complete.push(completeTask);
        task.splice(task.indexOf(completeTask), 1);
      }
      else if (typeof completeTask === "object") {
        for (var i = 0; i < completeTask.length; i++) {
          complete.push(completeTask[i]);
          task.splice(task.indexOf(completeTask[i]), 1);
          tasksdone.inc()
        }
      }
      res.redirect("/");
    });

Add the forethought_current_tasks Metric

  1. Define the metric:

    const taskgauge = new prom.Gauge({
      name: 'forethought_current_tasks',
      help: 'Amount of incomplete tasks'
    });
  2. Add an increase to the /addtask method:

    // add a task
    app.post("/addtask", function(req, res) {
      var newTask = req.body.newtask;
      task.push(newTask);
      res.redirect("/");
      tasksadded.inc();
      taskgauge.inc();
    });
  3. Add a decrease to the /removetask method:

    // remove a task
    app.post("/removetask", function(req, res) {
      var completeTask = req.body.check;
      if (typeof completeTask === "string") {
        complete.push(completeTask);
        task.splice(task.indexOf(completeTask), 1);
      }
      else if (typeof completeTask === "object") {
        for (var i = 0; i < completeTask.length; i++) {
          complete.push(completeTask[i]);
          task.splice(task.indexOf(completeTask[i]), 1);
          tasksdone.inc();
          taskgauge.dec();
        }
      }
      res.redirect("/");
    });

    Save and exit.

Add the forethought_response_time_summary Metric

  1. Add the response-time module:

    npm install response-time --save
  2. Reopen the index.js file.

  3. Define the metric:

    const responsetimesumm = new prom.Summary ({
      name: 'forethought_response_time_summary',
      help: 'Latency in percentiles',
    });
  4. Add response-time:

    var responseTime = require('response-time');
  5. Write the code to retrieve the response time:

    // tracking response time
    
    app.use(responseTime(function (req, res, time) {
      responsetimesumm.observe(time);
    }));

Add the forethought_response_time_histogram Metric

  1. Define the metric:

    const responsetimehist = new prom.Histogram ({
      name: 'forethought_response_time_histogram',
      help: 'Latency in history form',
      buckets: [0.1, 0.25, 0.5, 1, 2.5, 5, 10]
    });
  2. Call the histogram in the existing response-time code:

    app.use(responseTime(function (req, res, time) {
      responsetimesumm.observe(time);
      responsetimehist.observe(time);
    }));

Redeploy the Application

  1. Stop the current Docker container for our application:

    docker stop ft-app
  2. Remove the container:

    docker rm ft-app
  3. Remove the image:

    docker image rm forethought
  4. Rebuild the image:

    docker build -t forethought .
  5. Deploy the new container:

    docker run --name ft-app -p 80:8080 -d forethought
  6. Switch to the Monitoring server terminal.

  7. Add our application endpoint to Prometheus (replacing PRIVATE_IP with the private IP for the Application server listed on the lab page):

    sudo $EDITOR /etc/prometheus/prometheus.yml
    
      - job_name: 'forethought'
        static_configs:
        - targets: ['PRIVATE_IP:80']

    Save and exit.

  8. Restart Prometheus:

    sudo systemctl restart prometheus

Conclusion

Congratulations on completing this hands-on lab!