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

com.e_gineering.metrics.instrumental.InstrumentalReporter Maven / Gradle / Ivy

/**
 * Copyright 2015 E-Gineering, LLC.
 *
 * 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.e_gineering.metrics.instrumental;

import com.codahale.metrics.Clock;
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.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.ScheduledReporter;
import com.codahale.metrics.Snapshot;
import com.codahale.metrics.Timer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Locale;
import java.util.Map;
import java.util.SortedMap;
import java.util.concurrent.TimeUnit;

/**
 * Handles reporting to the Instrumental backend on a regularly scheduled basis.
 *
 * Largely based upon the graphite reporting module from Dropwizard Metrics.
 */
public class InstrumentalReporter extends ScheduledReporter {

	/**
	 * Returns a new {@link Builder} for {@link InstrumentalReporter}
	 *
	 * @param registry the registry to report
	 * @return a {@link Builder} instance for a {@link InstrumentalReporter}
	 */
	public static Builder forRegistry(MetricRegistry registry) {
		return new Builder(registry);
	}

	/**
	 * A builder for a {@link InstrumentalReporter} instances. Defaults to not using a prefix, using the default clock,
	 * converting rates to events/second, converting durations to milliseconds, and not filtering metrics.
	 */
	public static class Builder {
		private final MetricRegistry registry;
		private Clock clock;
		private String prefix;
		private TimeUnit rateUnit;
		private TimeUnit durationUnit;
		private MetricFilter filter;

		private Builder(MetricRegistry registry) {
			this.registry = registry;
			this.clock = Clock.defaultClock();
			this.prefix = null;
			this.rateUnit = TimeUnit.SECONDS;
			this.durationUnit = TimeUnit.MILLISECONDS;
			this.filter = MetricFilter.ALL;
		}

		/**
		 * Use the given {@link Clock} instance for the time.
		 *
		 * @param clock a {@link Clock} instance
		 * @return {@code this}
		 */
		public Builder withClock(Clock clock) {
			this.clock = clock;
			return this;
		}

		/**
		 * Prefix all metric names with the given string.
		 *
		 * @param prefix the prefix for all metric names
		 * @return {@code this}
		 */
		public Builder prefixedWith(String prefix) {
			this.prefix = prefix;
			return this;
		}

		/**
		 * Convert rates to the given time unit.
		 *
		 * @param rateUnit a unit of time
		 * @return {@code this}
		 */
		public Builder convertRatesTo(TimeUnit rateUnit) {
			this.rateUnit = rateUnit;
			return this;
		}

		/**
		 * Convert durations to the given time unit.
		 *
		 * @param durationUnit a unit of time
		 * @return {@code this}
		 */
		public Builder convertDurationsTo(TimeUnit durationUnit) {
			this.durationUnit = durationUnit;
			return this;
		}

		/**
		 * Only report metrics which match the given filter.
		 *
		 * @param filter a {@link MetricFilter}
		 * @return {@code this}
		 */
		public Builder filter(MetricFilter filter) {
			this.filter = filter;
			return this;
		}

		/**
		 * Builds a {@link InstrumentalReporter} with the given properties, sending metrics
		 * using the given {@link InstrumentalSender}
		 */
		public InstrumentalReporter build(InstrumentalSender instrumental) {
			return new InstrumentalReporter(registry, instrumental, clock, prefix, rateUnit, durationUnit, filter);
		}
	}

	private static final Logger LOGGER = LoggerFactory.getLogger(InstrumentalReporter.class);

	private final InstrumentalSender instrumental;
	private final Clock clock;
	private final String prefix;

	private InstrumentalReporter(MetricRegistry registry,
	                             InstrumentalSender instrumental,
	                             Clock clock,
	                             String prefix,
	                             TimeUnit rateUnit,
	                             TimeUnit durationUnit,
	                             MetricFilter filter) {
		super(registry, "instrumental-reporter", filter, rateUnit, durationUnit);
		this.instrumental = instrumental;
		this.clock = clock;
		this.prefix = prefix;
	}

	@Override
	public void report(SortedMap gauges, SortedMap counters, SortedMap histograms, SortedMap meters, SortedMap timers) {
		final long timestamp = clock.getTime() / 1000;

		// oh it'd be lovely to use Java 7 here
		try {
			if (!instrumental.isConnected()) {
				instrumental.connect();
			}

			for (Map.Entry entry : gauges.entrySet()) {
				reportGauge(entry.getKey(), entry.getValue(), timestamp);
			}

			for (Map.Entry entry : counters.entrySet()) {
				reportCounter(entry.getKey(), entry.getValue(), timestamp);
			}

			for (Map.Entry entry : histograms.entrySet()) {
				reportHistogram(entry.getKey(), entry.getValue(), timestamp);
			}

			for (Map.Entry entry : meters.entrySet()) {
				reportMetered(entry.getKey(), entry.getValue(), timestamp);
			}

			for (Map.Entry entry : timers.entrySet()) {
				reportTimer(entry.getKey(), entry.getValue(), timestamp);
			}

			instrumental.flush();
		} catch (IOException e) {
			LOGGER.warn("Unable to report to Instrumental", instrumental, e);
			try {
				instrumental.close();
			} catch (IOException e1) {
				LOGGER.warn("Error closing Instrumental", instrumental, e1);
			}
		}
	}

