diff --git a/VERSION b/VERSION index 26aaba0..f0bb29e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.0 +1.3.0 diff --git a/install.sh b/install.sh index 055579c..e2e0a65 100755 --- a/install.sh +++ b/install.sh @@ -303,7 +303,12 @@ if [ "$HAS_APACHE" -eq 1 ] && [ -z "$APACHE_LOG" ]; then "/home/*/logs/*/access.log" \ "/var/www/vhosts/*/logs/access_log" \ "/usr/local/apache/domlogs/*" \ - "/var/log/httpd/*access*.log"; do + "/var/log/httpd/*access*.log" \ + "/var/log/apache2/*/error.log" \ + "/home/users/*/logs/*/error.log" \ + "/home/*/logs/*/error.log" \ + "/var/log/apache2/error.log" \ + "/var/log/httpd/*error*.log"; do # shellcheck disable=SC2086 if ls $_pat >/dev/null 2>&1; then _pats="$_pats $_pat" @@ -313,7 +318,8 @@ if [ "$HAS_APACHE" -eq 1 ] && [ -z "$APACHE_LOG" ]; then # /var/log/apache2/access.log vorhanden, aber 0 Bytes — ein Input darauf # liefert still nichts. for _p in /var/log/apache2/access.log /var/log/httpd/access_log \ - /var/log/apache2/other_vhosts_access.log; do + /var/log/apache2/other_vhosts_access.log \ + /var/log/apache2/error.log /var/log/httpd/error_log; do [ -s "$_p" ] && _pats="$_pats $_p" done # Nichts gefunden? Dann die CustomLog-Direktive befragen (verlaesslichste @@ -331,7 +337,9 @@ if [ "$HAS_APACHE" -eq 1 ] && [ -z "$APACHE_LOG" ]; then # Letzter Ausweg: breiter Scan der ueblichen Wurzelverzeichnisse. if [ -z "$_pats" ]; then _pats="$(find /var/log/apache2 /var/log/httpd /home -maxdepth 4 \ - -name 'access.log' -o -name 'access_log' 2>/dev/null | head -200 | tr '\n' ' ')" + \( -name 'access.log' -o -name 'access_log' \ + -o -name 'error.log' -o -name 'error_log' \) 2>/dev/null \ + | head -200 | tr '\n' ' ')" fi [ -n "$_pats" ] && APACHE_LOG="$(printf '%s' "$_pats" | sed 's/^ *//; s/ *$//')" [ -n "$APACHE_LOG" ] || APACHE_LOG="/var/log/apache2/access.log" @@ -352,12 +360,28 @@ if [ "$HAS_APACHE" -eq 1 ] || [ "$ROLE" = "apache" ]; then # feste Einzelpfade und jede NEUE Domain fiele heraus, bis der Installer # erneut laeuft. Wir wollen die Muster in der Conf, Telegraf loest sie bei # jedem Lauf selbst auf. + # TRENNUNG access/error — wichtig fuer die Metriken: + # Der Grok-Parser versteht nur das ACCESS-Format + # IP ident auth [HTTPDATE] "VERB REQ" CODE BYTES + # Eine Error-Zeile sieht anders aus: + # [Mon Sep 14 00:10:01.123456 2026] [core:error] [pid 9] AH00124: ... + # Laeuft sie durch den Access-Parser, entstehen keine Metriken (Muster passt + # nicht) — schlimmer: der Aggregator zaehlt sie in Gruppen mit, die es nicht + # gibt. Deshalb bekommt der Access-Input nur Access-Pfade, und die Error-Logs + # gehen ueber einen eigenen Input roh nach Loki. set -f for _p in $APACHE_LOG; do - APACHE_FILES="${APACHE_FILES:+$APACHE_FILES, }\"$_p\"" + case "$_p" in + *error*|*Error*) + APACHE_ERROR_FILES="${APACHE_ERROR_FILES:+$APACHE_ERROR_FILES, }\"$_p\"" ;; + *) + APACHE_FILES="${APACHE_FILES:+$APACHE_FILES, }\"$_p\"" ;; + esac done set +f [ -n "$APACHE_FILES" ] || APACHE_FILES='"/var/log/apache2/access.log"' + APACHE_ERROR_FILES="[${APACHE_ERROR_FILES:-\"/var/log/apache2/error.log\"}]" + export APACHE_ERROR_FILES APACHE_FILES="[$APACHE_FILES]" fi # Sicherheitsnetz: die Variable MUSS nicht-leer sein, bevor apache.conf gerendert @@ -625,9 +649,9 @@ fetch_conf() { fetch "$1" "$2.tmp" load_defaults if command -v envsubst >/dev/null; then - envsubst '${NODE_NAME} ${PROM_URL} ${LOKI_URL} ${TELEGRAF_TOKEN} ${PVE_TOKEN} ${PVE_NODE} ${PVE_URL} ${APACHE_LOG} ${APACHE_FILES}' < "$2.tmp" > "$2" + envsubst '${NODE_NAME} ${PROM_URL} ${LOKI_URL} ${TELEGRAF_TOKEN} ${PVE_TOKEN} ${PVE_NODE} ${PVE_URL} ${APACHE_LOG} ${APACHE_FILES} ${APACHE_ERROR_FILES}' else - subst_vars "$2.tmp" "$2" NODE_NAME PROM_URL LOKI_URL TELEGRAF_TOKEN PVE_TOKEN PVE_NODE PVE_URL APACHE_LOG APACHE_FILES + subst_vars "$2.tmp" "$2" NODE_NAME PROM_URL LOKI_URL TELEGRAF_TOKEN PVE_TOKEN PVE_NODE PVE_URL APACHE_LOG APACHE_FILES APACHE_ERROR_FILES fi if grep -q '\${' "$2"; then log "WARNUNG: $2 enthaelt noch unaufgeloeste Platzhalter:" diff --git a/telegraf/apache.conf b/telegraf/apache.conf index e70452a..2e17c70 100644 --- a/telegraf/apache.conf +++ b/telegraf/apache.conf @@ -35,15 +35,42 @@ [inputs.tail.tags] source = "apache" +# Vereinheitlicht den Feldnamen auf `message` — fuer JEDEN Log-Input. +# `apache_error` muss mit drin sein: sonst heisst das Feld dort `value` und +# jede Panel-/Alert-Query mit `| logfmt | line_format "{{.message}}"` findet +# die Error-Logs nicht. Das faellt nicht auf, solange niemand danach sucht. [[processors.rename]] - namepass = ["journal"] + namepass = ["journal", "apache_error"] order = 1 [[processors.rename.replace]] field = "value" dest = "message" # -------------------------------------------------------------------------- -# 2) Request-Metriken (Common/Combined Log Format) → Prometheus +# 2) Error-Logs → Loki (NUR roh, KEINE Metriken) +# -------------------------------------------------------------------------- +# Error-Logs haben ein anderes Format als Access-Logs: +# [Mon Sep 14 00:10:01.123456 2026] [core:error] [pid 9] AH00124: ... +# Der Access-Grok weiter unten kann das nicht lesen. Liefen sie durch ihn, +# gaebe es keine Metriken — aber der Aggregator wuerde sie in leere Gruppen +# einordnen. Deshalb ein EIGENER Input, der sie unveraendert nach Loki schickt. +# +# Eigener Messungsname `apache_error` statt `journal`: so sind sie in Loki +# eindeutig filterbar, und das Logs-Panel kann sie getrennt zeigen. +[[inputs.tail]] + files = ${APACHE_ERROR_FILES} + name_override = "apache_error" + initial_read_offset = "saved-or-end" + data_format = "value" + data_type = "string" + path_tag = "logfile" + # Der Remote-Write-Output laesst Logs per namedrop draussen; Loki nimmt sie. + [inputs.tail.tags] + source = "apache" + logtype = "error" + +# -------------------------------------------------------------------------- +# 3) Request-Metriken (Common/Combined Log Format) → Prometheus # -------------------------------------------------------------------------- # Common Log Format: # 127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 diff --git a/telegraf/base.conf b/telegraf/base.conf index 91bf2bc..531e7c6 100644 --- a/telegraf/base.conf +++ b/telegraf/base.conf @@ -17,7 +17,10 @@ url = "${PROM_URL}" data_format = "prometheusremotewrite" timeout = "10s" - namedrop = ["docker_log", "apache_log", "journal"] + # Roh-Logs gehoeren NICHT in Prometheus — sie sind Text, keine Zeitreihen. + # `apache_error` hier ergaenzen, sonst versucht der Remote-Write-Output + # Textzeilen als Metriken zu senden. + namedrop = ["docker_log", "apache_log", "journal", "apache_error"] [outputs.http.headers] Content-Type = "application/x-protobuf" Content-Encoding = "snappy" @@ -29,7 +32,10 @@ domain = "${LOKI_URL}" endpoint = "/loki/api/v1/push" timeout = "10s" - namepass = ["docker_log", "apache_log", "journal"] + # `apache_error` MUSS hier stehen: ohne den Namen sammelt der Input die + # Error-Logs ein und dieser Output verwirft sie wieder — kein Fehler, keine + # Meldung, die Panels bleiben leer. Derselbe stille Fehler wie bei `journal`. + namepass = ["docker_log", "apache_log", "journal", "apache_error"] sanitize_label_names = true metric_name_label = "" http_headers = { "Authorization" = "Bearer ${TELEGRAF_TOKEN}" }