This is an automated email from the ASF dual-hosted git repository.

dlmarion pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/accumulo.git


The following commit(s) were added to refs/heads/main by this push:
     new 9fe0950136 Added new Messages page to Monitor (#6346)
9fe0950136 is described below

commit 9fe09501360894c0cb7bd932bd1fa510976f0611
Author: Dave Marion <[email protected]>
AuthorDate: Fri May 1 11:22:59 2026 -0400

    Added new Messages page to Monitor (#6346)
    
    This change adds a new Messages menu item that takes the
    users to a new Messages page. The new page will display items
    from the messages endpoint. The messages have a priority and
    category associated with them. The page has switches for enabling /
    disabling the inclusion of high and info priority messages and
    messages from different categories. Messages with a priority
    of critical are always shown.
    
    Closes #6185
    
    
    Co-authored-by: Dom G. <[email protected]>
---
 .../apache/accumulo/monitor/next/Endpoints.java    |  65 +++++-
 .../accumulo/monitor/next/SystemInformation.java   |  44 +++-
 .../org/apache/accumulo/monitor/view/WebViews.java |  18 ++
 .../accumulo/monitor/resources/js/coordinator.js   |   7 -
 .../accumulo/monitor/resources/js/functions.js     |  43 +++-
 .../accumulo/monitor/resources/js/messages.js      | 221 +++++++++++++++++++++
 .../monitor/resources/js/server_process_common.js  |   5 +-
 .../apache/accumulo/monitor/templates/messages.ftl |  41 ++++
 .../apache/accumulo/monitor/templates/navbar.ftl   |   3 +
 9 files changed, 417 insertions(+), 30 deletions(-)

diff --git 
a/server/monitor/src/main/java/org/apache/accumulo/monitor/next/Endpoints.java 
b/server/monitor/src/main/java/org/apache/accumulo/monitor/next/Endpoints.java
index 4164a32f3a..360829b328 100644
--- 
a/server/monitor/src/main/java/org/apache/accumulo/monitor/next/Endpoints.java
+++ 
b/server/monitor/src/main/java/org/apache/accumulo/monitor/next/Endpoints.java
@@ -23,9 +23,12 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.stream.Collectors;
@@ -39,6 +42,7 @@ import jakarta.ws.rs.NotFoundException;
 import jakarta.ws.rs.Path;
 import jakarta.ws.rs.PathParam;
 import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
 import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.MediaType;
 
@@ -53,6 +57,8 @@ import org.apache.accumulo.monitor.Monitor;
 import org.apache.accumulo.monitor.next.InformationFetcher.InstanceSummary;
 import 
org.apache.accumulo.monitor.next.SystemInformation.CompactionGroupSummary;
 import 
org.apache.accumulo.monitor.next.SystemInformation.CompactionTableSummary;
+import org.apache.accumulo.monitor.next.SystemInformation.MessageCategory;
+import org.apache.accumulo.monitor.next.SystemInformation.MessagePriority;
 import org.apache.accumulo.monitor.next.SystemInformation.TableSummary;
 import 
org.apache.accumulo.monitor.next.SystemInformation.TimeOrderedRunningCompactionSet;
 import org.apache.accumulo.monitor.next.deployment.DeploymentOverview;
@@ -423,11 +429,62 @@ public class Endpoints {
   }
 
   @GET
-  @Path("suggestions")
+  @Path("message/categories")
   @Produces(MediaType.APPLICATION_JSON)
-  @Description("Returns a list of suggestions")
-  public Set<String> getSuggestions() {
-    return 
monitor.getInformationFetcher().getSummaryForEndpoint().getSuggestions();
+  @Description("Returns a list of message categories")
+  public Set<MessageCategory> getMessageCategories() {
+    return EnumSet.allOf(SystemInformation.MessageCategory.class);
+  }
+
+  public record Message(String priority, String category, String message) {
+  }
+
+  @GET
+  @Path("messages")
+  @Produces(MediaType.APPLICATION_JSON)
+  @Description("Returns a list of messages")
+  public List<Message> getMessages(@QueryParam("high") boolean includeHigh,
+      @QueryParam("info") boolean includeInfo, @QueryParam("category") 
List<String> categories) {
+    List<Message> results = new ArrayList<>();
+
+    Map<MessagePriority,Map<MessageCategory,Set<String>>> messages =
+        monitor.getInformationFetcher().getSummaryForEndpoint().getMessages();
+
+    for (Entry<MessagePriority,Map<MessageCategory,Set<String>>> e : 
messages.entrySet()) {
+      MessagePriority prio = e.getKey();
+      Map<MessageCategory,Set<String>> value = e.getValue();
+      switch (prio) {
+        case Critical:
+          // Always include critical messages
+          value.forEach((cat, msgs) -> {
+            msgs.forEach(m -> results.add(new Message(prio.name(), cat.name(), 
m)));
+          });
+          break;
+        case High:
+          if (!includeHigh) {
+            break;
+          }
+          value.forEach((cat, msgs) -> {
+            if (categories.contains(cat.name())) {
+              msgs.forEach(m -> results.add(new Message(prio.name(), 
cat.name(), m)));
+            }
+          });
+          break;
+        case Info:
+          if (!includeInfo) {
+            break;
+          }
+          value.forEach((cat, msgs) -> {
+            if (categories.contains(cat.name())) {
+              msgs.forEach(m -> results.add(new Message(prio.name(), 
cat.name(), m)));
+            }
+          });
+          break;
+        default:
+          break;
+      }
+    }
+    return results;
   }
 
   @GET
diff --git 
a/server/monitor/src/main/java/org/apache/accumulo/monitor/next/SystemInformation.java
 
b/server/monitor/src/main/java/org/apache/accumulo/monitor/next/SystemInformation.java
index 855e442348..92eed0e75e 100644
--- 
a/server/monitor/src/main/java/org/apache/accumulo/monitor/next/SystemInformation.java
+++ 
b/server/monitor/src/main/java/org/apache/accumulo/monitor/next/SystemInformation.java
@@ -20,6 +20,11 @@ package org.apache.accumulo.monitor.next;
 
 import static com.google.common.base.Suppliers.memoize;
 import static org.apache.accumulo.core.metrics.MetricsInfo.QUEUE_TAG_KEY;
+import static 
org.apache.accumulo.monitor.next.SystemInformation.MessageCategory.Configuration;
+import static 
org.apache.accumulo.monitor.next.SystemInformation.MessageCategory.Table;
+import static 
org.apache.accumulo.monitor.next.SystemInformation.MessagePriority.Critical;
+import static 
org.apache.accumulo.monitor.next.SystemInformation.MessagePriority.High;
+import static 
org.apache.accumulo.monitor.next.SystemInformation.MessagePriority.Info;
 
 import java.nio.ByteBuffer;
 import java.time.Duration;
@@ -36,6 +41,7 @@ import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
+import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentSkipListSet;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -365,6 +371,14 @@ public class SystemInformation {
   public record CompactionGroupSummary(String groupId, long running) {
   }
 
+  public enum MessagePriority {
+    Critical, High, Info;
+  }
+
+  public enum MessageCategory {
+    Configuration, Table;
+  }
+
   private static final Logger LOG = 
LoggerFactory.getLogger(SystemInformation.class);
 
   private final DistributionStatisticConfig DSC =
@@ -426,7 +440,8 @@ public class SystemInformation {
   private final Map<ResourceGroupId,Map<ServerId.Type,ProcessSummary>> 
deployment =
       new ConcurrentHashMap<>();
 
-  private final Set<String> suggestions = new ConcurrentSkipListSet<>();
+  private final Map<MessagePriority,Map<MessageCategory,Set<String>>> messages 
=
+      new EnumMap<>(MessagePriority.class);
 
   private final Set<String> configuredCompactionResourceGroups = 
ConcurrentHashMap.newKeySet();
 
@@ -467,7 +482,7 @@ public class SystemInformation {
     tables.clear();
     tablets.clear();
     deployment.clear();
-    suggestions.clear();
+    messages.clear();
     runningCompactionsPerGroup.clear();
     runningCompactionsPerTable.clear();
     tableCompactions.clear();
@@ -478,6 +493,11 @@ public class SystemInformation {
     serverMetricsView.clear();
   }
 
+  private void addMessage(MessagePriority pri, MessageCategory cat, String 
msg) {
+    messages.computeIfAbsent(pri, k -> new EnumMap<>(MessageCategory.class))
+        .computeIfAbsent(cat, k -> new TreeSet<>()).add(msg);
+  }
+
   private void updateAggregates(final MetricResponse response,
       final Map<Id,CumulativeDistributionSummary> total,
       final Map<String,Map<Id,CumulativeDistributionSummary>> rg) {
@@ -657,7 +677,7 @@ public class SystemInformation {
         .add(sti);
     tables.computeIfAbsent(tableId, (t) -> new 
TableSummary(tableName)).addTablet(sti);
     if (sti.getEstimatedEntries() == 0) {
-      suggestions.add("Tablet " + sti.getTabletId().toString() + " (tid: "
+      addMessage(Info, Table, "Tablet " + sti.getTabletId().toString() + " 
(tid: "
           + sti.getTabletId().getTable() + ") may have zero entries and could 
be merged.");
     }
   }
@@ -695,8 +715,9 @@ public class SystemInformation {
       String balancerRG = 
tconf.get(TableLoadBalancer.TABLE_ASSIGNMENT_GROUP_PROPERTY);
       balancerRG = balancerRG == null ? Constants.DEFAULT_RESOURCE_GROUP_NAME 
: balancerRG;
       if (!tservers.containsKey(balancerRG)) {
-        suggestions.add("Table " + table.tableName() + " configured to balance 
tablets in resource"
-            + " group " + balancerRG + ", but there are no TabletServers.");
+        addMessage(Critical, Table,
+            "Table " + table.tableName() + " configured to balance tablets in 
resource group "
+                + balancerRG + ", but there are no TabletServers.");
       }
     }
 
@@ -713,8 +734,8 @@ public class SystemInformation {
         Number numQueued = getMetricValue(queued.orElseThrow());
         if (numQueued.longValue() > 0) {
           if (rgCompactors == null || rgCompactors.size() == 0) {
-            suggestions.add("Compactor group " + rg + " has " + 
numQueued.longValue()
-                + " queued compactions but no running compactors");
+            addMessage(Critical, Configuration, "Compactor group " + rg + " 
has "
+                + numQueued.longValue() + " queued compactions but no running 
compactors");
           } else {
             // Check for idle compactors.
             Map<Id,CumulativeDistributionSummary> rgMetrics =
@@ -728,7 +749,8 @@ public class SystemInformation {
             if (idleMetric.isPresent()) {
               var metric = idleMetric.orElseThrow().getValue();
               if (metric.max() == 1.0D) {
-                suggestions.add("Compactor group " + rg + " has queued jobs 
and idle compactors.");
+                addMessage(High, Configuration,
+                    "Compactor group " + rg + " has queued jobs and idle 
compactors.");
               }
             }
 
@@ -739,7 +761,7 @@ public class SystemInformation {
 
     for (var compactorGroup : compactors.keySet()) {
       if (!configuredCompactionResourceGroups.contains(compactorGroup)) {
-        suggestions.add("Compactor group " + compactorGroup
+        addMessage(High, Configuration, "Compactor group " + compactorGroup
             + " has running compactors, but no configuration uses them.");
       }
     }
@@ -953,8 +975,8 @@ public class SystemInformation {
     return this.deploymentOverview;
   }
 
-  public Set<String> getSuggestions() {
-    return this.suggestions;
+  public Map<MessagePriority,Map<MessageCategory,Set<String>>> getMessages() {
+    return this.messages;
   }
 
   public long getTimestamp() {
diff --git 
a/server/monitor/src/main/java/org/apache/accumulo/monitor/view/WebViews.java 
b/server/monitor/src/main/java/org/apache/accumulo/monitor/view/WebViews.java
index 1f67affd68..bc09781c95 100644
--- 
a/server/monitor/src/main/java/org/apache/accumulo/monitor/view/WebViews.java
+++ 
b/server/monitor/src/main/java/org/apache/accumulo/monitor/view/WebViews.java
@@ -162,6 +162,24 @@ public class WebViews {
     return model;
   }
 
+  /**
+   * Returns the messages template
+   *
+   * @return Messages model
+   */
+  @GET
+  @Path("messages")
+  @Template(name = "/default.ftl")
+  public Map<String,Object> getMessages() {
+
+    Map<String,Object> model = getModel();
+    model.put("title", "Messages"); // Need this for the browser tab title
+    model.put("tablesTitle", "Messages");
+    model.put("template", "messages.ftl");
+    model.put("js", "messages.js");
+    return model;
+  }
+
   /**
    * Returns the tservers templates
    *
diff --git 
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/coordinator.js
 
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/coordinator.js
index 849288b7ce..a20ae9786c 100644
--- 
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/coordinator.js
+++ 
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/coordinator.js
@@ -26,13 +26,6 @@ const runningQueueHtmlTable = '#queue_running';
 var tableRunning;
 var queueRunning;
 
-function getStoredArray(storageKey) {
-  if (!sessionStorage[storageKey]) {
-    return [];
-  }
-  return JSON.parse(sessionStorage[storageKey]);
-}
-
 function refresh() {
   $.when(getRunningCompactionsByTable(), getRunningCompactionsByGroup(), 
getCoordinatorQueueView(), getManagersCompactionView()).then(function () {
     refreshTable(coordinatorHtmlTable, MANAGER_COMPACTION_SERVER_PROCESS_VIEW);
diff --git 
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/functions.js
 
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/functions.js
index a449eb78d5..bdab1c9ad4 100644
--- 
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/functions.js
+++ 
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/functions.js
@@ -37,6 +37,8 @@ const TABLET_SERVER_PROCESS_VIEW = 'tserversView';
 var STATUS_REQUEST = null;
 const RUNNING_COMPACTIONS_BY_TABLE = 'runningCompactionsByTable';
 const RUNNING_COMPACTIONS_BY_GROUP = 'runningCompactionsByGroup';
+const MESSAGE_CATEGORIES = 'messageCategories';
+const MESSAGES = 'messages';
 
 // Override Length Menu options for dataTables
 if ($.fn && $.fn.dataTable) {
@@ -372,6 +374,20 @@ function getJSONForTable(call, sessionDataVar) {
   });
 }
 
+function getStoredJson(storageKey, defaultValue) {
+  var storedValue = sessionStorage.getItem(storageKey);
+  if (!storedValue) {
+    return defaultValue;
+  }
+
+  return JSON.parse(storedValue);
+}
+
+function getStoredArray(storageKey) {
+  var storedValue = getStoredJson(storageKey, []);
+  return Array.isArray(storedValue) ? storedValue : [];
+}
+
 /**
  * Performs POST call and builds console logging message if successful
  * @param {string} call REST url called
@@ -540,11 +556,30 @@ function getTserversSummary(group) {
 }
 
 /**
- * REST GET call for /suggestions,
- * stores it on a sessionStorage variable
+ * REST GET call for /message/categories
+ * store it on a sessionStorage variable
  */
-function getSuggestions() {
-  return getJSONForTable(REST_V2_PREFIX + '/suggestions', 'suggestions');
+function getMessageCategories() {
+  return getJSONForTable(REST_V2_PREFIX + '/message/categories', 
MESSAGE_CATEGORIES);
+}
+
+/**
+ * REST GET call for /messages,
+ * results are not stored in session as this
+ * function takes parameters driven by toggles
+ * in the UI.
+ */
+function getMessages(high, info, cats) {
+
+  const params = new URLSearchParams();
+  params.append('high', high);
+  params.append('info', info);
+  $.each(cats, function (index, cat) {
+    params.append('category', cat);
+  });
+
+  var call = REST_V2_PREFIX + '/messages?' + params.toString();
+  return getJSONForTable(call, MESSAGES);
 }
 
 /**
diff --git 
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/messages.js
 
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/messages.js
new file mode 100644
index 0000000000..f3fbdc61b2
--- /dev/null
+++ 
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/messages.js
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+"use strict";
+
+const messageCategoryList = '#message-category-list';
+const messagePriorityList = '#message-priority-list';
+const messageHtmlTable = '#messagesTable';
+const messagePriorities = ['High', 'Info'];
+
+var dataTableRef;
+
+/**
+ * Updates the list of message priorities
+ */
+function updateMessagePriorities() {
+  var priorityList = $(messagePriorityList);
+  $.each(messagePriorities, function (index, pri) {
+    var switchId = "msg-pri-switch-" + pri;
+    var switchElement = "#" + switchId;
+    var savedValue = localStorage.getItem(switchId + "-state");
+
+    if ($(switchElement).length) {
+      // update it
+      if (savedValue === null || savedValue === 'true') {
+        $(switchElement).prop('checked', true);
+      } else {
+        $(switchElement).prop('checked', false);
+      }
+    } else {
+      // create it
+      var div = $(document.createElement("div"));
+      div.addClass("form-check form-switch");
+
+      var input = $(document.createElement("input"));
+      input.addClass("form-check-input");
+      input.attr("type", "checkbox");
+      input.attr("role", "switch");
+      input.attr("id", switchId);
+
+      if (savedValue === null || savedValue === 'true') {
+        input.prop('checked', true);
+      } else {
+        input.prop('checked', false);
+      }
+
+      input.on("change", function () {
+        localStorage.setItem("msg-pri-switch-" + pri + "-state", 
$(this).is(':checked'));
+        refresh();
+      });
+      div.append(input);
+
+      var label = $(document.createElement("label"));
+      label.addClass("form-check-label fs-6");
+      label.attr("for", switchId);
+      label.text(pri);
+      div.append(label);
+
+      priorityList.append(div);
+    }
+  });
+
+}
+
+/**
+ * Updates the list of message categories
+ */
+function updateMessageCategories() {
+
+  var categories = getStoredArray(MESSAGE_CATEGORIES);
+
+  var categoryList = $(messageCategoryList);
+  $.each(categories, function (index, cat) {
+
+    var switchId = "msg-cat-switch-" + cat;
+    var switchElement = "#" + switchId;
+    var savedValue = localStorage.getItem(switchId + "-state");
+
+    if ($(switchElement).length) {
+      // update it
+      if (savedValue === null || savedValue === 'true') {
+        $(switchElement).prop('checked', true);
+      } else {
+        $(switchElement).prop('checked', false);
+      }
+    } else {
+      // create it
+      var div = $(document.createElement("div"));
+      div.addClass("form-check form-check-inline form-switch");
+
+      var input = $(document.createElement("input"));
+      input.addClass("form-check-input");
+      input.attr("type", "checkbox");
+      input.attr("role", "switch");
+      input.attr("id", switchId);
+
+      if (savedValue === null || savedValue === 'true') {
+        input.prop('checked', true);
+      } else {
+        input.prop('checked', false);
+      }
+
+      input.on("change", function () {
+        localStorage.setItem("msg-cat-switch-" + cat + "-state", 
$(this).is(':checked'));
+        refresh();
+      });
+      div.append(input);
+
+      var label = $(document.createElement("label"));
+      label.addClass("form-check-label fs-6");
+      label.attr("for", switchId);
+      label.text(cat);
+      div.append(label);
+
+      categoryList.append(div);
+    }
+  });
+
+}
+
+function fetchTableData() {
+
+  var highSwitchId = "msg-pri-switch-High";
+  var savedValue = localStorage.getItem(highSwitchId + "-state");
+  var high = false;
+  if (savedValue === null || savedValue === 'true') {
+    high = true;
+  }
+
+  var InfoSwitchId = "msg-pri-switch-Info";
+  savedValue = localStorage.getItem(InfoSwitchId + "-state");
+  var info = false;
+  if (savedValue === null || savedValue === 'true') {
+    info = true;
+  }
+
+  var categories = getStoredArray(MESSAGE_CATEGORIES);
+  if (categories.length === 0) {
+    sessionStorage.setItem(MESSAGES, JSON.stringify([]));
+    return $.Deferred().resolve().promise();
+  }
+
+  var cats = [];
+  $.each(categories, function (index, cat) {
+    var savedValue = localStorage.getItem("msg-cat-switch-" + cat + "-state");
+    if (savedValue === null || savedValue === 'true') {
+      cats.push(cat);
+    }
+  });
+  return getMessages(high, info, cats);
+}
+
+function getTableData() {
+  return getStoredArray(MESSAGES);
+}
+
+function loadMessagesPageData() {
+  return getMessageCategories().then(function () {
+    return fetchTableData();
+  });
+}
+
+function refresh() {
+  return loadMessagesPageData().then(function () {
+    updateMessagePriorities();
+    updateMessageCategories();
+    if (dataTableRef) {
+      ajaxReloadTable(dataTableRef);
+    }
+  });
+}
+
+function createDataTable() {
+  dataTableRef = $(messageHtmlTable).DataTable({
+    "autoWidth": false,
+    "ajax": function (data, callback) {
+      callback({
+        data: getTableData()
+      });
+    },
+    "stateSave": true,
+    "colReorder": true,
+    "columnDefs": [{
+      targets: '_all',
+      defaultContent: '-'
+    }],
+    "columns": [{
+        "data": "priority"
+      },
+      {
+        "data": "category"
+      },
+      {
+        "data": "message"
+      }
+    ]
+  });
+}
+
+$(function () {
+  loadMessagesPageData().then(function () {
+    updateMessagePriorities();
+    updateMessageCategories();
+    createDataTable();
+  });
+});
diff --git 
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/server_process_common.js
 
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/server_process_common.js
index b35c694951..6cb5ca188c 100644
--- 
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/server_process_common.js
+++ 
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/server_process_common.js
@@ -67,10 +67,7 @@ var dataTableRefs = new Map();
  * This function returns the entire response from session storage
  */
 function getStoredView(storageKey) {
-  if (!sessionStorage[storageKey]) {
-    return {};
-  }
-  return JSON.parse(sessionStorage[storageKey]);
+  return getStoredJson(storageKey, {});
 }
 
 /**
diff --git 
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/messages.ftl
 
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/messages.ftl
new file mode 100644
index 0000000000..185801c3fb
--- /dev/null
+++ 
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/messages.ftl
@@ -0,0 +1,41 @@
+<#--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      https://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+    <br />
+    <div class="col-xs-12" id="messages">
+      <h5>Message Priority Filters <small>(critical messages are always 
shown)</small></h5>
+      <div id="message-priority-list"></div>
+      <br />
+      <h5>Message Category Filters</h5>
+      <div id="message-category-list"></div>
+      <br />
+      <h5>Messages</h5>
+      <table id="messagesTable"
+        class="table table-bordered table-striped table-condensed" 
style="width: 100%;">
+        <thead>
+          <tr>
+            <th class="col-xs-2">Priority</th>
+            <th class="col-xs-2">Category</th>
+            <th class="col-xs-8">Message</th>
+          </tr>
+        </thead>
+        <tbody></tbody>
+      </table>
+    </div>
diff --git 
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/navbar.ftl
 
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/navbar.ftl
index 1cf41fcd89..27e44524ec 100644
--- 
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/navbar.ftl
+++ 
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/navbar.ftl
@@ -60,6 +60,9 @@
                 <li><a class="dropdown-item" href="scans">Scans</a></li>
               </ul>
             </li>
+            <li>
+              <a class="nav-link" aria-current="page" 
href="messages">Messages</a>
+            </li>            
             <li class="dropdown">
               <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown"
               role="button" data-bs-toggle="dropdown" 
aria-expanded="false">REST

Reply via email to