Data Flow¶
End-to-end trace of how data moves through LogSys from source to dashboard.
Complete Data Flow¶
%%{init: {'theme':'default','themeVariables':{'primaryColor':'#1a73e8','secondaryColor':'#e8f0fe','tertiaryColor':'#f8f9fa','lineColor':'#5f6368','fontFamily':'Inter','fontSize':'13px'}}}%%
flowchart TD
subgraph SRC["📥 Sources"]
S1[GitLab Webhook<br/>POST /webhooks/gitlab]
S2[File Upload<br/>POST /api/sources/ephemeral]
S3[Jira Polling<br/>REST API v3]
S4[Prometheus Poll<br/>Instant Query]
S5[Azure DevOps<br/>WIQL Query]
end
subgraph ROUTE["🔀 Routing"]
V1[Vector :8686<br/>ephemeral_http]
V2[Vector :8687<br/>webhook_http]
C1[Collectors :8080<br/>webhook_receiver]
end
subgraph KAFKA["📨 Kafka Topics"]
K1[(raw_logs)]
K2[(raw_metrics)]
K3[(raw_traces)]
K4[(raw_events)]
K5[(canonical-events)]
K6[(dlq)]
end
subgraph PIPE["⚙️ Pipeline Stages"]
P1[Parse<br/>Drain3]
P2[Normalize<br/>Pydantic + PII]
P3[Dedup<br/>Redis Lua]
P4[Features<br/>Polars]
P5[Classify<br/>RoBERTa/Rule]
P6[Store<br/>executemany INSERT]
end
subgraph STORE["💾 Storage"]
PG[(PostgreSQL<br/>events table<br/>partitioned)]
TP[(template_priority<br/>cache)]
DLQ[(dlq_events)]
end
subgraph SERVE["🌐 API Layer"]
API[FastAPI<br/>REST + WS]
REDIS[(Redis<br/>KPI cache)]
MV[OLAP Views<br/>mv_dashboard_aggregates<br/>mv_timeseries_hourly<br/>mv_top_templates]
end
subgraph UI["🎨 Frontend"]
DASH[Dashboard<br/>KPIs, Charts]
MON[Monitoring<br/>Log Table]
INS[AI Insights<br/>RCA, Anomalies]
end
%% Flow
S1 -->|HTTP| C1
S2 -->|HTTP| V1
S3 -->|HTTP| C1
S4 -->|HTTP| C1
S5 -->|HTTP| C1
C1 -->|Produce| K4
V1 -->|Produce| K5
V2 -->|Produce| K5
K1 -.->|Consume| PIPE
K2 -.->|Consume| PIPE
K3 -.->|Consume| PIPE
K4 -.->|Consume| PIPE
PIPE --> P1 --> P2 --> P3 --> P4 --> P5 --> P6
P2 -.->|Reject| K6
P3 -.->|Duplicate| SKIP[Skip]
P6 -->|Write| PG
P6 -->|Write| TP
P2 -.->|DLQ| DLQ
PG -.->|Refresh 30s| MV
PG -.->|Read| API
MV -.->|Read| API
REDIS -.->|Cache| API
API -.->|WS Push| UI
UI -.->|Upload| V1
Stage-by-Stage Detail¶
1. Ingestion¶
| Source | Protocol | Auth | Frequency | Output Topic |
|---|---|---|---|---|
| GitLab Webhook | HTTP POST | HMAC SHA-256 | Event-driven | raw_events |
| File Upload | HTTP POST (multipart) | JWT Bearer | On-demand | canonical-events (via Vector) |
| Jira | REST GET | Basic + API Token | 10 min | raw_events |
| Prometheus | HTTP GET (PromQL) | None | 5 min | raw_metrics |
| Azure DevOps | REST POST (WIQL) | PAT | 10 min | raw_events |
Collector Implementation (services/collectors/):
- Single process: APScheduler + FastAPI webhook receiver
- State persisted in Redis (polling:cursor:{source})
- Retry with exponential backoff (tenacity)
- Publish via shared Kafka client (acks=all, LZ4)
2. Vector HTTP Router¶
# services/vector/vector.yaml
sources:
ephemeral_http:
type: http
address: "0.0.0.0:8686"
webhook_http:
type: http
address: "0.0.0.0:8687"
transforms:
ephemeral_enrich:
type: remap
inputs: [ephemeral_http]
webhook_enrich:
type: remap
inputs: [webhook_http]
sinks:
kafka:
type: kafka
inputs: [ephemeral_enrich, webhook_enrich]
bootstrap_servers: ${KAFKA_BROKERS}
topic: canonical-events
encoding: json
- Port 8686: Ephemeral file uploads (from API)
- Port 8687: Generic webhooks
- Transforms via VRL (Vector Remap Language)
- Single Kafka sink (JSON encoding, no compression)
3. Kafka Topics¶
| Topic | Partitions | Retention | Key | Purpose |
|---|---|---|---|---|
raw_logs |
1 | 1 day | source | Raw log lines from collectors |
raw_metrics |
1 | 1 day | source | Prometheus metric samples |
raw_traces |
1 | 1 day | trace_id | Distributed trace spans |
raw_events |
1 | 1 day | source | Jira, Azure, GitLab events |
canonical-events |
1 | 1 day | dedup_key | Normalized, ready for pipeline |
dlq |
1 | 30 days | — | Failed processing (never lose data) |
Retention: Kafka is a transport buffer — durable storage is PostgreSQL (
eventspartitions). 1-day retention (set indocker-compose.ymlviaKAFKA_LOG_RETENTION_HOURS=24and per-topicretention.ms=86400000) is safe as long as the consumer keeps up; if the pipeline is down > 1 day, unconsumed messages in Kafka are purged.Production: Increase partitions to match pipeline replicas for parallel consumption.
4. Pipeline Stages (Consumer Pipeline)¶
flowchart LR
BATCH[Kafka Poll<br/>100 msgs / 0.5s] --> FILTER[Noise Filter<br/>is_noise()]
FILTER --> PARSE[1. Parse<br/>Drain3]
PARSE --> NORM[2. Normalize<br/>Pydantic + PII]
NORM -->|Invalid| DLQ[DLQ]
NORM --> DEDUP[3. Dedup<br/>Redis Lua]
DEDUP -->|Duplicate| SKIP
DEDUP --> FEAT[4. Features<br/>Polars]
FEAT --> CLASS[5. Classify<br/>Cache → RoBERTa → Rule]
CLASS --> STORE[6. Store<br/>executemany INSERT]
STORE --> PG[(PostgreSQL)]
STORE -->|Notify| REDIS[Redis Pub/Sub]
Stage Details¶
| Stage | Input | Output | Key Logic | Perf |
|---|---|---|---|---|
| Noise Filter | Raw Kafka msg | Filtered msg | Drop heartbeats, health checks | O(1) |
| Parse (Drain3) | Message text | template_id, template_text |
Online clustering, sim_th=0.4 |
~2K eps |
| Normalize | Raw + template | CanonicalEvent |
PII mask, severity regex, Pydantic validate | ~2.2K eps |
| Dedup | CanonicalEvent | Unique events | SHA-256(key) + Redis Lua INCR+EXPIRE |
~2.1K eps |
| Features | Event | Enriched event | Polars vectorized: severity_rank, counts |
~2.3K eps |
| Classify | Event + template | Priority P1–P4 | Cache → RoBERTa → Rule fallback | ~2K eps |
| Store | Enriched batch | PG rows | executemany INSERT, intra-batch merge | ~2K eps |
5. Storage & Indexing¶
Table: events (partitioned by month, PK (id, timestamp))
-- Key indexes
CREATE INDEX ix_events_timestamp ON events (timestamp DESC);
CREATE INDEX ix_events_source_sev_ts ON events (source, severity, timestamp DESC);
CREATE INDEX ix_events_priority_ts ON events (priority, timestamp DESC) WHERE priority IS NOT NULL;
CREATE INDEX ix_events_template_id ON events (template_id);
CREATE INDEX ix_events_message_trgm ON events USING GIN (message gin_trgm_ops);
-- Deduplication backstop (partition-aware)
CREATE UNIQUE INDEX ux_events_dedup_key ON events (dedup_key, timestamp) WHERE dedup_key IS NOT NULL;
Materialized Views (refreshed every 30s concurrently):
| View | Purpose | Refresh |
|---|---|---|
mv_dashboard_aggregates |
KPI totals, rates, top errors | 30s |
mv_timeseries_hourly |
Hourly buckets by severity/priority | 30s |
mv_top_templates |
Top templates by volume | 30s |
6. API Layer¶
Query Patterns:
| Endpoint | Query Strategy | Cache |
|---|---|---|
GET /api/logs |
Keyset pagination (cursor) | Redis 30s |
GET /api/logs/meta/timeseries |
MV mv_timeseries_hourly |
Redis 30s |
GET /api/kpis |
MV mv_dashboard_aggregates |
Redis 30s |
GET /api/ai-insights/engine |
On-demand compute | Redis 30s |
Real-time: WebSocket /ws/kpis → Redis pub/sub → broadcast
Failure Paths¶
flowchart TD
PROD[Producer] -->|Network fail| RETRY[Retry + Backoff]
RETRY -->|Max retries| DLQ1[DLQ Topic]
PIPE[Pipeline] -->|Parse fail| DLQ2[DLQ Topic + dlq_events table]
PIPE -->|Normalize fail| DLQ2
PIPE -->|Storage fail| RETRY2[Retry batch]
RETRY2 -->|Persist fail| DLQ2
API[API] -->|DB timeout| CACHE[Serve from Redis]
API -->|Cache miss| ERROR[500 + alert]
DLQ1 & DLQ2 --> REPLAY[Manual replay via admin UI]
Latency Budget (P99)¶
| Segment | Budget | Typical |
|---|---|---|
| Source → Vector | 10 ms | 2 ms |
| Vector → Kafka | 5 ms | 1 ms |
| Kafka → Pipeline | 50 ms | 20 ms |
| Pipeline (all stages) | 200 ms | 80 ms |
| Pipeline → PostgreSQL | 50 ms | 15 ms |
| PostgreSQL → API | 30 ms | 10 ms |
| API → WebSocket | 20 ms | 5 ms |
| Total E2E | < 500 ms | ~133 ms |