# OpenClaw Usage Exporter FastAPI Prometheus exporter for OpenClaw usage, session, and runtime-adjacent metrics. ## What it exports Reads OpenClaw session data from disk and exposes `/metrics` with as much non-sensitive data as possible. ### Why timeframe switching looked wrong before Prometheus scrapes snapshots. The old exporter leaned heavily on "today so far" gauges, so a Grafana timeframe change could look almost unchanged unless the panel query explicitly used range math. The exporter now also exposes: - monotonic all-time counters for tokens, cost, and usage entries - retained daily buckets - retained hourly buckets That gives dashboards something real to aggregate over a selected time range. ### Daily usage metrics - `openclaw_daily_total_tokens` - `openclaw_daily_input_tokens` - `openclaw_daily_output_tokens` - `openclaw_daily_cache_read_tokens` - `openclaw_daily_cache_write_tokens` - `openclaw_daily_reasoning_tokens` - `openclaw_daily_usage_entries` - `openclaw_daily_cost_input` - `openclaw_daily_cost_output` - `openclaw_daily_cost_cache_read` - `openclaw_daily_cost_cache_write` - `openclaw_daily_cost_total` - `openclaw_daily_message_records` - `openclaw_daily_non_message_records` - `openclaw_daily_unique_sessions` - `openclaw_daily_unique_models` - `openclaw_daily_unique_providers` ### Daily breakdowns - `openclaw_daily_messages_total{role=...}` - `openclaw_daily_stop_reasons_total{stop_reason=...,model=...,provider=...}` - `openclaw_daily_tool_calls_total{tool_name=...}` - `openclaw_daily_errors_total{error_type=...}` - `openclaw_daily_model_total_tokens{model=...,provider=...}` - `openclaw_daily_model_input_tokens{model=...,provider=...}` - `openclaw_daily_model_output_tokens{model=...,provider=...}` - `openclaw_daily_model_reasoning_tokens{model=...,provider=...}` - `openclaw_daily_session_total_tokens{session_id=...}` - `openclaw_daily_session_input_tokens{session_id=...}` - `openclaw_daily_session_output_tokens{session_id=...}` - `openclaw_daily_session_messages_total{session_id=...,role=...}` - `openclaw_daily_session_errors_total{session_id=...}` - `openclaw_daily_session_tool_calls_total{session_id=...,tool_name=...}` ### Session inventory metrics Derived from `sessions.json`: - `openclaw_sessions_visible_count` - `openclaw_sessions_total_count` - `openclaw_sessions_has_more` - `openclaw_session_info{session_id=...,status=...,chat_type=...,last_channel=...,model=...,provider=...,auth_profile_override=...,system_sent=...,aborted_last_run=...,total_tokens_fresh=...}` - `openclaw_session_age_seconds{session_id=...}` - `openclaw_session_updated_timestamp_seconds{session_id=...}` - `openclaw_session_started_timestamp_seconds{session_id=...}` - `openclaw_session_last_interaction_timestamp_seconds{session_id=...}` - `openclaw_session_total_tokens{session_id=...}` - `openclaw_session_input_tokens{session_id=...}` - `openclaw_session_output_tokens{session_id=...}` - `openclaw_session_context_tokens{session_id=...}` - `openclaw_session_runtime_seconds{session_id=...}` - `openclaw_session_estimated_cost_usd{session_id=...}` - `openclaw_session_compaction_count{session_id=...}` - `openclaw_session_usage_family_session_count{session_id=...}` ### Time-range-friendly counters and buckets - `openclaw_total_tokens_total` - `openclaw_input_tokens_total` - `openclaw_output_tokens_total` - `openclaw_cache_read_tokens_total` - `openclaw_cache_write_tokens_total` - `openclaw_reasoning_tokens_total` - `openclaw_usage_entries_total` - `openclaw_cost_input_usd_total` - `openclaw_cost_output_usd_total` - `openclaw_cost_cache_read_usd_total` - `openclaw_cost_cache_write_usd_total` - `openclaw_cost_total_usd_total` - `openclaw_message_records_total` - `openclaw_non_message_records_total` - `openclaw_model_total_tokens_total{model=...,provider=...}` - `openclaw_model_input_tokens_total{model=...,provider=...}` - `openclaw_model_output_tokens_total{model=...,provider=...}` - `openclaw_session_total_tokens_total{session_id=...}` - `openclaw_day_total_tokens{date=...,tz=...}` - `openclaw_day_cost_total{date=...,tz=...}` - `openclaw_day_usage_entries{date=...,tz=...}` - `openclaw_hour_total_tokens{hour=...,tz=...}` - `openclaw_hour_cost_total{hour=...,tz=...}` - `openclaw_hour_usage_entries{hour=...,tz=...}` ### Exporter/runtime metrics - `openclaw_exporter_files_scanned` - `openclaw_exporter_lines_scanned` - `openclaw_exporter_parse_errors` - `openclaw_exporter_negative_cost_entries_skipped` - `openclaw_exporter_negative_cost_fields_skipped{cost_field=...}` - `openclaw_exporter_negative_cost_amount_skipped{cost_field=...}` - `openclaw_exporter_history_days_retained` - `openclaw_exporter_history_hours_retained` - `openclaw_usage_cost_cache_files_tracked` - `openclaw_usage_cost_cache_version` - `openclaw_usage_cost_cache_updated_timestamp_seconds` ## Privacy boundary This exporter intentionally skips obvious revealing fields like message content, sender labels, sender ids, phone numbers, response ids, idempotency keys, and session keys. Session ids are exported. It also ignores invalid negative cost values found in some legacy/raw usage records and exposes exporter metrics for how many were skipped. ## Code layout - `app.py` keeps the uvicorn entrypoint tiny - `openclaw_usage_exporter/config.py` holds env-driven paths and retention settings - `openclaw_usage_exporter/models.py` holds report dataclasses and cost handling - `openclaw_usage_exporter/collectors.py` reads session files and cache/index data - `openclaw_usage_exporter/metrics.py` renders Prometheus output - `openclaw_usage_exporter/utils.py` keeps shared helpers small ## Run locally ```bash python3 -m venv .venv . .venv/bin/activate pip install -r requirements.txt uvicorn app:app --host 0.0.0.0 --port 9487 ``` ## Runtime - Canonical repo path: `/root/projects/openclaw-usage-exporter` - Compatibility workspace path: `/root/.openclaw/workspace/openclaw-daily-tokens-exporter` - systemd user unit: `~/.config/systemd/user/openclaw-daily-tokens-exporter.service` - Direct metrics: `http://100.74.255.106:9487/metrics` - Tailscale Serve path: `https://luna.taile7522.ts.net/openclaw-daily-tokens` ## Env - `OPENCLAW_SESSIONS_DIR` default: `/root/.openclaw/agents/main/sessions` - `OPENCLAW_EXPORTER_TZ` default: `Europe/Berlin` - `OPENCLAW_SESSIONS_INDEX` default: `/root/.openclaw/agents/main/sessions/sessions.json` - `OPENCLAW_USAGE_COST_CACHE` default: `/root/.openclaw/agents/main/sessions/.usage-cost-cache.json` - `OPENCLAW_HISTORY_DAYS` default: `30` - `OPENCLAW_HISTORY_HOURS` default: `168` - `PORT` ignored by app directly; set it in the service or uvicorn command