Skip to content

Observability

Gatewyse exposes Prometheus metrics for scraping, ships a ready-to-import Grafana dashboard and alert rules, and can export OpenTelemetry traces, metrics, and logs over OTLP. The monitoring assets live under monitoring/ in the repository.

Prometheus metrics

The server exposes Prometheus metrics at GET /metrics. The endpoint is unauthenticated by default — put it behind network policy or a metrics-only listener in production.

All metric names use the aigw_ prefix, and every label set is bounded. Metrics are deliberately not labelled by tenant, user, or API key — that cardinality would explode the Prometheus series count, so per-tenant/user/key breakdowns live in Mongo usage records instead. Default Node.js process metrics are also collected under the aigw_ prefix (e.g. aigw_nodejs_eventloop_lag_p99_seconds).

MetricTypeLabelsDescription
aigw_http_requests_totalCountermethod, path, statusTotal HTTP requests
aigw_http_request_duration_secondsHistogrammethod, pathHTTP request duration in seconds
aigw_provider_request_duration_secondsHistogramprovider, model, statusProvider request duration in seconds
aigw_provider_errors_totalCounterprovider, error_typeTotal provider errors
aigw_tokens_totalCounterprovider, model, typeTotal tokens processed (type: input | output)
aigw_cost_usd_totalCounterprovider, modelTotal estimated cost in USD
aigw_guard_blocks_totalCounterguard_type, phaseRequests/responses blocked by a guard (phase: request | response)
aigw_budget_blocks_totalCounterscope, limit_typeRequests blocked by a budget limit
aigw_cache_requests_totalCounterresultCache lookups by result (hit | semantic_hit | miss)
aigw_auth_failures_totalCounterreasonAuthentication failures by reason (bounded error-code set)
aigw_rate_limit_rejections_totalCounterlevelRequests rejected by a rate limiter (ip | apiKey | tenant | daily)
aigw_active_streamsGaugeNumber of active streaming connections
aigw_stream_ttft_secondsHistogramproviderStreaming time-to-first-token in seconds
aigw_queue_depthGaugequeue, stateBullMQ job counts (state: waiting | active | delayed | failed)
aigw_circuit_breaker_stateGaugeproviderCircuit breaker state (0=closed, 1=half_open, 2=open)

URL paths are normalized before being used as label values — ObjectId-like segments and UUIDs are replaced with :id and query strings are stripped — to prevent label explosion.

Scraping

monitoring/prometheus/scrape.example.yml contains a scrape configuration and rule_files block to merge into your prometheus.yml. The primary Prometheus surface is the server’s /metrics endpoint:

scrape_configs:
- job_name: gatewyse-server
metrics_path: /metrics
static_configs:
- targets: ['gatewyse-server:3000']
labels:
service: gatewyse-server

The worker exports its telemetry over OTLP rather than a Prometheus endpoint, so scrape it only if you add a Prometheus exporter to it.

Grafana dashboard

monitoring/grafana/gatewyse-overview.json is a ready-to-import dashboard. Import it via Dashboards → New → Import, then select your Prometheus data source for the DS_PROMETHEUS input.

Panels cover:

  • HTTP request rate by status and HTTP latency (p50 / p95 / p99)
  • Provider latency p95 and provider error rate, by provider
  • Token throughput by direction and estimated spend (USD/hour) by provider
  • Active streams and streaming TTFT p95 by provider
  • Circuit-breaker state per provider
  • Guard blocks by guard type and budget blocks by scope
  • Cache hit ratio
  • Queue depth by queue/state, and auth failures & rate-limit rejections

Alerting

monitoring/prometheus/alerts.yml defines alerting rules — load it via rule_files: in prometheus.yml. Every expression references a metric the gateway actually exports; thresholds are conservative starting points, tune per deployment SLO.

AlertSeverityFires when
GatewyseTargetDowncriticalA gatewyse.* scrape target has been down for 2m
GatewyseCircuitBreakerOpenwarningA provider’s circuit breaker has been open for 1m
GatewyseHighHttpErrorRatecritical5xx responses exceed 5% over 5m
GatewyseHighProviderErrorRatewarningA provider’s error rate exceeds 10% over 5m
GatewyseHighRequestLatencyP95warningHTTP p95 latency exceeds 2s for 10m
GatewyseHighProviderLatencyP95warningA provider’s p95 latency exceeds 30s for 10m
GatewyseBudgetBlockSpikeinfoBudget blocks sustained above 1/s for 5m
GatewyseGuardBlockSpikeinfoA guard blocks above 1/s for 5m
GatewyseAuthFailureSpikewarningAuth failures sustained above 5/s for 5m
GatewyseQueueBacklogwarningA queue has >1000 waiting jobs for 10m
GatewyseHighEventLoopLagwarningNode.js event-loop lag p99 exceeds 250ms for 10m

OpenTelemetry (traces, metrics, logs)

Both the server (service.name = ai-gateway) and the worker (service.name = ai-gateway-worker) can export OTLP traces, metrics, and logs. The entire pipeline is gated on OTEL_EXPORTER_OTLP_ENDPOINT: when it is unset (the default) the OpenTelemetry bootstrap is a no-op with zero overhead, and the manual provider spans resolve to no-op spans.

When enabled:

  • Traces — auto-instrumentation for HTTP, Express (server), Mongoose, ioredis, and undici (outbound HTTP). Manual provider spans are emitted from the base adapter regardless of auto-instrumentation.
  • Metrics — the auto-instrumentations emit HTTP/DB/Redis metrics via OTLP alongside the Prometheus /metrics endpoint.
  • Logs — Winston records are forwarded into the OTLP logs pipeline with active trace context (trace_id / span_id) injected onto each record.

Both resources are tagged service.namespace = gatewyse. The exporters append /v1/traces, /v1/metrics, and /v1/logs to the configured endpoint base URL.

For full ESM auto-instrumentation, start each process with the OTel bootstrap imported first:

Terminal window
node --import ./dist/telemetry/otel.js ./dist/index.js

Environment variables

VariableDescription
OTEL_EXPORTER_OTLP_ENDPOINTCollector base URL (e.g. http://otel-collector:4318); /v1/{traces,metrics,logs} are appended. Unset = telemetry disabled.
OTEL_SERVICE_NAMEOverrides service.name (defaults ai-gateway / ai-gateway-worker).
OTEL_METRIC_EXPORT_INTERVAL_MSMetric export interval in milliseconds (default 30000).

Health and readiness endpoints

EndpointPurpose
GET /healthLiveness — the process is up
GET /readyReadiness — dependencies (MongoDB, Redis) are reachable and the service can accept traffic
GET /metricsPrometheus metrics (see above)

Use /health for liveness probes and /ready for readiness probes and load-balancer health checks.