π Introduction
In today's cloud-native and microservices-based systems, observability has become a critical component for ensuring system health and performance. While traditional monitoring focuses on known failure scenarios, observability empowers teams to explore the unknown by collecting telemetry data like metrics, logs, and traces.
This article walks through a real-world implementation of observability using Prometheus and Grafana to monitor a simple Node.js API. We'll explore how to instrument the application, export metrics, collect them with Prometheus, and visualize them in Grafana.
By the end of this guide, you will have:
- A running Node.js REST API that exposes useful metrics
- Prometheus configured to scrape those metrics
- Grafana visualizing system behavior in real time
π§ What is Observability?
Observability answers the question: βWhatβs going on inside my system?β
It relies on three fundamental pillars:
Pillar | Description |
---|---|
Metrics | Numeric representation of data over time (e.g., CPU usage) |
Logs | Text records of events (e.g., errors, requests) |
Traces | Track request flow across distributed services |
Unlike basic uptime monitoring, observability helps answer why something is wrong, not just what is wrong.
π§° Tools and Technologies Used
- Node.js + Express: Lightweight web server to simulate an API
- prom-client: Prometheus client library for Node.js
- Prometheus: Time-series database that scrapes and stores metrics
- Grafana: Visualization tool for dashboards and alerts
- Docker + Docker Compose: To containerize and orchestrate services
- GitHub: For source code versioning and sharing
βοΈ System Architecture
User
|
v
Node.js API (Express)
|
βββ /metrics (prom-client)
|
Prometheus
|
Grafana
π οΈ Step-by-Step Implementation
1οΈβ£ Setting up the Node.js API
1.1 Install dependencies
npm init -y
npm install express prom-client
1.2 API Code (index.js)
const express = require('express');
const client = require('prom-client');
const app = express();
// Enable collection of default metrics like memory usage, CPU, etc.
client.collectDefaultMetrics();
const httpRequestDurationMicroseconds = new client.Histogram({
name: 'http_request_duration_ms',
help: 'Duration of HTTP requests in ms',
labelNames: ['method', 'route', 'code'],
buckets: [50, 100, 300, 500, 750, 1000, 2000]
});
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
httpRequestDurationMicroseconds
.labels(req.method, req.route?.path || req.path, res.statusCode)
.observe(duration);
});
next();
});
app.get('/hello', (req, res) => {
res.send('Hello, observability world!');
});
app.get('/metrics', async (req, res) => {
res.set('Content-Type', client.register.contentType);
res.end(await client.register.metrics());
});
app.listen(3000, () => {
console.log('Server listening on http://localhost:3000');
});
2οΈβ£ Prometheus Configuration
1.1 Create a file named prometheus.yml:
global:
scrape_interval: 5s
scrape_configs:
- job_name: 'nodeapp'
static_configs:
- targets: ['nodeapp:3000']
Prometheus will now scrape metrics from /metrics every 5 seconds.
3οΈβ£ Dockerizing Everything
Dockerfile
FROM node:18
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "index.js"]
docker-compose.yml
version: '3.8'
services:
nodeapp:
build: .
ports:
- "3000:3000"
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
grafana:
image: grafana/grafana
ports:
- "3001:3000"
4οΈβ£ Running the Project
Start everything with:
docker-compose up --build
Then, go to:
`http://localhost:3000/hello β API endpoint
http://localhost:3000/metrics β Raw Prometheus metrics
http://localhost:9090 β Prometheus dashboard
http://localhost:3001 β Grafana (login: admin/admin)`
You can find the full source code and instructions here:
π GitHub Repo
Top comments (0)