From 47dd362409ebdd7f50465a8e75308330a99c5504 Mon Sep 17 00:00:00 2001 From: Holger Sielaff Date: Mon, 29 Sep 2025 17:58:46 +0200 Subject: [PATCH] ... --- src/etc/php/8.3/fpm/pool.d/www-zabbix.conf | 14 ++++++ .../zabbix_agentd.d/php-exceptions.conf | 44 +++++++++++++++++++ src/usr/lib/zabbix-php.php | 39 +++++++++++++++- .../zabbix_template_php_exceptions.yaml | 40 ++++++++++------- 4 files changed, 119 insertions(+), 18 deletions(-) create mode 100644 src/etc/php/8.3/fpm/pool.d/www-zabbix.conf create mode 100644 src/etc/zabbix/zabbix_agentd.d/php-exceptions.conf diff --git a/src/etc/php/8.3/fpm/pool.d/www-zabbix.conf b/src/etc/php/8.3/fpm/pool.d/www-zabbix.conf new file mode 100644 index 0000000..9bd9c26 --- /dev/null +++ b/src/etc/php/8.3/fpm/pool.d/www-zabbix.conf @@ -0,0 +1,14 @@ +; Zabbix PHP Exception Monitoring - www pool override +; This file modifies the default www pool to include Zabbix exception monitoring +; Place this file AFTER the www.conf is loaded (higher number or 'z' prefix) + +[www] +; Add Zabbix PHP Exception Handler to existing www pool +; This will override any previous auto_prepend_file setting +php_admin_value[auto_prepend_file] = /usr/lib/zabbix-php.php + +; Ensure error logging is enabled +php_admin_value[log_errors] = On + +; Optional: Set custom error log location +; php_admin_value[error_log] = /var/log/php/www-error.log diff --git a/src/etc/zabbix/zabbix_agentd.d/php-exceptions.conf b/src/etc/zabbix/zabbix_agentd.d/php-exceptions.conf new file mode 100644 index 0000000..a8112d2 --- /dev/null +++ b/src/etc/zabbix/zabbix_agentd.d/php-exceptions.conf @@ -0,0 +1,44 @@ +# Zabbix Agent configuration for PHP Exception Monitoring +# UserParameter ohne jq-Abhängigkeit für passive checks + +# Vollständigen Log-Inhalt lesen (für neue Einträge) +UserParameter=php.exception.log.content,cat /var/log/zabbix/php.exceptions.log 2>/dev/null || echo "" + +# Neueste Exception (letzte Zeile) +UserParameter=php.exception.latest,tail -n 1 /var/log/zabbix/php.exceptions.log 2>/dev/null || echo "" + +# Get last exception timestamp (mit jq) +UserParameter=php.exception.last,tail -n 1 /var/log/zabbix/php.exceptions.log 2>/dev/null | jq -r '.timestamp // "never"' 2>/dev/null || echo "never" + +# Get last exception message (mit jq) +UserParameter=php.exception.message,tail -n 1 /var/log/zabbix/php.exceptions.log 2>/dev/null | jq -r '.message // ""' 2>/dev/null || echo "" + +# Get last exception file (mit jq) +UserParameter=php.exception.file,tail -n 1 /var/log/zabbix/php.exceptions.log 2>/dev/null | jq -r '.file // ""' 2>/dev/null || echo "" + +# Get last exception line (mit jq) +UserParameter=php.exception.line,tail -n 1 /var/log/zabbix/php.exceptions.log 2>/dev/null | jq -r '.line // 0' 2>/dev/null || echo "0" + +# Get last exception unique_id (mit jq) +UserParameter=php.exception.unique_id,tail -n 1 /var/log/zabbix/php.exceptions.log 2>/dev/null | jq -r '.unique_id // ""' 2>/dev/null || echo "" + +# Count recent exceptions (last hour) +UserParameter=php.exception.count.hour,awk -v since="$(date -d '1 hour ago' '+%Y-%m-%d %H:%M:%S')" '$0 > since' /var/log/zabbix/php.exceptions.log 2>/dev/null | wc -l + +# Count recent exceptions (last 24 hours) +UserParameter=php.exception.count.day,awk -v since="$(date -d '1 day ago' '+%Y-%m-%d %H:%M:%S')" '$0 > since' /var/log/zabbix/php.exceptions.log 2>/dev/null | wc -l + +# Log-Datei Größe (für Änderungsüberwachung) +UserParameter=php.exception.log.size,stat -c%s /var/log/zabbix/php.exceptions.log 2>/dev/null || echo "0" + +# Letzte Änderungszeit der Log-Datei +UserParameter=php.exception.log.mtime,stat -c%Y /var/log/zabbix/php.exceptions.log 2>/dev/null || echo "0" + +# Anzahl Zeilen in Log-Datei +UserParameter=php.exception.log.lines,wc -l < /var/log/zabbix/php.exceptions.log 2>/dev/null || echo "0" + +# Check ob Log-Datei existiert und lesbar ist +UserParameter=php.exception.log.accessible,[ -r /var/log/zabbix/php.exceptions.log ] && echo "1" || echo "0" + +# Check if PHP exception handler is active +# UserParameter=php.exception.handler.active,php -r "echo (class_exists('ZabbixPHPExceptionHandler') ? 1 : 0);" 2>/dev/null || echo "0" diff --git a/src/usr/lib/zabbix-php.php b/src/usr/lib/zabbix-php.php index f1d2b29..069140f 100644 --- a/src/usr/lib/zabbix-php.php +++ b/src/usr/lib/zabbix-php.php @@ -72,7 +72,7 @@ class ZabbixPHPExceptionHandler { // Get HTTP_HOST or set default $httpHost = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : - (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'cli'); + (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'cli'); // Create unique identifier for deduplication $uniqueId = md5($httpHost . '|' . $exception->getFile() . '|' . $exception->getLine() . '|' . $exception->getMessage()); @@ -84,7 +84,7 @@ class ZabbixPHPExceptionHandler { 'file' => $exception->getFile(), 'line' => $exception->getLine(), 'message' => $exception->getMessage(), - 'stacktrace' => $exception->getTraceAsString(), + 'stacktrace' => self::makePrettyException($exception), 'unique_id' => $uniqueId, 'request_uri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '', 'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '', @@ -110,8 +110,43 @@ class ZabbixPHPExceptionHandler { error_log("Zabbix PHP Exception Handler failed: " . $e->getMessage()); } } + public static function makePrettyException(Throwable $e) { + $ret=["# Exception: {$e->getMessage()}", "Traceback (most recent call last) "]; + foreach((array)$e->getTrace() as $index => $trace){ + $o=[]; + $result = "* #{$index} @ File: {$trace['file']}:{$trace['line']} - "; + if(!empty($trace['class'])) { + $result .= $trace['class']; + $result .= '->'; + } + if(!empty($trace['function'])){ + $result .= $trace['function']; + $result .= '();'; + } + if(!empty($trace['file'])){ + $von = intval($trace['line']) - 8; + $von = max($von, 1); + $bis = intval($trace['line']) + 8; + exec("sed -n '{$von},{$bis}p' {$trace['file']}", $o, $e); + if(!$e && $o){ + $result .= "\n"; + # $result.="\n\n"; + foreach($o as $line => $_){ + $result.=str_pad(strval($line+$von), 6, " ", \STR_PAD_LEFT) .": $_\n"; + } + # $result.=''; + } + } + + $ret[]=$result; + + } + + return join("\n", $ret); + } } + // Initialize the exception handler ZabbixPHPExceptionHandler::init(); ?> diff --git a/src/usr/share/doc/zabbix-php-monitoring/zabbix_template_php_exceptions.yaml b/src/usr/share/doc/zabbix-php-monitoring/zabbix_template_php_exceptions.yaml index 5fc12ae..364c397 100644 --- a/src/usr/share/doc/zabbix-php-monitoring/zabbix_template_php_exceptions.yaml +++ b/src/usr/share/doc/zabbix-php-monitoring/zabbix_template_php_exceptions.yaml @@ -41,7 +41,8 @@ zabbix_export: var formattedStack = 'No stacktrace available'; if (entry.stacktrace && entry.stacktrace.length > 0) { formattedStack = entry.stacktrace.split('\n').map(function(line, index) { - return (index + 1) + ': ' + line.trim(); + // return (index + 1) + ': ' + line.trim(); + return line.trim(); }).join('\n'); } @@ -67,27 +68,34 @@ zabbix_export: event_name: 'New PHP Exception Logged' priority: HIGH description: | - =============================================== - PHP EXCEPTION DETAILS - =============================================== + 🚨 PHP EXCEPTION DETECTED 🚨 + ═══════════════════════════════════════════════════════ - Raw Exception Data: + 📊 FORMATTED EXCEPTION DATA: {ITEM.LASTVALUE} - =============================================== - STACKTRACE AND FULL DETAILS - =============================================== + ═══════════════════════════════════════════════════════ + 🔍 QUICK ACCESS LINKS: - This section contains the complete exception information including: - - Exception message and location - - Full stacktrace with line numbers - - Request context (host, URI, user agent) - - Unique identifier for deduplication - - Timestamp of occurrence + • Latest Data: Monitoring → Latest Data → {HOST.NAME} + • Full History: Monitoring → Latest Data → "PHP Exception Monitor" + • Host Overview: Monitoring → Hosts → {HOST.NAME} - Click on "Latest Data" for this item to see the formatted JSON. + ═══════════════════════════════════════════════════════ + ℹ️ INFORMATION: - Note: This alert requires manual acknowledgment. + This alert contains the complete exception details including: + ✓ Exception message and exact location + ✓ Full stacktrace with numbered lines + ✓ Request context (host, URI, headers) + ✓ Unique identifier for correlation + ✓ Exact timestamp of occurrence + + ⚠️ ACTION REQUIRED: This alert requires manual acknowledgment. + Use "Actions" → "Close" to dismiss after reviewing. + + ═══════════════════════════════════════════════════════ + Host: {HOST.NAME} | Time: {EVENT.TIME} | Severity: {TRIGGER.SEVERITY} manual_close: 'YES' tags: - tag: scope