This commit is contained in:
Holger Sielaff
2025-09-29 17:58:46 +02:00
parent 3be1eab3e8
commit 47dd362409
4 changed files with 119 additions and 18 deletions

View File

@@ -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

View File

@@ -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"

View File

@@ -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<code>\n";
foreach($o as $line => $_){
$result.=str_pad(strval($line+$von), 6, " ", \STR_PAD_LEFT) .": $_\n";
}
# $result.='</code>';
}
}
$ret[]=$result;
}
return join("\n", $ret);
}
}
// Initialize the exception handler
ZabbixPHPExceptionHandler::init();
?>

View File

@@ -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