136 lines
5.3 KiB
YAML
136 lines
5.3 KiB
YAML
# =============================================================================
|
|
# VECTOR CONFIGURATION
|
|
# Zweck: Docker-Container-Logs einsammeln, aufbereiten und an Loki (-> Grafana)
|
|
# senden. Zusätzlich Debug-Ausgabe in der Konsole.
|
|
# =============================================================================
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# GLOBAL / API (optional, aber praktisch für Monitoring von Vector selbst)
|
|
# -----------------------------------------------------------------------------
|
|
api:
|
|
enabled: true
|
|
address: "127.0.0.1:8686" # Vector-eigenes Dashboard/API, nur lokal erreichbar
|
|
|
|
# =============================================================================
|
|
# SOURCES: Woher kommen die Daten?
|
|
# =============================================================================
|
|
sources:
|
|
docker_logs:
|
|
type: docker_logs
|
|
# Nur laufende + gestoppte Container-Logs seit Vector-Start (Default: true)
|
|
include_containers: [] # leer = alle Container
|
|
exclude_containers:
|
|
- "vector" # verhindert, dass Vector seine eigenen Logs einsammelt (Log-Loop!)
|
|
auto_partial_merge: true # zusammengehörige, von Docker aufgeteilte Zeilen mergen
|
|
|
|
# =============================================================================
|
|
# TRANSFORMS: Verarbeitung der Daten
|
|
# =============================================================================
|
|
transforms:
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 1. Grundbereinigung + feste Metadaten
|
|
# ---------------------------------------------------------------------------
|
|
parse_logs:
|
|
type: remap
|
|
inputs: [docker_logs]
|
|
source: |
|
|
# --- Message bereinigen (Newlines raus, damit Loki/Grafana sauber anzeigen) ---
|
|
if is_string(.message) {
|
|
.message = replace!(.message, "\n", " ")
|
|
.message = strip_whitespace(.message)
|
|
}
|
|
|
|
# --- Statisches Service-/Cluster-Label ---
|
|
.service = "my-cluster"
|
|
|
|
# --- Docker-Metadaten absichern (falls mal Felder fehlen) ---
|
|
.container_name = .container_name || "unknown"
|
|
.host = .host || "unknown"
|
|
|
|
# --- Container-Image ohne Tag/Hash extrahieren (weniger Kardinalität) ---
|
|
if exists(.image) {
|
|
image_parts = split(string!(.image), ":")
|
|
.image_name = image_parts[0]
|
|
}
|
|
|
|
# --- Log-Level heuristisch erkennen (falls die App das nicht strukturiert liefert) ---
|
|
.level = "info"
|
|
msg_lower = downcase(to_string(.message) ?? "")
|
|
if contains(msg_lower, "error") || contains(msg_lower, "err ") || contains(msg_lower, "exception") {
|
|
.level = "error"
|
|
} else if contains(msg_lower, "warn") {
|
|
.level = "warn"
|
|
} else if contains(msg_lower, "debug") {
|
|
.level = "debug"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 2. Versuch, JSON-Logs (strukturierte Logs von Apps) zu parsen
|
|
# Falls die Message JSON ist, wird sie geflattet -> mehr Kontext in Grafana.
|
|
# Falls nicht, bleibt .message unverändert (kein Fehler, kein Absturz).
|
|
# ---------------------------------------------------------------------------
|
|
parse_json_if_possible:
|
|
type: remap
|
|
inputs: [parse_logs]
|
|
source: |
|
|
parsed, err = parse_json(.message)
|
|
if err == null {
|
|
# nur mergen, wenn wirklich ein Objekt rauskam (kein Array/String/Zahl)
|
|
if is_object(parsed) {
|
|
. = merge(., object!(parsed), deep: true)
|
|
}
|
|
}
|
|
|
|
# =============================================================================
|
|
# SINKS: Wohin sollen die Daten?
|
|
# =============================================================================
|
|
sinks:
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Debug-Ausgabe in der Konsole (praktisch zum Testen, kann später raus)
|
|
# ---------------------------------------------------------------------------
|
|
stdout_debug:
|
|
type: console
|
|
inputs: [parse_json_if_possible]
|
|
encoding:
|
|
codec: json
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Loki-Sink -> Grafana
|
|
# ---------------------------------------------------------------------------
|
|
loki_sink:
|
|
type: loki
|
|
inputs: [parse_json_if_possible]
|
|
endpoint: "http://docker1.lan:3100"
|
|
|
|
# Falls dein Loki Multi-Tenancy nutzt (Standard bei vielen Setups, auch Single-User).
|
|
# Falls "no org id"-Fehler auftauchen: diese Zeile aktivieren.
|
|
tenant_id: "fake"
|
|
|
|
encoding:
|
|
codec: json
|
|
# Diese Felder werden bereits als Loki-Labels genutzt -> nicht doppelt im JSON-Body
|
|
except_fields: ["host", "service", "container_name"]
|
|
|
|
# WICHTIG: Nur Felder mit niedriger Kardinalität als Labels verwenden!
|
|
# container_id, message, timestamp etc. NIEMALS als Label -> "too many streams" in Loki.
|
|
labels:
|
|
host: "{{ host }}"
|
|
service: "{{ service }}"
|
|
container_name: "{{ container_name }}"
|
|
level: "{{ level }}"
|
|
|
|
# Reihenfolge der Log-Zeilen pro Stream sicherstellen
|
|
out_of_order_action: rewrite_timestamp
|
|
|
|
batch:
|
|
max_bytes: 1048576 # 1 MB pro Batch
|
|
timeout_secs: 5
|
|
|
|
request:
|
|
retry_attempts: 10
|
|
retry_max_duration_secs: 30
|
|
|
|
healthcheck:
|
|
enabled: true |