13 KiB
name, description
| name | description |
|---|---|
| ngx-http-monitoring-client | Use this skill when connecting clients or agents to an ngx_http_monitoring_module service, querying its JSON/SSE/Prometheus endpoints, reading API response structures, debugging access problems, or integrating dashboards/health checks. Supports deployments protected by Nginx Basic Auth and/or monitor_api_token. |
ngx_http_monitoring_module Client
Use this skill to consume a deployed ngx_http_monitoring_module service from scripts, agents, dashboards, health checks, CI, or observability clients.
Inputs To Determine
Before calling the service, identify:
base_url: root URL that serves/monitor, for examplehttp://127.0.0.1:8080- optional API token: sent as
X-Monitor-Token,Authorization: Bearer, or?token=... - optional Nginx Basic Auth credentials:
username:password - target endpoint: dashboard, JSON API, SSE, Prometheus, or health
Never invent credentials. Ask the user, read documented deployment config, or use provided environment variables.
Recommended environment variables:
MONITOR_BASE_URL=http://127.0.0.1:8080
MONITOR_TOKEN=change-me
MONITOR_BASIC_AUTH=user:password
Endpoints
GET /monitor- embedded HTML dashboardGET /monitor/api- full JSON metricsGET /monitor/api/system- CPU, load, memory, swap, uptimeGET /monitor/api/nginx- Nginx connection/request/worker metricsGET /monitor/api/network- network interfaces and countersGET /monitor/api/disk- disk and filesystem metricsGET /monitor/api/processes- process, TCP, socket, and worker statsGET /monitor/api/upstreams- observed upstream/backend statsGET /monitor/api/connections- connection/SSE/keepalive countersGET /monitor/api/requests- request counters, histograms, percentiles, top URLsGET /monitor/live- Server-Sent Events streamGET /monitor/metrics- Prometheus text formatGET /monitor/health- lightweight JSON health check
API Structures
All JSON API responses are compact, versioned, and timestamped. Treat unknown fields as forward-compatible additions, and treat missing sections as disabled collectors, unsupported Nginx build options, or unavailable Linux /proc data.
Common envelope:
{
"version": 1,
"module": "1.0.0",
"timestamp": 1710000000,
"msec": 123,
"scope": "full",
"pid": 12345
}
Endpoint-specific API routes such as /monitor/api/system return the common envelope plus one top-level field matching the route. The examples below show that section's value unless explicitly shown as a complete response.
GET /monitor/api returns the envelope plus:
{
"system": {},
"nginx": {},
"network": {},
"disk": {},
"processes": {},
"upstreams": [],
"connections": {},
"requests": {},
"history": []
}
GET /monitor/api/system:
{
"cpu": {
"usage": 12.5,
"cores": 8,
"load": [0.12, 0.20, 0.30]
},
"memory": {
"total": 16777216,
"available": 8388608,
"free": 4194304,
"used": 8388608,
"used_pct": 50.0
},
"swap": {
"total": 2097152,
"free": 1048576,
"used_pct": 50.0
},
"uptime": 123456
}
GET /monitor/api/nginx:
{
"connections": {
"accepted": 1000,
"handled": 1000,
"active": 20,
"reading": 1,
"writing": 4,
"waiting": 15
},
"requests": {
"total": 50000,
"responses": 49990,
"requests_per_sec": 125.5,
"responses_per_sec": 125.3
},
"ssl": {
"requests": 100,
"handshakes": 20
},
"keepalive": {
"requests": 250
},
"workers": []
}
GET /monitor/api/network:
{
"rx_bytes": 123456789,
"tx_bytes": 987654321,
"rx_packets": 10000,
"tx_packets": 12000,
"rx_errors": 0,
"tx_errors": 0,
"interfaces": [
{
"name": "eth0",
"rx_bytes": 123456789,
"tx_bytes": 987654321,
"rx_packets": 10000,
"tx_packets": 12000,
"rx_errors": 0,
"tx_errors": 0,
"up": true
}
]
}
GET /monitor/api/disk:
{
"reads": 1000,
"writes": 2000,
"read_bytes": 4096000,
"write_bytes": 8192000,
"devices": [
{
"name": "sda",
"reads": 1000,
"writes": 2000,
"read_bytes": 4096000,
"write_bytes": 8192000,
"io_ms": 120
}
],
"filesystems": [
{
"path": "/",
"type": "ext4",
"total": 107374182400,
"used": 53687091200,
"free": 53687091200,
"avail": 53687091200,
"files": 1000000,
"files_free": 750000
}
]
}
GET /monitor/api/processes:
{
"process_count": 140,
"tcp": {
"established": 20,
"listen": 8
},
"sockets": {
"used": 512,
"tcp": 120,
"udp": 16
},
"workers": []
}
GET /monitor/api/connections:
{
"accepted": 1000,
"handled": 1000,
"active": 20,
"reading": 1,
"writing": 4,
"waiting": 15,
"ssl_requests": 100,
"ssl_handshakes": 20,
"keepalive_requests": 250,
"sse_clients": 2,
"sse_events": 500,
"rate_limited": 0
}
GET /monitor/api/requests:
{
"total": 50000,
"responses": 49990,
"bytes": 104857600,
"requests_per_sec": 125.5,
"responses_per_sec": 125.3,
"error_rate": 0.0078,
"latency": {
"avg": 8.4,
"p50": 4,
"p90": 15,
"p95": 22,
"p99": 80
},
"status": {
"1xx": 0,
"2xx": 49000,
"3xx": 600,
"4xx": 300,
"5xx": 90
},
"methods": {
"GET": 45000,
"POST": 4000,
"PUT": 100,
"DELETE": 50,
"HEAD": 500,
"OPTIONS": 200,
"PATCH": 50,
"OTHER": 100
},
"latency_histogram": [
{
"le": 1,
"count": 100
},
{
"le": "+Inf",
"count": 50000
}
],
"size_histogram": [
{
"le": 512,
"count": 10000
},
{
"le": "+Inf",
"count": 50000
}
],
"top_urls": [],
"user_agents": []
}
GET /monitor/api/upstreams:
[
{
"peer": "127.0.0.1:9000",
"requests": 1000,
"failures": 2,
"status_4xx": 10,
"status_5xx": 2,
"avg_latency": 12.5,
"last_seen": 1710000000
}
]
History samples in GET /monitor/api:
[
{
"timestamp": 1710000000,
"cpu": 12.5,
"memory": 50.0,
"swap": 0.0,
"rps": 125.5,
"responses_per_sec": 125.3,
"network_rx_per_sec": 1024,
"network_tx_per_sec": 2048,
"disk_read_per_sec": 0,
"disk_write_per_sec": 4096,
"latency_p95": 22,
"requests_total": 50000,
"status_4xx": 300,
"status_5xx": 90
}
]
Shared nested structures:
{
"worker": {
"slot": 0,
"pid": 12345,
"active": true,
"requests": 10000,
"bytes": 10485760,
"errors": 3,
"vm_size": 102400,
"vm_rss": 20480,
"voluntary_ctxt": 100,
"nonvoluntary_ctxt": 5,
"last_seen": 1710000000
},
"top_url": {
"url": "/api/orders",
"hits": 1000,
"errors": 3,
"bytes": 1048576,
"avg_latency": 7.8,
"last_seen": 1710000000
},
"user_agent": {
"user_agent": "curl/8.0.1",
"hits": 100,
"errors": 0,
"bytes": 20480,
"avg_latency": 2.1,
"last_seen": 1710000000
}
}
SSE GET /monitor/live sends metrics events whose data payload has this structure:
{
"version": 1,
"timestamp": 1710000000,
"sequence": 42,
"system": {
"cpu": {
"usage": 12.5,
"cores": 8
},
"memory": {
"used_pct": 50.0,
"total": 16777216,
"available": 8388608
}
},
"requests": {
"total": 50000,
"requests_per_sec": 125.5,
"responses_per_sec": 125.3,
"latency": {
"p95": 22,
"p99": 80
},
"status": {
"4xx": 300,
"5xx": 90
}
},
"connections": {
"active": 20,
"reading": 1,
"writing": 4,
"waiting": 15,
"sse_clients": 2,
"keepalive_requests": 250
},
"network": {
"rx_bytes": 123456789,
"tx_bytes": 987654321
},
"disk": {
"read_bytes": 4096000,
"write_bytes": 8192000
}
}
GET /monitor/metrics exposes Prometheus text metrics including:
nginx_monitor_requests_total
nginx_monitor_active_connections
nginx_monitor_cpu_usage_ratio
nginx_monitor_memory_used_ratio
nginx_monitor_latency_p95_ms
GET /monitor/health returns a complete health response:
{
"version": 1,
"module": "1.0.0",
"timestamp": 1710000000,
"msec": 123,
"scope": "health",
"pid": 12345,
"status": "ok",
"generation": 100,
"sse_clients": 2
}
Auth Rules
If the service is behind Nginx Basic Auth, include Basic Auth on every request, including /monitor, /monitor/live, and /monitor/metrics.
If monitor_api_token is configured, include the token on non-dashboard endpoints. Prefer a header over query string unless the client cannot set headers.
Preferred headers:
-H "X-Monitor-Token: $MONITOR_TOKEN"
Alternative bearer header:
-H "Authorization: Bearer $MONITOR_TOKEN"
Basic Auth with curl:
-u "$MONITOR_BASIC_AUTH"
When both Basic Auth and API token are enabled, send both.
Do not print secrets in final answers, logs, or copied command output. Redact with <redacted> when summarizing.
Curl Patterns
Build arguments conditionally:
base="${MONITOR_BASE_URL:-http://127.0.0.1:8080}"
auth_args=""
token_args=""
if [ "${MONITOR_BASIC_AUTH:-}" != "" ]; then
auth_args="-u $MONITOR_BASIC_AUTH"
fi
if [ "${MONITOR_TOKEN:-}" != "" ]; then
token_args="-H X-Monitor-Token: $MONITOR_TOKEN"
fi
For exact shell execution, prefer explicit commands instead of string-expanded credentials when possible:
curl -fsS -u "$MONITOR_BASIC_AUTH" -H "X-Monitor-Token: $MONITOR_TOKEN" \
"$MONITOR_BASE_URL/monitor/api"
Without token:
curl -fsS -u "$MONITOR_BASIC_AUTH" "$MONITOR_BASE_URL/monitor/api"
Without Basic Auth:
curl -fsS -H "X-Monitor-Token: $MONITOR_TOKEN" "$MONITOR_BASE_URL/monitor/api"
Health check:
curl -fsS -u "$MONITOR_BASIC_AUTH" -H "X-Monitor-Token: $MONITOR_TOKEN" \
"$MONITOR_BASE_URL/monitor/health"
Prometheus:
curl -fsS -u "$MONITOR_BASIC_AUTH" -H "X-Monitor-Token: $MONITOR_TOKEN" \
"$MONITOR_BASE_URL/monitor/metrics"
SSE sample:
curl -fsS -N -u "$MONITOR_BASIC_AUTH" -H "X-Monitor-Token: $MONITOR_TOKEN" \
"$MONITOR_BASE_URL/monitor/live"
Browser And Dashboard
Open:
<base_url>/monitor
If the API token is required and the browser cannot set headers for EventSource, append the token:
<base_url>/monitor?token=<token>
The dashboard forwards the token query parameter to API and SSE calls. Basic Auth is handled by the browser after the Nginx auth challenge.
JavaScript Client
Use headers for JSON polling:
const baseUrl = process.env.MONITOR_BASE_URL ?? "http://127.0.0.1:8080";
const token = process.env.MONITOR_TOKEN;
const headers = token ? { "X-Monitor-Token": token } : {};
const res = await fetch(`${baseUrl}/monitor/api`, { headers });
if (!res.ok) throw new Error(`monitor api failed: ${res.status}`);
const metrics = await res.json();
For browser EventSource, headers are not available. Use query token if needed:
const url = new URL("/monitor/live", baseUrl);
if (token) url.searchParams.set("token", token);
const events = new EventSource(url);
events.addEventListener("metrics", (event) => {
const metrics = JSON.parse(event.data);
});
If Basic Auth is required in browser clients, rely on the browser's authenticated session. Do not embed user:password in URLs.
Interpreting Responses
Every JSON response includes:
versionmoduletimestampscope
Common fields:
- CPU usage:
system.cpu.usage - memory used percent:
system.memory.used_pct - request rate:
requests.requests_per_secornginx.requests.requests_per_sec - latency:
requests.latency.p50,p90,p95,p99 - errors:
requests.status["4xx"],requests.status["5xx"] - active connections:
connections.activeornginx.connections.active
Treat missing fields as disabled collectors, unsupported Nginx build options, or insufficient traffic. For exact active/reading/writing/waiting connection counters, the Nginx build must include --with-http_stub_status_module.
Troubleshooting
401: Basic Auth or API token is missing or wrong. If both are configured, send both.403:monitor_allow/monitor_denyblocked the client IP.404:monitor on,monitor_api on, ormonitor_sse onmay not be enabled in the matching location.429:monitor_rate_limitwas exceeded.- Empty connection counters: Nginx may not have been built with stub status support.
- SSE connects then drops: proxy buffering or idle timeout may be in front of the service. Set
proxy_buffering offand allow long-lived responses.
Security Defaults
When integrating with another client:
- prefer HTTPS
- prefer header token over query token
- avoid logging headers, URLs containing
token=, or Basic Auth strings - use least-privilege network ACLs in front of
/monitor - avoid embedding Basic Auth credentials in browser source