Analytics (event stream)
Beyond Prometheus counters, Goma Gateway can emit a per-request analytics event stream to Redis. Where /metrics gives you aggregate numbers, the stream carries one compact, privacy-safe event per request — enough to build full HTTP traffic, performance and web-analytics dashboards downstream.
Miabi consumes this stream to power its Workspace Analytics (traffic by country, latency percentiles, unique visitors, top pages, …). You can also consume it yourself with any Redis Streams client.
How it works
- On each proxied request Goma builds a small
AnalyticsEventand appends it to a Redis Stream (defaultgoma:analytics) with a batched, pipelinedXADD. - Delivery is non-blocking: it never adds latency to the request path — a full in-memory buffer drops the event rather than waiting. An approximate
MAXLENcap bounds the stream so a lagging consumer can’t grow Redis unbounded. - Requires Redis to be configured (the transport). It reuses the same Redis the gateway already uses for caching / distributed rate limiting.
⚠️ Same Redis, same database. The consumer must read from the exact Redis and database index Goma writes to. If nothing appears downstream, check that
GOMA_REDIS_DBmatches the consumer’s Redis DB — a mismatch silently breaks the pipeline.
Privacy
The stream is designed to carry no PII:
- The client IP never leaves the gateway. It is used only to (a) derive a daily-salted visitor hash (
vid) for counting unique visitors and (b) look up a country via GeoIP — then it is dropped. - No cookies are set or required.
Enabling it
Analytics is configured in goma.yml alongside the rest of the gateway:
version: 2
gateway:
redis:
addr: redis:6379 # required — Redis is the transport
password: "${REDIS_PASSWORD}"
analytics:
enabled: true # off by default
stream: goma:analytics # Redis stream key
sample: 1 # 0..1; 1 = every request
maxLen: 1000000 # approximate stream length cap
gatewayId: "" # stamped on each event, for multi-gateway installs
geoip:
database: /etc/goma/country.mmdb # optional; see GeoIP below
geoip sits beside analytics rather than inside it because three features read the same database: the country field on events, the gateway_requests_by_country_total metric, and the geoBlock middleware. It loads whether or not analytics is enabled.
Or entirely from the environment, which is equivalent:
GOMA_ANALYTICS_ENABLED=true # off by default
GOMA_ANALYTICS_STREAM=goma:analytics # Redis stream key
GOMA_REDIS_DB=0 # must match the consumer's Redis DB
# Optional country enrichment (see GeoIP below):
GOMA_GEOIP_DB=/etc/goma/country.mmdb
Configuration
Each setting can come from the config file or the environment. The environment wins, field by field — so a container can override what the file ships with, and an existing GOMA_ANALYTICS_* deployment keeps working unchanged after adding the analytics: block.
gateway.analytics | Environment | Default | Description |
|---|---|---|---|
enabled | GOMA_ANALYTICS_ENABLED | false | Emit the event stream. |
stream | GOMA_ANALYTICS_STREAM | goma:analytics | Redis stream key events are appended to. |
sample | GOMA_ANALYTICS_SAMPLE | 1 | Sampling rate 0..1; e.g. 0.25 records ~25% of requests. 1 = every request. |
maxLen | GOMA_ANALYTICS_MAXLEN | 1000000 | Approximate stream length cap (XADD MAXLEN ~). |
gatewayId | GOMA_GATEWAY_ID | "" | Identifier stamped on each event (gw); useful with multiple gateways. |
Alongside it, at gateway level:
gateway | Environment | Default | Description |
|---|---|---|---|
geoip.database | GOMA_GEOIP_DB | (well-known paths) | Path to the GeoIP .mmdb for the country field. |
| — | GOMA_REDIS_DB | 0 | Redis database index (must match the consumer). |
A malformed GOMA_ANALYTICS_SAMPLE or GOMA_ANALYTICS_MAXLEN is logged and ignored, falling back to the configured value — a typo must not silently drop your sampling rate or uncap the stream.
Event schema
Each stream entry has a single field e whose value is the JSON below.
| Field | Type | Description |
|---|---|---|
ts | int | Event time, unix milliseconds. |
gw | string | Gateway id (GOMA_GATEWAY_ID). |
name | string | Matched route name. |
host | string | Request Host. |
method | string | HTTP method. |
status | int | Response status code. |
path | string | Request path. |
path_template | string | Matched route path pattern. |
req_bytes | int | Request body bytes received. |
resp_bytes | int | Response body bytes sent. |
duration_ms | int | Total request duration. |
upstream_ms | int | Upstream/backend duration (overhead = duration_ms − upstream_ms). |
vid | string | Daily-salted visitor hash (not the IP). |
country | string | ISO country code from GeoIP (empty when no database). |
ua | string | Raw User-Agent (parsed into browser/OS/device downstream). |
referer_host | string | Host of the Referer, if any. |
GeoIP (country enrichment)
Save a country-level .mmdb database at /etc/goma/country.mmdb and Goma loads it at startup with no configuration; geoip.database (or GOMA_GEOIP_DB) overrides the path. MaxMind, DB-IP and IP2Location all publish a suitable database — any of them works, since all three expose a country ISO code.
An explicitly configured path is used exactly as given: Goma will not quietly fall back to a well-known location, because enriching events from a database nobody chose is worse than no country data at all.
It enriches the country field on events, the gateway_requests_by_country_total metric, and powers the geoBlock middleware. Everything keeps working without it — you just lose the country dimension.
Goma ships no database: the ones worth having carry licenses that bind whoever uses or displays the data, which is a choice only you can make for your deployment.