Skip to main content
Version: v0.15

Analytics

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 AnalyticsEvent and appends it to a Redis Stream (default goma:analytics) with a batched, pipelined XADD.
  • 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 MAXLEN cap 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_DB matches 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.analyticsEnvironmentDefaultDescription
enabledGOMA_ANALYTICS_ENABLEDfalseEmit the event stream.
streamGOMA_ANALYTICS_STREAMgoma:analyticsRedis stream key events are appended to.
sampleGOMA_ANALYTICS_SAMPLE1Sampling rate 0..1; e.g. 0.25 records ~25% of requests. 1 = every request.
maxLenGOMA_ANALYTICS_MAXLEN1000000Approximate stream length cap (XADD MAXLEN ~).
gatewayIdGOMA_GATEWAY_ID""Identifier stamped on each event (gw); useful with multiple gateways.

Alongside it, at gateway level:

gatewayEnvironmentDefaultDescription
geoip.databaseGOMA_GEOIP_DB(well-known paths)Path to the GeoIP .mmdb for the country field.
GOMA_REDIS_DB0Redis 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.

FieldTypeDescription
tsintEvent time, unix milliseconds.
gwstringGateway id (GOMA_GATEWAY_ID).
namestringMatched route name.
hoststringRequest Host.
methodstringHTTP method.
statusintResponse status code.
pathstringRequest path.
path_templatestringMatched route path pattern.
req_bytesintRequest body bytes received.
resp_bytesintResponse body bytes sent.
duration_msintTotal request duration.
upstream_msintUpstream/backend duration (overhead = duration_ms − upstream_ms).
vidstringDaily-salted visitor hash (not the IP).
countrystringISO country code from GeoIP (empty when no database).
uastringRaw User-Agent (parsed into browser/OS/device downstream).
referer_hoststringHost 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.