org.zenoss.metrics.reporter.ZenossMetricsReporter.java Source code

Java tutorial

Introduction

Here is the source code for org.zenoss.metrics.reporter.ZenossMetricsReporter.java

Source

/*
 * ****************************************************************************
 *
 *  Copyright (C) Zenoss, Inc. 2013, all rights reserved.
 *
 *  This content is made available according to terms specified in
 *  License.zenoss distributed with this file.
 *
 * ***************************************************************************
 */
package org.zenoss.metrics.reporter;

import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.yammer.metrics.Metrics;
import com.yammer.metrics.core.Clock;
import com.yammer.metrics.core.Counter;
import com.yammer.metrics.core.Gauge;
import com.yammer.metrics.core.Histogram;
import com.yammer.metrics.core.Metered;
import com.yammer.metrics.core.Metric;
import com.yammer.metrics.core.MetricName;
import com.yammer.metrics.core.MetricPredicate;
import com.yammer.metrics.core.MetricProcessor;
import com.yammer.metrics.core.MetricsRegistry;
import com.yammer.metrics.core.Sampling;
import com.yammer.metrics.core.Summarizable;
import com.yammer.metrics.core.Timer;
import com.yammer.metrics.core.VirtualMachineMetrics;
import com.yammer.metrics.reporting.AbstractPollingReporter;
import com.yammer.metrics.stats.Snapshot;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpResponseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.Thread.State;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.concurrent.TimeUnit;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Report metrics to Zenoss using the configure MetricPoster method
 */
public class ZenossMetricsReporter extends AbstractPollingReporter implements MetricProcessor<MetricBatch> {

    private static final Logger LOG = LoggerFactory.getLogger(ZenossMetricsReporter.class);
    public static final Joiner DOT_JOINER = Joiner.on(".").skipNulls();

    private final MetricPredicate filter;
    private final Clock clock;
    private final boolean reportJvmMetrics;
    private final String metricPrefix;
    private final MetricPoster poster;
    private final Map<String, String> tags;
    private final VirtualMachineMetrics vm;
    private final long period;
    private final TimeUnit periodUnit;
    private final long shutdownTimeout;
    private final TimeUnit shutdownTimeoutUnit;

    private ZenossMetricsReporter(MetricsRegistry registry, String name, MetricPoster poster,
            MetricPredicate filter, String metricPrefix, Map<String, String> tags, Clock clock,
            VirtualMachineMetrics vm, boolean reportJvmMetrics, long period, TimeUnit periodUnit,
            long shutdownTimeout, TimeUnit shutdownTimeoutUnit) {
        super(registry, name);
        this.poster = poster;
        this.filter = filter;
        this.clock = clock;
        this.reportJvmMetrics = reportJvmMetrics;
        this.metricPrefix = Strings.nullToEmpty(metricPrefix).trim();
        this.tags = Maps.newHashMap(tags);
        this.vm = vm;
        this.period = period;
        this.periodUnit = periodUnit;
        this.shutdownTimeout = shutdownTimeout;
        this.shutdownTimeoutUnit = shutdownTimeoutUnit;
    }

    @Override
    public void start(long period, TimeUnit unit) {
        LOG.info("Starting ZenossMetricsReporter on {} {} frequency with poster {}", period, unit,
                poster.getClass());
        super.start(period, unit);
        this.poster.start();
    }

    public void start() {
        this.start(period, periodUnit);
    }

    @Override
    public void shutdown(long timeout, TimeUnit unit) throws InterruptedException {
        super.shutdown(timeout, unit);
        this.poster.shutdown();
    }

    public void stop() throws InterruptedException {
        this.shutdown(shutdownTimeout, shutdownTimeoutUnit);
    }

    @Override
    public void run() {
        final long timestamp = clock.time() / 1000;
        final MetricBatch batchContext = new MetricBatch(timestamp);
        if (reportJvmMetrics) {
            collectVmMetrics(batchContext);
        }
        collectMetrics(batchContext);
        try {

            LOG.debug("Posting {} metrics", batchContext.getMetrics().size());
            if (LOG.isTraceEnabled()) {
                for (org.zenoss.app.consumer.metric.data.Metric m : batchContext.getMetrics()) {
                    LOG.trace("Sending metric {}", m.toString());
                }
            }
            post(batchContext);
        } catch (HttpResponseException e) {
            if (e.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                // the cookies were cleared so try again
                try {
                    LOG.debug("Error posting metrics. Posting with batchContext.", e);
                    post(batchContext);
                    return;
                } catch (IOException j) {
                    // redefine the exception e
                    LOG.error("Error posting metrics", j);
                }
            } else {
                LOG.error("Error posting metrics", e);
            }
        } catch (IOException e) {
            LOG.error("Error posting metrics", e);
        }
    }

