This is an automated email from the ASF dual-hosted git repository. remm pushed a commit to branch 8.5.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/8.5.x by this push: new 3612ff2ec2 Add JSON stats to the status servlet 3612ff2ec2 is described below commit 3612ff2ec24786d9533aef271d98cbb1805054b7 Author: remm <r...@apache.org> AuthorDate: Tue Oct 17 15:57:46 2023 +0200 Add JSON stats to the status servlet The format is easy to parse and validate and is a straight equivalent to the XML view. I did not add the detailed processor view, it seems way to verbose these days. --- .../catalina/manager/StatusManagerServlet.java | 54 ++--- .../apache/catalina/manager/StatusTransformer.java | 264 ++++++++++++++++++--- .../catalina/manager/TestStatusTransformer.java | 82 +++++++ webapps/docs/changelog.xml | 5 + webapps/docs/manager-howto.xml | 5 + 5 files changed, 345 insertions(+), 65 deletions(-) diff --git a/java/org/apache/catalina/manager/StatusManagerServlet.java b/java/org/apache/catalina/manager/StatusManagerServlet.java index febce3dbdd..9473a99ad0 100644 --- a/java/org/apache/catalina/manager/StatusManagerServlet.java +++ b/java/org/apache/catalina/manager/StatusManagerServlet.java @@ -183,12 +183,16 @@ public class StatusManagerServlet extends HttpServlet implements NotificationLis StringManager smClient = StringManager.getManager(Constants.Package, request.getLocales()); - // mode is flag for HTML or XML output + // mode is flag for HTML, JSON or XML output int mode = 0; // if ?XML=true, set the mode to XML if (request.getParameter("XML") != null && request.getParameter("XML").equals("true")) { mode = 1; } + // if ?JSON=true, set the mode to JSON + if (request.getParameter("JSON") != null && request.getParameter("JSON").equals("true")) { + mode = 2; + } StatusTransformer.setContentType(response, mode); PrintWriter writer = response.getWriter(); @@ -295,32 +299,28 @@ public class StatusManagerServlet extends HttpServlet implements NotificationLis // use StatusTransformer to output status StatusTransformer.writeVMState(writer, mode, args); - for (ObjectName objectName : threadPools) { - String name = objectName.getKeyProperty("name"); - args = new Object[19]; - args[0] = smClient.getString("htmlManagerServlet.connectorStateMaxThreads"); - args[1] = smClient.getString("htmlManagerServlet.connectorStateThreadCount"); - args[2] = smClient.getString("htmlManagerServlet.connectorStateThreadBusy"); - args[3] = smClient.getString("htmlManagerServlet.connectorStateAliveSocketCount"); - args[4] = smClient.getString("htmlManagerServlet.connectorStateMaxProcessingTime"); - args[5] = smClient.getString("htmlManagerServlet.connectorStateProcessingTime"); - args[6] = smClient.getString("htmlManagerServlet.connectorStateRequestCount"); - args[7] = smClient.getString("htmlManagerServlet.connectorStateErrorCount"); - args[8] = smClient.getString("htmlManagerServlet.connectorStateBytesReceived"); - args[9] = smClient.getString("htmlManagerServlet.connectorStateBytesSent"); - args[10] = smClient.getString("htmlManagerServlet.connectorStateTableTitleStage"); - args[11] = smClient.getString("htmlManagerServlet.connectorStateTableTitleTime"); - args[12] = smClient.getString("htmlManagerServlet.connectorStateTableTitleBSent"); - args[13] = smClient.getString("htmlManagerServlet.connectorStateTableTitleBRecv"); - args[14] = smClient.getString("htmlManagerServlet.connectorStateTableTitleClientForw"); - args[15] = smClient.getString("htmlManagerServlet.connectorStateTableTitleClientAct"); - args[16] = smClient.getString("htmlManagerServlet.connectorStateTableTitleVHost"); - args[17] = smClient.getString("htmlManagerServlet.connectorStateTableTitleRequest"); - args[18] = smClient.getString("htmlManagerServlet.connectorStateHint"); - // use StatusTransformer to output status - StatusTransformer.writeConnectorState(writer, objectName, name, mBeanServer, globalRequestProcessors, - requestProcessors, mode, args); - } + args = new Object[19]; + args[0] = smClient.getString("htmlManagerServlet.connectorStateMaxThreads"); + args[1] = smClient.getString("htmlManagerServlet.connectorStateThreadCount"); + args[2] = smClient.getString("htmlManagerServlet.connectorStateThreadBusy"); + args[3] = smClient.getString("htmlManagerServlet.connectorStateAliveSocketCount"); + args[4] = smClient.getString("htmlManagerServlet.connectorStateMaxProcessingTime"); + args[5] = smClient.getString("htmlManagerServlet.connectorStateProcessingTime"); + args[6] = smClient.getString("htmlManagerServlet.connectorStateRequestCount"); + args[7] = smClient.getString("htmlManagerServlet.connectorStateErrorCount"); + args[8] = smClient.getString("htmlManagerServlet.connectorStateBytesReceived"); + args[9] = smClient.getString("htmlManagerServlet.connectorStateBytesSent"); + args[10] = smClient.getString("htmlManagerServlet.connectorStateTableTitleStage"); + args[11] = smClient.getString("htmlManagerServlet.connectorStateTableTitleTime"); + args[12] = smClient.getString("htmlManagerServlet.connectorStateTableTitleBSent"); + args[13] = smClient.getString("htmlManagerServlet.connectorStateTableTitleBRecv"); + args[14] = smClient.getString("htmlManagerServlet.connectorStateTableTitleClientForw"); + args[15] = smClient.getString("htmlManagerServlet.connectorStateTableTitleClientAct"); + args[16] = smClient.getString("htmlManagerServlet.connectorStateTableTitleVHost"); + args[17] = smClient.getString("htmlManagerServlet.connectorStateTableTitleRequest"); + args[18] = smClient.getString("htmlManagerServlet.connectorStateHint"); + StatusTransformer.writeConnectorsState(writer, mBeanServer, threadPools, globalRequestProcessors, + requestProcessors, mode, args); if (request.getPathInfo() != null && request.getPathInfo().equals("/all")) { // Note: Retrieving the full status is much slower diff --git a/java/org/apache/catalina/manager/StatusTransformer.java b/java/org/apache/catalina/manager/StatusTransformer.java index e67d1aa46b..0d36a3e349 100644 --- a/java/org/apache/catalina/manager/StatusTransformer.java +++ b/java/org/apache/catalina/manager/StatusTransformer.java @@ -35,6 +35,7 @@ import javax.management.ObjectName; import javax.servlet.http.HttpServletResponse; import org.apache.tomcat.util.ExceptionUtils; +import org.apache.tomcat.util.json.JSONFilter; import org.apache.tomcat.util.security.Escape; /** @@ -52,6 +53,8 @@ public class StatusTransformer { response.setContentType("text/html;charset=" + Constants.CHARSET); } else if (mode == 1) { response.setContentType("text/xml;charset=" + Constants.CHARSET); + } else if (mode == 2) { + response.setContentType("application/json"); } } @@ -61,7 +64,7 @@ public class StatusTransformer { * * @param writer the PrintWriter to use * @param args Path prefix for URLs - * @param mode - 0 = HTML header, 1 = XML declaration + * @param mode - 0 = HTML header, 1 = XML declaration, 2 = JSON */ public static void writeHeader(PrintWriter writer, Object[] args, int mode) { if (mode == 0) { @@ -71,6 +74,8 @@ public class StatusTransformer { writer.write(Constants.XML_DECLARATION); writer.print(MessageFormat.format(Constants.XML_STYLE, args)); writer.write("<status>"); + } else if (mode == 2) { + writer.append('{').append('"').append("tomcat").append('"').append(':').append('{').println(); } } @@ -123,6 +128,8 @@ public class StatusTransformer { writer.print(Constants.HTML_TAIL_SECTION); } else if (mode == 1) { writer.write("</status>"); + } else if (mode == 2) { + writer.append('}').append('}'); } } @@ -269,11 +276,87 @@ public class StatusTransformer { } writer.write("</jvm>"); + } else if (mode == 2) { + writer.append('"').append("jvm").append('"').append(':').append('{').println(); + + writer.append('"').append("memory").append('"').append(':').append('{'); + appendJSonValue(writer, "free", Long.toString(Runtime.getRuntime().freeMemory())).append(','); + appendJSonValue(writer, "total", Long.toString(Runtime.getRuntime().totalMemory())).append(','); + appendJSonValue(writer, "max", Long.toString(Runtime.getRuntime().maxMemory())); + writer.append('}').append(',').println(); + + writer.append('"').append("memorypool").append('"').append(':').append('['); + boolean first = true; + for (MemoryPoolMXBean memoryPoolMBean : memoryPoolMBeans.values()) { + MemoryUsage usage = memoryPoolMBean.getUsage(); + if (first) { + first = false; + } else { + writer.append(',').println(); + } + writer.append('{'); + appendJSonValue(writer, "name", JSONFilter.escape(memoryPoolMBean.getName())).append(','); + appendJSonValue(writer, "type", memoryPoolMBean.getType().toString()).append(','); + appendJSonValue(writer, "usageInit", Long.toString(usage.getInit())).append(','); + appendJSonValue(writer, "usageCommitted", Long.toString(usage.getCommitted())).append(','); + appendJSonValue(writer, "usageMax", Long.toString(usage.getMax())).append(','); + appendJSonValue(writer, "usageUsed", Long.toString(usage.getUsed())); + writer.append('}'); + } + writer.append(']').println(); + + writer.append('}'); } } + private static PrintWriter appendJSonValue(PrintWriter writer, String name, String value) { + return writer.append('"').append(name).append('"').append(':').append('"').append(value).append('"'); + } + + + /** + * Write connector state. + * + * @param writer The output writer + * @param mBeanServer MBean server + * @param threadPools MBean names for the thread pools of the connectors + * @param globalRequestProcessors MBean names for the global request processors + * @param requestProcessors MBean names for the request processors + * @param mode Mode <code>0</code> will generate HTML. Mode <code>1</code> will generate XML. + * @param args I18n labels for the Connector state values + * + * @throws Exception Propagated JMX error + */ + public static void writeConnectorsState(PrintWriter writer, MBeanServer mBeanServer, + Vector<ObjectName> threadPools, + Vector<ObjectName> globalRequestProcessors, Vector<ObjectName> requestProcessors, int mode, Object[] args) + throws Exception { + if (mode == 2) { + writer.append(',').println(); + writer.append('"').append("connector").append('"').append(':').append('[').println(); + } + boolean first = true; + for (ObjectName objectName : threadPools) { + if (first) { + first = false; + } else { + if (mode == 2) { + writer.append(',').println(); + } + } + String name = objectName.getKeyProperty("name"); + // use StatusTransformer to output status + StatusTransformer.writeConnectorState(writer, objectName, name, mBeanServer, globalRequestProcessors, + requestProcessors, mode, args); + } + if (mode == 2) { + writer.append(']').println(); + } + } + + /** * Write connector state. * @@ -410,6 +493,38 @@ public class StatusTransformer { } writer.write("</connector>"); + } else if (mode == 2) { + writer.append('{').println(); + + appendJSonValue(writer, "name", JSONFilter.escape(name)).append(',').println(); + writer.append('"').append("threadInfo").append('"').append(':').append('{'); + appendJSonValue(writer, "maxThreads", mBeanServer.getAttribute(tpName, "maxThreads").toString()).append(','); + appendJSonValue(writer, "currentThreadCount", mBeanServer.getAttribute(tpName, "currentThreadCount").toString()).append(','); + appendJSonValue(writer, "currentThreadsBusy", mBeanServer.getAttribute(tpName, "currentThreadsBusy").toString()); + writer.append('}'); + + ObjectName grpName = null; + for (ObjectName objectName : globalRequestProcessors) { + // Find the HTTP/1.1 RequestGroupInfo - BZ 65404 + if (name.equals(objectName.getKeyProperty("name")) && objectName.getKeyProperty("Upgrade") == null) { + grpName = objectName; + } + } + + if (grpName != null) { + writer.append(',').println(); + writer.append('"').append("requestInfo").append('"').append(':').append('{'); + appendJSonValue(writer, "maxTime", mBeanServer.getAttribute(grpName, "maxTime").toString()).append(','); + appendJSonValue(writer, "processingTime", mBeanServer.getAttribute(grpName, "processingTime").toString()).append(','); + appendJSonValue(writer, "requestCount", mBeanServer.getAttribute(grpName, "requestCount").toString()).append(','); + appendJSonValue(writer, "errorCount", mBeanServer.getAttribute(grpName, "errorCount").toString()).append(','); + appendJSonValue(writer, "bytesReceived", mBeanServer.getAttribute(grpName, "bytesReceived").toString()).append(','); + appendJSonValue(writer, "bytesSent", mBeanServer.getAttribute(grpName, "bytesSent").toString()); + writer.append('}').println(); + // Note: No detailed per processor info + } + + writer.append('}'); } } @@ -596,9 +711,9 @@ public class StatusTransformer { */ public static void writeDetailedState(PrintWriter writer, MBeanServer mBeanServer, int mode) throws Exception { + ObjectName queryHosts = new ObjectName("*:j2eeType=WebModule,*"); + Set<ObjectName> hostsON = mBeanServer.queryNames(queryHosts, null); if (mode == 0) { - ObjectName queryHosts = new ObjectName("*:j2eeType=WebModule,*"); - Set<ObjectName> hostsON = mBeanServer.queryNames(queryHosts, null); // Navigation menu writer.print("<h1>"); @@ -641,6 +756,21 @@ public class StatusTransformer { } else if (mode == 1) { // for now we don't write out the Detailed state in XML + } else if (mode == 2) { + writer.append(',').println(); + writer.append('"').append("context").append('"').append(':').append('['); + Iterator<ObjectName> iterator = hostsON.iterator(); + boolean first = true; + while (iterator.hasNext()) { + if (first) { + first = false; + } else { + writer.append(',').println(); + } + ObjectName contextON = iterator.next(); + writeContext(writer, contextON, mBeanServer, mode); + } + writer.append(']').println(); } } @@ -659,42 +789,43 @@ public class StatusTransformer { protected static void writeContext(PrintWriter writer, ObjectName objectName, MBeanServer mBeanServer, int mode) throws Exception { - if (mode == 0) { - String webModuleName = objectName.getKeyProperty("name"); - String name = webModuleName; - if (name == null) { - return; - } + String webModuleName = objectName.getKeyProperty("name"); + String name = webModuleName; + if (name == null) { + return; + } - String hostName = null; - String contextName = null; - if (name.startsWith("//")) { - name = name.substring(2); - } - int slash = name.indexOf('/'); - if (slash != -1) { - hostName = name.substring(0, slash); - contextName = name.substring(slash); - } else { - return; - } + String hostName = null; + String contextName = null; + if (name.startsWith("//")) { + name = name.substring(2); + } + int slash = name.indexOf('/'); + if (slash != -1) { + hostName = name.substring(0, slash); + contextName = name.substring(slash); + } else { + return; + } - ObjectName queryManager = new ObjectName( - objectName.getDomain() + ":type=Manager,context=" + contextName + ",host=" + hostName + ",*"); - Set<ObjectName> managersON = mBeanServer.queryNames(queryManager, null); - ObjectName managerON = null; - for (ObjectName aManagersON : managersON) { - managerON = aManagersON; - } + ObjectName queryManager = new ObjectName( + objectName.getDomain() + ":type=Manager,context=" + contextName + ",host=" + hostName + ",*"); + Set<ObjectName> managersON = mBeanServer.queryNames(queryManager, null); + ObjectName managerON = null; + for (ObjectName aManagersON : managersON) { + managerON = aManagersON; + } - ObjectName queryJspMonitor = - new ObjectName(objectName.getDomain() + ":type=JspMonitor,WebModule=" + webModuleName + ",*"); - Set<ObjectName> jspMonitorONs = mBeanServer.queryNames(queryJspMonitor, null); + ObjectName queryJspMonitor = + new ObjectName(objectName.getDomain() + ":type=JspMonitor,WebModule=" + webModuleName + ",*"); + Set<ObjectName> jspMonitorONs = mBeanServer.queryNames(queryJspMonitor, null); - // Special case for the root context - if (contextName.equals("/")) { - contextName = ""; - } + // Special case for the root context + if (contextName.equals("/")) { + contextName = ""; + } + + if (mode == 0) { writer.print("<h1>"); writer.print(Escape.htmlElementContent(name)); @@ -725,6 +856,35 @@ public class StatusTransformer { } else if (mode == 1) { // for now we don't write out the context in XML + } else if (mode == 2) { + writer.append('{'); + appendJSonValue(writer, "name", JSONFilter.escape(JSONFilter.escape(name))).append(','); + appendJSonValue(writer, "startTime", + new Date(((Long) mBeanServer.getAttribute(objectName, "startTime")).longValue()).toString()).append(','); + appendJSonValue(writer, "startupTime", mBeanServer.getAttribute(objectName, "startupTime").toString()).append(','); + appendJSonValue(writer, "tldScanTime", mBeanServer.getAttribute(objectName, "tldScanTime").toString()); + if (managerON != null) { + writeManager(writer, managerON, mBeanServer, mode); + } + if (jspMonitorONs != null) { + writeJspMonitor(writer, jspMonitorONs, mBeanServer, mode); + } + writer.append(',').println(); + writer.append('"').append("wrapper").append('"').append(':').append('['); + String onStr = objectName.getDomain() + ":j2eeType=Servlet,WebModule=" + webModuleName + ",*"; + ObjectName servletObjectName = new ObjectName(onStr); + Set<ObjectInstance> set = mBeanServer.queryMBeans(servletObjectName, null); + boolean first = true; + for (ObjectInstance oi : set) { + if (first) { + first = false; + } else { + writer.append(',').println(); + } + writeWrapper(writer, oi.getObjectName(), mBeanServer, mode); + } + writer.append(']').println(); + writer.append('}'); } } @@ -763,6 +923,18 @@ public class StatusTransformer { writer.print(formatTime(mBeanServer.getAttribute(objectName, "processingTime"), false)); } else if (mode == 1) { // for now we don't write out the wrapper details + } else if (mode == 2) { + writer.append(',').println(); + writer.append('"').append("manager").append('"').append(':').append('{'); + appendJSonValue(writer, "activeSessions", mBeanServer.getAttribute(objectName, "activeSessions").toString()).append(','); + appendJSonValue(writer, "sessionCounter", mBeanServer.getAttribute(objectName, "sessionCounter").toString()).append(','); + appendJSonValue(writer, "maxActive", mBeanServer.getAttribute(objectName, "maxActive").toString()).append(','); + appendJSonValue(writer, "rejectedSessions", mBeanServer.getAttribute(objectName, "rejectedSessions").toString()).append(','); + appendJSonValue(writer, "expiredSessions", mBeanServer.getAttribute(objectName, "expiredSessions").toString()).append(','); + appendJSonValue(writer, "sessionMaxAliveTime", mBeanServer.getAttribute(objectName, "sessionMaxAliveTime").toString()).append(','); + appendJSonValue(writer, "sessionAverageAliveTime", mBeanServer.getAttribute(objectName, "sessionAverageAliveTime").toString()).append(','); + appendJSonValue(writer, "processingTime", mBeanServer.getAttribute(objectName, "processingTime").toString()); + writer.append('}'); } } @@ -799,6 +971,12 @@ public class StatusTransformer { writer.print(jspReloadCount); } else if (mode == 1) { // for now we don't write out anything + } else if (mode == 2) { + writer.append(',').println(); + writer.append('"').append("jsp").append('"').append(':').append('{'); + appendJSonValue(writer, "jspCount", Integer.toString(jspCount)).append(','); + appendJSonValue(writer, "jspReloadCount", Integer.toString(jspReloadCount)); + writer.append('}'); } } @@ -816,11 +994,11 @@ public class StatusTransformer { public static void writeWrapper(PrintWriter writer, ObjectName objectName, MBeanServer mBeanServer, int mode) throws Exception { - if (mode == 0) { - String servletName = objectName.getKeyProperty("name"); + String servletName = objectName.getKeyProperty("name"); - String[] mappings = (String[]) mBeanServer.invoke(objectName, "findMappings", null, null); + String[] mappings = (String[]) mBeanServer.invoke(objectName, "findMappings", null, null); + if (mode == 0) { writer.print("<h2>"); writer.print(Escape.htmlElementContent(servletName)); if (mappings != null && mappings.length > 0) { @@ -851,6 +1029,16 @@ public class StatusTransformer { writer.print("</p>"); } else if (mode == 1) { // for now we don't write out the wrapper details + } else if (mode == 2) { + writer.append('{'); + appendJSonValue(writer, "servletName", JSONFilter.escape(servletName)).append(','); + appendJSonValue(writer, "processingTime", mBeanServer.getAttribute(objectName, "processingTime").toString()).append(','); + appendJSonValue(writer, "maxTime", mBeanServer.getAttribute(objectName, "maxTime").toString()).append(','); + appendJSonValue(writer, "requestCount", mBeanServer.getAttribute(objectName, "requestCount").toString()).append(','); + appendJSonValue(writer, "errorCount", mBeanServer.getAttribute(objectName, "errorCount").toString()).append(','); + appendJSonValue(writer, "loadTime", mBeanServer.getAttribute(objectName, "loadTime").toString()).append(','); + appendJSonValue(writer, "classLoadTime", mBeanServer.getAttribute(objectName, "classLoadTime").toString()); + writer.append('}'); } } diff --git a/test/org/apache/catalina/manager/TestStatusTransformer.java b/test/org/apache/catalina/manager/TestStatusTransformer.java new file mode 100644 index 0000000000..b031004979 --- /dev/null +++ b/test/org/apache/catalina/manager/TestStatusTransformer.java @@ -0,0 +1,82 @@ +/* + * 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 + * + * http://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. + */ +package org.apache.catalina.manager; + +import java.io.File; + +import org.junit.Assert; +import org.junit.Test; + +import static org.apache.catalina.startup.SimpleHttpClient.CRLF; +import org.apache.catalina.Context; +import org.apache.catalina.Wrapper; +import org.apache.catalina.startup.SimpleHttpClient; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.json.JSONParser; + +public class TestStatusTransformer extends TomcatBaseTest { + + @Test + public void testJSON() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + // Add default servlet to make some requests + File appDir = new File("test/webapp"); + Context ctxt = tomcat.addContext("", appDir.getAbsolutePath()); + ctxt.setPrivileged(true); + Wrapper defaultServlet = Tomcat.addServlet(ctxt, "default", + "org.apache.catalina.servlets.DefaultServlet"); + defaultServlet.addInitParameter("fileEncoding", "ISO-8859-1"); + ctxt.addServletMappingDecoded("/", "default"); + Tomcat.addServlet(ctxt, "status", "org.apache.catalina.manager.StatusManagerServlet"); + ctxt.addServletMappingDecoded("/status/*", "status"); + ctxt.addMimeMapping("html", "text/html"); + Context ctxt2 = tomcat.addContext("/test", null); + Tomcat.addServlet(ctxt2, "status", "org.apache.catalina.manager.StatusManagerServlet"); + ctxt.addServletMappingDecoded("/somepath/*", "status"); + tomcat.start(); + + SimpleHttpClient client = new SimpleHttpClient() { + @Override + public boolean isResponseBodyOK() { + return true; + } + }; + client.setPort(getPort()); + client.setRequest(new String[] { + "GET /index.html HTTP/1.1" + CRLF + + "Host: localhost" + CRLF + + "Connection: Close" + CRLF + CRLF }); + client.connect(); + client.processRequest(true); + + client.setRequest(new String[] { + "GET /status/all?JSON=true HTTP/1.1" + CRLF + + "Host: localhost" + CRLF + + "Connection: Close" + CRLF + CRLF }); + client.connect(); + client.processRequest(true); + String json = client.getResponseBody(); + + JSONParser parser = new JSONParser(json); + String result = parser.parse().toString(); + System.out.println(result); + Assert.assertTrue(result.contains("name=localhost/")); + } + +} diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index a82490a6a8..fe075b3259 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -111,6 +111,11 @@ <bug>67667</bug>: <code>TLSCertificateReloadListener</code> prints unreadable rendering of <code>X509Certificate#getNotAfter()</code>. (michaelo) </fix> + <update> + The status servlet included in the manager webapp can now output + statistics as JSON, using the <code>JSON=true</code> URL parameter. + (remm) + </update> </changelog> </subsection> <subsection name="Other"> diff --git a/webapps/docs/manager-howto.xml b/webapps/docs/manager-howto.xml index c9f2541e2f..f2a0e1b785 100644 --- a/webapps/docs/manager-howto.xml +++ b/webapps/docs/manager-howto.xml @@ -1023,6 +1023,11 @@ http://localhost:8080/manager/status/all?XML=true</source> <p>Displays server status information in XML format.</p> +<source>http://localhost:8080/manager/status?JSON=true +http://localhost:8080/manager/status/all?JSON=true</source> + +<p>Displays server status information in JSON format.</p> + <p>First, you have the server and JVM version number, JVM provider, OS name and number followed by the architecture type.</p> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org