Skip to content

Commit 16b7c67

Browse files
CopilotneSpecc
andcommitted
Add metrics integration tests and documentation
Co-authored-by: neSpecc <3684889+neSpecc@users.noreply.github.com>
1 parent 0e4005f commit 16b7c67

3 files changed

Lines changed: 215 additions & 0 deletions

File tree

docs/METRICS.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# Prometheus Metrics
2+
3+
This application exposes Prometheus-compatible metrics on a separate port from the main API server.
4+
5+
## Configuration
6+
7+
The metrics server runs on a separate port configured via the `METRICS_PORT` environment variable:
8+
9+
```bash
10+
# Default: 9090
11+
METRICS_PORT=9090
12+
```
13+
14+
Add this to your `.env` file. See `.env.sample` for reference.
15+
16+
## Metrics Endpoint
17+
18+
The metrics are served at:
19+
20+
```
21+
http://localhost:9090/metrics
22+
```
23+
24+
(Replace `9090` with your configured `METRICS_PORT` if different)
25+
26+
## Available Metrics
27+
28+
### Default Node.js Metrics
29+
30+
The following default Node.js metrics are automatically collected:
31+
32+
- **nodejs_version_info** - Node.js version information
33+
- **process_cpu_user_seconds_total** - Total user CPU time spent in seconds
34+
- **process_cpu_system_seconds_total** - Total system CPU time spent in seconds
35+
- **nodejs_heap_size_total_bytes** - Total heap size in bytes
36+
- **nodejs_heap_size_used_bytes** - Used heap size in bytes
37+
- **nodejs_external_memory_bytes** - External memory in bytes
38+
- **nodejs_heap_space_size_total_bytes** - Total heap space size in bytes
39+
- **nodejs_heap_space_size_used_bytes** - Used heap space size in bytes
40+
- **nodejs_eventloop_lag_seconds** - Event loop lag in seconds
41+
- **nodejs_eventloop_lag_min_seconds** - Minimum event loop lag
42+
- **nodejs_eventloop_lag_max_seconds** - Maximum event loop lag
43+
- **nodejs_eventloop_lag_mean_seconds** - Mean event loop lag
44+
- **nodejs_eventloop_lag_stddev_seconds** - Standard deviation of event loop lag
45+
- **nodejs_eventloop_lag_p50_seconds** - 50th percentile event loop lag
46+
- **nodejs_eventloop_lag_p90_seconds** - 90th percentile event loop lag
47+
- **nodejs_eventloop_lag_p99_seconds** - 99th percentile event loop lag
48+
49+
### Custom HTTP Metrics
50+
51+
#### http_request_duration_seconds (Histogram)
52+
53+
Duration of HTTP requests in seconds, labeled by:
54+
- `method` - HTTP method (GET, POST, etc.)
55+
- `route` - Request route/path
56+
- `status_code` - HTTP status code
57+
58+
Buckets: 0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10 seconds
59+
60+
#### http_requests_total (Counter)
61+
62+
Total number of HTTP requests, labeled by:
63+
- `method` - HTTP method (GET, POST, etc.)
64+
- `route` - Request route/path
65+
- `status_code` - HTTP status code
66+
67+
## Testing
68+
69+
### Manual Testing
70+
71+
You can test the metrics endpoint using curl:
72+
73+
```bash
74+
curl http://localhost:9090/metrics
75+
```
76+
77+
Or run the provided test script:
78+
79+
```bash
80+
./test-metrics.sh
81+
```
82+
83+
### Integration Tests
84+
85+
Integration tests for metrics are located in `test/integration/cases/metrics.test.ts`.
86+
87+
Run them with:
88+
89+
```bash
90+
npm run test:integration
91+
```
92+
93+
## Implementation Details
94+
95+
The metrics implementation uses the `prom-client` library and consists of:
96+
97+
1. **Metrics Module** (`src/metrics/index.ts`):
98+
- Initializes a Prometheus registry
99+
- Configures default Node.js metrics collection
100+
- Defines custom HTTP metrics (duration histogram and request counter)
101+
- Provides middleware for tracking HTTP requests
102+
- Creates a separate Express app for serving metrics
103+
104+
2. **Integration** (`src/index.ts`):
105+
- Adds metrics middleware to the main Express app
106+
- Starts metrics server on a separate port
107+
- Keeps metrics server isolated from main API traffic
108+
109+
## Prometheus Configuration
110+
111+
To scrape these metrics with Prometheus, add the following to your `prometheus.yml`:
112+
113+
```yaml
114+
scrape_configs:
115+
- job_name: 'hawk-api'
116+
static_configs:
117+
- targets: ['localhost:9090']
118+
```
119+
120+
Adjust the target host and port according to your deployment.

