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

com.spotify.helios.servicescommon.statistics.FastForwardReporter Maven / Gradle / Ivy

There is a newer version: 0.9.9
Show newest version
/*
 * Copyright (c) 2014 Spotify AB.
 *
 * Licensed 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.spotify.helios.servicescommon.statistics;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.net.HostAndPort;
import com.google.common.util.concurrent.ThreadFactoryBuilder;

import com.codahale.metrics.Counter;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Metered;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Snapshot;
import com.codahale.metrics.Timer;
import com.spotify.helios.common.Version;

import eu.toolchain.ffwd.FastForward;
import eu.toolchain.ffwd.Metric;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

import io.dropwizard.lifecycle.Managed;

/**
 * Sends the metrics in a v3 MetricRegistry to
 * FastForward.
 *
 * 

The String "name" of the Metric is sent as the 'what' attribute in the FastForward metric. No * translation of the (possibly long, Java classname-ish) name is done. The type of metric is * included in the 'metric_type' attribute.

* *

For meters, timers, and histograms, the various component values of the * com.codahale.metrics.Metric (m1/m5/m15, p50, p99 etc) are sent as individual {@link Metric} * instances. The 'stat' attribute is filled out with the corresponding statistic.

* *

Note that not every statistic from Meters/Timers/Histograms is sent to FastForward in order * to limit the amount of data being sent and needing to be stored downstream. For Metered * instances, only 1m and 5m is sent. For Histograms, just median, p75 and p99 (plus * min/max/mean/stddev). *

*/ public class FastForwardReporter implements Managed { private static final Logger log = LoggerFactory.getLogger(FastForwardReporter.class); public static FastForwardReporter create(MetricRegistry registry, Optional address, String metricKey, int intervalSeconds) throws SocketException, UnknownHostException { final FastForward ff; if (address.isPresent()) { ff = FastForward.setup(address.get().getHostText(), address.get().getPort()); } else { ff = FastForward.setup(); } final ThreadFactory threadFactory = new ThreadFactoryBuilder() .setDaemon(true) .setNameFormat("fast-forward-reporter-%d") .build(); final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(threadFactory); return new FastForwardReporter(ff, registry, executorService, metricKey, intervalSeconds, TimeUnit.SECONDS); } private final FastForward fastForward; private final MetricRegistry metricRegistry; private final ScheduledExecutorService executor; private final String key; private final long interval; private final TimeUnit intervalTimeUnit; FastForwardReporter(FastForward fastForward, MetricRegistry metricRegistry, ScheduledExecutorService executor, String key, long interval, TimeUnit intervalTimeUnit) { this.fastForward = fastForward; this.metricRegistry = metricRegistry; this.executor = executor; this.key = key; this.interval = interval; this.intervalTimeUnit = intervalTimeUnit; } @Override public void start() throws Exception { log.info("Scheduling reporting of metrics every {} {}", interval, intervalTimeUnit.name().toLowerCase()); // wrap the runnable in a try-catch as uncaught exceptions will prevent subsequent executions executor.scheduleAtFixedRate(() -> { try { reportOnce(); } catch (Exception e) { log.error("Exception in reporting loop", e); } }, interval, interval, intervalTimeUnit); } @Override public void stop() throws Exception { executor.shutdown(); } @VisibleForTesting void reportOnce() { metricRegistry.getGauges().forEach(this::reportGauge); metricRegistry.getCounters().forEach(this::reportCounter); metricRegistry.getMeters().forEach(this::reportMeter); metricRegistry.getHistograms().forEach(this::reportHistogram); metricRegistry.getTimers().forEach(this::reportTimer); } private void reportGauge(String name, Gauge gauge) { final Metric metric = createMetric(name, "gauge") .value(convert(gauge.getValue())); send(metric); } private void reportCounter(String name, Counter counter) { final Metric metric = createMetric(name, "counter") .value(counter.getCount()); send(metric); } private void reportMeter(String name, Meter meter) { final Metric metric = createMetric(name, "meter") .attribute("unit", "n/s"); reportMetered(metric, meter); } private void reportMetered(Metric metric, Metered metered) { //purposefully don't emit 15m as it is not very useful send(metric.attribute("stat", "1m").value(metered.getOneMinuteRate())); send(metric.attribute("stat", "5m").value(metered.getOneMinuteRate())); } private void reportHistogram(String name, Histogram histogram) { final Metric metric = createMetric(name, "histogram"); reportHistogram(metric, histogram.getSnapshot()); } private void reportHistogram(Metric metric, Snapshot snapshot) { send(metric.attribute("stat", "min").value(snapshot.getMin())); send(metric.attribute("stat", "max").value(snapshot.getMax())); send(metric.attribute("stat", "mean").value(snapshot.getMean())); send(metric.attribute("stat", "stddev").value(snapshot.getStdDev())); send(metric.attribute("stat", "median").value(snapshot.getMedian())); send(metric.attribute("stat", "p75").value(snapshot.get75thPercentile())); send(metric.attribute("stat", "p99").value(snapshot.get99thPercentile())); } private void reportTimer(String name, Timer timer) { final Metric metric = createMetric(name, "timer") .attribute("unit", "ns"); reportHistogram(metric, timer.getSnapshot()); reportMetered(metric, timer); } private Metric createMetric(String metricName, String metricType) { return FastForward.metric(key) .attribute("helios_version", Version.POM_VERSION) .attribute("metric_type", metricType) .attribute("what", metricName); } private double convert(Object value) { if (value instanceof Number) { return Number.class.cast(value).doubleValue(); } if (value instanceof Boolean) { return (Boolean) value ? 1 : 0; } return 0; } private void send(Metric metric) { try { fastForward.send(metric); } catch (IOException e) { log.error("Error sending metric to FastForward", e); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy