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