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

com.librato.metrics.LibratoReporter Maven / Gradle / Ivy

package com.librato.metrics;

import com.ning.http.client.*;
import com.yammer.metrics.Metrics;
import com.yammer.metrics.core.*;
import com.yammer.metrics.reporting.AbstractPollingReporter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.SortedMap;
import java.util.concurrent.TimeUnit;

/**
 * User: mihasya
 * Date: 6/14/12
 * Time: 1:08 PM
 * A reporter for publishing metrics to Librato Metrics
 */
public class LibratoReporter extends AbstractPollingReporter implements MetricProcessor {
    private final String source;

    private final Realm authRealm;
    private final String apiUrl;
    private final long timeout;
    private final TimeUnit timeoutUnit;

    private final LibratoUtil.Sanitizer sanitizer;

    protected final MetricsRegistry registry;
    protected final MetricPredicate predicate;
    protected final Clock clock;
    protected final VirtualMachineMetrics vm;
    protected final boolean reportVmMetrics;

    private final AsyncHttpClient httpClient = new AsyncHttpClient();

    private static final LibratoUtil util = new LibratoUtil();

    private static final Logger LOG = LoggerFactory.getLogger(LibratoReporter.class);

    /**
     * private to prevent someone from accidentally actually using this constructor. see .builder()
     */
    private LibratoReporter(Realm authRealm, String apiUrl, String name, final LibratoUtil.Sanitizer customSanitizer,
                            String source, long timeout, TimeUnit timeoutUnit, MetricsRegistry registry,
                            MetricPredicate predicate, Clock clock, VirtualMachineMetrics vm, boolean reportVmMetrics) {
        super(registry, name);
        this.authRealm = authRealm;
        this.sanitizer = new LibratoUtil.Sanitizer() {
            @Override
            public String apply(String name) {
                return LibratoUtil.lastPassSanitizer.apply(customSanitizer.apply(name));
            }
        };
        this.apiUrl = apiUrl;
        this.source = source;
        this.timeout = timeout;
        this.timeoutUnit = timeoutUnit;
        this.registry = registry;
        this.predicate = predicate;
        this.clock = clock;
        this.vm = vm;
        this.reportVmMetrics = reportVmMetrics;
    }

    @Override
    public void run() {
        // accumulate all the metrics in the batch, then post it allowing the LibratoBatch class to break up the work
        MetricsLibratoBatch batch = new MetricsLibratoBatch(LibratoBatch.DEFAULT_BATCH_SIZE, timeout, timeoutUnit);
        if (reportVmMetrics) {
            reportVmMetrics(batch);
        }
        reportRegularMetrics(batch);
        AsyncHttpClient.BoundRequestBuilder builder = httpClient.preparePost(apiUrl);
        builder.addHeader("Content-Type", "application/json");
        builder.setRealm(authRealm);

        try {
            batch.post(builder, source, TimeUnit.MILLISECONDS.toSeconds(Clock.defaultClock().time()));
        } catch (Exception e) {
            LOG.error("Librato post failed: ", e);
        }
    }

    protected void reportVmMetrics(MetricsLibratoBatch batch) {
        util.addVmMetricsToBatch(vm, batch);
    }

    protected void reportRegularMetrics(MetricsLibratoBatch batch) {
        for (Map.Entry> entry :
                getMetricsRegistry().groupedMetrics(predicate).entrySet()) {

            for (Map.Entry subEntry : entry.getValue().entrySet()) {
                final Metric metric = subEntry.getValue();
                if (metric != null) {
                    try {
                        metric.processWith(this, subEntry.getKey(), batch);
                    } catch (Exception e) {
                        LOG.error("Error processing regular metrics:", e);
                    }
                }
            }
        }
    }

    private String getStringName(MetricName fullName) {
        return sanitizer.apply(util.nameToString(fullName));
    }

    @Override
    public void processMeter(MetricName name, Metered meter, MetricsLibratoBatch batch) throws Exception {
        batch.addMetered(getStringName(name), meter);
    }

    @Override
    public void processCounter(MetricName name, Counter counter, MetricsLibratoBatch batch) throws Exception {
         batch.addCounterMeasurement(getStringName(name), counter.count());
    }

    @Override
    public void processHistogram(MetricName name, Histogram histogram, MetricsLibratoBatch batch) throws Exception {
        String sanitizedName = getStringName(name);
        batch.addSummarizable(sanitizedName, histogram);
        batch.addSampling(sanitizedName, histogram);
    }

    @Override
    public void processTimer(MetricName name, Timer timer, MetricsLibratoBatch batch) throws Exception {
        String sanitizedName = getStringName(name);
        batch.addMetered(sanitizedName, timer);
        batch.addSummarizable(sanitizedName, timer);
        batch.addSampling(sanitizedName, timer);
    }

