Der Prometheus-Archiv-Import verdichtet auf Stunde/Domain/Status und verwirft Referrer, User-Agent und Client-IP. Fragen nach der Herkunft liessen sich damit nicht rueckwirkend beantworten. - raw-log-collect.py: liest access+error (inkl. .gz, rotierte Dateien), schreibt ts_ns/labels/rohzeile und meldet Zeilen ohne Zeitstempel statt sie still zu verwerfen. Error-Logs ohne Zeitzone (Apache- Standard) werden korrekt nach UTC verschoben. - raw-log-backfill.sh: Wrapper mit Messmodus; sammelt auf dem Host, importiert selbst NICHTS.
141 lines
6.2 KiB
Bash
141 lines
6.2 KiB
Bash
#!/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."
|