diff --git a/.github/ISSUE_TEMPLATE/agents/code-quality.agent.md b/.github/ISSUE_TEMPLATE/agents/code-quality.agent.md new file mode 100644 index 0000000..0c93293 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/agents/code-quality.agent.md @@ -0,0 +1,39 @@ +--- +description: "This Custom agent acts as a quality assurance specialist, focusing on code quality, best practices, and maintainability." +name: "Code Quality Specialist" +tools: ["search/codebase", "edit/editFiles", "web/githubRepo", "vscode/extensions", "execute/getTerminalOutput", "web"] +model: "Claude Sonnet 4.5" +--- + +# Code Quality Specialist +You are a Code Quality Specialist agent. Your role is to ensure that the codebase adheres to high standards of quality, best practices, and maintainability. You have access to various tools to help you perform your tasks effectively . + +The technology stack you will work with is a lamp stack (Linux, Apache, MySQL, PHP) along with JavaScript for frontend development. + + +## Capabilities +- **Code Review:** Analyze code for adherence to coding standards, best practices, and design patterns. +- **Refactoring:** Suggest and implement code refactoring to improve readability, maintainability, and performance. +- **Testing:** Ensure that code is well-tested, with appropriate unit tests, integration tests, and end-to-end tests. +- **Documentation:** Verify that code is well-documented, with clear comments and comprehensive documentation. +- **Performance Optimization:** Identify and address performance bottlenecks in the codebase. +- **Security Best Practices:** Ensure that code follows security best practices to prevent vulnerabilities. +- **Continuous Integration/Continuous Deployment (CI/CD):** Review and improve CI/CD pipelines to ensure smooth and reliable deployments. +- **Code Metrics:** Utilize code metrics to assess code quality and identify areas for improvement. + +## Tools +You have access to the following tools to assist you in your tasks: +- **search/codebase:** Search through the codebase for relevant information or code snippets. +- **edit/editFiles:** Edit code files to implement improvements or fixes. +- **githubRepo:** Interact with the GitHub repository to manage issues, pull requests, and code reviews. +- **extensions:** Utilize extensions that can enhance your capabilities in code quality assurance. +- **web:** Access the web for additional resources, documentation, or best practices. + + +## Instructions +When assisting with tasks, follow these guidelines: +1. **Understand the Request:** Clearly understand the user's request or issue before proceeding. +2. **Gather Information:** Use the available tools to gather necessary information about the codebase, coding standards, and existing issues. +3. **Provide Solutions:** Offer clear and actionable solutions or recommendations based on best practices and your expertise. +4. **Communicate Clearly:** Ensure that your explanations are clear and easy to understand, especially for users who may not be code quality experts. +5. **Follow Up:** If necessary, follow up on previous tasks to ensure that code quality issues have been resolved or improvements have been successfully implemented. diff --git a/.github/ISSUE_TEMPLATE/agents/mysql-mariadb.agent.md b/.github/ISSUE_TEMPLATE/agents/mysql-mariadb.agent.md new file mode 100644 index 0000000..4ab939b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/agents/mysql-mariadb.agent.md @@ -0,0 +1,65 @@ +--- +description: "This custom agent assits with enhancements, troubleshooting, and management of MySQL and MariaDB databases." +name: "MySQL/ MariaDB Database Administrator" +tools: ["search/codebase", "edit/editFiles", "web/githubRepo", "vscode/extensions", "execute/getTerminalOutput", "web"] +model: "Claude Sonnet 4.5" +--- + +# MySQL/ MariaDB Database Administrator + +You are a MySQL and MariaDB Database Administrator agent. Your role is to assist with enhancements, troubleshooting, and management of MySQL and MariaDB databases. You have access to various tools to help you perform your tasks effectively. + +## Capabilities +- **Database Management:** Assist with database creation, configuration, optimization, and maintenance tasks. +- **Query Optimization:** Analyze and optimize SQL queries for better performance. +- **Troubleshooting:** Diagnose and resolve database-related issues, including connection problems, performance bottlenecks, and data integrity concerns. +- **Backup and Recovery:** Provide guidance on backup strategies and recovery procedures. +- **Security:** Advise on best practices for securing MySQL and MariaDB databases. +- **Version Upgrades:** Assist with planning and executing database version upgrades. +- **Monitoring:** Recommend tools and techniques for monitoring database performance and health. +- **Scripting:** Help with writing and optimizing scripts for database automation tasks. + +## Tools +You have access to the following tools to assist you in your tasks: +- **search/codebase:** Search through the codebase for relevant information or code snippets. +- **edit/editFiles:** Edit configuration files, scripts, or code as needed. +- **githubRepo:** Interact with the GitHub repository to manage issues, pull requests, and code reviews. +- **extensions:** Utilize extensions that can enhance your capabilities in managing databases. +- **web:** Access the web for additional resources, documentation, or troubleshooting guides. + +## Instructions +When assisting with tasks, follow these guidelines: +1. **Understand the Request:** Clearly understand the user's request or issue before proceeding. +2. **Gather Information:** Use the available tools to gather necessary information about the database environment, configurations, and any existing issues. +3. **Provide Solutions:** Offer clear and actionable solutions or recommendations based on best practices and your expertise. +4. **Communicate Clearly:** Ensure that your explanations are clear and easy to understand, especially for users who may not be database experts. +5. **Follow Up:** If necessary, follow up on previous tasks to ensure that issues have been resolved or enhancements have been successfully implemented. + + +## Sample design patternsHere are some common design patterns and best practices for MySQL and MariaDB database management: +- **Normalization:** Ensure that database schemas are normalized to reduce redundancy and improve data integrity. +- **Indexing:** Use appropriate indexing strategies to enhance query performance. +- **Connection Pooling:** Implement connection pooling to manage database connections efficiently and improve application performance + + + +## Built in Cacti DB functions are included from the cacti project. Here are some of the commonly used functions: +## you can find the included file in the cacti project here: +- [Cacti DB Functions](https://github.com/Cacti/cacti/blob/1.2.x/lib/database.php) +- `db_fetch_row($result)`: Fetches a single row from the result set as an associative array. +- `db_fetch_assoc($result)`: Fetches a single row from the result set as an associative array. +- `db_query($query)`: Executes a SQL query and returns the result set. +- `db_insert($table, $data)`: Inserts a new record into the specified table. +- `db_update($table, $data, $where)`: Updates records in the specified table based on the given conditions. +- `db_delete($table, $where)`: Deletes records from the specified table based on the given conditions. +- `db_escape_string($string)`: Escapes special characters in a string for use in a SQL query. +- `db_num_rows($result)`: Returns the number of rows in the result set. +- `db_last_insert_id()`: Retrieves the ID of the last inserted record. + + +##web documentation +For additional information and best practices, refer to the official MySQL and MariaDB documentation: +- [MySQL Documentation](https://dev.mysql.com/doc/) +- [MariaDB Documentation](https://mariadb.com/kb/en/documentation/) + +Use your capabilities and tools effectively to assist users with their MySQL and MariaDB database needs. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/agents/php-developer.agent.md b/.github/ISSUE_TEMPLATE/agents/php-developer.agent.md new file mode 100644 index 0000000..1992350 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/agents/php-developer.agent.md @@ -0,0 +1,41 @@ +--- +description: "This custom agent acts as a PHP developer, assisting with PHP code development, debugging, and optimization." +name: "PHP Developer" +tools: ["search/codebase", "edit/editFiles", "web/githubRepo", "vscode/extensions", "execute/getTerminalOutput", "web"] +model: "Claude Sonnet 4.5" +--- + +# PHP Developer +You are a PHP Developer agent. Your role is to assist with PHP code development, debugging, and optimization. You have access to various tools to help you perform your tasks effectively. +You are to focus on PHP PSR-12 coding standards and best practices supporting modern PHP versions (PHP 8.1 and above). +Your other roles include: +- **Code Review:** Analyze PHP code for adherence to coding standards, best practices, and design patterns. +- **Debugging:** Identify and resolve bugs or issues in PHP code. +- **Performance Optimization:** Suggest and implement optimizations to improve the performance of PHP applications. +- **Testing:** Ensure that PHP code is well-tested, with appropriate unit tests and integration tests. +- **Documentation:** Verify that PHP code is well-documented, with clear comments and comprehensive documentation. +- **Security Best Practices:** Ensure that PHP code follows security best practices to prevent vulnerabilities. + +## Tools +You have access to the following tools to assist you in your tasks: +- **search/codebase:** Search through the codebase for relevant information or code snippets. +- **edit/editFiles:** Edit PHP code files to implement improvements or fixes. +- **githubRepo:** Interact with the GitHub repository to manage issues, pull requests, and code reviews. +- **extensions:** Utilize extensions that can enhance your capabilities in PHP development. +- **web:** Access the web for additional resources, documentation, or best practices. + + + +## The project in this repo calls on functions from the cacti project. You can find the cacti documentation and main github repo here: +- [Cacti GitHub Repository](https://github.com/Cacti/cacti/tree/1.2.x) +- [Cacti Documentation](https://www.github.com/Cacti/documentation) + + + +## Instructions +When assisting with tasks, follow these guidelines: +1. **Understand the Request:** Clearly understand the user's request or issue before proceeding. +2. **Gather Information:** Use the available tools to gather necessary information about the PHP codebase, coding standards, and existing issues. +3. **Provide Solutions:** Offer clear and actionable solutions or recommendations based on best practices and your expertise. +4. **Communicate Clearly:** Ensure that your explanations are clear and easy to understand, especially for users who may not be PHP experts. +5. **Follow Up:** If necessary, follow up on previous tasks to ensure that PHP code issues have been resolved or improvements have been successfully implemented. diff --git a/.github/ISSUE_TEMPLATE/copilot-instructions.md b/.github/ISSUE_TEMPLATE/copilot-instructions.md index ced1322..d89f764 100644 --- a/.github/ISSUE_TEMPLATE/copilot-instructions.md +++ b/.github/ISSUE_TEMPLATE/copilot-instructions.md @@ -11,6 +11,7 @@ This is the **Syslog Plugin** for Cacti, a PHP-based network monitoring and grap - **Critical:** ALWAYS use the `syslog_db_*` wrapper functions (defined in `database.php`) for all database operations. NEVER use standard Cacti `db_*` functions directly for syslog tables, as they will fail if a dedicated database is configured. - **Integration:** The plugin integrates with Cacti via hooks defined in `setup.php`. - **Poller Integration:** Background processes (`syslog_process.php`, `syslog_removal.php`) are triggered by Cacti's poller or run independently. +- **Syslog Reception:** Syslog messages are directly inserted into `syslog_incoming` table syslog_process.php then processes them. ## Critical Developer Workflows @@ -55,3 +56,8 @@ This is the **Syslog Plugin** for Cacti, a PHP-based network monitoring and grap - `config.php.dist`: Template for database configuration. - `functions.php`: Core logic and utility functions. - `syslog.php`: Main UI entry point. + + +**Documentation & Resources** +- [Cacti main repo](https://github.com/Cacti/cacti/tree/1.2.x) +- [cacti documentation](https://www.github.com/Cacti/documentation) \ No newline at end of file diff --git a/.gitignore b/.gitignore index ec5e85f..efca3d2 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,6 @@ # | http://www.cacti.net/ | # +-------------------------------------------------------------------------+ -.git* locales/po/*.mo config.php diff --git a/functions.php b/functions.php index 5cb78ca..c9e0a36 100644 --- a/functions.php +++ b/functions.php @@ -684,9 +684,11 @@ function syslog_row_color($priority, $message) { break; } - print ""; + print ""; + return $class; } + function sql_hosts_where($tab) { global $hostfilter, $hostfilter_log, $syslog_incoming_config; global $syslogdb_default; diff --git a/syslog.css b/syslog.css index e8a1867..12adcbe 100644 --- a/syslog.css +++ b/syslog.css @@ -93,3 +93,28 @@ p { text-align:left; font-size:12px; } + +/* Syslog message grouping styles */ +.syslog-group-toggle { + transition: transform 0.2s ease; + color: #337ab7; + display: inline-block; +} + +.syslog-group-toggle:hover { + color: #23527c; +} + +.syslog-group-toggle.fa-chevron-up { + transform: rotate(0deg); +} + +.syslog-group-toggle.fa-chevron-down { + transform: rotate(0deg); +} + + +.syslog-detail-row:hover { + /* Slightly highlight on hover but keep parent coloring visible */ + background: rgba(0,0,0,0.02) !important; +} diff --git a/syslog.php b/syslog.php index 1fec1c9..70103a7 100644 --- a/syslog.php +++ b/syslog.php @@ -199,7 +199,7 @@ function syslog_view_alarm() { global $syslogdb_default; print ""; - print ""; + print ""; print " + + + +
" . __('Syslog Alert View', 'syslog') . "
" . __('Syslog Alert View', 'syslog') . "
\n"; $html = syslog_db_fetch_cell('SELECT html FROM `' . $syslogdb_default . '`.`syslog_logs` WHERE seq=' . get_request_var('id')); @@ -662,6 +662,7 @@ function applyFilter() { strURL += '×pan=' + $('#timespan').val(); strURL += '&rfilter=' + base64_encode($('#rfilter').val()); strURL += '&rows=' + $('#rows').val(); + strURL += '&grouping=' + ($('#grouping').length ? $('#grouping').val() : '0'); loadPageNoHeader(strURL); } @@ -758,11 +759,17 @@ function syslog_request_validation($current_tab, $force = false) { 'pageset' => true, 'default' => read_user_setting('syslog_eprogram', '-1', $force), ), + 'grouping' => array( + 'filter' => FILTER_VALIDATE_INT, + 'pageset' => true, + 'default' => read_user_setting('syslog_grouping', '0', $force), + ), 'rfilter' => array( 'filter' => FILTER_VALIDATE_IS_REGEX, 'pageset' => true, 'default' => '' ), + 'date1' => array( 'filter' => FILTER_CALLBACK, 'pageset' => true, @@ -996,35 +1003,128 @@ function get_syslog_messages(&$sql_where, $rows, $tab) { } if ($tab == 'syslog') { - if (get_request_var('removal') == '-1') { - $query_sql = "SELECT syslog.*, syslog_programs.program, 'main' AS mtype - FROM `" . $syslogdb_default . "`.`syslog` - LEFT JOIN `" . $syslogdb_default . "`.`syslog_programs` - ON syslog.program_id=syslog_programs.program_id " . - $sql_where . " - $sql_order - $sql_limit"; - } elseif (get_request_var('removal') == '1') { - $query_sql = "(SELECT syslog.*, syslog_programs.program, 'main' AS mtype - FROM `" . $syslogdb_default . "`.`syslog` AS syslog - LEFT JOIN `" . $syslogdb_default . "`.`syslog_programs` - ON syslog.program_id=syslog_programs.program_id " . - $sql_where . " - ) UNION (SELECT syslog.*, syslog_programs.program, 'remove' AS mtype - FROM `" . $syslogdb_default . "`.`syslog_removed` AS syslog - LEFT JOIN `" . $syslogdb_default . "`.`syslog_programs` - ON syslog.program_id=syslog_programs.program_id " . - $sql_where . ") + // Check if grouping is enabled + $grouping_enabled = isset_request_var('grouping') && get_request_var('grouping') == '1'; + + if ($grouping_enabled) { + // When grouping, we need to get grouped results with counts + if (get_request_var('removal') == '-1') { + $query_sql = "SELECT + syslog.host_id, + syslog.message, + syslog.program_id, + syslog.facility_id, + syslog.priority_id, + syslog_programs.program, + 'main' AS mtype, + COUNT(*) AS occurrence_count, + MIN(syslog.logtime) AS first_logtime, + MAX(syslog.logtime) AS logtime, + MIN(syslog.seq) AS seq, + GROUP_CONCAT(syslog.seq ORDER BY syslog.logtime DESC SEPARATOR ',') AS seq_list + FROM `" . $syslogdb_default . "`.`syslog` + LEFT JOIN `" . $syslogdb_default . "`.`syslog_programs` + ON syslog.program_id=syslog_programs.program_id " . + $sql_where . " + GROUP BY syslog.host_id, syslog.message, syslog.program_id, syslog.facility_id, syslog.priority_id + $sql_order + $sql_limit"; + } elseif (get_request_var('removal') == '1') { + $query_sql = "SELECT * FROM ( + (SELECT + syslog.host_id, + syslog.message, + syslog.program_id, + syslog.facility_id, + syslog.priority_id, + syslog_programs.program, + 'main' AS mtype, + COUNT(*) AS occurrence_count, + MIN(syslog.logtime) AS first_logtime, + MAX(syslog.logtime) AS logtime, + MIN(syslog.seq) AS seq, + GROUP_CONCAT(syslog.seq ORDER BY syslog.logtime DESC SEPARATOR ',') AS seq_list + FROM `" . $syslogdb_default . "`.`syslog` AS syslog + LEFT JOIN `" . $syslogdb_default . "`.`syslog_programs` + ON syslog.program_id=syslog_programs.program_id " . + $sql_where . " + GROUP BY syslog.host_id, syslog.message, syslog.program_id, syslog.facility_id, syslog.priority_id + ) UNION (SELECT + syslog.host_id, + syslog.message, + syslog.program_id, + syslog.facility_id, + syslog.priority_id, + syslog_programs.program, + 'remove' AS mtype, + COUNT(*) AS occurrence_count, + MIN(syslog.logtime) AS first_logtime, + MAX(syslog.logtime) AS logtime, + MIN(syslog.seq) AS seq, + GROUP_CONCAT(syslog.seq ORDER BY syslog.logtime DESC SEPARATOR ',') AS seq_list + FROM `" . $syslogdb_default . "`.`syslog_removed` AS syslog + LEFT JOIN `" . $syslogdb_default . "`.`syslog_programs` + ON syslog.program_id=syslog_programs.program_id " . + $sql_where . " + GROUP BY syslog.host_id, syslog.message, syslog.program_id, syslog.facility_id, syslog.priority_id + ) + ) AS grouped_results $sql_order $sql_limit"; + } else { + $query_sql = "SELECT + syslog.host_id, + syslog.message, + syslog.program_id, + syslog.facility_id, + syslog.priority_id, + syslog_programs.program, + 'remove' AS mtype, + COUNT(*) AS occurrence_count, + MIN(syslog.logtime) AS first_logtime, + MAX(syslog.logtime) AS logtime, + MIN(syslog.seq) AS seq, + GROUP_CONCAT(syslog.seq ORDER BY syslog.logtime DESC SEPARATOR ',') AS seq_list + FROM `" . $syslogdb_default . "`.`syslog_removed` AS syslog + LEFT JOIN `" . $syslogdb_default . "`.`syslog_programs` AS syslog_programs + ON syslog.program_id=syslog_programs.program_id " . + $sql_where . " + GROUP BY syslog.host_id, syslog.message, syslog.program_id, syslog.facility_id, syslog.priority_id + $sql_order + $sql_limit"; + } } else { - $query_sql = "SELECT syslog.*, syslog_programs.program, 'remove' AS mtype - FROM `" . $syslogdb_default . "`.`syslog_removed` AS syslog - LEFT JOIN `" . $syslogdb_default . "`.`syslog_programs` AS syslog_programs - ON syslog.program_id=syslog_programs.program_id " . - $sql_where . " - $sql_order - $sql_limit"; + // Original non-grouped queries + if (get_request_var('removal') == '-1') { + $query_sql = "SELECT syslog.*, syslog_programs.program, 'main' AS mtype + FROM `" . $syslogdb_default . "`.`syslog` + LEFT JOIN `" . $syslogdb_default . "`.`syslog_programs` + ON syslog.program_id=syslog_programs.program_id " . + $sql_where . " + $sql_order + $sql_limit"; + } elseif (get_request_var('removal') == '1') { + $query_sql = "(SELECT syslog.*, syslog_programs.program, 'main' AS mtype + FROM `" . $syslogdb_default . "`.`syslog` AS syslog + LEFT JOIN `" . $syslogdb_default . "`.`syslog_programs` + ON syslog.program_id=syslog_programs.program_id " . + $sql_where . " + ) UNION (SELECT syslog.*, syslog_programs.program, 'remove' AS mtype + FROM `" . $syslogdb_default . "`.`syslog_removed` AS syslog + LEFT JOIN `" . $syslogdb_default . "`.`syslog_programs` + ON syslog.program_id=syslog_programs.program_id " . + $sql_where . ") + $sql_order + $sql_limit"; + } else { + $query_sql = "SELECT syslog.*, syslog_programs.program, 'remove' AS mtype + FROM `" . $syslogdb_default . "`.`syslog_removed` AS syslog + LEFT JOIN `" . $syslogdb_default . "`.`syslog_programs` AS syslog_programs + ON syslog.program_id=syslog_programs.program_id " . + $sql_where . " + $sql_order + $sql_limit"; + } } } else { $query_sql = "SELECT syslog.*, sf.facility, sp.priority, spr.program, sa.name, sa.severity @@ -1251,6 +1351,7 @@ function applyFilter() { strURL += '&trimval='+$('#trimval').val(); strURL += '&removal='+$('#removal').val(); strURL += '&refresh='+$('#refresh').val(); + strURL += '&grouping=' + ($('#grouping').length ? $('#grouping').val() : '0'); loadPageNoHeader(strURL); } @@ -1638,6 +1739,19 @@ function timeshiftFilterRight() { '> + + + + + +
@@ -1746,25 +1860,53 @@ function syslog_messages($tab = 'syslog') { syslog_filter($sql_where, $tab); if ($tab == 'syslog') { - if (get_request_var('removal') == 1) { - $total_rows = syslog_db_fetch_cell("SELECT SUM(totals) - FROM ( - SELECT count(*) AS totals + // Check if grouping is enabled for row count + $grouping_enabled = isset_request_var('grouping') && get_request_var('grouping') == '1'; + + if ($grouping_enabled) { + // When grouping, count distinct groups instead of individual rows + if (get_request_var('removal') == 1) { + $total_rows = syslog_db_fetch_cell("SELECT SUM(totals) + FROM ( + SELECT COUNT(DISTINCT CONCAT(host_id, '|', message, '|', program_id, '|', facility_id, '|', priority_id)) AS totals + FROM `" . $syslogdb_default . "`.`syslog` AS syslog + $sql_where + UNION + SELECT COUNT(DISTINCT CONCAT(host_id, '|', message, '|', program_id, '|', facility_id, '|', priority_id)) AS totals + FROM `" . $syslogdb_default . "`.`syslog_removed` AS syslog + $sql_where + ) AS rowcount"); + } elseif (get_request_var('removal') == -1) { + $total_rows = syslog_db_fetch_cell("SELECT COUNT(DISTINCT CONCAT(host_id, '|', message, '|', program_id, '|', facility_id, '|', priority_id)) FROM `" . $syslogdb_default . "`.`syslog` AS syslog - $sql_where - UNION - SELECT count(*) AS totals + $sql_where"); + } else { + $total_rows = syslog_db_fetch_cell("SELECT COUNT(DISTINCT CONCAT(host_id, '|', message, '|', program_id, '|', facility_id, '|', priority_id)) FROM `" . $syslogdb_default . "`.`syslog_removed` AS syslog - $sql_where - ) AS rowcount"); - } elseif (get_request_var('removal') == -1) { - $total_rows = syslog_db_fetch_cell("SELECT count(*) - FROM `" . $syslogdb_default . "`.`syslog` AS syslog - $sql_where"); + $sql_where"); + } } else { - $total_rows = syslog_db_fetch_cell("SELECT count(*) - FROM `" . $syslogdb_default . "`.`syslog_removed` AS syslog - $sql_where"); + // Original non-grouped row counting + if (get_request_var('removal') == 1) { + $total_rows = syslog_db_fetch_cell("SELECT SUM(totals) + FROM ( + SELECT count(*) AS totals + FROM `" . $syslogdb_default . "`.`syslog` AS syslog + $sql_where + UNION + SELECT count(*) AS totals + FROM `" . $syslogdb_default . "`.`syslog_removed` AS syslog + $sql_where + ) AS rowcount"); + } elseif (get_request_var('removal') == -1) { + $total_rows = syslog_db_fetch_cell("SELECT count(*) + FROM `" . $syslogdb_default . "`.`syslog` AS syslog + $sql_where"); + } else { + $total_rows = syslog_db_fetch_cell("SELECT count(*) + FROM `" . $syslogdb_default . "`.`syslog_removed` AS syslog + $sql_where"); + } } } else { $total_rows = syslog_db_fetch_cell("SELECT count(*) @@ -1781,6 +1923,9 @@ function syslog_messages($tab = 'syslog') { } if ($tab == 'syslog') { + // Check if grouping is enabled for display + $grouping_enabled = isset_request_var('grouping') && get_request_var('grouping') == '1'; + if (api_plugin_user_realm_auth('syslog_alerts.php')) { $display_text = array( 'nosortt' => array(__('Actions', 'syslog'), 'ASC'), @@ -1790,6 +1935,11 @@ function syslog_messages($tab = 'syslog') { 'message' => array(__('Message', 'syslog'), 'ASC'), 'facility_id' => array(__('Facility', 'syslog'), 'ASC'), 'priority_id' => array(__('Priority', 'syslog'), 'ASC')); + + // Add count column if grouping is enabled + if ($grouping_enabled) { + $display_text['occurrence_count'] = array(__('Count', 'syslog'), 'DESC'); + } } else { $display_text = array( 'logtime' => array(__('Date', 'syslog'), 'ASC'), @@ -1798,6 +1948,11 @@ function syslog_messages($tab = 'syslog') { 'message' => array(__('Message', 'syslog'), 'ASC'), 'facility_id' => array(__('Facility', 'syslog'), 'ASC'), 'priority_id' => array(__('Priority', 'syslog'), 'ASC')); + + // Add count column if grouping is enabled + if ($grouping_enabled) { + $display_text['occurrence_count'] = array(__('Count', 'syslog'), 'DESC'); + } } $nav = html_nav_bar("syslog.php?tab=$tab", MAX_DISPLAY_PAGES, get_request_var_request('page'), $rows, $total_rows, cacti_sizeof($display_text), __('Messages', 'syslog'), 'page', 'main'); @@ -1847,14 +2002,68 @@ function syslog_messages($tab = 'syslog') { form_selectable_cell($url, $sm['seq'], '', 'left'); } - form_selectable_cell($sm['logtime'], $sm['seq'], '', 'left'); + // Display grouped or individual messages + if ($grouping_enabled && isset($sm['occurrence_count']) && $sm['occurrence_count'] > 1) { + // Grouped message display with expand/collapse + $expand_icon = ""; + form_selectable_cell($expand_icon . $sm['logtime'], $sm['seq'], '', 'left'); + } else { + form_selectable_cell($sm['logtime'], $sm['seq'], '', 'left'); + } + form_selectable_cell(isset($hosts[$sm['host_id']]) ? $hosts[$sm['host_id']]:__('Unknown', 'syslog'), $sm['seq'], '', 'left'); form_selectable_cell($sm['program'], $sm['seq'], '', 'left'); form_selectable_cell(filter_value(title_trim($sm[$syslog_incoming_config['textField']], get_request_var_request('trimval')), get_request_var('rfilter')), $sm['seq'], '', 'left syslogMessage'); form_selectable_cell(isset($facilities[$sm['facility_id']]) ? $facilities[$sm['facility_id']]:__('Unknown', 'syslog'), $sm['seq'], '', 'left'); form_selectable_cell(isset($priorities[$sm['priority_id']]) ? $priorities[$sm['priority_id']]:__('Unknown', 'syslog'), $sm['seq'], '', 'left'); + // Add occurrence count if grouping is enabled + if ($grouping_enabled) { + form_selectable_cell(isset($sm['occurrence_count']) ? $sm['occurrence_count'] : 1, $sm['seq'], '', 'right'); + } + form_end_row(); + + // If grouping is enabled and there are multiple occurrences, add hidden detail rows + if ($grouping_enabled && isset($sm['occurrence_count']) && $sm['occurrence_count'] > 1 && isset($sm['seq_list'])) { + $seq_array = explode(',', $sm['seq_list']); + + // Get individual messages for this group + $detail_messages = syslog_db_fetch_assoc("SELECT syslog.*, syslog_programs.program + FROM `" . $syslogdb_default . "`.`" . (($sm['mtype'] == 'main') ? 'syslog' : 'syslog_removed') . "` AS syslog + LEFT JOIN `" . $syslogdb_default . "`.`syslog_programs` + ON syslog.program_id=syslog_programs.program_id + WHERE syslog.seq IN (" . implode(',', array_map('intval', $seq_array)) . ") + ORDER BY syslog.logtime DESC"); + + if (cacti_sizeof($detail_messages)) { + foreach ($detail_messages as $dm) { + $severity_class = syslog_row_color($dm['priority_id'], $dm['message']); + print ""; + if (api_plugin_user_realm_auth('syslog_alerts.php')) { + $url = ''; + if ($sm['mtype'] == 'main') { + $url .= ""; + $url .= ""; + } + print "" . $url . ""; + } + + print "" . $dm['logtime'] . ""; + print "" . (isset($hosts[$dm['host_id']]) ? $hosts[$dm['host_id']] : __('Unknown', 'syslog')) . ""; + print "" . $dm['program'] . ""; + print "" . filter_value(title_trim($dm[$syslog_incoming_config['textField']], get_request_var_request('trimval')), get_request_var('rfilter')) . ""; + print "" . (isset($facilities[$dm['facility_id']]) ? $facilities[$dm['facility_id']] : __('Unknown', 'syslog')) . ""; + print "" . (isset($priorities[$dm['priority_id']]) ? $priorities[$dm['priority_id']] : __('Unknown', 'syslog')) . ""; + + if ($grouping_enabled) { + print ""; + } + + print ""; + } + } + } } } else { print "" . __('No Syslog Messages', 'syslog') . ""; @@ -1888,6 +2097,26 @@ function syslog_messages($tab = 'syslog') { }).on('click', function() { $(this).tooltip('close'); }); + + // Handle syslog group expand/collapse + $('.syslog-group-toggle').off('click').on('click', function(e) { + e.preventDefault(); + e.stopPropagation(); + + var seq = $(this).data('seq'); + var detailRows = $('.syslog-detail-' + seq); + var icon = $(this); + + if (detailRows.is(':visible')) { + // Collapse + detailRows.hide(); + icon.removeClass('fa-chevron-up').addClass('fa-chevron-down'); + } else { + // Expand + detailRows.show(); + icon.removeClass('fa-chevron-down').addClass('fa-chevron-up'); + } + }); });