85 lines
3.0 KiB
Bash
Executable File
85 lines
3.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# journal-tail.sh — journald-Logs inkrementell als Influx Line Protocol liefern.
|
|
#
|
|
# Warum dieses Script:
|
|
# * inputs.journal existiert in den offiziellen Telegraf-Builds NICHT
|
|
# (Docker-Image + InfluxData-Debian-Paket 1.40 geprüft).
|
|
# * Auf PVE gibt es KEIN rsyslog — /var/log/syslog existiert dort nicht.
|
|
# * Der JSON-Parser von inputs.exec liest nur EIN Objekt, keine NDJSON-Zeilen.
|
|
# * Das fertige Line Protocol ist dasselbe Muster wie gpu-rocm.sh (bewährt).
|
|
#
|
|
# Zustand: der Cursor steckt in jeder journalctl-JSON-Zeile ("__CURSOR").
|
|
# Er wird in $STATE_DIR/cursor gemerkt -> keine Duplikate, kein Verlust.
|
|
#
|
|
# Telegraf:
|
|
# [[inputs.exec]]
|
|
# interval = "30s"
|
|
# timeout = "25s"
|
|
# data_format = "influx"
|
|
# commands = ["/etc/telegraf/journal-tail.sh"]
|
|
|
|
set -u
|
|
|
|
PRIORITY="${JOURNAL_PRIORITY:-warning}" # warning|err|info|debug
|
|
SINCE="${JOURNAL_SINCE:-}" # optional, nur beim Erstanlauf
|
|
MAX_LINES="${JOURNAL_MAX_LINES:-500}" # Schutz vor Flut pro Lauf
|
|
STATE_DIR="${JOURNAL_STATE_DIR:-/var/lib/telegraf-journal}"
|
|
CURSOR_FILE="$STATE_DIR/cursor"
|
|
|
|
mkdir -p "$STATE_DIR" 2>/dev/null || true
|
|
|
|
args=(-o json --no-pager --no-hostname -p "$PRIORITY" -n "$MAX_LINES")
|
|
if [ -s "$CURSOR_FILE" ]; then
|
|
args+=(--after-cursor "$(cat "$CURSOR_FILE" 2>/dev/null)")
|
|
elif [ -n "$SINCE" ]; then
|
|
args+=(--since "$SINCE")
|
|
else
|
|
args+=(--since "-5min")
|
|
fi
|
|
|
|
last_cursor=""
|
|
|
|
journalctl "${args[@]}" 2>/dev/null | while IFS= read -r line; do
|
|
case "$line" in \{*) ;; *) continue ;; esac
|
|
|
|
# Felder aus dem JSON ziehen (jq-frei, damit kein Extra-Paket nötig ist)
|
|
get() { printf '%s' "$line" | sed -n "s/.*\"$1\":\"\([^\"]*\)\".*/\1/p" | head -1; }
|
|
getnum() { printf '%s' "$line" | sed -n "s/.*\"$1\":\([0-9]*\).*/\1/p" | head -1; }
|
|
|
|
cur="$(get '__CURSOR')"
|
|
[ -n "$cur" ] && last_cursor="$cur"
|
|
|
|
msg="$(get 'MESSAGE')"
|
|
[ -n "$msg" ] || continue
|
|
|
|
unit="$(get '_SYSTEMD_UNIT')"; [ -n "$unit" ] || unit="$(get 'SYSLOG_IDENTIFIER')"
|
|
ident="$(get 'SYSLOG_IDENTIFIER')"
|
|
prio="$(get 'PRIORITY')"
|
|
ts_us="$(getnum '__REALTIME_TIMESTAMP')"
|
|
host="$(get '_HOSTNAME')"
|
|
|
|
# Line Protocol: Escaping für Tag-Werte und String-Felder
|
|
esc_tag() { printf '%s' "$1" | sed 's/[ ,=]/\\&/g'; }
|
|
esc_str() { printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'; }
|
|
|
|
# Nanosekunden: __REALTIME_TIMESTAMP ist in Mikrosekunden
|
|
if [ -n "$ts_us" ]; then
|
|
ns="${ts_us}000"
|
|
else
|
|
ns="$(date +%s)000000000"
|
|
fi
|
|
|
|
# Tag "jhost" statt "host": Telegrafs global_tag host=<node> hat Vorrang behalten,
|
|
# sonst filtert das Dashboard-Log-Panel nach dem falschen Knoten.
|
|
printf 'journal,unit=%s,ident=%s,priority=%s,jhost=%s message="%s" %s\n' \
|
|
"$(esc_tag "${unit:-unknown}")" \
|
|
"$(esc_tag "${ident:-unknown}")" \
|
|
"$(esc_tag "${prio:-0}")" \
|
|
"$(esc_tag "${host:-unknown}")" \
|
|
"$(esc_str "$msg")" \
|
|
"$ns"
|
|
|
|
# Zustand nach jedem Datensatz sichern
|
|
[ -n "$cur" ] && { printf '%s' "$cur" > "$CURSOR_FILE.tmp" && mv -f "$CURSOR_FILE.tmp" "$CURSOR_FILE"; }
|
|
done
|