#!/bin/bash # raw-log-backfill.sh — Rohlogs eines KeyHelp-Hosts fuer den LOKI-Import sammeln. # # Warum es dieses Skript gibt: # Der Prometheus-Archiv-Import verdichtet jede Logzeile auf Stunde/Domain/ # Status und VERWIRFT dabei Referrer, User-Agent und Client-IP. Fragen nach # Herkunft ("woher kommen die Besucher") liessen sich damit nicht # rueckwirkend beantworten. Lokis Rohzeilen koennen es — aber die # Live-Sammlung reicht nur bis zur Installation zurueck. # Dieses Skript holt die Historie in eine Datei, die Loki aufnehmen kann. # # DIESES SKRIPT AENDERT NICHTS AM LAUFENDEN DIENST. Es liest nur Logdateien, # es stoppt nichts, es loescht nichts. # # AUFRUF (auf dem Host, als root): # curl -fsSL https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main/scripts/raw-log-backfill.sh -o /root/raw-backfill.sh # bash /root/raw-backfill.sh # MESSEN (aendert nichts) # bash /root/raw-backfill.sh --collect # Rohdatei bauen # bash /root/raw-backfill.sh --collect --since 2025-01-01 # bash /root/raw-backfill.sh --collect --tarball /root/apache-archive/apache-archive.tar.gz # # Am Ende steht der exakte 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)}" COLLECT=0 SINCE="" UNTIL="" TARBALL="" ROOTS="/var/log/apache2 /home/users" while [ $# -gt 0 ]; do case "$1" in --collect) COLLECT=1 ;; --since) SINCE="${2:?Datum fehlt}"; shift ;; --until) UNTIL="${2:?Datum fehlt}"; shift ;; --tarball) TARBALL="${2:?Pfad fehlt}"; shift ;; --node) NODE="${2:?Name fehlt}"; shift ;; --roots) ROOTS="${2:?Pfade fehlen}"; shift ;; *) echo "Unbekannte Option: $1" >&2; exit 2 ;; esac shift done log() { printf '\033[1;34m[raw-backfill]\033[0m %s\n' "$*"; } warn() { printf '\033[1;33m[raw-backfill] WARNUNG:\033[0m %s\n' "$*" >&2; } die() { printf '\033[1;31m[raw-backfill] FEHLER:\033[0m %s\n' "$*" >&2; exit 1; } [ "$(id -u)" -eq 0 ] || die "Bitte als root ausfuehren (die Logs liegen unter /home/users mit 0700)." mkdir -p "$OUT_DIR" COLLECTOR="$OUT_DIR/raw-log-collect.py" if [ -r "$COLLECTOR" ] && [ -n "${SKIP_FETCH:-}" ]; then log "Collector lokal uebernommen: $COLLECTOR" else log "Lade Collector vom Repo …" curl -fsSL -m 30 "$REPO_RAW/scripts/raw-log-collect.py" -o "$COLLECTOR" \ || die "raw-log-collect.py konnte nicht geladen werden." fi python3 -c "import sys; sys.exit(0)" || die "python3 fehlt auf diesem Host." # -------------------------------------------------------------------------- # 1) MESSEN — was ist ueberhaupt da? (aendert nichts) # -------------------------------------------------------------------------- log "Bestandsaufnahme der Apache-Logs (liest nur) …" FILES=$(find $ROOTS -name 'access.log*' -o -name 'error.log*' 2>/dev/null | wc -l) GZ=$(find $ROOTS -name '*.log*.gz' 2>/dev/null | wc -l) RAW_BYTES=$(find $ROOTS -name 'access.log*' -o -name 'error.log*' 2>/dev/null -printf '%s\n' 2>/dev/null | awk '{s+=$1} END {print s+0}') log " Logdateien: $FILES (davon komprimiert: $GZ)" log " Rohgroesse: $(( RAW_BYTES / 1024 / 1024 )) MB" log " Aelteste Datei: $(find $ROOTS -name 'access.log*' -printf '%TY-%Tm-%Td %p\n' 2>/dev/null | sort | head -1 | cut -c1-10)" if [ -n "$TARBALL" ] && [ -r "$TARBALL" ]; then log " Tarball: $TARBALL ($(( $(stat -c%s "$TARBALL") / 1024 / 1024 )) MB)" log " Rohlogs im Tar: $(tar -tzf "$TARBALL" 2>/dev/null | grep -c 'raw/.*log' || echo 0) Dateien" fi if [ "$COLLECT" -eq 0 ]; then echo log "Das war NUR die Messung — es wurde nichts geschrieben." echo " Weiter mit: bash $0 --collect${SINCE:+ --since $SINCE}" echo " Optional zusaetzlich Rohlogs aus dem Archiv-Tarball:" echo " --tarball $OUT_DIR/apache-archive.tar.gz" exit 0 fi # -------------------------------------------------------------------------- # 2) SAMMELN # -------------------------------------------------------------------------- ARGS="--node $NODE --out $OUT_DIR --roots $ROOTS" [ -n "$SINCE" ] && ARGS="$ARGS --since $SINCE" [ -n "$UNTIL" ] && ARGS="$ARGS --until $UNTIL" [ -n "$TARBALL" ] && [ -r "$TARBALL" ] && ARGS="$ARGS --include-tarball $TARBALL" log "Sammle Rohlogs … (bei viel Historie dauert das einige Minuten)" # shellcheck disable=SC2086 python3 "$COLLECTOR" $ARGS 2>&1 | tee "$OUT_DIR/collect.log" RC=${PIPESTATUS[0]} [ "$RC" -eq 0 ] || die "Collector brach ab (Code $RC). Siehe $OUT_DIR/collect.log" PART=$(ls -t "$OUT_DIR"/loki-raw-*/raw.tsv 2>/dev/null | head -1) STATE=$(ls -t "$OUT_DIR"/loki-raw-*/state.json 2>/dev/null | head -1) [ -n "$PART" ] && [ -r "$PART" ] || die "Keine Rohdatei gefunden." [ -n "$STATE" ] && [ -r "$STATE" ] || die "Keine Zustandsdatei gefunden." LINES=$(wc -l < "$PART") SIZE=$(( $(stat -c%s "$PART") / 1024 / 1024 )) log "Rohdatei: $PART" log " $LINES Zeilen, $SIZE MB" # Gegenprobe: stimmt die Zeilenzahl mit der Zustandsdatei ueberein? EXPECT=$(python3 -c "import json,sys; print(json.load(open('$STATE'))['lines'])") if [ "$LINES" != "$EXPECT" ]; then warn "Zeilenzahl weicht ab: Datei=$LINES, Auftrag=$EXPECT — bitte pruefen." else log " Gegenprobe: Zeilenzahl stimmt mit dem Auftrag ueberein ($LINES)." fi echo log "NAECHSTER SCHRITT — diese beiden Zeilen auf dem imac ausfuehren:" echo echo " # 1) Rohdatei und Auftrag uebertragen" echo " scp $PART $STATE mm@100.83.134.46:/tmp/" echo echo " # 2) Einlesen (der Import laeuft LOKAL auf dem imac — kein Token noetig)" echo " ssh mm@100.83.134.46 \\" echo " 'python3 /usr/local/bin/loki-raw-import.py \\" echo " --file /tmp/$(basename "$PART") --state /tmp/$(basename "$STATE") \\" echo " --url http://127.0.0.1:3100'" echo echo " # Vorher pruefen (empfohlen, aendert nichts):" echo " ssh mm@100.83.134.46 \\" echo " 'python3 /usr/local/bin/loki-raw-import.py \\" echo " --file /tmp/$(basename "$PART") --state /tmp/$(basename "$STATE") \\" echo " --url http://127.0.0.1:3100 --dry-run'" echo log "Danach im Grafana pruefen: {imported=\"1\"} (Dashboard: Weblogs-Analyse)" warn "Das Skript selbst schreibt NICHTS nach Loki — der Import ist ein eigener Schritt."