    void collectVmMetrics(MetricBatch batchContext) {

        addMetric("jvm.memory", "heap_usage", vm.heapUsage(), batchContext);
        addMetric("jvm.memory", "non_heap_usage", vm.nonHeapUsage(), batchContext);
        for (Entry<String, Double> pool : vm.memoryPoolUsage().entrySet()) {
            addMetric("jvm.memory.memory_pool_usages", safeString(pool.getKey()), pool.getValue(), batchContext);
        }

        addMetric("jvm", "daemon_thread_count", vm.daemonThreadCount(), batchContext);
        addMetric("jvm", "thread_count", vm.threadCount(), batchContext);
        addMetric("jvm", "uptime", vm.uptime(), batchContext);
        addMetric("jvm", "fd_usage", vm.fileDescriptorUsage(), batchContext);

        for (Entry<State, Double> entry : vm.threadStatePercentages().entrySet()) {
            addMetric("jvm.thread-states", entry.getKey().toString().toLowerCase(), entry.getValue(), batchContext);
        }

        for (Entry<String, VirtualMachineMetrics.GarbageCollectorStats> entry : vm.garbageCollectors().entrySet()) {
            final String name = "jvm.gc." + safeString(entry.getKey());
            addMetric(name, "time", entry.getValue().getTime(TimeUnit.MILLISECONDS), batchContext);
            addMetric(name, "runs", entry.getValue().getRuns(), batchContext);
        }
    }

    private String safeString(String key) {
        return key.replace(" ", "_"); //To change body of created methods use File | Settings | File Templates.
    }

    private void collectMetrics(MetricBatch batchContext) {
        final Collection<SortedMap<MetricName, Metric>> values = getMetricsRegistry().groupedMetrics(this.filter)
                .values();
        for (SortedMap<MetricName, Metric> map : values) {
            for (Entry<MetricName, Metric> entry : map.entrySet()) {
                final Metric metric = entry.getValue();
                if (metric != null) {
                    try {
                        metric.processWith(this, entry.getKey(), batchContext);
                    } catch (Exception e) {
                        LOG.error("Error processing metric " + entry.getKey().getName(), e);
                    }
                }
            }
        }
    }

    @Override
    public void processMeter(MetricName metricName, Metered meter, MetricBatch context) throws Exception {
        addMetric(metricName, "count", meter.count(), context);
        addMetric(metricName, "meanRate", meter.meanRate(), context);
        addMetric(metricName, "1MinuteRate", meter.oneMinuteRate(), context);
        addMetric(metricName, "5MinuteRate", meter.fiveMinuteRate(), context);
        addMetric(metricName, "15MinuteRate", meter.fifteenMinuteRate(), context);
    }

    @Override
    public void processCounter(MetricName name, Counter counter, MetricBatch context) throws Exception {
        addMetric(name, "count", counter.count(), context);
    }

    @Override
    public void processHistogram(MetricName name, Histogram histogram, MetricBatch context) throws Exception {
        processSummarizable(context, name, histogram);
        processSampling(context, name, histogram);
    }

    @Override
    public void processTimer(MetricName name, Timer timer, MetricBatch context) throws Exception {
        processMeter(name, timer, context);
        processSummarizable(context, name, timer);
        processSampling(context, name, timer);
    }

    private void processSummarizable(MetricBatch context, MetricName mName, Summarizable metric)
            throws IOException {
        addMetric(mName, "min", metric.min(), context);
        addMetric(mName, "max", metric.max(), context);
        addMetric(mName, "mean", metric.mean(), context);
        addMetric(mName, "stddev", metric.stdDev(), context);
    }

    private void processSampling(MetricBatch context, MetricName mName, Sampling metric) throws IOException {
        final Snapshot snapshot = metric.getSnapshot();
        addMetric(mName, "median", snapshot.getMedian(), context);
        addMetric(mName, "75Percentile", snapshot.get75thPercentile(), context);
        addMetric(mName, "95Percentile", snapshot.get95thPercentile(), context);
        addMetric(mName, "98Percentile", snapshot.get98thPercentile(), context);
        addMetric(mName, "99Percentile", snapshot.get99thPercentile(), context);
        addMetric(mName, "999Percentile", snapshot.get999thPercentile(), context);
    }

