Skip to content

Observability

v2 writes operational data to the persistence backend so native, multi-instance, and edge deployments can share the same view. Hot-path settings are loaded into the control-plane snapshot.

Every gateway request gets a request id. Usage records, downstream logs, and upstream logs carry that id so one call can be joined across tables and console views.

Usage records include:

  • request id and timestamp;
  • route name, provider id, credential id;
  • org, team, user, and user key ids;
  • operation and kind;
  • model;
  • input, output, cache-read, and cache-creation tokens;
  • cost;
  • latency and usage source.

Usage is controlled by instance_settings.enable_usage, which defaults to true. Settlement also updates quotas and token-limit counters.

Request logging is split into downstream and upstream streams:

Setting Captures
enable_downstream_log Client-facing method, path, query, status, headers.
enable_downstream_log_body Downstream request and response bodies.
enable_upstream_log Provider URL, method, status, latency, headers.
enable_upstream_log_body Upstream request and response bodies.

Redaction is on by default. disable_log_redaction exists for debugging, but it can expose secrets and should not be enabled casually.

Admin and portal mutation paths emit audit rows with actor id/name, action, target, status, and source IP. Use these to answer “who changed the control plane” rather than to debug LLM payloads.

The Console paginates usage, request logs, and audit rows with direct page navigation. Usage and request-log pages support their existing time and routing filters; audit pages can filter by time, actor, action, target, status, and source IP.

Credential-wide status rows track each credential/channel pair. Model-specific rows separately track each credential/channel/final-upstream-model tuple:

  • health_kind;
  • optional structured health_json;
  • checked_at;
  • last_error.

The pipeline and channel response classifier decide when a credential or only one model should be retried, cooled down, or treated as auth-dead. The console keeps these scopes separate through /admin/credential-statuses and /admin/credential-model-statuses. Per-credential views use /admin/credentials/{id}/status and /admin/credentials/{id}/model-statuses, respectively.

Model-bound failures default to the exact credential/final-upstream-model pair and are never promoted from failure counts or cross-model correlation. Credential-wide operations such as model-list, usage, and token refresh update credential-wide health instead.

Both per-credential endpoints also accept DELETE as an operator reset: it drops the persisted snapshot and resets the breaker and cooldown of the instance that served the request (the Console health badges call this on click). Health is per-instance soft state, so a multi-instance deployment has to clear each instance — or wait out the cooldown. A reset only clears the decision inputs: a still-failing upstream re-trips the breaker on the next attempt.

/metrics is admin-gated and renders Prometheus text from persisted aggregate data, not process-local counters. Current families include:

  • gproxy_requests_total
  • gproxy_tokens_total
  • gproxy_upstream_latency_ms
  • gproxy_credential_health
  • gproxy_quota_total
  • gproxy_quota_used

This design keeps metrics meaningful across native multi-instance and edge deployments where process-local counters would be misleading.

instance_settings.retention_days controls cleanup of usage and request-log rows. None or a non-positive value retains rows indefinitely. Retention is for logs and usage data; it should not delete business/control-plane records.

On native SQLite, instance_settings.max_database_size_mb sets an optional physical database size cap. The server checks it every five minutes. When the cap is exceeded, it removes the oldest upstream/downstream request logs and audit rows, then compacts the database to 90% of the configured cap. Usage rows and usage rollups are never removed by this size-pressure cleanup. If retained usage and control-plane data alone exceed the target, cleanup stops and emits a warning rather than deleting them. PostgreSQL, MySQL, and edge/libSQL ignore this setting because ordinary row deletion cannot reliably shrink their physical storage.