Logs and metrics
The gateway gives you three sources of information: the gateway log, the access log and Prometheus metrics.
| Source | Content | Where |
|---|---|---|
| Gateway log | What the gateway itself is doing: startup, config loading, errors | standard error |
| Access log | One JSON line per request | standard output |
| Prometheus metrics | Counters, histograms and gauges | /metrics on the support server |
Gateway log
Section titled “Gateway log”Written to standard error. Configure it with --gLogLevel (trace, debug, info, warn, error) and --gLogFormat (json for machines, console for people). See Command line.
Access log
Section titled “Access log”One JSON line is written for every request the gateway handled, to standard output. Lines are written in batches, a request never waits for the log. If the log can’t keep up, events are dropped rather than slowing requests down, and counted in gw_access_log_dropped_total.
Use --gAccessLogSinks none to turn it off, --gAccessLogSinks stdout (the default) to keep it on. See Command line.
Example line (wrapped here for reading):
{"stream":"access","ts":"2026-10-20T15:04:05.123Z","request_path":"/a/limited/ping","request_method":"GET", "response_status":429,"error_reason":"Too many requests","caller_ip":"203.0.113.7","caller_id":"", "request_id":"","nocleye_request_id":"9f2c...","auth_method":"","request_size_bytes":0, "response_size_bytes":17,"upstream_service":"svc-a","upstream_latency_ms":0,"latency_ms":0.21, "user_agent":"curl/8.5.0","path_pattern":"/a/limited","route_matched":"a-limited","gateway_version":"1.0.0"}| Field | Description |
|---|---|
stream |
Always access. |
ts |
Time the request was received, UTC, millisecond precision. |
request_path, request_method |
Path and method of the request. |
response_status |
Status code returned to the caller. |
error_reason |
Why the gateway itself rejected the request: Too many requests, Unauthorized, API NOT FOUND, BAD REQUEST, API UNAVAILABLE, UPSTREAM ERROR or CLIENT CANCELED. Empty when the response came from the backend. |
caller_ip |
IP of the caller. See --gTrustedProxies in Command line. |
caller_id |
Identity from the token when the route is authenticated, otherwise empty. |
request_id |
The X-Request-Id sent by the caller, if it is valid. Only recorded, never reused. |
nocleye_request_id |
Request ID generated by the gateway. It is also sent to the backend as X-Nocleye-Request-Id, use it to correlate logs. |
auth_method |
Name of the authentication method applied, empty if none. |
request_size_bytes, response_size_bytes |
Body sizes. |
upstream_service |
Name of the server the request was routed to. |
upstream_latency_ms |
Time spent waiting for the backend. |
latency_ms |
Total time spent in the gateway, including the backend. |
user_agent |
User-Agent of the caller. |
path_pattern, route_matched |
PathPattern and name of the matched route. Empty when no route matched. |
gateway_version |
Version of the gateway that handled the request. |
caller_ip and caller_id can be personal data. Keep that in mind when you ship or store the access log.
Requests that don’t match any route, or that are rejected as a bad request, are counted in the metrics but are not written to the access log.
Support server
Section titled “Support server”An internal HTTP server, bound to --sHost:--sPort (default 127.0.0.1:8081). Do not expose it publicly. It also hosts the live config API, see Support server.
| Endpoint | Description |
|---|---|
GET /healthz |
Liveness probe. Answers 200 whenever the process is up. |
GET /readyz |
Readiness probe. Answers 200 with ok once the config is loaded, and 503 while starting and once shutdown has begun, so load balancers stop sending traffic before the gateway drains. |
GET /metrics |
Prometheus metrics. Only available when --sPrometheusEnabled is true. |
Prometheus metrics
Section titled “Prometheus metrics”Turn Prometheus on with --sPrometheusEnabled, then choose what to expose with --sPrometheusMetrics, a comma separated list of metric families, or all. With an empty list /metrics has no series, and nocleye check warns about it.
nocleye gateway --gConfigPath ./gateway.yaml \ --sPrometheusEnabled \ --sPrometheusMetrics gw_requests_total,gw_request_duration_seconds,go,processScrape it with:
scrape_configs: - job_name: gateway static_configs: - targets: ["gateway:8081"]Requests
Section titled “Requests”| Metric | Type | Labels | Description |
|---|---|---|---|
gw_requests_total |
counter | route, upstream, auth_method, method, code, source, reason |
Total requests handled. |
gw_request_duration_seconds |
histogram | route, upstream |
Request latency as seen by the gateway. |
gw_upstream_duration_seconds |
histogram | route, upstream |
Backend latency. |
gw_request_size_bytes |
histogram | route |
Request body size. |
gw_response_size_bytes |
histogram | route |
Response body size. |
gw_in_flight_requests |
gauge | upstream |
Requests currently being proxied to a backend. |
gw_upstream_last_success_timestamp_seconds |
gauge | upstream |
Unix time of the last successful response from a backend. |
Requests that match no route are counted under route="__unmatched__".
The source label tells who produced the response, and reason tells why when the gateway or the caller ended the request:
source |
reason |
Meaning |
|---|---|---|
upstream |
none |
The response came from the backend, whatever its status. |
upstream |
upstream_error |
The backend could not be reached or failed mid request. |
gateway |
rate_limited |
A usage control limit was exceeded. |
gateway |
unauthorized |
Authentication failed. |
gateway |
route_not_found |
No route matched. |
gateway |
bad_request |
The request was rejected as malformed. |
gateway |
upstream_unavailable |
The route’s server is not available. |
client |
client_canceled |
The caller closed the connection. |
Usage control
Section titled “Usage control”| Metric | Type | Labels | Description |
|---|---|---|---|
gw_ratelimit_key_utilization |
histogram | rule |
Distribution of how much of the limit each caller has used. |
gw_ratelimit_keys |
gauge | rule |
Number of callers currently tracked by a rule. |
Callers
Section titled “Callers”These use approximate counting, so memory stays small no matter how many callers you have.
| Metric | Type | Labels | Description |
|---|---|---|---|
gw_unique_callers |
gauge | route, window |
Approximate distinct callers per route. |
gw_unique_caller_ips |
gauge | route, window |
Approximate distinct caller IPs per route. |
gw_auth_failure_unique_ips |
gauge | auth_method |
Approximate distinct IPs failing authentication. |
gw_unmatched_unique_paths |
gauge | none | Approximate distinct paths that matched no route. |
Access log
Section titled “Access log”| Metric | Type | Labels | Description |
|---|---|---|---|
gw_sink_events_total |
counter | sink, result |
Access log events seen by a sink. |
gw_sink_dropped_total |
counter | sink, reason |
Access log events dropped by a sink. |
gw_sink_queue_depth |
gauge | sink |
Current queue depth of a sink. |
gw_sink_queue_capacity |
gauge | sink |
Queue capacity of a sink. |
gw_sink_last_success_timestamp_seconds |
gauge | sink |
Unix time of a sink’s last successful write. |
gw_sink_last_write_seconds |
gauge | sink |
Duration of a sink’s last write. |
gw_access_log_dropped_total |
counter | reason |
Access log events dropped before reaching any sink. |
Gateway
Section titled “Gateway”| Metric | Type | Labels | Description |
|---|---|---|---|
gw_build_info |
gauge | version, go_version |
Build information, always 1. |
gw_config_info |
gauge | hash |
The loaded config, always 1. hash changes whenever the config changes. |
gw_config_reloads_total |
counter | result |
Config loads, by result. |
gw_routes |
gauge | none | Number of routes in the loaded config. |
gw_metrics_series_overflow_total |
counter | none | Requests whose label combination was refused a new series because of --sPrometheusMaxSeries. |
Runtime
Section titled “Runtime”go (Go runtime metrics) and process (CPU, memory, file descriptors) can be listed in --sPrometheusMetrics like any other family.
Limiting cardinality
Section titled “Limiting cardinality”gw_requests_total creates one series per label combination. --sPrometheusMaxSeries (default 20000) caps them. Once the cap is reached, new combinations are folded into a single route="__overflow__" series and counted in gw_metrics_series_overflow_total, so memory stays bounded.
