Valerter

Architecture

How Valerter works under the hood.

Overview

Valerter is a real-time log alerting daemon that streams logs from VictoriaLogs and sends notifications with full log context.

Pipeline: VictoriaLogs → Parse → Throttle → Template → Notify

Project Structure

src/
├── main.rs              # Entry point, startup, shutdown
├── lib.rs               # Library root, module re-exports
├── cli.rs               # CLI argument parsing (clap)
├── engine.rs            # RuleEngine - orchestrates rule tasks
├── error.rs             # Error types (thiserror)
├── tail.rs              # VictoriaLogs streaming client
├── parser.rs            # Regex/JSON field extraction
├── throttle.rs          # LRU cache-based rate limiting
├── template.rs          # Jinja2 templating (minijinja)
├── stream_buffer.rs     # UTF-8 safe NDJSON buffering
├── metrics.rs           # Prometheus metrics server
├── config/              # Configuration module
│   ├── mod.rs           # Module root
│   ├── types.rs         # Config, RuleConfig, etc.
│   ├── notifiers.rs     # Notifier-specific configs
│   ├── runtime.rs       # Compiled runtime config
│   ├── validation.rs    # Validation functions
│   ├── env.rs           # Environment variable resolution
│   ├── secret.rs        # SecretString for sensitive values
│   └── tests.rs         # Config tests
└── notify/              # Notification module
    ├── mod.rs           # Module root
    ├── traits.rs        # Notifier async trait
    ├── registry.rs      # NotifierRegistry
    ├── payload.rs       # AlertPayload structure
    ├── queue.rs         # NotificationQueue + Worker
    ├── mattermost.rs    # Mattermost notifier
    ├── email.rs         # Email notifier (SMTP)
    ├── webhook.rs       # Webhook notifier
    └── tests.rs         # Notify tests

Pipeline Stages

1. Tail (Streaming)

Valerter connects to VictoriaLogs’ /select/logsql/tail endpoint via HTTP streaming. Unlike polling-based approaches:

Each rule maintains its own streaming connection, providing isolation.

2. Parse

Extracts structured fields from log lines:

If parsing fails, the error is logged and the line is skipped (no crash).

3. Throttle

Prevents alert spam using a sliding window algorithm:

4. Template

Renders the final message using Jinja2 templates (via minijinja):

5. Notify

Sends alerts to configured destinations via NotifierRegistry:

Concurrency Model

main.rs
    │
    ├── MetricsServer::run() ────────────────► :9090/metrics
    │
    ├── NotificationWorker::run() ───────────► consumes from bounded queue
    │
    └── RuleEngine::run()
            │
            └── JoinSet<()>
                    ├── rule_task("rule-1") ──► tail → parse → throttle → template → queue
                    ├── rule_task("rule-2") ──► tail → parse → throttle → template → queue
                    └── rule_task("rule-N") ──► ...

Key properties:

Reconnection Strategy

When VictoriaLogs connection fails:

  1. Exponential backoff: 1s → 2s → 4s → 8s → … → 60s (max)
  2. Metric update: valerter_victorialogs_up set to 0 (restored to 1 on successful reconnection)
  3. Reconnection metric: valerter_reconnections_total incremented
  4. On success: Throttle cache is reset (prevents stale state)

Notification Queue

Uses Tokio’s broadcast channel with ring buffer semantics:

RuleEngine (producers)              NotificationWorker (consumer)
    │                                       │
    ├── rule_task ─┐                        │
    ├── rule_task ──┼── broadcast::Sender ──┼── broadcast::Receiver
    └── rule_task ─┘                        │
                                            ▼
                                    NotifierRegistry
                                       ├── mattermost-ops
                                       ├── email-alerts
                                       └── webhook-pagerduty

Fail-Fast Validation

At startup, Valerter validates (in order):

  1. YAML syntax — Config file is valid YAML
  2. Required fields — All mandatory fields present
  3. Template syntax — All templates compile (minijinja)
  4. Notifier config — URLs, credentials, env vars resolve correctly
  5. Destinations exist — Rule destinations match notifier names in registry
  6. Email body_html — Templates used with email destinations have body_html
  7. Mattermost channel warning — Warns if mattermost_channel set but no Mattermost notifier in destinations

If any validation fails, Valerter exits immediately with a clear error message.

Multi-File Configuration

Config can be split across multiple files:

/etc/valerter/
├── config.yaml         # Main config (victorialogs, defaults, metrics)
├── rules.d/            # Rules as HashMap (name: config)
├── templates.d/        # Templates as HashMap
└── notifiers.d/        # Notifiers as HashMap

Files are loaded alphabetically. Duplicate names across files cause startup failure.

Metrics Pipeline

Rule tasks ─────┐
                ├──► metrics::counter!() ──► Prometheus Recorder ──► /metrics
NotificationWorker ─┘

Metrics are emitted throughout the pipeline:

Error Handling

Log+Continue pattern: Errors in spawned tasks are logged, never propagated up.

// ✅ CORRECT
match process().await {
    Ok(_) => continue,
    Err(e) => {
        tracing::error!(error = %e, "Processing failed");
        tokio::time::sleep(backoff).await;
    }
}

// ❌ NEVER DO THIS
tokio::spawn(async { process().unwrap(); }); // Silent crash

Security

See Also