diff --git a/.github/ISSUE_TEMPLATE/agents/mysql-mariadb.agent.md b/.github/ISSUE_TEMPLATE/agents/mysql-mariadb.agent.md
index 4ab939b..8f4843b 100644
--- a/.github/ISSUE_TEMPLATE/agents/mysql-mariadb.agent.md
+++ b/.github/ISSUE_TEMPLATE/agents/mysql-mariadb.agent.md
@@ -62,4 +62,4 @@ For additional information and best practices, refer to the official MySQL and M
- [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
+Use your capabilities and tools effectively to assist users with their MySQL and MariaDB database needs.
diff --git a/.github/ISSUE_TEMPLATE/copilot-instructions.md b/.github/ISSUE_TEMPLATE/copilot-instructions.md
index d89f764..bc5cbe4 100644
--- a/.github/ISSUE_TEMPLATE/copilot-instructions.md
+++ b/.github/ISSUE_TEMPLATE/copilot-instructions.md
@@ -60,4 +60,4 @@ This is the **Syslog Plugin** for Cacti, a PHP-based network monitoring and grap
**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
+- [cacti documentation](https://www.github.com/Cacti/documentation)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9bd2ae9..fc9d494 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,9 @@
* issue: Don't use MyISAM for non-analytical tables
* issue: The install advisor for Syslog was broken in current Cacti releases
* feature: Allow the use of Aria Storage Engine for MariaDB databases
+* feature: Add message grouping functionality to collapse duplicate syslog messages and display occurrence counts
+* feature: Refactor JavaScript by consolidating inline code from PHP files into centralized js/functions.js
+* feature: Enhanced hostname validation to resolve against Cacti host table when DNS lookup fails, replacing hostname with Cacti host description
--- 4.2 ---
diff --git a/README.md b/README.md
index 6e95b69..0a7775d 100644
--- a/README.md
+++ b/README.md
@@ -176,6 +176,11 @@ configuration. For example:
sql_mode=NO_ENGINE_SUBSTITUTION,NO_AUTO_CREATE_USER
```
+### Special Note for the Grouping feature
+Syslog now supports the ability to group similar messeges from a specific host together
+for easy summarization for larger systems you may need to expand the default mariadb group_concat_max_len
+The default is 1MB you may need to raise it much higher depending on your needs
+
After this change, you should log into the mysql server and run the following
command:
diff --git a/functions.php b/functions.php
index 5cb78ca..6eacafa 100644
--- a/functions.php
+++ b/functions.php
@@ -22,6 +22,13 @@
+-------------------------------------------------------------------------+
*/
+function syslog_include_js() {
+ global $config;
+ ?>
+
+ ";
+ print "
";
+ return $class;
}
function sql_hosts_where($tab) {
diff --git a/js/functions.js b/js/functions.js
new file mode 100644
index 0000000..a96d47d
--- /dev/null
+++ b/js/functions.js
@@ -0,0 +1,652 @@
+/**
+ * Syslog Plugin - JavaScript Functions
+ * Consolidated functions from inline JavaScript in PHP files
+ */
+
+/* ========================================================================
+ * Statistics View Functions (syslog.php - stats tab)
+ * ======================================================================== */
+
+/**
+ * Clear filter for statistics view
+ */
+function clearFilterStats() {
+ strURL = 'syslog.php?tab=stats&clear=1&header=false';
+ loadPageNoHeader(strURL);
+}
+
+/**
+ * Apply filter for statistics view
+ */
+function applyFilterStats() {
+ var strURL = 'syslog.php?header=false';
+ strURL += '&none=true';
+ strURL += '&facility=' + $('#facility').val();
+ strURL += '&host=' + $('#host').val();
+ strURL += '&priority=' + $('#priority').val();
+ strURL += '&program=' + $('#eprogram').val();
+ strURL += '×pan=' + $('#timespan').val();
+ strURL += '&rfilter=' + base64_encode($('#rfilter').val());
+ strURL += '&rows=' + $('#rows').val();
+ strURL += '&grouping=' + ($('#grouping').length ? $('#grouping').val() : '0');
+ loadPageNoHeader(strURL);
+}
+
+/**
+ * Initialize statistics view
+ */
+function initSyslogStats() {
+ $(function() {
+ $('#go').click(function() {
+ applyFilterStats();
+ });
+
+ $('#clear').click(function() {
+ clearFilterStats();
+ });
+
+ $('#host').selectmenu({
+ open: function() {
+ $('div.ui-selectmenu-menu li.ui-menu-item').each(function(idx){
+ $(this).addClass( $('#host option').eq(idx).attr('class') )
+ })
+ }
+ });
+ });
+}
+
+/* ========================================================================
+ * Main Syslog View Functions (syslog.php - syslog/alerts tabs)
+ * ======================================================================== */
+
+/**
+ * Apply timespan filter
+ */
+function applyTimespan() {
+ var strURL = urlPath+'plugins/syslog/syslog.php?header=false';
+ strURL += '&predefined_timespan=' + $('#predefined_timespan').val();
+ loadPageNoHeader(strURL);
+}
+
+/**
+ * Apply main syslog filter
+ */
+function applyFilter() {
+ var strURL = 'syslog.php?tab='+(window.pageTab || '');
+ strURL += '&header=false';
+ strURL += '&date1='+$('#date1').val();
+ strURL += '&date2='+$('#date2').val();
+ strURL += '&host='+$('#host').val();
+ strURL += '&rfilter='+base64_encode($('#rfilter').val());
+ strURL += '&efacility='+$('#efacility').val();
+ strURL += '&epriority='+$('#epriority').val();
+ strURL += '&eprogram='+$('#eprogram').val();
+ strURL += '&rows='+$('#rows').val();
+ strURL += '&trimval='+$('#trimval').val();
+ strURL += '&removal='+$('#removal').val();
+ strURL += '&refresh='+$('#refresh').val();
+ strURL += '&grouping=' + ($('#grouping').length ? $('#grouping').val() : '0');
+ loadPageNoHeader(strURL);
+}
+
+/**
+ * Export records to CSV
+ */
+function exportRecords() {
+ document.location = 'syslog.php?export=true';
+ Pace.stop();
+}
+
+/**
+ * Clear main syslog filter
+ */
+function clearFilter() {
+ var strURL = 'syslog.php?tab=' + (window.pageTab || '');
+ strURL += '&header=false&clear=true';
+ loadPageNoHeader(strURL);
+}
+
+/**
+ * Save syslog filter settings
+ */
+function saveSettings() {
+ var strURL = 'syslog.php?action=save&tab=' + (window.pageTab || '');
+ var data = {};
+
+ data.trimval = $('#trimval').val();
+ data.rows = $('#rows').val();
+ data.removal = $('#removal').val();
+ data.refresh = $('#refresh').val();
+ data.efacility = $('#efacility').val();
+ data.epriority = $('#epriority').val();
+ data.eprogram = $('#eprogram').val();
+ data.__csrf_magic = csrfMagicToken;
+
+ if ($('#predefined_timespan').val() > 0) {
+ data.predefined_timespan = $('#predefined_timespan').val();
+ }
+
+ data.predefined_timeshift = $('#predefined_timeshift').val();
+
+ $.post(strURL, data).done(function() {
+ $('#text').show().text('Filter Settings Saved').fadeOut(2000);
+ });
+}
+
+/**
+ * Shift time filter left (backward)
+ */
+function timeshiftFilterLeft() {
+ var strURL = 'syslog.php?tab='+(window.pageTab || '')+'&header=false';
+ strURL += '&shift_left=true';
+ strURL += '&date1='+$('#date1').val();
+ strURL += '&date2='+$('#date2').val();
+ strURL += '&predefined_timeshift='+$('#predefined_timeshift').val();
+ loadPageNoHeader(strURL);
+}
+
+/**
+ * Shift time filter right (forward)
+ */
+function timeshiftFilterRight() {
+ var strURL = 'syslog.php?tab='+(window.pageTab || '')+'&header=false';
+ strURL += '&shift_right=true';
+ strURL += '&date1='+$('#date1').val();
+ strURL += '&date2='+$('#date2').val();
+ strURL += '&predefined_timeshift='+$('#predefined_timeshift').val();
+ loadPageNoHeader(strURL);
+}
+
+/**
+ * Initialize main syslog view
+ * @param {object} config - Configuration object containing: pageTab, placeHolder, noneSelectedText, devicesSelectedText, allDevicesText
+ */
+function initSyslogMain(config) {
+ var date1Open = false;
+ var date2Open = false;
+ var pageTab = config.pageTab || '';
+ var hostTerm = '';
+ var placeHolder = config.placeHolder || '';
+
+ // Make pageTab global for other functions
+ window.pageTab = pageTab;
+
+ $(function() {
+ $('#syslog_form').submit(function(event) {
+ event.preventDefault();
+ applyFilter();
+ });
+
+ $('#host').multiselect({
+ menuHeight: $(window).height()*.7,
+ menuWidth: '220',
+ linkInfo: faIcons,
+ noneSelectedText: config.noneSelectedText || '',
+ selectedText: function(numChecked, numTotal, checkedItems) {
+ var myReturn = numChecked + ' ' + config.devicesSelectedText;
+ $.each(checkedItems, function(index, value) {
+ if (value.value == '0') {
+ myReturn = config.allDevicesText;
+ return false;
+ }
+ });
+ return myReturn;
+ },
+ uncheckAll: function() {
+ $(this).multiselect('widget').find(':checkbox:first').each(function() {
+ $(this).prop('checked', true);
+ });
+ $('#test').trigger('keyup');
+ },
+ checkAll: function() {
+ $(this).multiselect('widget').find(':checkbox').not(':first').each(function() {
+ $(this).prop('checked', true);
+ });
+ $(this).multiselect('widget').find(':checkbox:first').each(function() {
+ $(this).prop('checked', false);
+ });
+ },
+ open: function(event, ui) {
+ if ($('#term').length == 0) {
+ var width = parseInt($(this).multiselect('widget').find('.ui-multiselect-header').width() - 5);
+ $(this).multiselect('widget').find('.ui-multiselect-header').after(' ');
+ $('#term').on('keyup', function() {
+ $.getJSON('syslog.php?action=ajax_hosts&term='+$('#term').val(), function(data) {
+ $('#host').find('option').not(':selected').each(function() {
+ if ($(this).attr('id') != 'host_all') {
+ $(this).remove();
+ }
+ });
+
+ $.each(data, function(index, hostData) {
+ if ($('#host option[value="'+index+'"]').length == 0) {
+ $('#host').append(''+hostData.host+' ');
+ }
+ });
+
+ $('#host').multiselect('refresh');
+ });
+ });
+ }
+
+ $('#term').focus();
+ },
+ click: function(event, ui) {
+ var checked = $(this).multiselect('widget').find('input:checked').length;
+
+ if (ui.value == '0') {
+ if (ui.checked == true) {
+ $('#host').multiselect('uncheckAll');
+ $(this).multiselect('widget').find(':checkbox:first').each(function() {
+ $(this).prop('checked', true);
+ });
+ }
+ } else if (checked == 0) {
+ $(this).multiselect('widget').find(':checkbox:first').each(function() {
+ $(this).click();
+ });
+ } else if ($(this).multiselect('widget').find('input:checked:first').val() == '0') {
+ if (checked > 0) {
+ $(this).multiselect('widget').find(':checkbox:first').each(function() {
+ $(this).click();
+ $(this).prop('disable', true);
+ });
+ }
+ }
+ }
+ });
+
+ $('#save').click(function() {
+ saveSettings();
+ });
+
+ $('#go').click(function() {
+ applyFilter();
+ });
+
+ $('#clear').click(function() {
+ clearFilter();
+ });
+
+ $('#export').click(function() {
+ exportRecords();
+ });
+
+ $('#balerts').click(function() {
+ loadTopTab(urlPath+'plugins/syslog/syslog_alerts.php?header=false');
+ $('.maintabs').find('a').removeClass('selected');
+ $('#tab-console').addClass('selected');
+ });
+
+ $('#bremoval').click(function() {
+ loadTopTab(urlPath+'plugins/syslog/syslog_removal.php?header=false');
+ $('.maintabs').find('a').removeClass('selected');
+ $('#tab-console').addClass('selected');
+ });
+
+ $('#breports').click(function() {
+ loadTopTab(urlPath+'plugins/syslog/syslog_reports.php?header=false');
+ $('.maintabs').find('a').removeClass('selected');
+ $('#tab-console').addClass('selected');
+ });
+
+ $('#startDate').click(function() {
+ if (date1Open) {
+ date1Open = false;
+ $('#date1').datetimepicker('hide');
+ } else {
+ date1Open = true;
+ $('#date1').datetimepicker('show');
+ }
+ });
+
+ $('#endDate').click(function() {
+ if (date2Open) {
+ date2Open = false;
+ $('#date2').datetimepicker('hide');
+ } else {
+ date2Open = true;
+ $('#date2').datetimepicker('show');
+ }
+ });
+
+ $('#date1').datetimepicker({
+ minuteGrid: 10,
+ stepMinute: 1,
+ showAnim: 'slideDown',
+ numberOfMonths: 1,
+ timeFormat: 'HH:mm',
+ dateFormat: 'yy-mm-dd',
+ showButtonPanel: false
+ });
+
+ $('#date2').datetimepicker({
+ minuteGrid: 10,
+ stepMinute: 1,
+ showAnim: 'slideDown',
+ numberOfMonths: 1,
+ timeFormat: 'HH:mm',
+ dateFormat: 'yy-mm-dd',
+ showButtonPanel: false
+ });
+ });
+}
+
+/**
+ * Initialize syslog message tooltips and group expand/collapse functionality
+ * Call this after the syslog message table is rendered or updated
+ */
+function initSyslogMessagesDisplay() {
+ $(function() {
+ // Initialize tooltips for syslog rows
+ $('.syslogRow').tooltip({
+ track: true,
+ show: {
+ effect: 'fade',
+ duration: 250,
+ delay: 125
+ },
+ position: { my: 'left+15 center', at: 'right center' }
+ });
+
+ // Initialize tooltips for buttons
+ $('button').tooltip({
+ closed: true
+ }).on('focus', function() {
+ $('#filter').tooltip('close');
+ }).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');
+ }
+ });
+ });
+}
+
+/* ========================================================================
+ * Removal Rules Functions (syslog_removal.php)
+ * ======================================================================== */
+
+/**
+ * Apply filter for removal rules view
+ */
+function applyFilterRemoval() {
+ var strURL = 'syslog_removal.php?filter='+$('#filter').val()+'&enabled='+$('#enabled').val()+'&rows='+$('#rows').val()+'&page='+$('#page').val()+'&header=false';
+ loadPageNoHeader(strURL);
+}
+
+/**
+ * Clear filter for removal rules view
+ */
+function clearFilterRemoval() {
+ var strURL = 'syslog_removal.php?clear=1&header=false';
+ loadPageNoHeader(strURL);
+}
+
+/**
+ * Import removal rule
+ */
+function importRemoval() {
+ var strURL = 'syslog_removal.php?action=import&header=false';
+ loadPageNoHeader(strURL);
+}
+
+/**
+ * Change message textarea rows based on type
+ */
+function changeTypes() {
+ if ($('#type').val() == 'sql') {
+ $('#message').prop('rows', 5);
+ } else {
+ $('#message').prop('rows', 2);
+ }
+}
+
+/**
+ * Initialize removal rules view
+ * @param {boolean} allowEdits - Whether edits are allowed
+ */
+function initSyslogRemoval(allowEdits) {
+ $(function() {
+ if (!allowEdits) {
+ $('#syslog_edit').find('select, input, textarea, submit').not(':button').prop('disabled', true);
+ $('#syslog_edit').find('select').each(function() {
+ if ($(this).selectmenu('instance')) {
+ $(this).selectmenu('refresh');
+ }
+ });
+ }
+
+ $('#refresh').click(function() {
+ applyFilterRemoval();
+ });
+
+ $('#clear').click(function() {
+ clearFilterRemoval();
+ });
+
+ $('#import').click(function() {
+ importRemoval();
+ });
+
+ $('#removal').submit(function(event) {
+ event.preventDefault();
+ applyFilterRemoval();
+ });
+ });
+}
+
+/* ========================================================================
+ * Alert Rules Functions (syslog_alerts.php)
+ * ======================================================================== */
+
+/**
+ * Apply filter for alert rules view
+ */
+function applyFilterAlerts() {
+ var strURL = 'syslog_alerts.php?filter='+$('#filter').val()+'&enabled='+$('#enabled').val()+'&rows='+$('#rows').val()+'&page='+$('#page').val()+'&header=false';
+
+ loadPageNoHeader(strURL);
+}
+
+/**
+ * Clear filter for alert rules view
+ */
+function clearFilterAlerts() {
+ var strURL = 'syslog_alerts.php?clear=1&header=false';
+
+ loadPageNoHeader(strURL);
+}
+
+/**
+ * Import alert rule
+ */
+function importAlert() {
+ var strURL = 'syslog_alerts.php?action=import&header=false';
+
+ loadPageNoHeader(strURL);
+}
+
+/**
+ * Initialize alert rules view
+ */
+function initSyslogAlerts() {
+ $(function() {
+ $('#refresh').click(function() {
+ applyFilterAlerts();
+ });
+
+ $('#clear').click(function() {
+ clearFilterAlerts();
+ });
+
+ $('#import').click(function() {
+ importAlert();
+ });
+
+ $('#alert').submit(function(event) {
+ event.preventDefault();
+ applyFilterAlerts();
+ });
+ });
+}
+
+/* ========================================================================
+ * Report Rules Functions (syslog_reports.php)
+ * ======================================================================== */
+
+/**
+ * Apply filter for report rules view
+ */
+function applyFilterReports() {
+ var strURL = 'syslog_reports.php?filter='+$('#filter').val()+'&enabled='+$('#enabled').val()+'&rows='+$('#rows').val()+'&page='+$('#page').val()+'&header=false';
+
+ loadPageNoHeader(strURL);
+}
+
+/**
+ * Clear filter for report rules view
+ */
+function clearFilterReports() {
+ var strURL = 'syslog_reports.php?clear=1&header=false';
+
+ loadPageNoHeader(strURL);
+}
+
+/**
+ * Import report rule
+ */
+function importReport() {
+ var strURL = 'syslog_reports.php?action=import&header=false';
+
+ loadPageNoHeader(strURL);
+}
+
+/**
+ * Initialize report rules view
+ */
+function initSyslogReports() {
+ $(function() {
+ $('#refresh').click(function() {
+ applyFilterReports();
+ });
+
+ $('#clear').click(function() {
+ clearFilterReports();
+ });
+
+ $('#import').click(function() {
+ importReport();
+ });
+
+ $('#reports').submit(function(event) {
+ event.preventDefault();
+ applyFilterReports();
+ });
+ });
+}
+
+/* ========================================================================
+ * Autocomplete Form Callback Functions
+ * ======================================================================== */
+
+/**
+ * Initialize autocomplete for form dropdown fields
+ * @param {string} formName - The name of the form field
+ * @param {string} callback - The AJAX callback action
+ * @param {string} onChange - The onChange callback to execute when selection changes
+ */
+function initSyslogAutocomplete(formName, callback, onChange) {
+ var formNameTimer;
+ var formNameClickTimer;
+ var formNameOpen = false;
+
+ $(function() {
+ $('#' + formName + '_input').autocomplete({
+ source: window.location.pathname + '?action=' + callback,
+ autoFocus: true,
+ minLength: 0,
+ select: function(event, ui) {
+ $('#' + formName + '_input').val(ui.item.label);
+ if (ui.item.id) {
+ $('#' + formName).val(ui.item.id);
+ } else {
+ $('#' + formName).val(ui.item.value);
+ }
+ if (onChange) {
+ eval(onChange);
+ }
+ }
+ }).css('border', 'none').css('background-color', 'transparent');
+
+ $('#' + formName + '_wrap').on('dblclick', function() {
+ formNameOpen = false;
+ clearTimeout(formNameTimer);
+ clearTimeout(formNameClickTimer);
+ $('#' + formName + '_input').autocomplete('close');
+ }).on('click', function() {
+ if (formNameOpen) {
+ $('#' + formName + '_input').autocomplete('close');
+ clearTimeout(formNameTimer);
+ formNameOpen = false;
+ } else {
+ formNameClickTimer = setTimeout(function() {
+ $('#' + formName + '_input').autocomplete('search', '');
+ clearTimeout(formNameTimer);
+ formNameOpen = true;
+ }, 200);
+ }
+ }).on('mouseleave', function() {
+ formNameTimer = setTimeout(function() {
+ $('#' + formName + '_input').autocomplete('close');
+ }, 800);
+ });
+
+ var width = $('#' + formName + '_input').textBoxWidth();
+ if (width < 100) {
+ width = 100;
+ }
+
+ $('#' + formName + '_wrap').css('width', width + 20);
+ $('#' + formName + '_input').css('width', width);
+
+ $('ul[id^="ui-id"]').on('mouseenter', function() {
+ clearTimeout(formNameTimer);
+ }).on('mouseleave', function() {
+ formNameTimer = setTimeout(function() {
+ $('#' + formName + '_input').autocomplete('close');
+ }, 800);
+ });
+
+ $('ul[id^="ui-id"] > li').each().on('mouseenter', function() {
+ $(this).addClass('ui-state-hover');
+ }).on('mouseleave', function() {
+ $(this).removeClass('ui-state-hover');
+ });
+
+ $('#' + formName + '_wrap').on('mouseenter', function() {
+ $(this).addClass('ui-state-hover');
+ $('input#' + formName + '_input').addClass('ui-state-hover');
+ }).on('mouseleave', function() {
+ $(this).removeClass('ui-state-hover');
+ $('input#' + formName + '_input').removeClass('ui-state-hover');
+ });
+ });
+}
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..d5bcad1 100644
--- a/syslog.php
+++ b/syslog.php
@@ -92,6 +92,8 @@
} else {
general_header();
+ syslog_include_js();
+
syslog_display_tabs($current_tab);
if ($current_tab == 'current') {
@@ -628,43 +630,7 @@ function syslog_stats_filter() {
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 +968,127 @@ 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) {
+ 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
@@ -1061,259 +1125,13 @@ function syslog_filter($sql_where, $tab) {
?>
'>
+
+
+
+
+
+ '>
+ selected>
+ selected>
+
+
+
+
+
@@ -1746,25 +1577,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 +1640,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 +1652,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 +1665,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 +1719,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 "" . html_escape($dm['logtime']) . " ";
+ print "" . html_escape(isset($hosts[$dm['host_id']]) ? $hosts[$dm['host_id']] : __('Unknown', 'syslog')) . " ";
+ print "" . html_escape($dm['program']) . " ";
+ print "" . filter_value(title_trim($dm[$syslog_incoming_config['textField']], get_request_var_request('trimval')), get_request_var('rfilter')) . " ";
+ print "" . html_escape(isset($facilities[$dm['facility_id']]) ? $facilities[$dm['facility_id']] : __('Unknown', 'syslog')) . " ";
+ print "" . html_escape(isset($priorities[$dm['priority_id']]) ? $priorities[$dm['priority_id']] : __('Unknown', 'syslog')) . " ";
+
+ if ($grouping_enabled) {
+ print " ";
+ }
+
+ print " ";
+ }
+ }
+ }
}
} else {
print "" . __('No Syslog Messages', 'syslog') . " ";
@@ -1870,25 +1796,7 @@ function syslog_messages($tab = 'syslog') {
?>
'>
diff --git a/syslog_removal.php b/syslog_removal.php
index 421e87b..c961a81 100644
--- a/syslog_removal.php
+++ b/syslog_removal.php
@@ -61,6 +61,7 @@
break;
case 'import':
top_header();
+ syslog_include_js();
import();
bottom_footer();
@@ -72,6 +73,7 @@
case 'edit':
case 'newedit':
top_header();
+ syslog_include_js();
syslog_action_edit();
@@ -79,6 +81,7 @@
break;
default:
top_header();
+ syslog_include_js();
syslog_removal();
@@ -498,28 +501,7 @@ function syslog_action_edit() {
?>
'>
diff --git a/syslog_reports.php b/syslog_reports.php
index 8354843..abf5bc8 100644
--- a/syslog_reports.php
+++ b/syslog_reports.php
@@ -51,6 +51,7 @@
break;
case 'import':
top_header();
+ syslog_include_js();
import();
bottom_footer();
@@ -61,6 +62,7 @@
break;
case 'edit':
top_header();
+ syslog_include_js();
syslog_action_edit();
@@ -68,6 +70,7 @@
break;
default:
top_header();
+ syslog_include_js();
syslog_report();
@@ -614,41 +617,7 @@ function syslog_filter() {
'>