Skip to content

Observability

RunLore self-instruments with structured logs and Prometheus-compatible metrics, and ships a portable Grafana dashboard plus alert rules. Everything here works against either Prometheus or VictoriaMetrics — RunLore is metrics-backend-agnostic.

Logging

RunLore logs via Go’s log/slog. Output format and verbosity are configurable:

logging:
  format: json   # "text" (default, human-readable) | "json" (structured, for log aggregation)
  level: info    # debug | info | warn | error

The Helm chart defaults to JSON in-cluster (so logs flow cleanly into Loki/VictoriaLogs/CloudWatch); the CLI defaults to text. Both are overridable at startup without editing config:

RUNLORE_LOG_FORMAT=json RUNLORE_LOG_LEVEL=debug lore serve --config runlore.yaml

Level guidance: error = an operation failed; warn = a recoverable/degraded condition (a backend unavailable, a provider disabled); info = lifecycle and per-incident milestones; debug = per-step / per-tool tracing (off in production).

Metrics

When telemetry.metrics_enabled: true, RunLore serves the Prometheus exposition format at GET /metrics on the service port. Scrape it with a VMServiceScrape (vmServiceScrape.enabled: true in the chart) or any ServiceMonitor/scrape config.

All series are prefixed runlore_.

The seconds-scale latency histograms (*_duration_seconds, incident_resolution_seconds) carry explicit SLO-aligned bucket boundaries (seconds): 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 300. The OTel SDK defaults are millisecond-scale and would collapse most calls into the first bucket, breaking histogram_quantile. The boundaries are defined in internal/telemetry/setup.go via an explicit-bucket-histogram view. Other histograms (scores, batch size, token estimate) keep the SDK defaults and are read as heatmaps, not percentiles.

Pipeline & investigations

MetricTypeLabelsMeaning
runlore_build_infogaugeversionconstant 1; for absent() liveness + version display
runlore_leadergauge1 on the elected leader, 0 on standbys
runlore_alerts_received_totalcounterincidents passing the trigger gate into the coalescer
runlore_alerts_coalesced_totalcounterincidents folded into an existing batch
runlore_alerts_suppressed_totalcounterincidents dropped by cooldown
runlore_incidents_debounced_totalcounterfiring alerts dropped as self-resolving (a matching resolved webhook arrived within triggers.incidents.debounce)
runlore_incidents_dropped_on_shutdown_totalcounteralert LOSS — firing alerts still held in the debounce window when the process shut down: accepted (200 to Alertmanager) but never investigated, and not retried until Alertmanager’s repeat_interval. Any non-zero value is worth a look; see troubleshooting
runlore_investigations_started_totalcounterinvestigations actually begun
runlore_investigations_completed_totalcounterresultinvestigations finished (resolved/unresolved/recall/timeout/error/max_steps/max_steps_degraded/budget_exceeded/inconclusive/recurrence_suppressed)
runlore_investigation_duration_secondshistogramresultwall-clock per investigation
runlore_investigations_throttled_totalcounterstarts requeued by the rate limiter
runlore_investigations_dropped_totalcounterdropped (rate-limiter max-requeues or token-budget kill)
runlore_investigations_cancelled_totalcounterqueued (not yet started) investigations cancelled because the incident resolved first (triggers.incidents.cancel_queued_on_resolve)

Tools & model

MetricTypeLabelsMeaning
runlore_tool_calls_totalcountertool, resultinvestigation tool calls (ok/error)
runlore_tool_call_duration_secondshistogramtoolper-tool latency
runlore_model_requests_totalcounterprovider, resultLLM completion requests (ok/error)
runlore_model_request_duration_secondshistogramproviderLLM completion latency
runlore_investigation_tokens_estimatedhistogramper-investigation token estimate

Recall, learning loop & curation

MetricTypeLabelsMeaning
runlore_recall_hits_totalcounterresultinstant-recall short-circuits
runlore_recall_tokens_saved_totalcounterestimated tokens saved by recall
runlore_recall_rejections_totalcounterreasonrecalls rejected before short-circuit
runlore_recall_scorehistogramBM25 score at the recall decision
runlore_outcomes_opened_totalcounterkindinvestigations recorded as open
runlore_incidents_resolved_totalcounterresolve events matching an open investigation
runlore_recall_outcome_totalcounterresultresolved incidents whose answer was a recall
runlore_incident_resolution_secondshistogramopen→resolve duration
runlore_curations_totalcounterkind, resultcuration outcomes (opened/coalesced/error)
runlore_curation_dedup_scorehistogramcatalog top-hit BM25 score at the dedup decision
runlore_catalog_embed_degraded_totalcountercatalog reloads that left hybrid recall without vectors (embed failure — recall degrades to BM25-only until the next successful sync)

Grafana dashboard

A portable dashboard lives at deploy/observability/grafana/runlore.json. It uses a single datasource template variable (type Prometheus), so it works with a Prometheus or a VictoriaMetrics datasource with no edits. Import it via Dashboards → Import → Upload JSON, or provision it. See the grafana README.

It panels every runlore_ series above, including the output-truncation rate (tool_output_truncated_bytes_total), the coalesced-batch-size distribution (coalesce_batch_size, heatmap), and the curation dedup-score distribution (curation_dedup_score, heatmap).

Alerting

Alert rules ship as both a Prometheus-Operator PrometheusRule and a VictoriaMetrics-Operator VMRule (identical rules; pick the one your stack uses):

# kube-prometheus-stack
kubectl apply -f deploy/observability/alerts/prometheusrule.yaml
# VictoriaMetrics Operator
kubectl apply -f deploy/observability/alerts/vmrule.yaml

The rule set covers liveness (RunloreAgentDown), HA (RunloreNoActiveLeader, RunloreMultipleLeaders), pipeline health (RunlorePipelineStalled, RunloreInvestigationsDropped, throttling), quality (tool/model error rates, investigation errors), latency (model p95, slow resolution), and cost (RunloreInvestigationCostHigh). Thresholds are starting points — tune to your volume. See the alerts README for the per-alert metric dependencies and operator discovery notes.