Loki
If Alloy, Promtail or another shipper already sends your access logs to Grafana Loki, GeoMetrikks can read them from Loki instead of a file. Each LogQL stream selector is one source, and GeoMetrikks parses and geolocates its lines the same way it handles a tailed file.
LOKI_URL=http://loki:3100LOKI_QUERIES=["{job=\"nginx\"}", "{job=\"traefik\"}"]LOKI_SERVER_NAMES=["edge-01", "edge-02"]The app container has to reach LOKI_URL. http://loki:3100 works when
Loki runs on the same Docker network.
Without LOKI_SERVER_NAMES, GeoMetrikks stamps every selector with the
first LOGPARSER_SERVER_NAME. That is a name you choose for this server, not
your nginx server_name, and it defaults to the machine’s hostname. If the
selectors are different proxies, give each one its own server name.
- Loki must hold the raw access-log line. If a pipeline stage rewrites the line, for example into logfmt, GeoMetrikks has nothing it can parse. GeoMetrikks ignores labels and structured metadata.
- Don’t set the entry timestamp from the line when the line carries the
request’s start time, as Traefik’s
StartUTCdoes. In Alloy that isstage.timestamp, in Promtail thetimestampstage. The proxy writes the line when the request ends, so a websocket or a long download ends up with a timestamp minutes or hours older than the moment it reaches Loki. GeoMetrikks reads back from the newest line it has seen minusLOKI_LOOKBACK(60 seconds by default), and never more than an hour, so it skips that line. Without the stage, the shipper stamps each line when it reads it, and the lookback only has to cover shipping delay. nginx and Caddy stamp lines when they write them, so this doesn’t affect them. If you keep the stage, setLOKI_LOOKBACKabove your longest request, up to 3600. Every poll re-reads that whole window. - Loki can’t page inside a single timestamp, and GeoMetrikks reads 1000 lines per request for the whole selector. With second-precision timestamps, a selector that logs more than 1000 lines in one second loses the rest of that second. GeoMetrikks logs a warning once per such timestamp.
- A selector can match several streams, one per label set. GeoMetrikks
merges their lines in timestamp order under the selector’s server name. Each
selector has one log format, detected from its first lines or pinned with
LOKI_FORMATS, and GeoMetrikks counts lines in any other format as malformed. Use one selector per log format. - GeoMetrikks polls every
LOKI_POLL_INTERVALseconds, 5 by default. Reading starts when the app starts, as with a tailed file, so lines from while it was down are not read. To backfill a range from Loki’s history, use import-loki. - With
LOKI_QUERIESset andLOGPARSER_LOG_PATHSunset, GeoMetrikks tails no file. Set both to read files and Loki side by side. ALOGPARSER_LOG_PATHSline in.envor in a composeenvironment:block counts as set and keeps the file tail. The shipped.env.examplehas that line, so for a Loki-only install, remove it or comment it out. LOKI_TENANT_IDsendsX-Scope-OrgIDfor a multi-tenant Loki.LOKI_USERNAMEandLOKI_PASSWORDtogether send basic auth.- If Loki can’t be reached or refuses the query,
/healthlists the source underingestion.unavailableSourcesasloki:1,loki:2and so on, with the reasonunreachableorrejected. The Status page shows each selector with its server name, format and line count.
An Alloy configuration that ships a Traefik access log the way GeoMetrikks expects:
loki.source.file "traefik" { targets = [{ __path__ = "/var/log/traefik/access.log", job = "traefik", service_name = "traefik", }] tail_from_end = true forward_to = [loki.write.local.receiver]}
loki.write "local" { endpoint { url = "http://loki:3100/loki/api/v1/push" }}