    @Override
    public void processGauge(MetricName name, Gauge<?> gauge, MetricBatch context) throws Exception {
        Object gaugeValue = gauge.value();
        if (gaugeValue instanceof Number) {
            Number value = (Number) gaugeValue;
            addMetric(name, "value", value.doubleValue(), context);
        } else {
            LOG.debug("Un-reportable gauge %s; type: %s", name.getName(), gaugeValue.getClass());
        }
    }

    private String getName(MetricName metricName) {

        ArrayList<String> parts = new ArrayList<>(5);
        if (!Strings.isNullOrEmpty(metricPrefix)) {
            parts.add(metricPrefix);
        }
        parts.add(metricName.getGroup());
        parts.add(metricName.getType());
        parts.add(metricName.getName());
        if (metricName.hasScope()) {
            parts.add(metricName.getScope());
        }
        return DOT_JOINER.join(parts);
    }

    private void addMetric(MetricName mName, String valueName, double value, MetricBatch batch) {
        addMetric(getName(mName), valueName, value, batch);
    }

    private void addMetric(String metricName, String valueName, double value, MetricBatch batch) {
        final org.zenoss.app.consumer.metric.data.Metric metric = new org.zenoss.app.consumer.metric.data.Metric();
        metric.setMetric(String.format("%s.%s", metricName, valueName));
        metric.setTimestamp(batch.getTimestamp());
        metric.setValue(value);
        metric.setTags(this.tags);
        batch.addMetric(metric);
    }

    private void post(MetricBatch batchContext) throws IOException {
        this.poster.post(batchContext);
    }

    /**
     * Build a new ZenossMetricsReporter
     */
    public static final class Builder {
        private final MetricPoster poster;
        private String name = "zenoss-reporter";
        private MetricsRegistry registry = Metrics.defaultRegistry();
        private MetricPredicate predicate = MetricPredicate.ALL;
        private Map<String, String> tags = Collections.emptyMap();
        private String metricPrefix;
        private Clock clock = Clock.defaultClock();
        private VirtualMachineMetrics vm = VirtualMachineMetrics.getInstance();
        private boolean reportJvmMetrics = true;
        private long period = 30;
        private TimeUnit periodUnit = TimeUnit.SECONDS;
        private long shutdownTimeout = 5;
        private TimeUnit shutdownTimeoutUnit = TimeUnit.SECONDS;

        /**
         * Create a Builder for a ZenossMetricsReporter
         *
         * @param poster Required, how to send data
         */
        public Builder(MetricPoster poster) {
            checkNotNull(poster);
            this.poster = poster;
        }

        public Builder setPredicate(MetricPredicate predicate) {
            checkNotNull(predicate);
            this.predicate = predicate;
            return this;
        }

        public Builder setTags(Map<String, String> tags) {
            checkNotNull(tags);
            this.tags = tags;
            return this;
        }

        public Builder setName(String name) {
            checkArgument(Strings.nullToEmpty(name).trim().length() > 0);
            this.name = name;
            return this;
        }

        public Builder setClock(Clock clock) {
            checkNotNull(clock);
            this.clock = clock;
            return this;
        }

        public Builder setRegistry(MetricsRegistry registry) {
            checkNotNull(registry);
            this.registry = registry;
            return this;
        }

        public Builder setMetricPrefix(String metricPrefix) {
            this.metricPrefix = metricPrefix;
            return this;
        }

        public Builder setReportJvmMetrics(boolean report) {
            this.reportJvmMetrics = report;
            return this;
        }

        public Builder setFrequency(long period, TimeUnit periodUnit) {
            this.period = period;
            this.periodUnit = periodUnit;
            return this;
        }

        public Builder setShutdownTimeout(long timeout, TimeUnit unit) {
            this.shutdownTimeout = timeout;
            this.shutdownTimeoutUnit = unit;
            return this;
        }

        public ZenossMetricsReporter build() {
            return new ZenossMetricsReporter(registry, name, poster, predicate, metricPrefix, tags, clock, vm,
                    reportJvmMetrics, period, periodUnit, shutdownTimeout, shutdownTimeoutUnit);
        }
    }
}