Alloy Ingest Log Parsing Design
Date: 2026-01-03 Status: Approved
Revision (2026-01-04): Zeek log parsing removed per PR #592. Network/security logs (Zeek, FW_ADT, ACL) are better suited for SIEM tools and are no longer collected into Loki. The
detect_sourceandparse_logsstages below show the original design; current implementation handles onlyjournalanddockersources.
Problem
Section titled “Problem”Logs from Firewalla arrive at the K8s alloy-ingest via OTLP, but otelcol.exporter.loki wraps them in {"body": "..."} JSON format. This makes logs hard to read in Grafana and loses structured information.
Solution
Section titled “Solution”Add a multi-stage loki.process pipeline on the K8s alloy-ingest to:
- Unwrap the OTLP JSON body
- Detect log source from labels
- Apply source-specific parsing
- Normalize labels and write to Loki
Architecture
Section titled “Architecture”OTLP Receiver ↓otelcol.exporter.loki → loki.process "unwrap_otlp" ↓ loki.process "detect_source" ↓ loki.process "parse_logs" ↓ loki.process "finalize" ↓ loki.writeProcessing Stages
Section titled “Processing Stages”Stage 1: OTLP Unwrap
Section titled “Stage 1: OTLP Unwrap”Extracts the body field from the JSON wrapper created by otelcol.exporter.loki.
- Input:
{"body": "2026-01-03 13:28:46 magicsock: derp-27..."} - Output:
2026-01-03 13:28:46 magicsock: derp-27...
Stage 2: Source Detection
Section titled “Stage 2: Source Detection”Detects log source from the job label set by router Alloy:
| Job Label | Source Label |
|---|---|
firewalla-journal | journal |
docker | docker |
zeek | zeek |
| (other) | unknown |
Stage 3: Source-Specific Parsing
Section titled “Stage 3: Source-Specific Parsing”Journal logs:
- Extract log level from message text using regex
- Normalize level variants (warning→warn, critical→crit)
Docker logs:
- Parse JSON if present
- Extract message from common field names (msg, message)
- Extract level from various field names (level, log_level, severity)
Zeek logs:
- Parse JSON network connection data
- Extract protocol, service as labels
- Store IPs and ports as structured metadata (high cardinality)
Stage 4: Finalization
Section titled “Stage 4: Finalization”- Set default
level = "info"for logs without extracted level - Drop high-cardinality labels (filename, uid)
- Move Zeek connection details to structured metadata
- Add
cluster = "fzymgc-house"external label
Label Schema
Section titled “Label Schema”| Label | Source | Example Values |
|---|---|---|
host | All | firewalla |
job | All | firewalla-journal, docker, zeek |
source | All | journal, docker, zeek, unknown |
level | All | debug, info, warn, error, crit |
unit | Journal | docker.service, firewalla.service |
container | Docker | container name |
proto | Zeek | tcp, udp, icmp |
service | Zeek | http, dns, ssh |
Implementation
Section titled “Implementation”Modify argocd/cluster-app/templates/monitoring-alloy-ingest.yaml to add the processing stages between otelcol.exporter.loki and loki.write.
Testing
Section titled “Testing”- Deploy updated config
- Query Loki for
{host="firewalla"} - Verify logs show plain text (not JSON wrapped)
- Verify
levellabel is populated - Verify source-specific labels appear (proto, service for Zeek)