/* * 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 com.alibaba.metrics.rest; import com.alibaba.fastjson.JSON; import com.alibaba.metrics.ClusterHistogram; import com.alibaba.metrics.Compass; import com.alibaba.metrics.Counter; import com.alibaba.metrics.FastCompass; import com.alibaba.metrics.Gauge; import com.alibaba.metrics.Histogram; import com.alibaba.metrics.IMetricManager; import com.alibaba.metrics.Meter; import com.alibaba.metrics.MetricFilter; import com.alibaba.metrics.MetricLevel; import com.alibaba.metrics.MetricManager; import com.alibaba.metrics.MetricName; import com.alibaba.metrics.MetricRegistry; import com.alibaba.metrics.ReservoirType; import com.alibaba.metrics.Timer; import com.alibaba.metrics.common.CollectLevel; import com.alibaba.metrics.common.MetricObject; import com.alibaba.metrics.common.MetricsCollector; import com.alibaba.metrics.common.MetricsCollectorFactory; import com.alibaba.metrics.common.filter.MetricNameSetFilter; import com.alibaba.metrics.server.MetricsSearchService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.TimeUnit; import static com.alibaba.metrics.rest.Utils.buildResult; import static com.alibaba.metrics.rest.Utils.buildResultPojo; @Path("/metrics") public class MetricsResource { private final static Logger logger = LoggerFactory.getLogger(MetricsResource.class); private static IMetricManager manager = MetricManager.getIMetricManager(); private static final double rateFactor = TimeUnit.SECONDS.toSeconds(1); private static final double durationFactor = 1.0 / TimeUnit.MILLISECONDS.toNanos(1); private static final String MULTI_GROUP_DELIM = ","; private static final MetricName baseName = new MetricName("middleware.metrics.rest.url"); /** * list接口序列化MetricObject时过滤掉timestamp/value */ private static final MetricObjectPropertyFilter filter = new MetricObjectPropertyFilter(); @Path("/list") @GET @Produces({Constants.PRODUCE_JSON_WITH_QUALITY_SOURCE, MediaType.TEXT_HTML}) public Response listMetrics() { if (manager.isEnabled()) { Map<String, Set<MetricObject>> metrics = new LinkedHashMap<String, Set<MetricObject>>(); for (String groupName : manager.listMetricGroups()) { MetricRegistry registry = manager.getMetricRegistryByGroup(groupName); Set<MetricObject> metricsPerRegistry = new LinkedHashSet<MetricObject>(); metricsPerRegistry.addAll(buildMetricRegistry(registry)); metrics.put(groupName, metricsPerRegistry); } try { String data = JSON.toJSONString(buildResultPojo(metrics, true, ""), filter); return Response.ok(data).build(); } catch (Exception e) { return buildResult(null, false, e.toString()); } } else { return buildResult(null, false, "Metrics has been disabled explicitly!"); } } @GET @Produces({Constants.PRODUCE_JSON_WITH_QUALITY_SOURCE, MediaType.TEXT_HTML}) public Response getMetrics() { return getMetrics("all"); } @GET @Produces({Constants.PRODUCE_JSON_WITH_QUALITY_SOURCE, MediaType.TEXT_HTML}) @Path("/{group}") public Response getMetrics(@DefaultValue("all") @PathParam("group") String group) { MetricName name = baseName.tagged("url", "/" + group).level(MetricLevel.TRIVIAL); Timer urlTimer = manager.getTimer("metrics", name, ReservoirType.BUCKET); Timer.Context context = urlTimer.time(); try { if (!manager.isEnabled()) { return buildResult(null, false, "Metrics has been disabled explicitly!"); } Map<String, List<MetricObject>> metricsData = new TreeMap<String, List<MetricObject>>(); if ("all".equalsIgnoreCase(group) || group == null) { for (String groupName : manager.listMetricGroups()) { try { MetricRegistry registry = manager.getMetricRegistryByGroup(groupName); metricsData.put(groupName, buildMetricRegistry(registry)); } catch (Throwable e) { return buildResult(null, false, e.toString()); } } return buildResult(metricsData, true, ""); } else if (group.contains(MULTI_GROUP_DELIM)) { String[] groups = group.split(MULTI_GROUP_DELIM); for (String groupName : groups) { try { // FIXME manager.getMetricRegistryByGroup will create one if group does not exist if (!manager.listMetricGroups().contains(groupName)) { continue; } MetricRegistry registry = manager.getMetricRegistryByGroup(groupName); metricsData.put(groupName, buildMetricRegistry(registry)); } catch (Throwable e) { return buildResult(null, false, e.toString()); } } return buildResult(metricsData, true, ""); } else { // FIXME manager.getMetricRegistryByGroup will create one if group does not exist List<String> groups = manager.listMetricGroups(); if (!groups.contains(group)) { return buildResult(null, false, "The specified group is not found!"); } MetricRegistry registry = manager.getMetricRegistryByGroup(group); return buildResult(buildMetricRegistry(registry), true, ""); } } finally { context.stop(); } } @GET @Produces({Constants.PRODUCE_JSON_WITH_QUALITY_SOURCE, MediaType.TEXT_HTML}) @Path("/{group}/level/{level}") public Response getMetricByLevel(final @PathParam("group") String group, final @PathParam("level") String level, final @QueryParam("above") boolean above) { if (!manager.isEnabled()) { return Response.status(Response.Status.FORBIDDEN).build(); } if (group.contains(MULTI_GROUP_DELIM)) { Map<String, List<MetricObject>> metricsData = new TreeMap<String, List<MetricObject>>(); String[] groups = group.split(MULTI_GROUP_DELIM); String[] levels = level.split(MULTI_GROUP_DELIM); for (int i = 0; i < groups.length; i++) { try { // FIXME manager.getMetricRegistryByGroup will create one if group does not exist if (!manager.listMetricGroups().contains(groups[i])) { continue; } MetricRegistry registry = manager.getMetricRegistryByGroup(groups[i]); MetricLevelFilter levelFilter = new MetricLevelFilter(MetricLevel.valueOf(levels[i]), above); metricsData.put(groups[i], buildMetricRegistry(registry, levelFilter)); } catch (Throwable e) { return buildResult(null, false, e.toString()); } } return buildResult(metricsData, true, ""); } else { // FIXME manager.getMetricRegistryByGroup will create one if group does not exist List<String> groups = manager.listMetricGroups(); if (!groups.contains(group)) { return buildResult(null, false, "The specified group is not found!"); } MetricRegistry registry = manager.getMetricRegistryByGroup(group); try { MetricLevelFilter levelFilter = new MetricLevelFilter(MetricLevel.valueOf(level), above); List<MetricObject> metricObjects = buildMetricRegistry(registry, levelFilter); if (metricObjects.isEmpty()) { return buildResult(null, false, "No metric matching the specified level found!"); } return buildResult(metricObjects); } catch (IllegalArgumentException e) { return buildResult(null, false, e.toString()); } } } @GET @Produces({Constants.PRODUCE_JSON_WITH_QUALITY_SOURCE, MediaType.TEXT_HTML}) @Path("/{group}/{metric}") public Response getMetric(final @PathParam("group") String group, final @PathParam("metric") String metricName, final @QueryParam("tagKey") List<String> tagKeys, final @QueryParam("tagValue") List<String> tagValues) { if (!manager.isEnabled()) { return Response.status(Response.Status.FORBIDDEN).build(); } List<String> groups = manager.listMetricGroups(); // FIXME manager.getMetricRegistryByGroup will create one if group does not exist if (!groups.contains(group)) { return buildResult(null, false, "The specified group is not found!"); } MetricRegistry registry = manager.getMetricRegistryByGroup(group); List<MetricObject> metricObjects = buildMetricRegistry(registry, new MetricNameSetFilter(metricName)); if (metricObjects.isEmpty()) { return buildResult(null, false, "The specified metric is not found!"); } return buildResult(metricObjects); } @GET @Produces({Constants.PRODUCE_JSON_WITH_QUALITY_SOURCE, MediaType.TEXT_HTML}) @Path("/specific") public Response getMetric(final @QueryParam("metric") Set<String> metricNames, @QueryParam("zeroIgnore") boolean zeroIgnore) { if (!manager.isEnabled()) { return Response.status(Response.Status.FORBIDDEN).build(); } MetricName name = baseName.tagged("url", "/specific").level(MetricLevel.TRIVIAL); Timer urlTimer = manager.getTimer("metrics", name, ReservoirType.BUCKET); Timer.Context context = urlTimer.time(); try { return getMetricsInternal(metricNames, zeroIgnore); } finally { context.stop(); } } @POST @Produces({Constants.PRODUCE_JSON_WITH_QUALITY_SOURCE, MediaType.TEXT_HTML}) @Path("/specific") public Response getMetricsByPost(MultivaluedMap<String, String> params, @QueryParam("zeroIgnore") boolean zeroIgnore) { final Set<String> metricNames = new HashSet<String>(params.get("metric")); return getMetric(metricNames, zeroIgnore); } @POST @Consumes(MediaType.APPLICATION_JSON) @Produces({Constants.PRODUCE_JSON_WITH_QUALITY_SOURCE, MediaType.TEXT_HTML}) @Path("/search") public Response searchMetrics(String metricsSearch, @HeaderParam("REMOTE_ADDR") String remoteAddr, @HeaderParam("X-Forwarded-For") String xForwardedFor, @Context HttpServletRequest httpServletRequest) { if (httpServletRequest != null){ logger.info("httpheader REMOTE_ADDR {}, httpheader X-Forwarded-For {};socket remoteaddr {}, port {}", remoteAddr, xForwardedFor, httpServletRequest.getRemoteAddr(), httpServletRequest.getRemotePort()); }else{ logger.info("httpheader REMOTE_ADDR {}, httpheader X-Forwarded-For {},httpServletRequest is null", remoteAddr, xForwardedFor); } if (!manager.isEnabled()) { return Response.status(Response.Status.FORBIDDEN).build(); } MetricName name = baseName.tagged("url", "/search").level(MetricLevel.TRIVIAL); Timer urlTimer = manager.getTimer("metrics", name, ReservoirType.BUCKET); Timer.Context context = urlTimer.time(); try { MetricsSearchService service = MetricsSearchService.getInstance(); return Response.ok(service.search(metricsSearch)).build(); } catch (Throwable e) { logger.error("Error during handing search request: ", e); return Response.serverError().build(); } finally { context.stop(); } } private Response getMetricsInternal(final Set<String> metricNames, boolean zeroIgnore) { // FIXME manager.getMetricRegistryByGroup will create one if group does not exist List<String> groups = manager.listMetricGroups(); MetricFilter keyFilter = new MetricNameSetFilter(metricNames); List<MetricObject> metricObjects = new ArrayList<MetricObject>(); for (String group : groups) { MetricRegistry registry = manager.getMetricRegistryByGroup(group); metricObjects.addAll(buildMetricRegistry(registry, keyFilter)); } if (metricObjects.isEmpty()) { return buildResult(null, false, "The specified metric is not found!"); } if (zeroIgnore){ List<MetricObject> allMetricObjects = metricObjects; metricObjects = new ArrayList<MetricObject>(); for(MetricObject o : allMetricObjects){ if ((o != null) && (!Utils.checkZero(o.getValue()))){ metricObjects.add(o); } } } return buildResult(metricObjects); } private List<MetricObject> buildMetricRegistry(MetricRegistry registry) { return buildMetricRegistry(registry, null); } private List<MetricObject> buildMetricRegistry(MetricRegistry registry, MetricFilter filter) { long ts = System.currentTimeMillis(); MetricsCollector collector = MetricsCollectorFactory.createNew(CollectLevel.NORMAL, rateFactor, durationFactor, filter); SortedMap<MetricName, Gauge> gauges = filter == null ? registry.getGauges() : registry.getGauges(filter); for (Map.Entry<MetricName, Gauge> entry : gauges.entrySet()) { collector.collect(entry.getKey(), entry.getValue(), ts); } SortedMap<MetricName, Counter> counters = filter == null ? registry.getCounters() : registry.getCounters(filter); for (Map.Entry<MetricName, Counter> entry : counters.entrySet()) { collector.collect(entry.getKey(), entry.getValue(), ts); } SortedMap<MetricName, Meter> meters = filter == null ? registry.getMeters() : registry.getMeters(filter); for (Map.Entry<MetricName, Meter> entry : meters.entrySet()) { collector.collect(entry.getKey(), entry.getValue(), ts); } SortedMap<MetricName, Histogram> histograms = filter == null ? registry.getHistograms() : registry.getHistograms(filter); for (Map.Entry<MetricName, Histogram> entry : histograms.entrySet()) { collector.collect(entry.getKey(), entry.getValue(), ts); } SortedMap<MetricName, Timer> timers = filter == null ? registry.getTimers() : registry.getTimers(filter); for (Map.Entry<MetricName, Timer> entry : timers.entrySet()) { collector.collect(entry.getKey(), entry.getValue(), ts); } SortedMap<MetricName, Compass> compasses = filter == null ? registry.getCompasses() : registry.getCompasses(filter); for (Map.Entry<MetricName, Compass> entry : compasses.entrySet()) { collector.collect(entry.getKey(), entry.getValue(), ts); } SortedMap<MetricName, FastCompass> fastCompasses = filter == null ? registry.getFastCompasses() : registry.getFastCompasses(filter); for (Map.Entry<MetricName, FastCompass> entry : fastCompasses.entrySet()) { collector.collect(entry.getKey(), entry.getValue(), ts); } SortedMap<MetricName, ClusterHistogram> clusterHistograms = filter == null ? registry.getClusterHistograms() : registry.getClusterHistograms(filter); for (Map.Entry<MetricName, ClusterHistogram> entry : clusterHistograms.entrySet()) { collector.collect(entry.getKey(), entry.getValue(), ts); } return collector.build(); } }