From 20c9a18d226c7f347b63ba9874c89feefaea32ba Mon Sep 17 00:00:00 2001 From: Holger Sielaff Date: Mon, 29 Sep 2025 09:37:58 +0200 Subject: [PATCH] ... --- src/DEBIAN/control | 15 +++ src/DEBIAN/postinst | 34 +++++ src/DEBIAN/prerm | 15 +++ src/etc/logrotate.d/zabbix-php | 16 +++ src/etc/php/8.3/fpm/conf.d/99-zabbix-php.conf | 11 ++ src/etc/sudoers.d/zabbix-php-user | 18 +++ src/usr/lib/zabbix-php.php | 117 ++++++++++++++++++ .../zabbix_template_php_exceptions.xml | 112 +++++++++++++++++ zabbix_template_php_exceptions.yaml | 68 ++++++++++ 9 files changed, 406 insertions(+) create mode 100644 src/DEBIAN/control create mode 100755 src/DEBIAN/postinst create mode 100755 src/DEBIAN/prerm create mode 100644 src/etc/logrotate.d/zabbix-php create mode 100644 src/etc/php/8.3/fpm/conf.d/99-zabbix-php.conf create mode 100644 src/etc/sudoers.d/zabbix-php-user create mode 100644 src/usr/lib/zabbix-php.php create mode 100644 src/usr/share/doc/zabbix-php-monitoring/zabbix_template_php_exceptions.xml create mode 100644 zabbix_template_php_exceptions.yaml diff --git a/src/DEBIAN/control b/src/DEBIAN/control new file mode 100644 index 0000000..7e22a8e --- /dev/null +++ b/src/DEBIAN/control @@ -0,0 +1,15 @@ +Package: zabbix-php-monitoring +Version: 1.0.0 +Section: admin +Priority: optional +Architecture: all +Depends: php8.3-fpm, zabbix-agent, logrotate +Maintainer: System Administrator +Description: PHP Exception Monitoring for Zabbix + This package provides comprehensive PHP exception monitoring for Zabbix. + It includes: + - Automatic exception logging via auto_prepend_file + - Zabbix template for monitoring and alerting + - Log rotation configuration + - User permissions for log access and service management + - Deduplication of identical exceptions based on unique identifiers \ No newline at end of file diff --git a/src/DEBIAN/postinst b/src/DEBIAN/postinst new file mode 100755 index 0000000..c16693c --- /dev/null +++ b/src/DEBIAN/postinst @@ -0,0 +1,34 @@ +#!/bin/bash +set -e + +# Post-installation script for zabbix-php-monitoring + +# Create log directory with proper permissions +mkdir -p /var/log/zabbix +chown www-data:zabbix /var/log/zabbix +chmod 755 /var/log/zabbix + +# Create log file with proper permissions if it doesn't exist +if [ ! -f /var/log/zabbix/php.log ]; then + touch /var/log/zabbix/php.log + chown www-data:zabbix /var/log/zabbix/php.log + chmod 644 /var/log/zabbix/php.log +fi + +# Set permissions on sudoers file +chmod 440 /etc/sudoers.d/zabbix-php-user + +# Restart services to apply configuration +systemctl reload php8.3-fpm.service || true +systemctl restart zabbix-agent.service || true + +echo "Zabbix PHP monitoring has been installed successfully." +echo "Log file: /var/log/zabbix/php.log" +echo "Template file: /usr/share/doc/zabbix-php-monitoring/zabbix_template_php_exceptions.xml" +echo "" +echo "To complete setup:" +echo "1. Import the template into your Zabbix server" +echo "2. Assign the template to your hosts" +echo "3. Configure zabbix_agentd.conf with appropriate log monitoring" + +exit 0 \ No newline at end of file diff --git a/src/DEBIAN/prerm b/src/DEBIAN/prerm new file mode 100755 index 0000000..44eaba7 --- /dev/null +++ b/src/DEBIAN/prerm @@ -0,0 +1,15 @@ +#!/bin/bash +set -e + +# Pre-removal script for zabbix-php-monitoring + +echo "Removing zabbix-php-monitoring..." + +# Restart PHP-FPM to clear the auto_prepend_file configuration +systemctl reload php8.3-fpm.service || true + +# Note: We don't remove the log files as they may contain important data +echo "Log files in /var/log/zabbix/ have been preserved" +echo "Configuration files will be removed" + +exit 0 \ No newline at end of file diff --git a/src/etc/logrotate.d/zabbix-php b/src/etc/logrotate.d/zabbix-php new file mode 100644 index 0000000..f4ae19a --- /dev/null +++ b/src/etc/logrotate.d/zabbix-php @@ -0,0 +1,16 @@ +/var/log/zabbix/php.log { + daily + missingok + rotate 30 + compress + delaycompress + notifempty + create 0644 www-data zabbix + copytruncate + postrotate + # Signal php-fpm to reopen log files (optional) + /bin/systemctl reload php8.3-fpm.service > /dev/null 2>&1 || true + # Signal zabbix-agent to reopen log files + /bin/systemctl reload zabbix-agent.service > /dev/null 2>&1 || true + endscript +} \ No newline at end of file diff --git a/src/etc/php/8.3/fpm/conf.d/99-zabbix-php.conf b/src/etc/php/8.3/fpm/conf.d/99-zabbix-php.conf new file mode 100644 index 0000000..47e5605 --- /dev/null +++ b/src/etc/php/8.3/fpm/conf.d/99-zabbix-php.conf @@ -0,0 +1,11 @@ +; Zabbix PHP Exception Monitoring Configuration +; This configuration enables automatic exception logging for Zabbix monitoring + +; Auto-prepend the Zabbix exception handler to all PHP files +auto_prepend_file = /usr/lib/zabbix-php.php + +; Ensure error reporting is enabled +log_errors = On + +; Optional: Set custom error log for debugging (in addition to Zabbix logging) +; error_log = /var/log/php/error.log \ No newline at end of file diff --git a/src/etc/sudoers.d/zabbix-php-user b/src/etc/sudoers.d/zabbix-php-user new file mode 100644 index 0000000..5e1f124 --- /dev/null +++ b/src/etc/sudoers.d/zabbix-php-user @@ -0,0 +1,18 @@ +# Sudoers configuration for zabbix-php monitoring +# Allows user to manage zabbix-agent and monitor logs + +# Allow user to restart zabbix-agent without password +user ALL=(root) NOPASSWD: /bin/systemctl restart zabbix-agent.service +user ALL=(root) NOPASSWD: /bin/systemctl reload zabbix-agent.service +user ALL=(root) NOPASSWD: /bin/systemctl status zabbix-agent.service +user ALL=(root) NOPASSWD: /bin/systemctl stop zabbix-agent.service +user ALL=(root) NOPASSWD: /bin/systemctl start zabbix-agent.service + +# Allow user to view zabbix-agent logs via journalctl +user ALL=(root) NOPASSWD: /bin/journalctl -u zabbix-agent.service +user ALL=(root) NOPASSWD: /bin/journalctl -u zabbix-agent.service -f +user ALL=(root) NOPASSWD: /bin/journalctl -u zabbix-agent.service -n * + +# Allow user to tail PHP exception log +user ALL=(root) NOPASSWD: /usr/bin/tail -f /var/log/zabbix/php.log +user ALL=(root) NOPASSWD: /usr/bin/tail -n * /var/log/zabbix/php.log \ No newline at end of file diff --git a/src/usr/lib/zabbix-php.php b/src/usr/lib/zabbix-php.php new file mode 100644 index 0000000..f36c5f7 --- /dev/null +++ b/src/usr/lib/zabbix-php.php @@ -0,0 +1,117 @@ +getMessage() . "\n"; + } + exit(1); + } + } + + public static function handleFatalError() { + $error = error_get_last(); + if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) { + $exception = new ErrorException( + $error['message'], + 0, + $error['type'], + $error['file'], + $error['line'] + ); + self::logException($exception); + } + } + + private static function logException($exception) { + try { + // Ensure log directory exists + $logDir = dirname(self::$logFile); + if (!is_dir($logDir)) { + mkdir($logDir, 0755, true); + // Set proper ownership for zabbix log directory + if (function_exists('chown')) { + @chown($logDir, 'zabbix'); + @chgrp($logDir, 'zabbix'); + } + } + + // Get HTTP_HOST or set default + $httpHost = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : + (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'cli'); + + // Create unique identifier for deduplication + $uniqueId = md5($httpHost . '|' . $exception->getFile() . '|' . $exception->getLine() . '|' . $exception->getMessage()); + + // Prepare log entry + $logEntry = [ + 'timestamp' => date('Y-m-d H:i:s'), + 'host' => $httpHost, + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), + 'message' => $exception->getMessage(), + 'stacktrace' => $exception->getTraceAsString(), + 'unique_id' => $uniqueId, + 'request_uri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '', + 'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '', + 'remote_addr' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '', + 'exception_class' => get_class($exception) + ]; + + // Convert to JSON and write to log + $jsonEntry = json_encode($logEntry, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "\n"; + + // Write to log file with proper locking + file_put_contents(self::$logFile, $jsonEntry, FILE_APPEND | LOCK_EX); + + // Set proper permissions on log file + if (file_exists(self::$logFile)) { + @chmod(self::$logFile, 0644); + @chown(self::$logFile, 'www-data'); + @chgrp(self::$logFile, 'zabbix'); + } + + } catch (Exception $e) { + // Silently fail to avoid infinite loops + error_log("Zabbix PHP Exception Handler failed: " . $e->getMessage()); + } + } +} + +// Initialize the exception handler +ZabbixPHPExceptionHandler::init(); +?> \ No newline at end of file diff --git a/src/usr/share/doc/zabbix-php-monitoring/zabbix_template_php_exceptions.xml b/src/usr/share/doc/zabbix-php-monitoring/zabbix_template_php_exceptions.xml new file mode 100644 index 0000000..15dd1c8 --- /dev/null +++ b/src/usr/share/doc/zabbix-php-monitoring/zabbix_template_php_exceptions.xml @@ -0,0 +1,112 @@ + + + 6.4 + 2025-09-29T00:00:00Z + + + 7df96b18c230490a9a0a9e2307226338 + Templates/Applications + + + + + + + + adf96b18c230490a9a0a9e2307226341 + PHP Exception Severity + + + 1 + Fatal Error + + + 2 + Warning + + + 3 + Notice + + + + + \ No newline at end of file diff --git a/zabbix_template_php_exceptions.yaml b/zabbix_template_php_exceptions.yaml new file mode 100644 index 0000000..6348586 --- /dev/null +++ b/zabbix_template_php_exceptions.yaml @@ -0,0 +1,68 @@ +zabbix_export: + version: '6.4' + template_groups: + - uuid: 7df96b18c230490a9a0a9e2307226338 + name: 'Templates/Applications' + templates: + - uuid: c770cff7c4bf49e6b2b0e7a5f3d2c4a1 + template: 'PHP Exceptions Monitoring' + name: 'PHP Exceptions Monitoring' + description: 'Template for monitoring PHP exceptions with deduplication' + groups: + - name: 'Templates/Applications' + macros: + - macro: '{$PHP.LOG.FILE}' + value: '/var/log/zabbix/php.exceptions.log' + description: 'Path to PHP exception log file' + items: + - uuid: 8df96b18c230490a9a0a9e2307226339 + name: 'PHP Exception Monitor' + type: ZABBIX_PASSIVE + key: 'logrt[/var/log/zabbix/php.exceptions.log]' + delay: 30s + history: 7d + trends: '0' + value_type: LOG + description: 'Monitor PHP exceptions from log file' + triggers: + - uuid: 9df96b18c230490a9a0a9e2307226340 + expression: 'logeventid(/PHP Exceptions Monitoring/logrt[/var/log/zabbix/php.exceptions.log])' + name: 'PHP Exception occurred' + event_name: 'PHP Exception: {ITEM.LASTVALUE}' + priority: WARNING + description: | + A PHP exception has occurred. + Details: {ITEM.LASTVALUE} + manual_close: 'YES' + tags: + - tag: scope + value: application + - tag: component + value: php + preprocessing: + - type: REGEX + parameters: + - '^(.*)$' + - '\1' + - type: JAVASCRIPT + parameters: + - | + // Parse JSON log entry and format for display + try { + var entry = JSON.parse(value); + var formattedStack = entry.stacktrace.split('\n').map(function(line, index) { + return (index + 1) + ': ' + line.trim(); + }).join('\n'); + + return JSON.stringify({ + host: entry.host, + file: entry.file, + line: entry.line, + message: entry.message, + stacktrace: formattedStack, + timestamp: entry.timestamp, + unique_id: entry.unique_id + }); + } catch (e) { + return value; + }