test-metrics.sh

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/bin/bash
2+
3+
# Script to manually test the Prometheus metrics endpoint
4+
# This can be run locally with a running instance of the API
5+
6+
METRICS_PORT=${METRICS_PORT:-9090}
7+
METRICS_URL="http://localhost:${METRICS_PORT}/metrics"
8+
9+
echo "Testing Prometheus Metrics Endpoint..."
10+
echo "URL: ${METRICS_URL}"
11+
echo ""
12+
13+
# Test if the endpoint is accessible
14+
if curl -s -o /dev/null -w "%{http_code}" "${METRICS_URL}" | grep -q "200"; then
15+
echo "✓ Metrics endpoint is accessible (HTTP 200)"
16+
else
17+
echo "✗ Metrics endpoint is not accessible"
18+
exit 1
19+
fi
20+
21+
echo ""
22+
echo "Sample metrics output:"
23+
echo "======================"
24+
curl -s "${METRICS_URL}" | head -50
25+
echo ""
26+
echo "..."
27+
echo ""
28+
29+
# Check for specific metrics
30+
echo "Checking for required metrics..."
31+
32+
if curl -s "${METRICS_URL}" | grep -q "nodejs_version_info"; then
33+
echo "✓ Default Node.js metrics present"
34+
else
35+
echo "✗ Default Node.js metrics missing"
36+
exit 1
37+
fi
38+
39+
if curl -s "${METRICS_URL}" | grep -q "http_request_duration_seconds"; then
40+
echo "✓ HTTP request duration metrics present"
41+
else
42+
echo "✗ HTTP request duration metrics missing"
43+
exit 1
44+
fi
45+
46+
if curl -s "${METRICS_URL}" | grep -q "http_requests_total"; then
47+
echo "✓ HTTP request counter metrics present"
48+
else
49+
echo "✗ HTTP request counter metrics missing"
50+
exit 1
51+
fi
52+
53+
echo ""
54+
echo "All checks passed! ✓"
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import axios from 'axios';
2+
import dotenv from 'dotenv';
3+
import path from 'path';
4+
5+
/**
6+
* Env variables for API
7+
*/
8+
const apiEnv = dotenv.config({ path: path.join(__dirname, '../api.env') }).parsed || {};
9+
10+
/**
11+
* Axios instance to send requests to metrics endpoint
12+
*/
13+
const metricsInstance = axios.create({
14+
baseURL: `http://api:${apiEnv.METRICS_PORT || 9090}`,
15+
});
16+
17+
describe('Prometheus Metrics', () => {
18+
test('Metrics endpoint is accessible', async () => {
19+
const response = await metricsInstance.get('/metrics');
20+
21+
expect(response.status).toBe(200);
22+
expect(response.headers['content-type']).toMatch(/text\/plain/);
23+
});
24+
25+
test('Metrics endpoint returns prometheus format', async () => {
26+
const response = await metricsInstance.get('/metrics');
27+
28+
// Check for some default Node.js metrics
29+
expect(response.data).toContain('nodejs_version_info');
30+
expect(response.data).toContain('process_cpu_user_seconds_total');
31+
expect(response.data).toContain('nodejs_heap_size_total_bytes');
32+
});
33+
34+
test('Metrics endpoint includes custom HTTP metrics', async () => {
35+
const response = await metricsInstance.get('/metrics');
36+
37+
// Check for our custom metrics
38+
expect(response.data).toContain('http_request_duration_seconds');
39+
expect(response.data).toContain('http_requests_total');
40+
});
41+
});

0 commit comments

Comments
 (0)