Der x22-Backfill meldete "Zeitraum: 03.12.2024 .. 14.09.2026" und liest sich damit wie zwei Jahre Historie. Tatsaechlich enthielt die Quelle EINEN einzelnen Punkt vom 03.12.2024 (eine alte Logzeile) und sonst 30 Tage: belegte Stunden: 721 von 15.601 im Zeitraum (4.6 % Abdeckung) groesste Luecke: 620 Tage 03.12.2024 21:00 -> 15.08.2026 22:00 min..max allein sagt nichts ueber die Substanz. Die Selbstpruefung nennt jetzt zusaetzlich die Zahl der belegten Stunden gegen die Spanne, die prozentuale Abdeckung und — bei unter 50 % — die drei groessten Luecken mit Datum, plus den Hinweis auf fehlende/rotierte Logs als typische Ursache. Verifiziert gegen drei Faelle: (A) 1 alter Punkt + 30 Tage -> 4.6 %, Luecke benannt; (B) 15 Tage durchgehend -> 100 %, kein Alarm; (C) 29 Tage mit 20-h- Luecke -> 97.3 %, kein Alarm (echte kleine Luecken sind normal).
174 lines
7.8 KiB
Bash
174 lines
7.8 KiB
Bash
#!/bin/bash
|
|
# keyhelp-log-backfill.sh — Alte Apache-Logs eines KeyHelp-Hosts messen, parsen,
|
|
# als Archiv bereitstellen. (x22 und x14 sind damit beide bedient.)
|
|
# DIESES SKRIPT AENDERT NICHTS AM LAUFENDEN DIENST.
|
|
#
|
|
# SICHERHEIT (die drei Wege, die schon schiefgingen):
|
|
# 1. Es importiert NICHTS in die Produktions-TSDB. Der Import laeuft
|
|
# ausschliesslich ueber prom-archive-import.sh --file auf dem imac,
|
|
# der nur in die getrennte Archiv-Instanz (:9091) schreibt.
|
|
# 2. Es stoppt nichts und loescht nichts. Ausgabe geht nach /root/apache-archive.
|
|
# 3. Die Ausgabe wird VOR der Uebergabe geprueft (Einheiten, Zukunft, Inhalt).
|
|
#
|
|
# AUFRUF auf dem Host (als root):
|
|
# curl -fsSL https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main/scripts/keyhelp-log-backfill.sh -o /root/keyhelp-backfill.sh
|
|
# bash /root/keyhelp-backfill.sh # MESSEN (nichts wird veraendert)
|
|
# bash /root/keyhelp-backfill.sh --backfill # parsen und Tarball bauen
|
|
#
|
|
# Danach steht am Ende der genaue Befehl fuer den imac.
|
|
|
|
set -eu
|
|
|
|
REPO_RAW="${REPO_RAW:-https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main}"
|
|
OUT_DIR="${OUT_DIR:-/root/apache-archive}"
|
|
NODE="${NODE:-$(hostname -f 2>/dev/null || hostname)}"
|
|
BACKFILL=0
|
|
[ "${1:-}" = "--backfill" ] && BACKFILL=1
|
|
|
|
log() { printf '\033[1;34m[backfill]\033[0m %s\n' "$*"; }
|
|
warn() { printf '\033[1;33m[backfill] WARNUNG:\033[0m %s\n' "$*" >&2; }
|
|
die() { printf '\033[1;31m[backfill] FEHLER:\033[0m %s\n' "$*" >&2; exit 1; }
|
|
|
|
[ "$(id -u)" -eq 0 ] || die "Bitte als root ausfuehren."
|
|
|
|
# --------------------------------------------------------------------------
|
|
# 1) Bestand MESSEN — nicht raten
|
|
# --------------------------------------------------------------------------
|
|
log "Bestandsaufnahme der Apache-Logs (aendert nichts) …"
|
|
mkdir -p /tmp/log-archiv
|
|
curl -fsSL -m 30 "$REPO_RAW/scripts/apache-log-survey.py" -o /tmp/log-archiv/survey.py \
|
|
|| die "survey.py konnte nicht geladen werden."
|
|
# ACHTUNG: der Survey kennt KEIN --node (er beschreibt nur den lokalen Host).
|
|
# Der Fortschritt wird nach stdout UND in die Datei geschrieben; das Skript
|
|
# erzwingt Zeilenpufferung, sonst bleibt bestand.txt bei einer Pipe bis zum
|
|
# Prozessende leer und der laufende Scan sieht wie ein Haenger aus.
|
|
python3 /tmp/log-archiv/survey.py 2>&1 | tee /tmp/log-archiv/bestand.txt
|
|
|
|
# Wie viel Historie liegt wirklich vor? Das entscheidet ueber den Nutzen.
|
|
FILES=$(find /var/log/apache2 /home/users -name 'access.log*' -o -name 'error.log*' 2>/dev/null | wc -l)
|
|
log "Gefundene Logdateien (inkl. rotierte/.gz): $FILES"
|
|
|
|
if [ "$BACKFILL" -eq 0 ]; then
|
|
echo
|
|
log "Das war NUR die Messung — es wurde nichts veraendert."
|
|
echo " Weiter mit: bash $0 --backfill"
|
|
echo " Bestand: /tmp/log-archiv/bestand.txt"
|
|
exit 0
|
|
fi
|
|
|
|
# --------------------------------------------------------------------------
|
|
# 2) Archiv bauen
|
|
# --------------------------------------------------------------------------
|
|
log "Archiver laden …"
|
|
curl -fsSL -m 30 "$REPO_RAW/scripts/apache-log-archive.py" -o /tmp/log-archiv/archive.py \
|
|
|| die "apache-log-archive.py konnte nicht geladen werden."
|
|
|
|
log "Logs parsen (kann bei viel Historie dauern) …"
|
|
python3 /tmp/log-archiv/archive.py --node "$NODE" --out "$OUT_DIR" --include-raw 2>&1 | tail -20
|
|
|
|
# --------------------------------------------------------------------------
|
|
# 3) Selbst pruefen, BEVOR etwas weitergereicht wird
|
|
# --------------------------------------------------------------------------
|
|
OM="$OUT_DIR/apache-archive.openmetrics"
|
|
[ -s "$OM" ] || die "Keine Ausgabe erzeugt: $OM fehlt oder ist leer."
|
|
|
|
log "Selbstpruefung der Ausgabe"
|
|
python3 - "$OM" <<'PY' || exit 1
|
|
import sys, time, collections
|
|
path = sys.argv[1]
|
|
pts = [l.rstrip("\n") for l in open(path) if l and not l.startswith("#") and l.strip()]
|
|
if not pts:
|
|
print(" FEHLER: keine Datenpunkte"); sys.exit(1)
|
|
ts = [int(l.rsplit(" ", 1)[1]) for l in pts]
|
|
uniq = sorted(set(ts))
|
|
now = int(time.time())
|
|
f = lambda t: time.strftime("%d.%m.%Y %H:%M", time.gmtime(t))
|
|
print(f" Datenpunkte: {len(pts):,}")
|
|
print(f" Zeitraum: {f(uniq[0])} .. {f(uniq[-1])} UTC")
|
|
|
|
# WICHTIG: min..max allein ist irrefuehrend. Ein EINZELNER Punkt mit altem
|
|
# Zeitstempel (eine Logzeile von 2024 in einer sonst aktuellen Datei) laesst den
|
|
# Backfill wie zwei Jahre aussehen, obwohl nur 30 Tage Substanz da sind.
|
|
# Deshalb die tatsaechlich belegten Stunden nennen und die Abdeckung pruefen.
|
|
hours = sorted({t - (t % 3600) for t in uniq})
|
|
span_h = max(1, (hours[-1] - hours[0]) // 3600 + 1)
|
|
cover = 100.0 * len(hours) / span_h
|
|
print(f" belegte Stunden: {len(hours):,} von {span_h:,} im Zeitraum ({cover:.1f} % Abdeckung)")
|
|
if len(hours) < span_h * 0.5:
|
|
# Die groessten Luecken benennen — das sind meist fehlende/rotierte Logs.
|
|
gaps = [(hours[i+1] - hours[i], hours[i], hours[i+1]) for i in range(len(hours)-1)]
|
|
gaps.sort(reverse=True)
|
|
print(" WARNUNG: die Historie ist lueckenhaft. Groesste Luecken:")
|
|
for g, a, b in gaps[:3]:
|
|
if g > 3600:
|
|
print(f" {g/86400:.0f} Tage {f(a)} -> {f(b)}")
|
|
print(" Ursache meist: rotierte/alte Logs fehlen auf dem Quellhost.")
|
|
if len(hours) <= 3:
|
|
print(" ACHTUNG: nur wenige Stunden — vermutlich einzelne alte Logzeilen,")
|
|
print(" kein zusammenhaengender Zeitraum. Vor dem Import pruefen.")
|
|
|
|
domains = collections.Counter()
|
|
for l in pts:
|
|
i = l.find('domain="')
|
|
if i >= 0:
|
|
j = l.find('"', i + 8)
|
|
domains[l[i+8:j]] += 1
|
|
print(f" Domains: {len(domains)}")
|
|
for d, n in domains.most_common(8):
|
|
print(f" {d}: {n} Punkte")
|
|
|
|
# Einheiten-Wache: alles nach 2100 ist kein Sekundenwert
|
|
if uniq[-1] > 4102444800:
|
|
print(" FEHLER: Zeitstempel nach dem Jahr 2100 -> Millisekunden statt Sekunden")
|
|
print(" promtool wuerde Bloecke im Jahr 58.660 erzeugen. Abbruch.")
|
|
sys.exit(1)
|
|
print(" Einheit: OK (Sekunden)")
|
|
|
|
# Zukunft: der Fehler, der die Produktion kosten kann
|
|
z = [t for t in uniq if t > now + 300]
|
|
if z:
|
|
print(f" FEHLER: {len(z)} Punkte in der ZUKUNFT (max {f(z[-1])}).")
|
|
print(" Ursache: falsche Systemuhr oder Log-Zeitzone. Abbruch.")
|
|
sys.exit(1)
|
|
print(" Zukunft: OK")
|
|
|
|
if len(domains) < 2:
|
|
print(f" WARNUNG: nur {len(domains)} Domain(s) erkannt — Logs vollstaendig?")
|
|
PY
|
|
|
|
TAR="$OUT_DIR/apache-archive.tar.gz"
|
|
[ -s "$TAR" ] && log "Tarball: $TAR ($(du -h "$TAR" | cut -f1))"
|
|
|
|
# --------------------------------------------------------------------------
|
|
# 4) Uebergabe — der sichere Weg
|
|
# --------------------------------------------------------------------------
|
|
cat <<EOF
|
|
|
|
============================================================
|
|
FERTIG. Naechster Schritt auf dem imac.
|
|
============================================================
|
|
|
|
WICHTIG: NICHT von Hand entpacken. Die Bloecke gehoeren in die getrennte
|
|
Archiv-Instanz (:9091), nie in die Produktions-TSDB. Genau dieser Weg hat am
|
|
14.09.2026 die Zeitreferenz verschoben — 13 Nodes verloren ihre Metriken.
|
|
|
|
# 1) Uebertragen (vom Host aus, im NetBird)
|
|
scp $OM mm@100.83.134.46:/tmp/apache-archive.openmetrics
|
|
|
|
# 2) Auf dem imac einspielen (prueft Zukunft, Manifest, Gegenprobe)
|
|
ssh mm@192.168.188.177 'sudo /usr/local/bin/prom-archive-import.sh --file /tmp/apache-archive.openmetrics'
|
|
|
|
# 3) Zweiter Lauf derselben Datei wird automatisch uebersprungen.
|
|
# Anzeigen, was schon importiert ist:
|
|
ssh mm@192.168.188.177 'sudo /usr/local/bin/prom-archive-import.sh --list'
|
|
|
|
# 4) Pruefen (step >= 1h wegen Stunden-Buckets!)
|
|
ssh mm@192.168.188.177 'curl -s -G http://127.0.0.1:9091/api/v1/query_range \\
|
|
--data-urlencode "query=apache_archive_requests_total" \\
|
|
--data-urlencode "start=\$(date -d "-30 days" +%s)" \\
|
|
--data-urlencode "end=\$(date +%s)" --data-urlencode "step=3600" | head -c 400'
|
|
|
|
Die Produktions-TSDB bleibt bei diesem Weg unberuehrt. Der Tarball mit den
|
|
Rohlogs liegt unter $TAR — aufheben, er ist die Quelle fuer Einzelfaelle.
|
|
EOF
|