	@Override
	public void stop() {
		try {
			super.stop();
		} finally {
			try {
				instrumental.close();
			} catch (IOException e) {
				LOGGER.debug("Error disconnecting from Instrumental", instrumental, e);
			}
		}
	}

	private void reportTimer(String name, Timer timer, long timestamp) throws IOException {
		final Snapshot snapshot = timer.getSnapshot();

		instrumental.send(prefix(name, "max"), format(convertDuration(snapshot.getMax())), timestamp);
		instrumental.send(prefix(name, "mean"), format(convertDuration(snapshot.getMean())), timestamp);
		instrumental.send(prefix(name, "min"), format(convertDuration(snapshot.getMin())), timestamp);
		instrumental.send(prefix(name, "stddev"),
				             format(convertDuration(snapshot.getStdDev())),
				             timestamp);
		instrumental.send(prefix(name, "p50"),
				             format(convertDuration(snapshot.getMedian())),
				             timestamp);
		instrumental.send(prefix(name, "p75"),
				             format(convertDuration(snapshot.get75thPercentile())),
				             timestamp);
		instrumental.send(prefix(name, "p95"),
				             format(convertDuration(snapshot.get95thPercentile())),
				             timestamp);
		instrumental.send(prefix(name, "p98"),
				             format(convertDuration(snapshot.get98thPercentile())),
				             timestamp);
		instrumental.send(prefix(name, "p99"),
				             format(convertDuration(snapshot.get99thPercentile())),
				             timestamp);
		instrumental.send(prefix(name, "p999"),
				             format(convertDuration(snapshot.get999thPercentile())),
				             timestamp);

		reportMetered(name, timer, timestamp);
	}

	private void reportMetered(String name, Metered meter, long timestamp) throws IOException {
		instrumental.send(prefix(name, "count"), format(meter.getCount()), timestamp);
		instrumental.send(prefix(name, "m1_rate"),
				             format(convertRate(meter.getOneMinuteRate())),
				             timestamp);
		instrumental.send(prefix(name, "m5_rate"),
				             format(convertRate(meter.getFiveMinuteRate())),
				             timestamp);
		instrumental.send(prefix(name, "m15_rate"),
				             format(convertRate(meter.getFifteenMinuteRate())),
				             timestamp);
		instrumental.send(prefix(name, "mean_rate"),
				             format(convertRate(meter.getMeanRate())),
				             timestamp);
	}

	private void reportHistogram(String name, Histogram histogram, long timestamp) throws IOException {
		final Snapshot snapshot = histogram.getSnapshot();
		instrumental.send(prefix(name, "count"), format(histogram.getCount()), timestamp);
		instrumental.send(prefix(name, "max"), format(snapshot.getMax()), timestamp);
		instrumental.send(prefix(name, "mean"), format(snapshot.getMean()), timestamp);
		instrumental.send(prefix(name, "min"), format(snapshot.getMin()), timestamp);
		instrumental.send(prefix(name, "stddev"), format(snapshot.getStdDev()), timestamp);
		instrumental.send(prefix(name, "p50"), format(snapshot.getMedian()), timestamp);
		instrumental.send(prefix(name, "p75"), format(snapshot.get75thPercentile()), timestamp);
		instrumental.send(prefix(name, "p95"), format(snapshot.get95thPercentile()), timestamp);
		instrumental.send(prefix(name, "p98"), format(snapshot.get98thPercentile()), timestamp);
		instrumental.send(prefix(name, "p99"), format(snapshot.get99thPercentile()), timestamp);
		instrumental.send(prefix(name, "p999"), format(snapshot.get999thPercentile()), timestamp);
	}

	private void reportCounter(String name, Counter counter, long timestamp) throws IOException {
		instrumental.send(prefix(name, "count"), format(counter.getCount()), timestamp);
	}

	private void reportGauge(String name, Gauge gauge, long timestamp) throws IOException {
		final String value = format(gauge.getValue());
		if (value != null) {
			instrumental.send(prefix(name), value, timestamp);
		}
	}

	private String prefix(String... components) {
		return MetricRegistry.name(prefix, components);
	}

	private String format(long n) {
		return Long.toString(n);
	}

	private String format(double v) {
		return String.format(Locale.US, "%2.2f", v);
	}

	private String format(Object o) {
		if (o instanceof Float) {
			return format(((Float) o).floatValue());
		} else if (o instanceof Double) {
			return format(((Double) o).floatValue());
		} else if (o instanceof Byte) {
			return format(((Byte) o).floatValue());
		} else if (o instanceof Short) {
			return format(((Short) o).floatValue());
		} else if (o instanceof Integer) {
			return format(((Integer) o).floatValue());
		} else if (o instanceof Long) {
			return format(((Long) o).floatValue());
		}
		return null;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy