All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yammer.metrics.reporting.MetricsServlet Maven / Gradle / Ivy

package com.yammer.metrics.reporting;

import com.yammer.metrics.HealthChecks;
import com.yammer.metrics.Metrics;
import com.yammer.metrics.core.*;
import com.yammer.metrics.core.HealthCheck.Result;
import com.yammer.metrics.stats.Snapshot;
import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.Thread.State;
import java.text.MessageFormat;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.concurrent.TimeUnit;

public class MetricsServlet extends HttpServlet implements MetricProcessor {
    private static final long serialVersionUID = 1363903248255082791L;
    private static final Logger LOGGER = LoggerFactory.getLogger(MetricsServlet.class);
    public static final String ATTR_NAME_METRICS_REGISTRY = MetricsServlet.class.getSimpleName() + ":" + MetricsRegistry.class.getSimpleName();
    public static final String ATTR_NAME_HEALTHCHECK_REGISTRY = MetricsServlet.class.getSimpleName() + ":" + HealthCheckRegistry.class.getSimpleName();
    public static final String ATTR_NAME_VM_REGISTRY = MetricsServlet.class.getSimpleName() + ":" + VirtualMachineMetrics.class.getSimpleName();

    private static final String TEMPLATE = "\n" +
                                           "\n" +
                                           "\n" +
                                           "  Metrics\n" +
                                           "\n" +
                                           "\n" +
                                           "  

Operational Menu

\n" + " \n" + "\n" + ""; public static final String HEALTHCHECK_URI = "/healthcheck"; public static final String METRICS_URI = "/metrics"; public static final String PING_URI = "/ping"; public static final String THREADS_URI = "/threads"; private MetricsRegistry metricsRegistry; private HealthCheckRegistry healthCheckRegistry; private VirtualMachineMetrics vm; private JsonFactory factory; private String metricsUri, pingUri, threadsUri, healthcheckUri, contextPath; private boolean showJvmMetrics; public MetricsServlet() { this(new JsonFactory(new ObjectMapper()), HEALTHCHECK_URI, METRICS_URI, PING_URI, THREADS_URI, true); } public MetricsServlet(boolean showJvmMetrics) { this(new JsonFactory(new ObjectMapper()), HEALTHCHECK_URI, METRICS_URI, PING_URI, THREADS_URI, showJvmMetrics); } public MetricsServlet(JsonFactory factory) { this(factory, HEALTHCHECK_URI, METRICS_URI, PING_URI, THREADS_URI); } public MetricsServlet(JsonFactory factory, boolean showJvmMetrics) { this(factory, HEALTHCHECK_URI, METRICS_URI, PING_URI, THREADS_URI, showJvmMetrics); } public MetricsServlet(String healthcheckUri, String metricsUri, String pingUri, String threadsUri) { this(new JsonFactory(new ObjectMapper()), healthcheckUri, metricsUri, pingUri, threadsUri); } public MetricsServlet(JsonFactory factory, String healthcheckUri, String metricsUri, String pingUri, String threadsUri) { this(factory, healthcheckUri, metricsUri, pingUri, threadsUri, true); } public MetricsServlet(JsonFactory factory, String healthcheckUri, String metricsUri, String pingUri, String threadsUri, boolean showJvmMetrics) { this(Metrics.defaultRegistry(), HealthChecks.defaultRegistry(), factory, healthcheckUri, metricsUri, pingUri, threadsUri, showJvmMetrics); } public MetricsServlet(MetricsRegistry metricsRegistry, HealthCheckRegistry healthCheckRegistry, String healthcheckUri, String metricsUri, String pingUri, String threadsUri, boolean showJvmMetrics) { this(metricsRegistry, healthCheckRegistry, new JsonFactory(new ObjectMapper()), healthcheckUri, metricsUri, pingUri, threadsUri, showJvmMetrics); } public MetricsServlet(MetricsRegistry metricsRegistry, HealthCheckRegistry healthCheckRegistry, JsonFactory factory, String healthcheckUri, String metricsUri, String pingUri, String threadsUri, boolean showJvmMetrics) { this(metricsRegistry, healthCheckRegistry, VirtualMachineMetrics.getInstance(), factory, healthcheckUri, metricsUri, pingUri, threadsUri, showJvmMetrics); } public MetricsServlet(MetricsRegistry metricsRegistry, HealthCheckRegistry healthCheckRegistry, VirtualMachineMetrics vm, JsonFactory factory, String healthcheckUri, String metricsUri, String pingUri, String threadsUri, boolean showJvmMetrics) { this.metricsRegistry = metricsRegistry; this.healthCheckRegistry = healthCheckRegistry; this.vm = vm; this.factory = factory; this.metricsUri = metricsUri; this.pingUri = pingUri; this.threadsUri = threadsUri; this.healthcheckUri = healthcheckUri; this.showJvmMetrics = showJvmMetrics; } @Override public void init(ServletConfig config) throws ServletException { super.init(config); final ServletContext context = config.getServletContext(); this.contextPath = context.getContextPath(); this.metricsRegistry = putAttrIfAbsent(context, ATTR_NAME_METRICS_REGISTRY, this.metricsRegistry); this.healthCheckRegistry = putAttrIfAbsent(context, ATTR_NAME_HEALTHCHECK_REGISTRY, this.healthCheckRegistry); this.vm = putAttrIfAbsent(context, ATTR_NAME_VM_REGISTRY, this.vm); this.metricsUri = getParam(config.getInitParameter("metrics-uri"), this.metricsUri); this.pingUri = getParam(config.getInitParameter("ping-uri"), this.pingUri); this.threadsUri = getParam(config.getInitParameter("threads-uri"), this.threadsUri); this.healthcheckUri = getParam(config.getInitParameter("healthcheck-uri"), this.healthcheckUri); final String showJvmMetricsParam = config.getInitParameter("show-jvm-metrics"); if (showJvmMetricsParam != null) { this.showJvmMetrics = Boolean.parseBoolean(showJvmMetricsParam); } final Object factory = config.getServletContext().getAttribute(JsonFactory.class.getCanonicalName()); if (factory != null && factory instanceof JsonFactory) { this.factory = (JsonFactory) factory; } } private static String getParam(String initParam, String defaultValue) { return initParam == null ? defaultValue : initParam; } private static T putAttrIfAbsent(ServletContext context, String attrName, T defaultValue) { @SuppressWarnings("unchecked") T attrValue = (T)context.getAttribute(attrName); if (attrValue == null) { attrValue = defaultValue; context.setAttribute(attrName, attrValue); } return attrValue; } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); final String uri = req.getPathInfo(); final String path = this.contextPath + req.getServletPath(); if (uri == null || uri.equals("/")) { handleHome(path, resp); } else if (uri.startsWith(metricsUri)) { handleMetrics(req.getParameter("class"), Boolean.parseBoolean(req.getParameter("full-samples")), Boolean.parseBoolean(req.getParameter("pretty")), resp); } else if (uri.equals(pingUri)) { handlePing(resp); } else if (uri.equals(threadsUri)) { handleThreadDump(resp); } else if (uri.equals(healthcheckUri)) { handleHealthCheck(resp); } else { resp.sendError(HttpServletResponse.SC_NOT_FOUND); } } private void handleHome(String path, HttpServletResponse resp) throws IOException { resp.setStatus(HttpServletResponse.SC_OK); resp.setContentType("text/html"); final PrintWriter writer = resp.getWriter(); writer.println(MessageFormat.format(TEMPLATE, path, metricsUri, path, pingUri, path, threadsUri, path, healthcheckUri)); writer.close(); } @SuppressWarnings("ThrowableResultOfMethodCallIgnored") private void handleHealthCheck(HttpServletResponse resp) throws IOException { boolean allHealthy = true; final Map results = healthCheckRegistry.runHealthChecks(); for (Result result : results.values()) { allHealthy &= result.isHealthy(); } resp.setContentType("text/plain"); final PrintWriter writer = resp.getWriter(); if (results.isEmpty()) { resp.setStatus(HttpServletResponse.SC_NOT_IMPLEMENTED); writer.println("! No health checks registered."); } else { if (allHealthy) { resp.setStatus(HttpServletResponse.SC_OK); } else { resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } for (Entry entry : results.entrySet()) { final Result result = entry.getValue(); if (result.isHealthy()) { if (result.getMessage() != null) { writer.format("* %s: OK: %s\n", entry.getKey(), result.getMessage()); } else { writer.format("* %s: OK\n", entry.getKey()); } } else { if (result.getMessage() != null) { writer.format("! %s: ERROR\n! %s\n", entry.getKey(), result.getMessage()); } final Throwable error = result.getError(); if (error != null) { writer.println(); error.printStackTrace(writer); writer.println(); } } } } writer.close(); } private void handleThreadDump(HttpServletResponse resp) throws IOException { resp.setStatus(HttpServletResponse.SC_OK); resp.setContentType("text/plain"); final OutputStream output = resp.getOutputStream(); vm.threadDump(output); output.close(); } private static void handlePing(HttpServletResponse resp) throws IOException { resp.setStatus(HttpServletResponse.SC_OK); resp.setContentType("text/plain"); final PrintWriter writer = resp.getWriter(); writer.println("pong"); writer.close(); } static final class Context { public final boolean showFullSamples; public final JsonGenerator json; Context(JsonGenerator json, boolean showFullSamples) { this.json = json; this.showFullSamples = showFullSamples; } } private void handleMetrics(String classPrefix, boolean showFullSamples, boolean pretty, HttpServletResponse resp) throws IOException { resp.setStatus(HttpServletResponse.SC_OK); resp.setContentType("application/json"); final OutputStream output = resp.getOutputStream(); final JsonGenerator json = factory.createJsonGenerator(output, JsonEncoding.UTF8); if (pretty) { json.useDefaultPrettyPrinter(); } json.writeStartObject(); { if (showJvmMetrics && ("jvm".equals(classPrefix) || classPrefix == null)) { writeVmMetrics(json); } writeRegularMetrics(json, classPrefix, showFullSamples); } json.writeEndObject(); json.close(); } public void writeRegularMetrics(JsonGenerator json, String classPrefix, boolean showFullSamples) throws IOException { for (Entry> entry : metricsRegistry.groupedMetrics().entrySet()) { if (classPrefix == null || entry.getKey().startsWith(classPrefix)) { json.writeFieldName(entry.getKey()); json.writeStartObject(); { for (Entry subEntry : entry.getValue().entrySet()) { json.writeFieldName(subEntry.getKey().getName()); try { subEntry.getValue().processWith(this, subEntry.getKey(), new Context(json, showFullSamples)); } catch(Exception ignored) { } } } json.writeEndObject(); } } } @Override public void processHistogram(MetricName name, Histogram histogram, Context context) throws Exception { final JsonGenerator json = context.json; json.writeStartObject(); { json.writeStringField("type", "histogram"); json.writeNumberField("count", histogram.count()); writeSummarizable(histogram, json); writeSampling(histogram, json); if (context.showFullSamples) { json.writeObjectField("values", histogram.getSnapshot().getValues()); } } json.writeEndObject(); } @Override public void processCounter(MetricName name, Counter counter, Context context) throws Exception { final JsonGenerator json = context.json; json.writeStartObject(); { json.writeStringField("type", "counter"); json.writeNumberField("count", counter.count()); } json.writeEndObject(); } @Override public void processGauge(MetricName name, Gauge gauge, Context context) throws Exception { final JsonGenerator json = context.json; json.writeStartObject(); { json.writeStringField("type", "gauge"); json.writeObjectField("value", evaluateGauge(gauge)); } json.writeEndObject(); } private static Object evaluateGauge(Gauge gauge) { try { return gauge.value(); } catch (RuntimeException e) { LOGGER.warn("Error evaluating gauge", e); return "error reading gauge: " + e.getMessage(); } } private void writeVmMetrics(JsonGenerator json) throws IOException { json.writeFieldName("jvm"); json.writeStartObject(); { json.writeFieldName("vm"); json.writeStartObject(); { json.writeStringField("name", vm.name()); json.writeStringField("version", vm.version()); } json.writeEndObject(); json.writeFieldName("memory"); json.writeStartObject(); { json.writeNumberField("totalInit", vm.totalInit()); json.writeNumberField("totalUsed", vm.totalUsed()); json.writeNumberField("totalMax", vm.totalMax()); json.writeNumberField("totalCommitted", vm.totalCommitted()); json.writeNumberField("heapInit", vm.heapInit()); json.writeNumberField("heapUsed", vm.heapUsed()); json.writeNumberField("heapMax", vm.heapMax()); json.writeNumberField("heapCommitted", vm.heapCommitted()); json.writeNumberField("heap_usage", vm.heapUsage()); json.writeNumberField("non_heap_usage", vm.nonHeapUsage()); json.writeFieldName("memory_pool_usages"); json.writeStartObject(); { for (Entry pool : vm.memoryPoolUsage().entrySet()) { json.writeNumberField(pool.getKey(), pool.getValue()); } } json.writeEndObject(); } json.writeEndObject(); json.writeNumberField("daemon_thread_count", vm.daemonThreadCount()); json.writeNumberField("thread_count", vm.threadCount()); json.writeNumberField("current_time", System.currentTimeMillis()); json.writeNumberField("uptime", vm.uptime()); json.writeNumberField("fd_usage", vm.fileDescriptorUsage()); json.writeFieldName("thread-states"); json.writeStartObject(); { for (Entry entry : vm.threadStatePercentages().entrySet()) { json.writeNumberField(entry.getKey().toString().toLowerCase(), entry.getValue()); } } json.writeEndObject(); json.writeFieldName("garbage-collectors"); json.writeStartObject(); { for (Entry entry : vm.garbageCollectors().entrySet()) { json.writeFieldName(entry.getKey()); json.writeStartObject(); { final VirtualMachineMetrics.GarbageCollectorStats gc = entry.getValue(); json.writeNumberField("runs", gc.getRuns()); json.writeNumberField("time", gc.getTime(TimeUnit.MILLISECONDS)); } json.writeEndObject(); } } json.writeEndObject(); } json.writeEndObject(); } @Override public void processMeter(MetricName name, Metered meter, Context context) throws Exception { final JsonGenerator json = context.json; json.writeStartObject(); { json.writeStringField("type", "meter"); json.writeStringField("event_type", meter.eventType()); writeMeteredFields(meter, json); } json.writeEndObject(); } @Override public void processTimer(MetricName name, Timer timer, Context context) throws Exception { final JsonGenerator json = context.json; json.writeStartObject(); { json.writeStringField("type", "timer"); json.writeFieldName("duration"); json.writeStartObject(); { json.writeStringField("unit", timer.durationUnit().toString().toLowerCase()); writeSummarizable(timer, json); writeSampling(timer, json); if (context.showFullSamples) { json.writeObjectField("values", timer.getSnapshot().getValues()); } } json.writeEndObject(); json.writeFieldName("rate"); json.writeStartObject(); { writeMeteredFields(timer, json); } json.writeEndObject(); } json.writeEndObject(); } private static void writeSummarizable(Summarizable metric, JsonGenerator json) throws IOException { json.writeNumberField("min", metric.min()); json.writeNumberField("max", metric.max()); json.writeNumberField("mean", metric.mean()); json.writeNumberField("std_dev", metric.stdDev()); } private static void writeSampling(Sampling metric, JsonGenerator json) throws IOException { final Snapshot snapshot = metric.getSnapshot(); json.writeNumberField("median", snapshot.getMedian()); json.writeNumberField("p75", snapshot.get75thPercentile()); json.writeNumberField("p95", snapshot.get95thPercentile()); json.writeNumberField("p98", snapshot.get98thPercentile()); json.writeNumberField("p99", snapshot.get99thPercentile()); json.writeNumberField("p999",snapshot.get999thPercentile()); } private static void writeMeteredFields(Metered metered, JsonGenerator json) throws IOException { json.writeStringField("unit", metered.rateUnit().toString().toLowerCase()); json.writeNumberField("count", metered.count()); json.writeNumberField("mean", metered.meanRate()); json.writeNumberField("m1", metered.oneMinuteRate()); json.writeNumberField("m5", metered.fiveMinuteRate()); json.writeNumberField("m15", metered.fifteenMinuteRate()); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy