User Analytics
Build live dashboards on your gateway traffic, share them across teams, and get notified the moment something is off — all natively inside Otoroshi.
User Analytics ships with a dedicated PostgreSQL data exporter, a curated catalogue of queries, a grid of beautiful charts, and a threshold-based alert engine. No external observability stack to plug, no extra UI to learn — it lives where your routes, APIs and apikeys already do.
What you get
- Custom dashboards built from a rich grid of widgets — line, area, bar, pie, donut, big numbers, tables, heatmaps
- A point-and-click widget editor — pick a query, set a threshold, drop it on the grid
- Drag & drop reordering so dashboards stay tidy as they grow
- Adaptive time ranges — pick a preset (1h / 6h / 24h / 7d / 30d / 90d) or any datetime window
- Auto-refresh at the cadence you want, paused automatically when the tab is hidden
- Drill-down on click — clicking a route in a top-N chart filters the whole dashboard on it
- Deep links — every filter and time range lives in the URL, paste anywhere and the view comes back to life
- Per-dashboard saved views — set defaults so a teammate opening the dashboard sees what you intended
- Threshold alerts with conditions, AND/OR logic, severities, cooldown, and a full event log
- Tenant-scoped — each team sees only its own traffic, end to end
Get going in three minutes
1. Plug a PostgreSQL store
Go to Data Exporters → Add → User Analytics (PostgreSQL), fill in your PG connection (host, db, user, password — or a connection URI), set a retention window, save. Otoroshi creates the schema, the indexes and the alert log table automatically.
2. Mark it as the active store
On the exporter page, click Set as active analytics exporter. From now on, every gateway event Otoroshi processes flows into your store. Five default dashboards are seeded for you to start exploring immediately.
3. Open the dashboards
Features → Analytics → User Analytics (or /bo/dashboard/user-dashboards). Pick Global Overview to see the firehose, then drill into the others:
- Performance — latency percentiles, overhead, response-time heatmap
- Errors — error rate, top failing routes, status classes over time
- APIs & Routes — top consumers, traffic per API, domain breakdown
- Consumers — top API keys, top users, top countries
That's it. You're live.
Building a dashboard your way
Click Edit mode on any dashboard to unlock the editing toolbar. From here you can:
- Add a widget — pick a query from the catalogue, choose the visual style, set a width and height. Format hints (count / req/s / ms / bytes / percent) are auto-suggested but always overridable.
- Drag & drop widgets to rearrange them — the grid auto-reflows around their size.
- Edit a widget to tweak its title, query parameters, threshold colors, axis options, anything.
- Save view as default — capture the current time range, filters and refresh interval as the dashboard's opening view. Anyone clicking the dashboard from the menu lands on that exact configuration.
- Edit JSON for power users who want to copy-paste a dashboard to another instance or tweak fine-grained options not exposed in the form.
When you're done, click Done editing to lock the dashboard back. No risk of an accidental click during a demo.
The widget palette
Every widget renders independently with its own data fetch, loading state, error handling, and refresh button.
| Type | When to use |
|---|---|
| Line / Area | Trends over time — RPS, latency, error rate |
| Bar | Top routes / APIs / consumers — sortable horizontally |
| Pie / Donut | Categorical distribution — status codes, methods |
| Big number | Headline KPIs — total requests, total errors |
| Metric | Labelled value with optional thresholds |
| Table | Inline list of top-N items |
| Heatmap | Latency × time density grid |
All widgets adapt to the active theme (light/dark), respect tenant scoping, and inherit your global filters.
What you can measure
The query catalogue ships with everything most teams need, day one. Each one can be plotted in a widget AND used as the condition of an alert.
Volume
| Query | What it tells you |
|---|---|
requests_total | Total number of requests over the time window |
requests_per_second | Request rate over time |
requests_by_status | Distribution of HTTP status codes |
requests_by_status_class_ts | 2xx / 3xx / 4xx / 5xx counts over time, stacked |
requests_by_method | Distribution of HTTP methods |
traffic_in_out_ts | Bytes in and out over time |
Performance
| Query | What it tells you |
|---|---|
duration_avg_ts | Average response time over time |
duration_percentiles_ts | p50 / p75 / p95 / p99 latency over time |
overhead_avg_ts | Average Otoroshi overhead over time |
response_time_heatmap | Density of latency buckets over time (heatmap) |
Errors
| Query | What it tells you |
|---|---|
errors_total | Total errored requests over the window |
error_rate_ts | Fraction of errored requests over time |
top_error_routes | Top routes by errored request count |
Top consumers and entities
| Query | What it tells you |
|---|---|
requests_by_route | Top routes by request count |
requests_by_api | Top APIs by request count |
requests_by_apikey | Top API keys (most active integrators / customers) |
requests_by_user | Top end-users (when authenticated via private apps) |
requests_by_domain | Top hostnames hitting your gateway |
requests_by_country | Geographic breakdown |
Every query supports the same filters — route, API, API key, group, time window — so you can scope any of them to a specific slice of your traffic. Most timeseries queries also support the Compare period overlay for week-over-week or day-over-day visual checks.
Need something the catalogue doesn't have? Any Otoroshi extension can register additional queries — they show up in the dashboard widget picker and the alert conditions editor with no further work.
Filters that follow you everywhere
Every dashboard has a control bar with:
- From / To datetime pickers for absolute ranges
- Preset buttons —
1h,6h,24h,7d,30d,90dfor quick relative windows that stay alive in shared links - Last refresh indicator —
12s ago (14:35:50)so you always know how fresh the data is - Manual refresh plus an Auto toggle (10s / 30s / 1m / 5m)
- Entity filters — search any route, API, API key or service group with autocomplete
- Compare toggle to overlay the previous period on every time series
Click any bar in a "Top routes / APIs / API keys" chart and the whole dashboard reroutes onto that entity. Keep diving.
Sharing and bookmarking
The full state of what you're looking at lives in the URL. Hit Copy link, paste it in chat, in an incident ticket, in your runbook. Anyone with access to your tenant lands on the exact same view, including the relative time range — now-1h stays alive on tomorrow's reload.
You can also link directly from a route, API or API key detail page into a pre-filtered analytics view, by using query params. Power users build deep links from anywhere.
Alerts
Threshold alerts ride on the same query catalogue as your dashboards.
Define an alert
User Alerts → New Alert. Give it a name, a severity (info / warning / critical), a time window (e.g. last 5 minutes), and one or more conditions. Each condition is a one-liner:
Take query
error_rate_ts, reduce withmax, and fire if the result is>0.05
You can scope a condition to a specific route, API, API key or group. Combine multiple conditions with AND or OR.
The visual conditions editor lets you build it without writing a line of JSON. Power users can flip to JSON view at any time.
Tune the noise
- Evaluation interval — how often we re-check (e.g. every 60s)
- Cooldown — minimum time between two consecutive firings (don't spam your inbox)
- Window — how far back the condition looks
Where alerts land
Alerts emit standard Otoroshi AlertEvents with a recognizable signature (alert: "UserAnalyticsAlert"). Configure a Data Exporter of any type — mailer, webhook, Slack via webhook, Kafka, anything Otoroshi already speaks — and filter on that signature. The User Alerts page reminds you to do it the first time you create one and offers a one-click shortcut that pre-fills the filter.
The alert log
Every firing is also persisted, so you have a permanent searchable log per alert. Click 🔔 next to an alert to open it:
- See every firing with its severity, message, and a snapshot of the matched conditions (with the actual value vs the threshold)
- Filter by
unseen/seen/all - Mark an event as seen — or Mark all seen to wipe the inbox in one click
Click any firing to open a detailed breakdown of the conditions that fired it, with the observed value next to the configured threshold:
Tenant-aware by design
A tenant-admin sees only the traffic, dashboards, alerts and events of their own tenant. The injected SQL filter ensures one tenant's data never leaks to another. Super-admins switch tenants from the standard Otoroshi tenant dropdown — the analytics views follow.
Dashboards and alerts are normal Otoroshi entities, so they participate in your usual export / import / GitOps workflows.
Power-user features
A query catalogue you can extend
Every query the dashboards and alerts can reference is exposed at:
GET /api/analytics/_schema
This returns a typed catalogue of queries — id, human name, expected shape, default widget, parameters, whether it supports a "compare period" overlay.
Add your own queries from an admin extension
If you ship a custom Otoroshi extension (WAF, MCP, billing, anything), you can register your own analytics queries that show up in the dashboard widget picker AND can be referenced by alerts:
override def analyticsQueries(): Seq[AnalyticsQuery] = Seq(
MyExtensionQuery1,
MyExtensionQuery2
)
Prefix the query id with your extension namespace (waf.blocked_per_route, billing.cost_per_apikey) — they're fully integrated everywhere queries appear in the UI.
Bring your own delivery
Alerts go through the standard Otoroshi alert pipeline, so any data exporter type can deliver them: webhooks for PagerDuty / Opsgenie / generic services, mailer for email, Kafka for downstream pipelines, a custom WASM exporter for anything bespoke.
Tips for healthy dashboards
- Keep dashboards focused. A dashboard with one clear purpose ("checkout traffic", "platform errors") beats a 20-widget kitchen sink.
- Use the saved-view defaults. If your team always opens a dashboard on a 24h window, save it. Less ceremony, more signal.
- Drill, don't filter. It's faster to click a bar than to hunt the right entity in a dropdown.
- Set up alerts for the obvious. 5xx rate over a few percent, p95 over your SLO, daily request count dropping unexpectedly — three alerts cover a surprising amount of ground.
- Pair alerts with a runbook link in the alert message. Future you will thank present you.
Where to next
- Install your first dashboard now: head to Features → Analytics → User Analytics.
- Plug your first delivery channel: Data Exporters → Add → Mailer / Webhook / Slack with a filter on
{ "alert": "UserAnalyticsAlert" }. - Tune the defaults of a dashboard you use daily — your team will notice.