    @Override
    public void processGauge(MetricName name, Gauge gauge, MetricsLibratoBatch batch) throws Exception {
        if (gauge.value() instanceof Number) {
            batch.addGauge(getStringName(name), gauge);
        }
    }

    /**
     * a builder for the LibratoReporter class that requires things that cannot be inferred and uses
     * sane default values for everything else.
     */
    public static class Builder {
        private final String username;
        private final String token;
        private final String source;

        private String apiUrl = "https://metrics-api.librato.com/v1/metrics";

        private LibratoUtil.Sanitizer sanitizer = LibratoUtil.noopSanitizer;

        private long timeout = 5;
        private TimeUnit timeoutUnit = TimeUnit.SECONDS;

        private String name = "librato-reporter";
        private MetricsRegistry registry = Metrics.defaultRegistry();
        private MetricPredicate predicate = MetricPredicate.ALL;
        private Clock clock = Clock.defaultClock();
        private VirtualMachineMetrics vm = VirtualMachineMetrics.getInstance();
        private boolean reportVmMetrics = true;

        public Builder(String username, String token, String source) {
            this.username = username;
            this.token = token;
            this.source = source;
        }

        /**
         * publish to a custom URL (for internal testing)
         * @param apiUrl custom API endpoint to use
         * @return itself
         */
        public Builder setApiUrl(String apiUrl) {
            this.apiUrl = apiUrl;
            return this;
        }

        /**
         * set the HTTP timeout for a publishing attempt
         * @param timeout duration to expect a response
         * @param timeoutUnit unit for duration
         * @return itself
         */
        public Builder setTimeout(long timeout, TimeUnit timeoutUnit) {
            this.timeout = timeout;
            this.timeoutUnit = timeoutUnit;
            return this;
        }

        /**
         * Specify a custom name for this reporter
         * @param name the name to be used
         * @return itself
         */
        public Builder setName(String name) {
            this.name = name;
            return this;
        }

        /**
         * Use a custom sanitizer. All metric names are run through a sanitizer to ensure validity before being sent
         * along. Librato places some restrictions on the characters allowed in keys, so all keys are ultimately run
         * through LibratoUtil.lastPassSanitizer. Specifying an additional sanitizer (that runs before lastPassSanitizer)
         * allows the user to customize what they want done about invalid characters and excessively long metric names.
         * @param sanitizer the custom sanitizer to use  (defaults to a noop sanitizer).
         * @return itself
         */
        public Builder setSanitizer(LibratoUtil.Sanitizer sanitizer) {
            this.sanitizer = sanitizer;
            return this;
        }

        /**
         * override default MetricsRegistry
         * @param registry registry to be used
         * @return itself
         */
        public Builder setRegistry(MetricsRegistry registry) {
            this.registry = registry;
            return this;
        }

        /**
         * Filter the metrics that this particular reporter publishes
         * @param predicate the predicate by which the metrics are to be filtered
         * @return itself
         */
        public Builder setPredicate(MetricPredicate predicate) {
            this.predicate = predicate;
            return this;
        }

        /**
         * use a custom clock
         * @param clock to be used
         * @return itself
         */
        public Builder setClock(Clock clock) {
            this.clock = clock;
            return this;
        }

        /**
         * use a custom instance of VirtualMachineMetrics
         * @param vm the instance to use
         * @return itself
         */
        public Builder setVm(VirtualMachineMetrics vm) {
            this.vm = vm;
            return this;
        }

        /**
         * turn on/off reporting of VM internal metrics (if, for example, you already get those elsewhere)
         * @param reportVmMetrics true (report) or false (don't report)
         * @return itself
         */
        public Builder setReportVmMetrics(boolean reportVmMetrics) {
            this.reportVmMetrics = reportVmMetrics;
            return this;
        }

        /**
         * Build the LibratoReporter as configured by this Builder
         * @return a fully configured LibratoReporter
         */
        public LibratoReporter build() {
            return new LibratoReporter(new Realm.RealmBuilder().setPrincipal(username).setPassword(token).build(),
                    apiUrl, name, sanitizer, source, timeout, timeoutUnit,
                    registry, predicate, clock, vm, reportVmMetrics);
        }
    }

    /**
     * convenience method for creating a Builder
     */
    public static Builder builder(String username, String token, String source) {
        return new Builder(username, token, source);
    }

    /**
     * @param builder a LibratoReporter.Builder
     * @param interval the interval at which the metrics are to be reporter
     * @param unit the timeunit for interval
     */
    public static void enable(Builder builder, long interval, TimeUnit unit) {
        builder.build().start(interval, unit);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy