org.eclipse.jetty.servlet.StatisticsServlet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
The newest version!
//
// ========================================================================
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.servlet;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.QuotedQualityCSV;
import org.eclipse.jetty.io.ConnectionStatistics;
import org.eclipse.jetty.server.AbstractConnector;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.ConnectorStatistics;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.StatisticsHandler;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.ajax.JSON;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* Collect and report statistics about requests / responses / connections and more.
*
* You can use normal HTTP content negotiation to ask for the statistics.
* Specify a request Accept
header for one of the following formats:
*
*
* application/json
* text/xml
* text/html
* text/plain
- default if no Accept
header specified
*
*/
public class StatisticsServlet extends HttpServlet
{
private static final Logger LOG = Log.getLogger(StatisticsServlet.class);
boolean _restrictToLocalhost = true; // defaults to true
private StatisticsHandler _statsHandler;
private MemoryMXBean _memoryBean;
private List _connectors;
@Override
public void init() throws ServletException
{
ServletContext context = getServletContext();
ContextHandler.Context scontext = (ContextHandler.Context)context;
Server server = scontext.getContextHandler().getServer();
_statsHandler = server.getChildHandlerByClass(StatisticsHandler.class);
if (_statsHandler == null)
{
LOG.warn("Statistics Handler not installed!");
return;
}
_memoryBean = ManagementFactory.getMemoryMXBean();
_connectors = Arrays.asList(server.getConnectors());
if (getInitParameter("restrictToLocalhost") != null)
{
_restrictToLocalhost = "true".equals(getInitParameter("restrictToLocalhost"));
}
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
if (_statsHandler == null)
{
LOG.warn("Statistics Handler not installed!");
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
return;
}
if (_restrictToLocalhost)
{
if (!isLoopbackAddress(request.getRemoteAddr()))
{
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
}
if (Boolean.parseBoolean(request.getParameter("statsReset")))
{
response.setStatus(HttpServletResponse.SC_OK);
_statsHandler.statsReset();
return;
}
if (request.getParameter("xml") != null)
{
LOG.warn("'xml' parameter is deprecated, use 'Accept' request header instead");
}
List acceptable = getOrderedAcceptableMimeTypes(request);
for (String mimeType : acceptable)
{
switch (mimeType)
{
case "application/json":
writeJsonResponse(response);
return;
case "text/xml":
writeXmlResponse(response);
return;
case "text/html":
writeHtmlResponse(response);
return;
case "text/plain":
case "*/*":
writeTextResponse(response);
return;
default:
if (LOG.isDebugEnabled())
{
LOG.debug("Ignoring unrecognized mime-type {}", mimeType);
}
break;
}
}
// None of the listed `Accept` mime-types were found.
response.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE);
}
private void writeTextResponse(HttpServletResponse response) throws IOException
{
response.setCharacterEncoding("utf-8");
response.setContentType("text/plain");
CharSequence text = generateResponse(new TextProducer());
response.getWriter().print(text.toString());
}
private void writeHtmlResponse(HttpServletResponse response) throws IOException
{
response.setCharacterEncoding("utf-8");
response.setContentType("text/html");
Writer htmlWriter = new OutputStreamWriter(response.getOutputStream(), UTF_8);
htmlWriter.append("");
htmlWriter.append(this.getClass().getSimpleName());
htmlWriter.append(" \n");
CharSequence html = generateResponse(new HtmlProducer());
htmlWriter.append(html.toString());
htmlWriter.append("\n\n");
htmlWriter.flush();
}
private void writeXmlResponse(HttpServletResponse response) throws IOException
{
response.setCharacterEncoding("utf-8");
response.setContentType("text/xml");
CharSequence xml = generateResponse(new XmlProducer());
response.getWriter().print(xml.toString());
}
private void writeJsonResponse(HttpServletResponse response) throws IOException
{
// We intentionally don't put "UTF-8" into the response headers
// as the rules for application/json state that it should never be
// present on the HTTP Content-Type header.
// It is also true that the application/json mime-type is always UTF-8.
response.setContentType("application/json");
CharSequence json = generateResponse(new JsonProducer());
Writer jsonWriter = new OutputStreamWriter(response.getOutputStream(), UTF_8);
jsonWriter.append(json);
jsonWriter.flush();
}
private List getOrderedAcceptableMimeTypes(HttpServletRequest request)
{
QuotedQualityCSV values = new QuotedQualityCSV(QuotedQualityCSV.MOST_SPECIFIC_MIME_ORDERING);
// No accept header specified, try 'accept' parameter (for those clients that are
// so ancient that they cannot set the standard HTTP `Accept` header)
String acceptParameter = request.getParameter("accept");
if (acceptParameter != null)
{
values.addValue(acceptParameter);
}
Enumeration enumAccept = request.getHeaders(HttpHeader.ACCEPT.toString());
if (enumAccept != null)
{
while (enumAccept.hasMoreElements())
{
String value = enumAccept.nextElement();
if (StringUtil.isNotBlank(value))
{
values.addValue(value);
}
}
}
if (values.isEmpty())
{
// return that we allow ALL mime types
return Collections.singletonList("*/*");
}
return values.getValues();
}
private boolean isLoopbackAddress(String address)
{
try
{
InetAddress addr = InetAddress.getByName(address);
return addr.isLoopbackAddress();
}
catch (UnknownHostException e)
{
LOG.warn("Warning: attempt to access statistics servlet from " + address, e);
return false;
}
}
private CharSequence generateResponse(OutputProducer outputProducer)
{
Map top = new HashMap<>();
// requests
Map requests = new HashMap<>();
requests.put("statsOnMs", _statsHandler.getStatsOnMs());
requests.put("requests", _statsHandler.getRequests());
requests.put("requestsActive", _statsHandler.getRequestsActive());
requests.put("requestsActiveMax", _statsHandler.getRequestsActiveMax());
requests.put("requestsTimeTotal", _statsHandler.getRequestTimeTotal());
requests.put("requestsTimeMean", _statsHandler.getRequestTimeMean());
requests.put("requestsTimeMax", _statsHandler.getRequestTimeMax());
requests.put("requestsTimeStdDev", _statsHandler.getRequestTimeStdDev());
requests.put("dispatched", _statsHandler.getDispatched());
requests.put("dispatchedActive", _statsHandler.getDispatchedActive());
requests.put("dispatchedActiveMax", _statsHandler.getDispatchedActiveMax());
requests.put("dispatchedTimeTotal", _statsHandler.getDispatchedTimeTotal());
requests.put("dispatchedTimeMean", _statsHandler.getDispatchedTimeMean());
requests.put("dispatchedTimeMax", _statsHandler.getDispatchedTimeMax());
requests.put("dispatchedTimeStdDev", _statsHandler.getDispatchedTimeStdDev());
requests.put("asyncRequests", _statsHandler.getAsyncRequests());
requests.put("requestsSuspended", _statsHandler.getAsyncDispatches());
requests.put("requestsSuspendedMax", _statsHandler.getAsyncRequestsWaiting());
requests.put("requestsResumed", _statsHandler.getAsyncRequestsWaitingMax());
requests.put("requestsExpired", _statsHandler.getExpires());
requests.put("errors", _statsHandler.getErrors());
top.put("requests", requests);
// responses
Map responses = new HashMap<>();
responses.put("responses1xx", _statsHandler.getResponses1xx());
responses.put("responses2xx", _statsHandler.getResponses2xx());
responses.put("responses3xx", _statsHandler.getResponses3xx());
responses.put("responses4xx", _statsHandler.getResponses4xx());
responses.put("responses5xx", _statsHandler.getResponses5xx());
responses.put("responsesBytesTotal", _statsHandler.getResponsesBytesTotal());
top.put("responses", responses);
// connections
List