package org.apache.blur.metrics; /** * 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. */ import java.io.IOException; import java.io.Writer; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.yammer.metrics.Metrics; import com.yammer.metrics.core.Counter; import com.yammer.metrics.core.Gauge; import com.yammer.metrics.core.Histogram; import com.yammer.metrics.core.Meter; import com.yammer.metrics.core.Metered; import com.yammer.metrics.core.Metric; import com.yammer.metrics.core.MetricName; import com.yammer.metrics.core.MetricProcessor; import com.yammer.metrics.core.MetricsRegistry; import com.yammer.metrics.core.Timer; import com.yammer.metrics.reporting.AbstractPollingReporter; import com.yammer.metrics.stats.Snapshot; public class JSONReporter extends AbstractPollingReporter implements MetricProcessor<JSONReporter.Context> { private static final ResetableCharArrayWriter EMPTY = new ResetableCharArrayWriter() { { try { write("[]"); } catch (Exception e) { throw new RuntimeException(e); } } }; private static Log LOG = LogFactory.getLog(JSONReporter.class); private Context context; private int _256K = 262144; private ResetableCharArrayWriter writerInUse = new ResetableCharArrayWriter(_256K); private ResetableCharArrayWriter writerWriting = new ResetableCharArrayWriter(_256K); private static AtomicReference<ResetableCharArrayWriter> reading = new AtomicReference<ResetableCharArrayWriter>( EMPTY); public static void enable(String name, long period, TimeUnit unit, int numberOfElements) throws IOException { enable(Metrics.defaultRegistry(), name, period, unit, numberOfElements); } public static void enable(MetricsRegistry metricsRegistry, String name, long period, TimeUnit unit, int numberOfElements) throws IOException { JSONReporter reporter = new JSONReporter(metricsRegistry, name, numberOfElements); reporter.start(period, unit); } public static void writeJSONData(Writer writer) throws IOException { synchronized (reading) { ResetableCharArrayWriter reader = reading.get(); writer.write(reader.getBuffer(), 0, reader.size()); } } protected JSONReporter(MetricsRegistry registry, String name, int numberOfElements) { super(registry, name); this.context = new Context(numberOfElements); } static class Context { private final Map<MetricName, MetricInfo> metricInfoMap = new HashMap<MetricName, MetricInfo>(); private final Map<MetricName, String> typeTable; private final int numberOfElements; private long time; Context(int numberOfElements) { this.typeTable = new HashMap<MetricName, String>(); this.numberOfElements = numberOfElements; } long getTime() { return time; } void setTime(long time) { this.time = time; } public MetricInfo getMetricInfo(MetricName name) { MetricInfo info = metricInfoMap.get(name); if (info == null) { info = new MetricInfo(getName(name), typeTable.get(name), numberOfElements); metricInfoMap.put(name, info); } return info; } private String getName(MetricName metricName) { // String group = metricName.getGroup(); // String name = metricName.getName(); // String scope = metricName.getScope(); // String type = metricName.getType(); // JSONObject jsonObject = new JSONObject(); // jsonObject.put("name", name); // jsonObject.put("group", group); // jsonObject.put("scope", scope); // jsonObject.put("type", type); return metricName.toString(); } } @Override public void run() { try { context.setTime(System.currentTimeMillis()); for (Entry<String, SortedMap<MetricName, Metric>> entry : getMetricsRegistry().groupedMetrics().entrySet()) { for (Entry<MetricName, Metric> subEntry : entry.getValue().entrySet()) { MetricName name = subEntry.getKey(); Metric metric = subEntry.getValue(); if (metric instanceof Counter) { context.typeTable.put(name, "counter"); } else if (metric instanceof Gauge) { context.typeTable.put(name, "gauge"); } else if (metric instanceof Histogram) { context.typeTable.put(name, "histogram"); } else if (metric instanceof Meter) { context.typeTable.put(name, "meter"); } else if (metric instanceof Timer) { context.typeTable.put(name, "timer"); } metric.processWith(this, name, context); } } ResetableCharArrayWriter writer = getWriter(); writer.reset(); Set<Entry<MetricName, MetricInfo>> entrySet = context.metricInfoMap.entrySet(); writer.append('['); boolean flag = false; for (Entry<MetricName, MetricInfo> entry : entrySet) { if (flag) { writer.append(','); } entry.getValue().write(writer); flag = true; } writer.append(']'); swapWriter(); } catch (Throwable t) { LOG.error("Unknown error during the processing of metrics.", t); } } private void swapWriter() { synchronized (reading) { ResetableCharArrayWriter tmp1 = writerWriting; writerWriting = writerInUse; writerInUse = tmp1; reading.set(writerInUse); } } private ResetableCharArrayWriter getWriter() { return writerWriting; } @Override public void processMeter(MetricName name, Metered meter, Context context) throws Exception { MetricInfo info = context.getMetricInfo(name); long time = context.getTime(); addMeterInfo(time, meter, info); } @Override public void processCounter(MetricName name, Counter counter, Context context) throws Exception { MetricInfo info = context.getMetricInfo(name); long time = context.getTime(); info.addNumber("timestamp", time); info.addNumber("value", counter.count()); } @Override public void processHistogram(MetricName name, Histogram histogram, Context context) throws Exception { MetricInfo info = context.getMetricInfo(name); long time = context.getTime(); info.addNumber("timestamp", time); info.addNumber("min", histogram.min()); info.addNumber("max", histogram.max()); info.addNumber("mean", histogram.mean()); info.addNumber("stdDev", histogram.stdDev()); Snapshot snapshot = histogram.getSnapshot(); info.addNumber("median", snapshot.getMedian()); info.addNumber("75%", snapshot.get75thPercentile()); info.addNumber("95%", snapshot.get95thPercentile()); info.addNumber("98%", snapshot.get98thPercentile()); info.addNumber("99%", snapshot.get99thPercentile()); info.addNumber("99.9%", snapshot.get999thPercentile()); } @Override public void processTimer(MetricName name, Timer timer, Context context) throws Exception { MetricInfo info = context.getMetricInfo(name); long time = context.getTime(); addMeterInfo(time, timer, info); info.setMetaData("unit", timer.durationUnit().toString()); info.addNumber("min", timer.min()); info.addNumber("max", timer.max()); info.addNumber("mean", timer.mean()); info.addNumber("stdDev", timer.stdDev()); Snapshot snapshot = timer.getSnapshot(); info.addNumber("median", snapshot.getMedian()); info.addNumber("75%", snapshot.get75thPercentile()); info.addNumber("95%", snapshot.get95thPercentile()); info.addNumber("98%", snapshot.get98thPercentile()); info.addNumber("99%", snapshot.get99thPercentile()); info.addNumber("99.9%", snapshot.get999thPercentile()); } @Override public void processGauge(MetricName name, Gauge<?> gauge, Context context) throws Exception { MetricInfo info = context.getMetricInfo(name); long time = context.getTime(); info.addNumber("timestamp", time); info.addNumber("value", getDouble(gauge.value())); } private double getDouble(Object value) { if (value instanceof Integer) { Integer v = (Integer) value; return (int) v; } else if (value instanceof Long) { Long v = (Long) value; return (long) v; } else if (value instanceof Double) { Double v = (Double) value; return v; } else if (value instanceof Float) { Float v = (Float) value; return (float) v; } return 0; } private void addMeterInfo(Long time, Metered meter, MetricInfo info) { info.addNumber("timestamp", time); info.setMetaData("rateUnit", meter.rateUnit().toString()); info.setMetaData("eventType", meter.eventType()); info.addNumber("count", meter.count()); info.addNumber("meanRate", meter.meanRate()); info.addNumber("oneMinuteRate", meter.oneMinuteRate()); info.addNumber("fiveMinuteRate", meter.fiveMinuteRate()); info.addNumber("fifteenMinuteRate", meter.fifteenMinuteRate()); } }