Documentation menu

Metrics and tracing

Prometheus metrics on the admin listener, and OpenTelemetry traces that continue the caller's W3C trace context.

Prometheus metrics

Served at GET /metrics on the admin listener (:9090).

MetricLabelsMeaning
jwksbridge_snapshot_age_secondskeysetSeconds since the source last confirmed the keyset.
jwksbridge_refresh_totalkeyset, resultRefreshes: updated, unchanged, error, invalid.
jwksbridge_keyskeyset, statusKeys in the served snapshot per status.
jwksbridge_key_infokeyset, kid, status, signingOne series per key, value 1. signing="true" marks the key Kong signs with, which differs from the active key while the staging guard holds.
jwksbridge_requests_totalendpoint, codeRequests to jwks and keyset.
jwksbridge_request_duration_secondsendpointRequest latency histogram.
jwksbridge_source_errors_totalsourceErrors returned by Vault or the file source.
jwksbridge_build_infoversion, commitAlways 1.

Go runtime and process metrics are included.

Suggested alerts

- alert: JwksBridgeKeysetStale
  expr: max by (keyset) (jwksbridge_snapshot_age_seconds) > 1800
- alert: JwksBridgeRefreshFailing
  expr: sum by (keyset) (rate(jwksbridge_refresh_total{result=~"error|invalid"}[5m])) > 0
  for: 5m
- alert: JwksBridgeNoActiveKey
  expr: max by (keyset) (jwksbridge_keys{status="active"}) == 0
- alert: JwksBridgeKeysetErrors
  expr: sum(rate(jwksbridge_requests_total{endpoint="keyset",code!~"2..|304"}[5m])) > 0

W3C trace context

Every request on the public and private listeners extracts the traceparent and baggage headers, so a span started by Kong's opentelemetry plugin continues in the bridge. Calls to Vault carry the trace context on, and each background refresh is a keyset.refresh span with the Vault request as a child.

Spans are exported only when an exporter is configured with the standard OpenTelemetry variables:

OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector.observability:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf     # or grpc
OTEL_TRACES_SAMPLER=parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG=0.1
OTEL_SERVICE_NAME=jwks-bridge

Without them, propagation still works and nothing is exported. With JWKSB_LOG_LEVEL=debug, access log lines include trace_id and span_id.

Kong's jwt-signer plugin fetches the keyset from a background timer and sends no traceparent, so GET /keyset spans start a new trace. Requests that Kong proxies to the bridge, such as the public JWKS route, join the client's trace.

Grafana dashboard

The repository ships a dashboard in deploy/grafana/jwks-bridge.json. It picks the Prometheus, Loki and Jaeger datasources through variables, so it imports into any Grafana. Panels:

  • Signing kid, keys per status, snapshot age and source errors.
  • Key lifecycle: one bar per kid, colored staged, active (held back by the staging guard), signing or retired.
  • Kong data planes: requests per pod, failed requests and latency, from the Kong prometheus plugin.
  • Bridge requests, latency and refresh results.
  • Links to the Jaeger UI searches (set the jaeger_ui variable to its address), and the bridge logs from Loki with a keyset loaded annotation on every rotation.

Import it through the Grafana API, or copy it into a dashboard provider directory:

jq '{dashboard: (. + {id: null}), overwrite: true}' deploy/grafana/jwks-bridge.json \
  | curl -u admin:"$GRAFANA_PASSWORD" -H 'Content-Type: application/json' \
      -X POST "$GRAFANA_URL/api/dashboards/db" -d @-

The cluster variable reads the cluster label from jwksbridge_build_info. Without that label it stays empty and every panel still matches.