From c5da911b51b6d344e2644718ee68a51ba6f70166 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 10 Mar 2013 12:00:02 -0700 Subject: [PATCH 0001/2558] Remove metrics-scala. --- metrics-scala_2.9.1/pom.xml | 76 ------------- .../src/main/java/ignore/Ignore.java | 7 -- .../com/yammer/metrics/scala/Counter.scala | 31 ----- .../com/yammer/metrics/scala/Histogram.scala | 62 ---------- .../yammer/metrics/scala/Instrumented.scala | 21 ---- .../com/yammer/metrics/scala/Meter.scala | 70 ------------ .../yammer/metrics/scala/MetricsGroup.scala | 82 -------------- .../com/yammer/metrics/scala/Timer.scala | 106 ------------------ .../experiments/BiasedSampleBenchmark.scala | 50 --------- .../metrics/experiments/LongLivedRunner.scala | 28 ----- .../experiments/RecencyBiasExperiment.scala | 56 --------- .../metrics/scala/tests/CounterSpec.scala | 25 ----- .../metrics/scala/tests/MeterSpec.scala | 25 ----- .../metrics/scala/tests/TimerSpec.scala | 20 ---- pom.xml | 1 - 15 files changed, 660 deletions(-) delete mode 100644 metrics-scala_2.9.1/pom.xml delete mode 100644 metrics-scala_2.9.1/src/main/java/ignore/Ignore.java delete mode 100644 metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Counter.scala delete mode 100644 metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Histogram.scala delete mode 100644 metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Instrumented.scala delete mode 100644 metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Meter.scala delete mode 100644 metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/MetricsGroup.scala delete mode 100644 metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Timer.scala delete mode 100644 metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/experiments/BiasedSampleBenchmark.scala delete mode 100644 metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/experiments/LongLivedRunner.scala delete mode 100644 metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/experiments/RecencyBiasExperiment.scala delete mode 100644 metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/scala/tests/CounterSpec.scala delete mode 100644 metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/scala/tests/MeterSpec.scala delete mode 100644 metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/scala/tests/TimerSpec.scala diff --git a/metrics-scala_2.9.1/pom.xml b/metrics-scala_2.9.1/pom.xml deleted file mode 100644 index 94031ceaf0..0000000000 --- a/metrics-scala_2.9.1/pom.xml +++ /dev/null @@ -1,76 +0,0 @@ - - - 4.0.0 - - - com.yammer.metrics - metrics-parent - 3.0.0-SNAPSHOT - - - metrics-scala_2.9.1 - Metrics for Scala ${scala.version} - bundle - - - 2.9.1 - - - - - com.yammer.metrics - metrics-core - ${project.version} - - - org.scala-lang - scala-library - ${scala.version} - - - com.simple - simplespec_2.9.1 - 0.6.0 - test - - - junit - junit - - - - - - - - - org.scala-tools - maven-scala-plugin - 2.15.2 - - - - compile - testCompile - - - - - UTF-8 - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.8.1 - - false - -Xmx1024m - - **/*Spec.java - - - - - - diff --git a/metrics-scala_2.9.1/src/main/java/ignore/Ignore.java b/metrics-scala_2.9.1/src/main/java/ignore/Ignore.java deleted file mode 100644 index a852910a45..0000000000 --- a/metrics-scala_2.9.1/src/main/java/ignore/Ignore.java +++ /dev/null @@ -1,7 +0,0 @@ -package ignore; - -/** - * Ignore. - */ -public class Ignore { -} diff --git a/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Counter.scala b/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Counter.scala deleted file mode 100644 index 91064c7bc7..0000000000 --- a/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Counter.scala +++ /dev/null @@ -1,31 +0,0 @@ -package com.yammer.metrics.scala - -/** - * A Scala façade class for Counter. - */ -class Counter(metric: com.yammer.metrics.core.Counter) { - - /** - * Increments the counter by delta. - */ - def +=(delta: Long) { - metric.inc(delta) - } - - /** - * Decrements the counter by delta. - */ - def -=(delta: Long) { - metric.dec(delta) - } - - /** - * Returns the current count. - */ - def count = metric.getCount - - /** - * Resets the counter to 0. - */ - def clear() { metric.clear() } -} diff --git a/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Histogram.scala b/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Histogram.scala deleted file mode 100644 index e97ee017ac..0000000000 --- a/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Histogram.scala +++ /dev/null @@ -1,62 +0,0 @@ -package com.yammer.metrics.scala - -import collection.JavaConversions._ -import java.io.File - -/** - * A Scala façade class for HistogramMetric. - * - * @see HistogramMetric - */ -class Histogram(metric: com.yammer.metrics.core.Histogram) { - - /** - * Adds the recorded value to the histogram sample. - */ - def +=(value: Long) { - metric.update(value) - } - - /** - * Adds the recorded value to the histogram sample. - */ - def +=(value: Int) { - metric.update(value) - } - - /** - * Returns the number of values recorded. - */ - def count = metric.getCount - - /** - * Clears all recorded values. - */ - def clear() { metric.clear() } - - /** - * Returns the largest recorded value. - */ - def max = metric.getMax - - /** - * Returns the smallest recorded value. - */ - def min = metric.getMin - - /** - * Returns the arithmetic mean of all recorded values. - */ - def mean = metric.getMean - - /** - * Returns the standard deviation of all recorded values. - */ - def stdDev = metric.getStdDev - - /** - * Returns a snapshot of the values in the histogram's sample. - */ - def snapshot = metric.getSnapshot -} - diff --git a/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Instrumented.scala b/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Instrumented.scala deleted file mode 100644 index 72f40a5161..0000000000 --- a/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Instrumented.scala +++ /dev/null @@ -1,21 +0,0 @@ -package com.yammer.metrics.scala - -import com.yammer.metrics.Metrics - -/** - * The mixin trait for creating a class which is instrumented with metrics. - */ -trait Instrumented { - private lazy val metricsGroup = new MetricsGroup(getClass, metricsRegistry) - - /** - * Returns the MetricsGroup for the class. - */ - def metrics = metricsGroup - - /** - * Returns the MetricsRegistry for the class. - */ - def metricsRegistry = Metrics.defaultRegistry() -} - diff --git a/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Meter.scala b/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Meter.scala deleted file mode 100644 index 4b56eadc0e..0000000000 --- a/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Meter.scala +++ /dev/null @@ -1,70 +0,0 @@ -package com.yammer.metrics.scala - -/** - * A Scala façade class for Meter. - */ -class Meter(metric: com.yammer.metrics.core.Meter) { - - /** - * Marks the occurrence of an event. - */ - def mark() { - metric.mark() - } - - /** - * Marks the occurrence of a given number of events. - */ - def mark(count: Long) { - metric.mark(count) - } - - /** - * Returns the meter's rate unit. - */ - def rateUnit = metric.getRateUnit - - /** - * Returns the type of events the meter is measuring. - */ - def eventType = metric.getEventType - - /** - * Returns the number of events which have been marked. - */ - def count = metric.getCount - - /** - * Returns the fifteen-minute exponentially-weighted moving average rate at - * which events have occurred since the meter was created. - *

- * This rate has the same exponential decay factor as the fifteen-minute load - * average in the top Unix command. - */ - def fifteenMinuteRate = metric.getFifteenMinuteRate - - /** - * Returns the five-minute exponentially-weighted moving average rate at - * which events have occurred since the meter was created. - *

- * This rate has the same exponential decay factor as the five-minute load - * average in the top Unix command. - */ - def fiveMinuteRate = metric.getFiveMinuteRate - - /** - * Returns the mean rate at which events have occurred since the meter was - * created. - */ - def meanRate = metric.getMeanRate - - /** - * Returns the one-minute exponentially-weighted moving average rate at - * which events have occurred since the meter was created. - *

- * This rate has the same exponential decay factor as the one-minute load - * average in the top Unix command. - */ - def oneMinuteRate = metric.getOneMinuteRate -} - diff --git a/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/MetricsGroup.scala b/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/MetricsGroup.scala deleted file mode 100644 index 7cfb9cabfa..0000000000 --- a/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/MetricsGroup.scala +++ /dev/null @@ -1,82 +0,0 @@ -package com.yammer.metrics.scala - -import java.util.concurrent.TimeUnit -import com.yammer.metrics.Metrics -import com.yammer.metrics.core.{MetricsRegistry, Gauge} - -/** - * A helper class for creating and registering metrics. - */ -class MetricsGroup(val klass: Class[_], val metricsRegistry: MetricsRegistry = Metrics.defaultRegistry()) { - - /** - * Registers a new gauge metric. - * - * @param name the name of the gauge - * @param scope the scope of the gauge - * @param registry the registry for the gauge - */ - def gauge[A](name: String, scope: String = null, registry: MetricsRegistry = metricsRegistry)(f: => A) = { - registry.newGauge(klass, name, scope, new Gauge[A] { - def getValue = f - }) - } - - /** - * Creates a new counter metric. - * - * @param name the name of the counter - * @param scope the scope of the gauge - * @param registry the registry for the gauge - */ - def counter(name: String, scope: String = null, registry: MetricsRegistry = metricsRegistry) = - new Counter(registry.newCounter(klass, name, scope)) - - /** - * Creates a new histogram metrics. - * - * @param name the name of the histogram - * @param scope the scope of the histogram - * @param biased whether or not to use a biased sample - * @param registry the registry for the gauge - */ - def histogram(name: String, - scope: String = null, - biased: Boolean = false, - registry: MetricsRegistry = metricsRegistry) = - new Histogram(registry.newHistogram(klass, name, scope, biased)) - - /** - * Creates a new meter metric. - * - * @param name the name of the meter - * @param eventType the plural name of the type of events the meter is - * measuring (e.g., "requests") - * @param scope the scope of the meter - * @param unit the time unit of the meter - * @param registry the registry for the gauge - */ - def meter(name: String, - eventType: String, - scope: String = null, - unit: TimeUnit = TimeUnit.SECONDS, - registry: MetricsRegistry = metricsRegistry) = - new Meter(registry.newMeter(klass, name, scope, eventType, unit)) - - /** - * Creates a new timer metric. - * - * @param name the name of the timer - * @param scope the scope of the timer - * @param durationUnit the time unit for measuring duration - * @param rateUnit the time unit for measuring rate - * @param registry the registry for the gauge - */ - def timer(name: String, - scope: String = null, - durationUnit: TimeUnit = TimeUnit.MILLISECONDS, - rateUnit: TimeUnit = TimeUnit.SECONDS, - registry: MetricsRegistry = metricsRegistry) = - new Timer(registry.newTimer(klass, name, scope, durationUnit, rateUnit)) -} - diff --git a/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Timer.scala b/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Timer.scala deleted file mode 100644 index c233455730..0000000000 --- a/metrics-scala_2.9.1/src/main/scala/com/yammer/metrics/scala/Timer.scala +++ /dev/null @@ -1,106 +0,0 @@ -package com.yammer.metrics.scala - -import collection.JavaConversions._ -import java.util.concurrent.TimeUnit -import java.io.File - -/** - * A Scala façade class for Timer. - */ -class Timer(metric: com.yammer.metrics.core.Timer) { - /** - * Runs f, recording its duration, and returns the result of f. - */ - def time[A](f: => A): A = { - val ctx = metric.time - try { - f - } finally { - ctx.stop - } - } - - /** - * Adds a recorded duration. - */ - def update(duration: Long, unit: TimeUnit) { - metric.update(duration, unit) - } - - /** - * Returns a timing [[com.metrics.yammer.core.TimerContext]], - * which measures an elapsed time in nanoseconds. - */ - def timerContext() = metric.time() - - /** - * Returns the number of durations recorded. - */ - def count = metric.getCount - - /** - * Clears all recorded durations. - */ - def clear() { metric.clear() } - - /** - * Returns the longest recorded duration. - */ - def max = metric.getMax - - /** - * Returns the shortest recorded duration. - */ - def min = metric.getMin - - /** - * Returns the arithmetic mean of all recorded durations. - */ - def mean = metric.getMean - - /** - * Returns the standard deviation of all recorded durations. - */ - def stdDev = metric.getStdDev - - /** - * Returns a snapshot of the values in the timer's sample. - */ - def snapshot = metric.getSnapshot - - /** - * Returns the timer's rate unit. - */ - def rateUnit = metric.getRateUnit - - /** - * Returns the timer's duration unit. - */ - def durationUnit = metric.getDurationUnit - - /** - * Returns the type of events the timer is measuring. - */ - def eventType = metric.getEventType - - /** - * Returns the fifteen-minute rate of timings. - */ - def fifteenMinuteRate = metric.getFifteenMinuteRate - - /** - * Returns the five-minute rate of timings. - */ - def fiveMinuteRate = metric.getFiveMinuteRate - - /** - * Returns the mean rate of timings. - */ - def meanRate = metric.getMeanRate - - /** - * Returns the one-minute rate of timings. - */ - def oneMinuteRate = metric.getOneMinuteRate -} - diff --git a/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/experiments/BiasedSampleBenchmark.scala b/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/experiments/BiasedSampleBenchmark.scala deleted file mode 100644 index b66c5d49c7..0000000000 --- a/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/experiments/BiasedSampleBenchmark.scala +++ /dev/null @@ -1,50 +0,0 @@ -package com.yammer.metrics.experiments - -import com.yammer.metrics.stats.ExponentiallyDecayingSample -import java.util.concurrent.{CountDownLatch, TimeUnit, Executors} -import com.yammer.metrics.scala.Instrumented -import com.yammer.metrics.reporting.ConsoleReporter - -object BiasedSampleBenchmark extends Instrumented { - val updateTimer = metrics.timer("update", durationUnit = TimeUnit.MICROSECONDS) - - def main(args: Array[String]) { - ConsoleReporter.enable(1, TimeUnit.SECONDS) - - val workerCount = 100 - val iterationCount = 1000000 - - println("Warming up") - locally { // warmup - val sample = new ExponentiallyDecayingSample(1000, 0.015) - for (i <- 1 to iterationCount) { - sample.update(i) - } - } - - System.gc() - System.gc() - System.gc() - System.gc() - - val sample = new ExponentiallyDecayingSample(1000, 0.015) - val pool = Executors.newFixedThreadPool(workerCount) - - val latch = new CountDownLatch(workerCount) - - for (i <- 1 to workerCount) { - pool.execute(new Runnable { - def run() { - latch.countDown() - latch.await() - for (j <- 1 to iterationCount) { - updateTimer.time { sample.update(j) } - } - } - }) - } - - pool.shutdown() - pool.awaitTermination(10, TimeUnit.DAYS) - } -} diff --git a/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/experiments/LongLivedRunner.scala b/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/experiments/LongLivedRunner.scala deleted file mode 100644 index d716ecb454..0000000000 --- a/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/experiments/LongLivedRunner.scala +++ /dev/null @@ -1,28 +0,0 @@ -package com.yammer.metrics.experiments - -import com.yammer.metrics.scala.Instrumented -import java.util.concurrent.TimeUnit -import com.yammer.metrics.reporting.ConsoleReporter - -object LongLivedRunner extends Instrumented { - val counters = Seq("one", "two").map { s => s -> metrics.counter("counter", s) }.toMap - - def main(args: Array[String]) { - ConsoleReporter.enable(1, TimeUnit.SECONDS) - - val thread = new Thread { - override def run() { - while (true) { - counters("one") += 1 - counters("two") += 2 - } - Thread.sleep(100) - } - } - thread.setDaemon(true) - thread.start() - - println("Hit return to quit") - readLine() - } -} diff --git a/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/experiments/RecencyBiasExperiment.scala b/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/experiments/RecencyBiasExperiment.scala deleted file mode 100644 index f0684d432f..0000000000 --- a/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/experiments/RecencyBiasExperiment.scala +++ /dev/null @@ -1,56 +0,0 @@ -package com.yammer.metrics.experiments - -import collection.JavaConversions._ -import java.util.concurrent.TimeUnit -import com.yammer.metrics.stats.{ExponentiallyDecayingSample} -import com.yammer.metrics.stats.{UniformSample} -import java.io.{PrintWriter, FileOutputStream} - -/** - * A simple experiment to see how uniform and exponentially-decaying samples - * respond to a linearly-increasing set of measurements. - * - * For two hours, it measures the number of seconds the test has been running - * and places that value in each sample every second. - * - * Then for analysis, compares the mean of the uniform sample with the mean of - * the data set to date and compares the mean of the exponentially-decaying - * sample with the mean of the previous 5 minutes of values. - */ -object RecencyBiasExperiment { - def main(args: Array[String]) { - val expSample = new ExponentiallyDecayingSample(10, 0.015) - val uniSample = new UniformSample(10) - - val output = new PrintWriter(new FileOutputStream("timings.csv"), true) - output.println("t,exponential mean,expected exponential mean,uniform mean,expected uniform mean") - - for (t <- 1 to TimeUnit.HOURS.toSeconds(2).toInt) { - expSample.update(t) - uniSample.update(t) - - val expValues = expSample.getSnapshot.getValues.map {_.longValue}.sorted - val uniValues = uniSample.getSnapshot.getValues.map {_.longValue}.sorted - - val expMean = expValues.sum / expValues.size.toDouble - val expExpectedMean = ((t - 300).max(1) to t).sum / 300.0.min(t) - val uniMean = uniValues.sum / uniValues.size.toDouble - val uniExpectedMean = (1 to t).sum / t.toDouble - - println("=" * 80) - println("t: " + t) - println("exp: " + expValues.mkString(", ")) - printf( " mean: %2.2f\n", expMean) - printf( " expected: %2.2f\n", expExpectedMean) - println("uni: " + uniValues.mkString(", ")) - printf(" mean: %2.2f\n", uniMean) - printf(" expected: %2.2f\n", uniExpectedMean) - - output.println("%d,%2.2f,%2.2f,%2.2f,%2.2f".format(t, expMean, expExpectedMean, uniMean, uniExpectedMean)) - - Thread.sleep(TimeUnit.SECONDS.toMillis(1)) - } - - output.close() - } -} diff --git a/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/scala/tests/CounterSpec.scala b/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/scala/tests/CounterSpec.scala deleted file mode 100644 index 211505217f..0000000000 --- a/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/scala/tests/CounterSpec.scala +++ /dev/null @@ -1,25 +0,0 @@ -package com.yammer.metrics.scala.tests - -import org.junit.Test -import com.simple.simplespec.Spec -import com.yammer.metrics.scala.Counter - -class CounterSpec extends Spec { - class `A counter` { - val metric = mock[com.yammer.metrics.core.Counter] - val counter = new Counter(metric) - - @Test def `increments the underlying metric by an arbitrary amount` = { - counter += 12 - - verify.one(metric).inc(12) - } - - @Test def `decrements the underlying metric by an arbitrary amount` = { - counter -= 12 - - verify.one(metric).dec(12) - } - } -} - diff --git a/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/scala/tests/MeterSpec.scala b/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/scala/tests/MeterSpec.scala deleted file mode 100644 index ecabcb00f1..0000000000 --- a/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/scala/tests/MeterSpec.scala +++ /dev/null @@ -1,25 +0,0 @@ -package com.yammer.metrics.scala.tests - -import org.junit.Test -import com.simple.simplespec.Spec -import com.yammer.metrics.scala.Meter - -class MeterSpec extends Spec { - class `A meter` { - val metric = mock[com.yammer.metrics.core.Meter] - val meter = new Meter(metric) - - @Test def `marks the underlying metric` = { - meter.mark() - - verify.one(metric).mark() - } - - @Test def `marks the underlying metric by an arbitrary amount` = { - meter.mark(12) - - verify.one(metric).mark(12) - } - } -} - diff --git a/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/scala/tests/TimerSpec.scala b/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/scala/tests/TimerSpec.scala deleted file mode 100644 index 3c6898c4a0..0000000000 --- a/metrics-scala_2.9.1/src/test/scala/com/yammer/metrics/scala/tests/TimerSpec.scala +++ /dev/null @@ -1,20 +0,0 @@ -package com.yammer.metrics.scala.tests - -import org.junit.Test -import com.simple.simplespec.Spec -import com.yammer.metrics.Metrics -import com.yammer.metrics.scala.Timer - -class TimerSpec extends Spec { - class `A timer` { - val metric = Metrics.defaultRegistry().newTimer(classOf[TimerSpec], "timer") - val timer = new Timer(metric) - - @Test def `updates the underlying metric` = { - timer.time { Thread.sleep(100); 10 }.must(be(10)) - - metric.getMin.must(be(approximately(100.0, 10))) - } - } -} - diff --git a/pom.xml b/pom.xml index 92a848387b..1a0016b0f6 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,6 @@ metrics-jetty metrics-log4j metrics-logback - metrics-scala_2.9.1 metrics-servlet metrics-web From 934dbc95c605299999ad68bd63afa61d0c17f22f Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 10 Mar 2013 12:53:00 -0700 Subject: [PATCH 0002/2558] Totally revamp metrics-core. Big new changes: * Meter and timers no longer have units. Units will be the responsibility of reporters. * Metrics are identified by dotted names, not quasi-ObjectNames. * Reporting architecture has been radically simplified. * You can register metrics not created by the registry. * Separated health checks into their own modules. * No more default registries. I've pinned the other modules to metrics-core 2.2.0 to make a progressive upgrade easier. --- metrics-core/pom.xml | 34 +- .../AbstractPollingReporter.java | 46 +- .../com/yammer/metrics/{core => }/Clock.java | 5 +- .../com/yammer/metrics/ConsoleReporter.java | 134 +++++ .../yammer/metrics/{core => }/Counter.java | 4 +- .../java/com/yammer/metrics/CsvReporter.java | 24 + .../com/yammer/metrics/{stats => }/EWMA.java | 2 +- .../ExponentiallyDecayingSample.java | 4 +- .../com/yammer/metrics/{core => }/Gauge.java | 4 +- .../java/com/yammer/metrics/HealthChecks.java | 21 - .../yammer/metrics/{core => }/Histogram.java | 54 +- .../com/yammer/metrics/JmxAttributeGauge.java | 27 + .../java/com/yammer/metrics/JmxReporter.java | 468 ++++++++++++++++ .../com/yammer/metrics/{core => }/Meter.java | 38 +- .../yammer/metrics/{core => }/Metered.java | 18 +- .../com/yammer/metrics/{core => }/Metric.java | 2 +- .../com/yammer/metrics/MetricRegistry.java | 299 ++++++++++ .../metrics/MetricRegistryListener.java | 28 + .../main/java/com/yammer/metrics/Metrics.java | 27 - .../yammer/metrics/MovingWindowSample.java | 41 ++ .../java/com/yammer/metrics/Reporter.java | 11 + .../yammer/metrics/{stats => }/Sample.java | 2 +- .../java/com/yammer/metrics/SampleType.java | 34 ++ .../yammer/metrics/{core => }/Sampling.java | 4 +- .../yammer/metrics/{stats => }/Snapshot.java | 13 +- .../metrics/{core => }/Summarizable.java | 2 +- .../{stats => }/ThreadLocalRandom.java | 2 +- .../com/yammer/metrics/{core => }/Timer.java | 91 ++-- .../metrics/{stats => }/UniformSample.java | 4 +- .../com/yammer/metrics/core/HealthCheck.java | 202 ------- .../metrics/core/HealthCheckRegistry.java | 58 -- .../com/yammer/metrics/core/MetricName.java | 228 -------- .../yammer/metrics/core/MetricPredicate.java | 26 - .../yammer/metrics/core/MetricProcessor.java | 58 -- .../yammer/metrics/core/MetricsRegistry.java | 511 ------------------ .../metrics/core/MetricsRegistryListener.java | 23 - .../com/yammer/metrics/core/TimerContext.java | 35 -- .../metrics/core/VirtualMachineMetrics.java | 492 ----------------- .../metrics/reporting/AbstractReporter.java | 36 -- .../metrics/reporting/ConsoleReporter.java | 239 -------- .../yammer/metrics/reporting/CsvReporter.java | 278 ---------- .../yammer/metrics/reporting/JmxReporter.java | 475 ---------------- .../metrics/reporting/MetricDispatcher.java | 21 - .../com/yammer/metrics/util/AtomicGauge.java | 16 - .../metrics/util/DeadlockHealthCheck.java | 46 -- .../util/DeathRattleExceptionHandler.java | 55 -- .../com/yammer/metrics/util/JmxGauge.java | 49 -- .../com/yammer/metrics/util/PercentGauge.java | 13 - .../com/yammer/metrics/util/RatioGauge.java | 38 -- .../com/yammer/metrics/util/ToggleGauge.java | 21 - .../com/yammer/metrics/core/VMMFactory.java | 25 - .../yammer/metrics/core/tests/ClockTest.java | 41 -- .../metrics/core/tests/CounterTest.java | 66 --- .../yammer/metrics/core/tests/GaugeTest.java | 23 - .../core/tests/HealthCheckRegistryTest.java | 61 --- .../metrics/core/tests/HealthCheckTest.java | 143 ----- .../metrics/core/tests/HistogramTest.java | 109 ---- .../yammer/metrics/core/tests/MeterTest.java | 36 -- .../metrics/core/tests/MetricNameTest.java | 110 ---- .../core/tests/MetricsRegistryTest.java | 98 ---- .../yammer/metrics/core/tests/TimerTest.java | 190 ------- .../core/tests/VirtualMachineMetricsTest.java | 268 --------- .../metrics/examples/DirectoryLister.java | 51 -- .../metrics/examples/ExampleRunner.java | 55 -- .../tests/AbstractPollingReporterTest.java | 219 -------- .../reporting/tests/ConsoleReporterTest.java | 121 ----- .../reporting/tests/CsvReporterTest.java | 54 -- .../yammer/metrics/stats/tests/EWMATest.java | 322 ----------- .../stats/tests/UniformSampleTest.java | 39 -- .../tests/AbstractPollingReporterTest.java | 66 +++ .../com/yammer/metrics/tests/ClockTest.java | 43 ++ .../com/yammer/metrics/tests/CounterTest.java | 57 ++ .../com/yammer/metrics/tests/EWMATest.java | 225 ++++++++ .../ExponentiallyDecayingSampleTest.java | 105 ++-- .../metrics/tests/HealthChecksTest.java | 40 -- .../yammer/metrics/tests/HistogramTest.java | 72 +++ .../com/yammer/metrics/tests/MeterTest.java | 30 + .../metrics/tests/MetricRegistryTest.java | 232 ++++++++ .../metrics/tests/MovingWindowSampleTest.java | 30 + .../{stats => }/tests/SnapshotTest.java | 57 +- .../com/yammer/metrics/tests/TimerTest.java | 127 +++++ .../metrics/tests/UniformSampleTest.java | 33 ++ .../util/tests/DeadlockHealthCheckTest.java | 47 -- .../DeathRattleExceptionHandlerTest.java | 22 - .../metrics/util/tests/JmxGaugeTest.java | 25 - .../metrics/util/tests/PercentGaugeTest.java | 45 -- .../metrics/util/tests/RatioGaugeTest.java | 81 --- .../metrics/util/tests/ToggleGaugeTest.java | 26 - .../src/test/resources/recency-bias-graph.r | 20 - metrics-ehcache/pom.xml | 2 +- metrics-ganglia/pom.xml | 15 +- metrics-graphite/pom.xml | 15 +- metrics-healthchecks/pom.xml | 16 + metrics-httpclient/pom.xml | 2 +- metrics-jdbi/pom.xml | 2 +- metrics-jersey/pom.xml | 4 +- metrics-jetty/pom.xml | 2 +- metrics-log4j/pom.xml | 2 +- metrics-logback/pom.xml | 2 +- metrics-servlet/pom.xml | 4 +- metrics-web/pom.xml | 2 +- pom.xml | 23 +- 102 files changed, 2203 insertions(+), 5665 deletions(-) rename metrics-core/src/main/java/com/yammer/metrics/{reporting => }/AbstractPollingReporter.java (62%) rename metrics-core/src/main/java/com/yammer/metrics/{core => }/Clock.java (94%) create mode 100644 metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java rename metrics-core/src/main/java/com/yammer/metrics/{core => }/Counter.java (95%) create mode 100644 metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java rename metrics-core/src/main/java/com/yammer/metrics/{stats => }/EWMA.java (98%) rename metrics-core/src/main/java/com/yammer/metrics/{stats => }/ExponentiallyDecayingSample.java (99%) rename metrics-core/src/main/java/com/yammer/metrics/{core => }/Gauge.java (87%) delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/HealthChecks.java rename metrics-core/src/main/java/com/yammer/metrics/{core => }/Histogram.java (72%) create mode 100644 metrics-core/src/main/java/com/yammer/metrics/JmxAttributeGauge.java create mode 100644 metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java rename metrics-core/src/main/java/com/yammer/metrics/{core => }/Meter.java (76%) rename metrics-core/src/main/java/com/yammer/metrics/{core => }/Metered.java (85%) rename metrics-core/src/main/java/com/yammer/metrics/{core => }/Metric.java (74%) create mode 100644 metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java create mode 100644 metrics-core/src/main/java/com/yammer/metrics/MetricRegistryListener.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/Metrics.java create mode 100644 metrics-core/src/main/java/com/yammer/metrics/MovingWindowSample.java create mode 100644 metrics-core/src/main/java/com/yammer/metrics/Reporter.java rename metrics-core/src/main/java/com/yammer/metrics/{stats => }/Sample.java (94%) create mode 100644 metrics-core/src/main/java/com/yammer/metrics/SampleType.java rename metrics-core/src/main/java/com/yammer/metrics/{core => }/Sampling.java (73%) rename metrics-core/src/main/java/com/yammer/metrics/{stats => }/Snapshot.java (93%) rename metrics-core/src/main/java/com/yammer/metrics/{core => }/Summarizable.java (96%) rename metrics-core/src/main/java/com/yammer/metrics/{stats => }/ThreadLocalRandom.java (99%) rename metrics-core/src/main/java/com/yammer/metrics/{core => }/Timer.java (65%) rename metrics-core/src/main/java/com/yammer/metrics/{stats => }/UniformSample.java (97%) delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/core/HealthCheck.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/core/HealthCheckRegistry.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/core/MetricName.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/core/MetricPredicate.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/core/MetricProcessor.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/core/MetricsRegistry.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/core/MetricsRegistryListener.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/core/TimerContext.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/core/VirtualMachineMetrics.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/reporting/AbstractReporter.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/reporting/ConsoleReporter.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/reporting/CsvReporter.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/reporting/JmxReporter.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/reporting/MetricDispatcher.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/util/AtomicGauge.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/util/DeadlockHealthCheck.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/util/DeathRattleExceptionHandler.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/util/JmxGauge.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/util/PercentGauge.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/util/RatioGauge.java delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/util/ToggleGauge.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/core/VMMFactory.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/core/tests/ClockTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/core/tests/CounterTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/core/tests/GaugeTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/core/tests/HealthCheckRegistryTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/core/tests/HealthCheckTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/core/tests/HistogramTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/core/tests/MeterTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/core/tests/MetricNameTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/core/tests/MetricsRegistryTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/core/tests/TimerTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/core/tests/VirtualMachineMetricsTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/examples/DirectoryLister.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/examples/ExampleRunner.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/reporting/tests/AbstractPollingReporterTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/reporting/tests/ConsoleReporterTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/reporting/tests/CsvReporterTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/stats/tests/EWMATest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/stats/tests/UniformSampleTest.java create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/AbstractPollingReporterTest.java create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/ClockTest.java create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/CounterTest.java create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/EWMATest.java rename metrics-core/src/test/java/com/yammer/metrics/{stats => }/tests/ExponentiallyDecayingSampleTest.java (52%) delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/HealthChecksTest.java create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/MeterTest.java create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/MovingWindowSampleTest.java rename metrics-core/src/test/java/com/yammer/metrics/{stats => }/tests/SnapshotTest.java (54%) create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/UniformSampleTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/util/tests/DeadlockHealthCheckTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/util/tests/DeathRattleExceptionHandlerTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/util/tests/JmxGaugeTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/util/tests/PercentGaugeTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/util/tests/RatioGaugeTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/util/tests/ToggleGaugeTest.java delete mode 100755 metrics-core/src/test/resources/recency-bias-graph.r create mode 100644 metrics-healthchecks/pom.xml diff --git a/metrics-core/pom.xml b/metrics-core/pom.xml index 09cd53374f..14b014623b 100644 --- a/metrics-core/pom.xml +++ b/metrics-core/pom.xml @@ -1,5 +1,7 @@ - + 4.0.0 @@ -11,34 +13,4 @@ metrics-core Metrics Core Library bundle - - - - com.yammer.metrics - metrics-annotation - ${project.version} - - - org.slf4j - slf4j-api - ${slf4j.version} - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 2.3.2 - - - - test-jar - - - - - - diff --git a/metrics-core/src/main/java/com/yammer/metrics/reporting/AbstractPollingReporter.java b/metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java similarity index 62% rename from metrics-core/src/main/java/com/yammer/metrics/reporting/AbstractPollingReporter.java rename to metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java index dc87c3ff40..9858d0f15c 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/reporting/AbstractPollingReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java @@ -1,6 +1,4 @@ -package com.yammer.metrics.reporting; - -import com.yammer.metrics.core.MetricsRegistry; +package com.yammer.metrics; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -8,14 +6,11 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -/** - * An abstract base class for all reporter implementations which periodically poll registered - * metrics (e.g., to send the data to another service). - */ -public abstract class AbstractPollingReporter extends AbstractReporter implements Runnable { +public abstract class AbstractPollingReporter implements Reporter { /** * A simple named thread factory. */ + @SuppressWarnings("NullableProblems") private static class NamedThreadFactory implements ThreadFactory { private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); @@ -38,33 +33,40 @@ public Thread newThread(Runnable r) { } } + private final MetricRegistry registry; private final ScheduledExecutorService executor; /** * Creates a new {@link AbstractPollingReporter} instance. * - * @param registry the {@link MetricsRegistry} containing the metrics this reporter will - * report - * @param name the reporter's name - * @see AbstractReporter#AbstractReporter(MetricsRegistry) + * @param registry the {@link MetricRegistry} containing the metrics this reporter will report + * @param name the reporter's name */ - protected AbstractPollingReporter(MetricsRegistry registry, String name) { - super(registry); + protected AbstractPollingReporter(MetricRegistry registry, String name) { + this.registry = registry; this.executor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory(name)); } /** * Starts the reporter polling at the given period. * - * @param period the amount of time between polls - * @param unit the unit for {@code period} + * @param period the amount of time between polls + * @param unit the unit for {@code period} */ public void start(long period, TimeUnit unit) { - executor.scheduleAtFixedRate(this, period, period, unit); + executor.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + report(registry.getGauges(), + registry.getCounters(), + registry.getHistograms(), + registry.getMeters(), + registry.getTimers()); + } + }, period, period, unit); } - @Override - public void shutdown() { + public void stop() { executor.shutdown(); try { executor.awaitTermination(1, TimeUnit.SECONDS); @@ -72,10 +74,4 @@ public void shutdown() { // do nothing } } - - /** - * The method called when a a poll is scheduled to occur. - */ - @Override - public abstract void run(); } diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/Clock.java b/metrics-core/src/main/java/com/yammer/metrics/Clock.java similarity index 94% rename from metrics-core/src/main/java/com/yammer/metrics/core/Clock.java rename to metrics-core/src/main/java/com/yammer/metrics/Clock.java index a99eaa7531..e61c785cd4 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/core/Clock.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Clock.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.core; +package com.yammer.metrics; import java.lang.management.ManagementFactory; import java.lang.management.ThreadMXBean; @@ -30,13 +30,12 @@ public long getTime() { * * @return the default {@link Clock} instance * - * @see com.yammer.metrics.core.Clock.UserTimeClock + * @see Clock.UserTimeClock */ public static Clock defaultClock() { return DEFAULT; } - /** * A clock implementation which returns the current time in epoch nanoseconds. */ diff --git a/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java b/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java new file mode 100644 index 0000000000..c7b53e962e --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java @@ -0,0 +1,134 @@ +package com.yammer.metrics; + +import java.io.PrintStream; +import java.text.DateFormat; +import java.util.*; + +// TODO: 3/10/13 -- write tests +// TODO: 3/10/13 -- write docs + +public class ConsoleReporter extends AbstractPollingReporter { + private static final int CONSOLE_WIDTH = 80; + + private final PrintStream output; + private final Locale locale; + private final Clock clock; + private final DateFormat dateFormat; + + public ConsoleReporter(MetricRegistry registry, + PrintStream output, + Locale locale, + Clock clock, + TimeZone timeZone) { + super(registry, "console-reporter"); + this.output = output; + this.locale = locale; + this.clock = clock; + this.dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, + DateFormat.MEDIUM, + locale); + dateFormat.setTimeZone(timeZone); + } + + @Override + public void report(SortedMap gauges, + SortedMap counters, + SortedMap histograms, + SortedMap meters, + SortedMap timers) { + final String dateTime = dateFormat.format(new Date(clock.getTime())); + printWithBanner(dateTime, '='); + output.println(); + + printWithBanner("-- Gauges", '-'); + for (Map.Entry entry : gauges.entrySet()) { + output.println(entry.getKey()); + printGauge(entry); + } + output.println(); + + printWithBanner("-- Counters", '-'); + for (Map.Entry entry : counters.entrySet()) { + output.println(entry.getKey()); + printCounter(entry); + } + output.println(); + + printWithBanner("-- Histograms", '-'); + for (Map.Entry entry : histograms.entrySet()) { + output.println(entry.getKey()); + printHistogram(entry.getValue()); + } + output.println(); + + printWithBanner("-- Meters", '-'); + for (Map.Entry entry : meters.entrySet()) { + output.println(entry.getKey()); + printMetered(entry.getValue()); + } + output.println(); + + printWithBanner("-- Timers", '-'); + for (Map.Entry entry : timers.entrySet()) { + output.println(entry.getKey()); + printTimer(entry.getValue()); + } + output.println(); + + output.println(); + output.flush(); + } + + private void printCounter(Map.Entry entry) { + output.printf(locale, " count = %d%n", entry.getValue().getCount()); + } + + private void printGauge(Map.Entry entry) { + output.printf(locale, " value = %s%n", entry.getValue().getValue()); + } + + private void printHistogram(Histogram histogram) { + output.printf(locale, " count = %d%n", histogram.getCount()); + output.printf(locale, " min = %2.2f%n", histogram.getMin()); + output.printf(locale, " max = %2.2f%n", histogram.getMax()); + output.printf(locale, " mean = %2.2f%n", histogram.getMean()); + output.printf(locale, " stddev = %2.2f%n", histogram.getStdDev()); + printSnapshot(histogram.getSnapshot()); + } + + private void printTimer(Timer timer) { + final Snapshot snapshot = timer.getSnapshot(); + printMetered(timer); + output.printf(locale, " min = %2.2f%n", timer.getMin()); + output.printf(locale, " max = %2.2f%n", timer.getMax()); + output.printf(locale, " mean = %2.2f%n", timer.getMean()); + output.printf(locale, " stddev = %2.2f%n", timer.getStdDev()); + printSnapshot(snapshot); + } + + private void printSnapshot(Snapshot snapshot) { + output.printf(locale, " median = %2.2f%n", snapshot.getMedian()); + output.printf(locale, " 75%% <= %2.2f%n", snapshot.get75thPercentile()); + output.printf(locale, " 95%% <= %2.2f%n", snapshot.get95thPercentile()); + output.printf(locale, " 98%% <= %2.2f%n", snapshot.get98thPercentile()); + output.printf(locale, " 99%% <= %2.2f%n", snapshot.get99thPercentile()); + output.printf(locale, " 99.9%% <= %2.2f%n", snapshot.get999thPercentile()); + } + + private void printMetered(Metered timer) { + output.printf(locale, " count = %d%n", timer.getCount()); + output.printf(locale, " mean rate = %2.2f%n", timer.getMeanRate()); + output.printf(locale, " 1-minute rate = %2.2f%n", timer.getOneMinuteRate()); + output.printf(locale, " 5-minute rate = %2.2f%n", timer.getFiveMinuteRate()); + output.printf(locale, " 15-minute rate = %2.2f%n", timer.getFifteenMinuteRate()); + } + + private void printWithBanner(String s, char c) { + output.print(s); + output.print(' '); + for (int i = 0; i < (CONSOLE_WIDTH - s.length() - 1); i++) { + output.print(c); + } + output.println(); + } +} diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/Counter.java b/metrics-core/src/main/java/com/yammer/metrics/Counter.java similarity index 95% rename from metrics-core/src/main/java/com/yammer/metrics/core/Counter.java rename to metrics-core/src/main/java/com/yammer/metrics/Counter.java index 8191d7710f..a087a5fe8e 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/core/Counter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Counter.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.core; +package com.yammer.metrics; import java.util.concurrent.atomic.AtomicLong; @@ -8,7 +8,7 @@ public class Counter implements Metric { private final AtomicLong count; - Counter() { + public Counter() { this.count = new AtomicLong(0); } diff --git a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java new file mode 100644 index 0000000000..3458214cb9 --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java @@ -0,0 +1,24 @@ +package com.yammer.metrics; + +import java.util.SortedMap; + +// TODO: 3/10/13 -- implement CsvReporter + +public class CsvReporter extends AbstractPollingReporter { + /** + * Creates a new {@link CsvReporter} instance. + * + * @param registry the {@link MetricRegistry} containing the metrics this reporter will report + */ + protected CsvReporter(MetricRegistry registry) { + super(registry, "csv-reporter"); + } + + @Override + public void report(SortedMap gauges, + SortedMap counters, + SortedMap histograms, + SortedMap meters, + SortedMap timers) { + } +} diff --git a/metrics-core/src/main/java/com/yammer/metrics/stats/EWMA.java b/metrics-core/src/main/java/com/yammer/metrics/EWMA.java similarity index 98% rename from metrics-core/src/main/java/com/yammer/metrics/stats/EWMA.java rename to metrics-core/src/main/java/com/yammer/metrics/EWMA.java index a24746aad9..845a029296 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/stats/EWMA.java +++ b/metrics-core/src/main/java/com/yammer/metrics/EWMA.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.stats; +package com.yammer.metrics; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; diff --git a/metrics-core/src/main/java/com/yammer/metrics/stats/ExponentiallyDecayingSample.java b/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java similarity index 99% rename from metrics-core/src/main/java/com/yammer/metrics/stats/ExponentiallyDecayingSample.java rename to metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java index 03d8ce487e..a6ba909319 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/stats/ExponentiallyDecayingSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java @@ -1,6 +1,4 @@ -package com.yammer.metrics.stats; - -import com.yammer.metrics.core.Clock; +package com.yammer.metrics; import java.util.ArrayList; import java.util.concurrent.ConcurrentSkipListMap; diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/Gauge.java b/metrics-core/src/main/java/com/yammer/metrics/Gauge.java similarity index 87% rename from metrics-core/src/main/java/com/yammer/metrics/core/Gauge.java rename to metrics-core/src/main/java/com/yammer/metrics/Gauge.java index edbf832c90..fe279c5e6f 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/core/Gauge.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Gauge.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.core; +package com.yammer.metrics; /** @@ -15,7 +15,7 @@ * * @param the type of the metric's value */ -public abstract class Gauge implements Metric { +public interface Gauge extends Metric { /** * Returns the metric's current value. * diff --git a/metrics-core/src/main/java/com/yammer/metrics/HealthChecks.java b/metrics-core/src/main/java/com/yammer/metrics/HealthChecks.java deleted file mode 100644 index db36dbb287..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/HealthChecks.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.yammer.metrics; - -import com.yammer.metrics.core.HealthCheckRegistry; - -/** - * A default health check registry. - */ -public class HealthChecks { - private static final HealthCheckRegistry DEFAULT_REGISTRY = new HealthCheckRegistry(); - - private HealthChecks() { /* unused */ } - - /** - * Returns the (static) default registry. - * - * @return the registry - */ - public static HealthCheckRegistry defaultRegistry() { - return DEFAULT_REGISTRY; - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/Histogram.java b/metrics-core/src/main/java/com/yammer/metrics/Histogram.java similarity index 72% rename from metrics-core/src/main/java/com/yammer/metrics/core/Histogram.java rename to metrics-core/src/main/java/com/yammer/metrics/Histogram.java index 2a86f56d8d..95993c8aa3 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/core/Histogram.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Histogram.java @@ -1,9 +1,4 @@ -package com.yammer.metrics.core; - -import com.yammer.metrics.stats.ExponentiallyDecayingSample; -import com.yammer.metrics.stats.Sample; -import com.yammer.metrics.stats.Snapshot; -import com.yammer.metrics.stats.UniformSample; +package com.yammer.metrics; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; @@ -17,39 +12,6 @@ * variance */ public class Histogram implements Metric, Sampling, Summarizable { - private static final int DEFAULT_SAMPLE_SIZE = 1028; - private static final double DEFAULT_ALPHA = 0.015; - - /** - * The type of sampling the histogram should be performing. - */ - enum SampleType { - /** - * Uses a uniform sample of 1028 elements, which offers a 99.9% confidence level with a 5% - * margin of error assuming a normal distribution. - */ - UNIFORM { - @Override - public Sample newSample() { - return new UniformSample(DEFAULT_SAMPLE_SIZE); - } - }, - - /** - * Uses an exponentially decaying sample of 1028 elements, which offers a 99.9% confidence - * level with a 5% margin of error assuming a normal distribution, and an alpha factor of - * 0.015, which heavily biases the sample to the past 5 minutes of measurements. - */ - BIASED { - @Override - public Sample newSample() { - return new ExponentiallyDecayingSample(DEFAULT_SAMPLE_SIZE, DEFAULT_ALPHA); - } - }; - - public abstract Sample newSample(); - } - private final Sample sample; private final AtomicLong min = new AtomicLong(); private final AtomicLong max = new AtomicLong(); @@ -65,7 +27,7 @@ public Sample newSample() { * * @param type the type of sample to use */ - Histogram(SampleType type) { + public Histogram(SampleType type) { this(type.newSample()); } @@ -74,7 +36,7 @@ public Sample newSample() { * * @param sample the sample to create a histogram from */ - Histogram(Sample sample) { + public Histogram(Sample sample) { this.sample = sample; clear(); } @@ -124,7 +86,7 @@ public long getCount() { } /* (non-Javadoc) - * @see com.yammer.metrics.core.Summarizable#max() + * @see com.yammer.metrics.Summarizable#max() */ @Override public double getMax() { @@ -135,7 +97,7 @@ public double getMax() { } /* (non-Javadoc) - * @see com.yammer.metrics.core.Summarizable#min() + * @see com.yammer.metrics.Summarizable#min() */ @Override public double getMin() { @@ -146,7 +108,7 @@ public double getMin() { } /* (non-Javadoc) - * @see com.yammer.metrics.core.Summarizable#mean() + * @see com.yammer.metrics.Summarizable#mean() */ @Override public double getMean() { @@ -157,7 +119,7 @@ public double getMean() { } /* (non-Javadoc) - * @see com.yammer.metrics.core.Summarizable#stdDev() + * @see com.yammer.metrics.Summarizable#stdDev() */ @Override public double getStdDev() { @@ -168,7 +130,7 @@ public double getStdDev() { } /* (non-Javadoc) - * @see com.yammer.metrics.core.Summarizable#sum() + * @see com.yammer.metrics.Summarizable#sum() */ @Override public double getSum() { diff --git a/metrics-core/src/main/java/com/yammer/metrics/JmxAttributeGauge.java b/metrics-core/src/main/java/com/yammer/metrics/JmxAttributeGauge.java new file mode 100644 index 0000000000..8ae198fbd5 --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/JmxAttributeGauge.java @@ -0,0 +1,27 @@ +package com.yammer.metrics; + +import javax.management.*; + +// TODO: 3/10/13 -- write tests +// TODO: 3/10/13 -- write docs + +public class JmxAttributeGauge implements Gauge { + private final MBeanServer mBeanServer; + private final ObjectName objectName; + private final String attributeName; + + public JmxAttributeGauge(MBeanServer mBeanServer, ObjectName objectName, String attributeName) { + this.mBeanServer = mBeanServer; + this.objectName = objectName; + this.attributeName = attributeName; + } + + @Override + public Object getValue() { + try { + return mBeanServer.getAttribute(objectName, attributeName); + } catch (JMException e) { + return null; + } + } +} diff --git a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java new file mode 100644 index 0000000000..ed421cf4d6 --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java @@ -0,0 +1,468 @@ +package com.yammer.metrics; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.management.*; +import java.util.Hashtable; + +// TODO: 3/10/13 -- write tests +// TODO: 3/10/13 -- write docs + +public class JmxReporter { + private static final Logger LOGGER = LoggerFactory.getLogger(JmxReporter.class); + + // CHECKSTYLE:OFF + @SuppressWarnings("UnusedDeclaration") + public interface MetricMBean { + ObjectName objectName(); + } + // CHECKSTYLE:ON + + + private abstract static class AbstractBean implements MetricMBean { + private final ObjectName objectName; + + protected AbstractBean(ObjectName objectName) { + this.objectName = objectName; + } + + @Override + public ObjectName objectName() { + return objectName; + } + } + + // CHECKSTYLE:OFF + @SuppressWarnings("UnusedDeclaration") + public interface JmxGaugeMBean extends MetricMBean { + Object getValue(); + } + // CHECKSTYLE:ON + + private static class JmxGauge extends AbstractBean implements JmxGaugeMBean { + private final Gauge metric; + + private JmxGauge(Gauge metric, ObjectName objectName) { + super(objectName); + this.metric = metric; + } + + @Override + public Object getValue() { + return metric.getValue(); + } + } + + // CHECKSTYLE:OFF + @SuppressWarnings("UnusedDeclaration") + public interface JmxCounterMBean extends MetricMBean { + long getCount(); + } + // CHECKSTYLE:ON + + private static class JmxCounter extends AbstractBean implements JmxCounterMBean { + private final Counter metric; + + private JmxCounter(Counter metric, ObjectName objectName) { + super(objectName); + this.metric = metric; + } + + @Override + public long getCount() { + return metric.getCount(); + } + } + + // CHECKSTYLE:OFF + @SuppressWarnings("UnusedDeclaration") + public interface JmxHistogramMBean extends MetricMBean { + long getCount(); + + double getMin(); + + double getMax(); + + double getMean(); + + double getStdDev(); + + double get50thPercentile(); + + double get75thPercentile(); + + double get95thPercentile(); + + double get98thPercentile(); + + double get99thPercentile(); + + double get999thPercentile(); + + long[] values(); + } + // CHECKSTYLE:ON + + private static class JmxHistogram implements JmxHistogramMBean { + private final ObjectName objectName; + private final Histogram metric; + + private JmxHistogram(Histogram metric, ObjectName objectName) { + this.metric = metric; + this.objectName = objectName; + } + + @Override + public ObjectName objectName() { + return objectName; + } + + @Override + public double get50thPercentile() { + return metric.getSnapshot().getMedian(); + } + + @Override + public long getCount() { + return metric.getCount(); + } + + @Override + public double getMin() { + return metric.getMin(); + } + + @Override + public double getMax() { + return metric.getMax(); + } + + @Override + public double getMean() { + return metric.getMean(); + } + + @Override + public double getStdDev() { + return metric.getStdDev(); + } + + @Override + public double get75thPercentile() { + return metric.getSnapshot().get75thPercentile(); + } + + @Override + public double get95thPercentile() { + return metric.getSnapshot().get95thPercentile(); + } + + @Override + public double get98thPercentile() { + return metric.getSnapshot().get98thPercentile(); + } + + @Override + public double get99thPercentile() { + return metric.getSnapshot().get99thPercentile(); + } + + @Override + public double get999thPercentile() { + return metric.getSnapshot().get999thPercentile(); + } + + @Override + public long[] values() { + return metric.getSnapshot().getValues(); + } + } + + //CHECKSTYLE:OFF + @SuppressWarnings("UnusedDeclaration") + public interface JmxMeterMBean extends MetricMBean { + long getCount(); + + double getMeanRate(); + + double getOneMinuteRate(); + + double getFiveMinuteRate(); + + double getFifteenMinuteRate(); + } + //CHECKSTYLE:ON + + private static class JmxMeter extends AbstractBean implements JmxMeterMBean { + private final Metered metric; + + private JmxMeter(Metered metric, ObjectName objectName) { + super(objectName); + this.metric = metric; + } + + @Override + public long getCount() { + return metric.getCount(); + } + + @Override + public double getMeanRate() { + return metric.getMeanRate(); + } + + @Override + public double getOneMinuteRate() { + return metric.getOneMinuteRate(); + } + + @Override + public double getFiveMinuteRate() { + return metric.getFiveMinuteRate(); + } + + @Override + public double getFifteenMinuteRate() { + return metric.getFifteenMinuteRate(); + } + } + + // CHECKSTYLE:OFF + @SuppressWarnings("UnusedDeclaration") + public interface JmxTimerMBean extends JmxMeterMBean, JmxHistogramMBean { + } + // CHECKSTYLE:ON + + static class JmxTimer extends JmxMeter implements JmxTimerMBean { + private final Timer metric; + + private JmxTimer(Timer metric, ObjectName objectName) { + super(metric, objectName); + this.metric = metric; + } + + @Override + public double get50thPercentile() { + return metric.getSnapshot().getMedian(); + } + + @Override + public double getMin() { + return metric.getMin(); + } + + @Override + public double getMax() { + return metric.getMax(); + } + + @Override + public double getMean() { + return metric.getMean(); + } + + @Override + public double getStdDev() { + return metric.getStdDev(); + } + + @Override + public double get75thPercentile() { + return metric.getSnapshot().get75thPercentile(); + } + + @Override + public double get95thPercentile() { + return metric.getSnapshot().get95thPercentile(); + } + + @Override + public double get98thPercentile() { + return metric.getSnapshot().get98thPercentile(); + } + + @Override + public double get99thPercentile() { + return metric.getSnapshot().get99thPercentile(); + } + + @Override + public double get999thPercentile() { + return metric.getSnapshot().get999thPercentile(); + } + + @Override + public long[] values() { + return metric.getSnapshot().getValues(); + } + } + + private static class JmxListener implements MetricRegistryListener { + private final String name; + private final MBeanServer mBeanServer; + + public JmxListener(MBeanServer mBeanServer, String name) { + this.mBeanServer = mBeanServer; + this.name = name; + } + + @Override + public void onGaugeAdded(String name, Gauge gauge) { + try { + final ObjectName objectName = createName("gauges", name); + mBeanServer.registerMBean(new JmxGauge(gauge, objectName), objectName); + } catch (InstanceAlreadyExistsException e) { + LOGGER.debug("Unable to register gauge", e); + } catch (MBeanRegistrationException e) { + LOGGER.warn("Unable to register gauge", e); + } catch (NotCompliantMBeanException e) { + LOGGER.warn("Unable to register gauge", e); + } + } + + @Override + public void onGaugeRemoved(String name) { + try { + mBeanServer.unregisterMBean(createName("gauges", name)); + } catch (InstanceNotFoundException e) { + LOGGER.debug("Unable to unregister gauge", e); + } catch (MBeanRegistrationException e) { + LOGGER.warn("Unable to unregister gauge", e); + } + } + + @Override + public void onCounterAdded(String name, Counter counter) { + try { + final ObjectName objectName = createName("counters", name); + mBeanServer.registerMBean(new JmxCounter(counter, objectName), objectName); + } catch (InstanceAlreadyExistsException e) { + LOGGER.debug("Unable to register gauge", e); + } catch (MBeanRegistrationException e) { + LOGGER.warn("Unable to register gauge", e); + } catch (NotCompliantMBeanException e) { + LOGGER.warn("Unable to register gauge", e); + } + } + + @Override + public void onCounterRemoved(String name) { + try { + mBeanServer.unregisterMBean(createName("counters", name)); + } catch (InstanceNotFoundException e) { + LOGGER.debug("Unable to unregister counter", e); + } catch (MBeanRegistrationException e) { + LOGGER.warn("Unable to unregister counter", e); + } + } + + @Override + public void onHistogramAdded(String name, Histogram histogram) { + try { + final ObjectName objectName = createName("histograms", name); + mBeanServer.registerMBean(new JmxHistogram(histogram, objectName), objectName); + } catch (InstanceAlreadyExistsException e) { + LOGGER.debug("Unable to register histogram", e); + } catch (MBeanRegistrationException e) { + LOGGER.warn("Unable to register histogram", e); + } catch (NotCompliantMBeanException e) { + LOGGER.warn("Unable to register histogram", e); + } + } + + @Override + public void onHistogramRemoved(String name) { + try { + mBeanServer.unregisterMBean(createName("histograms", name)); + } catch (InstanceNotFoundException e) { + LOGGER.debug("Unable to unregister histogram", e); + } catch (MBeanRegistrationException e) { + LOGGER.warn("Unable to unregister histogram", e); + } + } + + @Override + public void onMeterAdded(String name, Meter meter) { + try { + final ObjectName objectName = createName("meters", name); + mBeanServer.registerMBean(new JmxMeter(meter, objectName), objectName); + } catch (InstanceAlreadyExistsException e) { + LOGGER.debug("Unable to register meter", e); + } catch (MBeanRegistrationException e) { + LOGGER.warn("Unable to register meter", e); + } catch (NotCompliantMBeanException e) { + LOGGER.warn("Unable to register meter", e); + } + } + + @Override + public void onMeterRemoved(String name) { + try { + mBeanServer.unregisterMBean(createName("meters", name)); + } catch (InstanceNotFoundException e) { + LOGGER.debug("Unable to unregister meter", e); + } catch (MBeanRegistrationException e) { + LOGGER.warn("Unable to unregister meter", e); + } + } + + @Override + public void onTimerAdded(String name, Timer timer) { + try { + final ObjectName objectName = createName("timers", name); + mBeanServer.registerMBean(new JmxTimer(timer, objectName), objectName); + } catch (InstanceAlreadyExistsException e) { + LOGGER.debug("Unable to register timer", e); + } catch (MBeanRegistrationException e) { + LOGGER.warn("Unable to register timer", e); + } catch (NotCompliantMBeanException e) { + LOGGER.warn("Unable to register timer", e); + } + } + + @Override + public void onTimerRemoved(String name) { + try { + mBeanServer.unregisterMBean(createName("timers", name)); + } catch (InstanceNotFoundException e) { + LOGGER.debug("Unable to unregister timer", e); + } catch (MBeanRegistrationException e) { + LOGGER.warn("Unable to unregister timer", e); + } + } + + private ObjectName createName(String type, String name) { + final Hashtable props = new Hashtable(); + props.put("type", type); + props.put("name", name); + try { + return new ObjectName(this.name, props); + } catch (MalformedObjectNameException e) { + props.put("name", ObjectName.quote(name)); + try { + return new ObjectName(this.name, props); + } catch (MalformedObjectNameException e1) { + LOGGER.warn("Unable to register {} {}", type, name, e1); + throw new RuntimeException(e1); + } + } + } + } + + private final MetricRegistry registry; + private final MetricRegistryListener listener; + + public JmxReporter(MBeanServer mBeanServer, MetricRegistry registry) { + this.registry = registry; + this.listener = new JmxListener(mBeanServer, registry.getName()); + } + + public void start() { + registry.addListener(listener); + } + + public void stop() { + registry.removeListener(listener); + } +} diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/Meter.java b/metrics-core/src/main/java/com/yammer/metrics/Meter.java similarity index 76% rename from metrics-core/src/main/java/com/yammer/metrics/core/Meter.java rename to metrics-core/src/main/java/com/yammer/metrics/Meter.java index 83296a89fd..2fa2ca0b4e 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/core/Meter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Meter.java @@ -1,6 +1,4 @@ -package com.yammer.metrics.core; - -import com.yammer.metrics.stats.EWMA; +package com.yammer.metrics; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; @@ -21,36 +19,26 @@ public class Meter implements Metered { private final AtomicLong count = new AtomicLong(); private final long startTime; private final AtomicLong lastTick; - private final TimeUnit rateUnit; - private final String eventType; private final Clock clock; + /** + * Creates a new {@link Meter}. + */ + public Meter() { + this(Clock.defaultClock()); + } + /** * Creates a new {@link Meter}. * - * @param eventType the plural name of the event the meter is measuring (e.g., {@code - * "requests"}) - * @param rateUnit the rate unit of the new meter * @param clock the clock to use for the meter ticks */ - Meter(String eventType, TimeUnit rateUnit, Clock clock) { - this.rateUnit = rateUnit; - this.eventType = eventType; + Meter(Clock clock) { this.clock = clock; this.startTime = this.clock.getTick(); this.lastTick = new AtomicLong(startTime); } - @Override - public TimeUnit getRateUnit() { - return rateUnit; - } - - @Override - public String getEventType() { - return eventType; - } - /** * Updates the moving averages. */ @@ -100,13 +88,13 @@ public long getCount() { @Override public double getFifteenMinuteRate() { tickIfNecessary(); - return m15Rate.getRate(rateUnit); + return m15Rate.getRate(TimeUnit.SECONDS); } @Override public double getFiveMinuteRate() { tickIfNecessary(); - return m5Rate.getRate(rateUnit); + return m5Rate.getRate(TimeUnit.SECONDS); } @Override @@ -122,10 +110,10 @@ public double getMeanRate() { @Override public double getOneMinuteRate() { tickIfNecessary(); - return m1Rate.getRate(rateUnit); + return m1Rate.getRate(TimeUnit.SECONDS); } private double convertNsRate(double ratePerNs) { - return ratePerNs * (double) rateUnit.toNanos(1); + return ratePerNs * (double) TimeUnit.SECONDS.toNanos(1); } } diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/Metered.java b/metrics-core/src/main/java/com/yammer/metrics/Metered.java similarity index 85% rename from metrics-core/src/main/java/com/yammer/metrics/core/Metered.java rename to metrics-core/src/main/java/com/yammer/metrics/Metered.java index 67ee04d110..99c84e8dd4 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/core/Metered.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Metered.java @@ -1,25 +1,9 @@ -package com.yammer.metrics.core; - -import java.util.concurrent.TimeUnit; +package com.yammer.metrics; /** * An object which maintains mean and exponentially-weighted rate. */ public interface Metered extends Metric { - /** - * Returns the meter's rate unit. - * - * @return the meter's rate unit - */ - TimeUnit getRateUnit(); - - /** - * Returns the type of events the meter is measuring. - * - * @return the meter's event type - */ - String getEventType(); - /** * Returns the number of events which have been marked. * diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/Metric.java b/metrics-core/src/main/java/com/yammer/metrics/Metric.java similarity index 74% rename from metrics-core/src/main/java/com/yammer/metrics/core/Metric.java rename to metrics-core/src/main/java/com/yammer/metrics/Metric.java index fecdc4b94e..370110d857 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/core/Metric.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Metric.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.core; +package com.yammer.metrics; /** * A tag interface to indicate that a class is a metric. diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java new file mode 100644 index 0000000000..8c50e652ef --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -0,0 +1,299 @@ +package com.yammer.metrics; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * A registry of metric instances. + */ +public class MetricRegistry { + private final Clock clock; + private final ConcurrentMap metrics; + private final List listeners; + private final String name; + + /** + * Creates a new {@link MetricRegistry} with the given name. + * + * @param name the name of the registry + */ + public MetricRegistry(String name) { + this(name, Clock.defaultClock()); + } + + /** + * Creates a new {@link MetricRegistry} with the given name and {@link Clock} instance. + * + * @param name the name of the registry + * @param clock a {@link Clock} instance + */ + public MetricRegistry(String name, Clock clock) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("A registry needs a name"); + } + this.name = name; + this.clock = clock; + this.metrics = new ConcurrentHashMap(); + this.listeners = new CopyOnWriteArrayList(); + } + + /** + * Given a {@link Metric}, registers it under the given name. + * + * @param name the name of the metric + * @param metric the metric + * @param the type of the metric + * @return {@code metric} + * @throws IllegalArgumentException if the name is already registered + */ + @SuppressWarnings("unchecked") + public T register(String name, T metric) throws IllegalArgumentException { + final Metric existing = metrics.putIfAbsent(name, metric); + if (existing == null) { + onMetricAdded(name, metric); + } else { + throw new IllegalArgumentException("A metric named " + name + " already exists"); + } + return metric; + } + + /** + * Creates a new {@link Counter} and registers it under the given name. + * + * @param name the name of the metric + * @return a new {@link Counter} + */ + public Counter counter(String name) { + return getOrAdd(name, MetricBuilder.COUNTERS); + } + + /** + * Creates a new {@link Histogram} and registers it under the given name. + * + * @param name the name of the metric + * @return a new {@link Histogram} + */ + public Histogram histogram(String name) { + return getOrAdd(name, MetricBuilder.HISTOGRAMS); + } + + /** + * Creates a new {@link Meter} and registers it under the given name. + * + * @param name the name of the metric + * @return a new {@link Meter} + */ + public Meter meter(String name) { + return getOrAdd(name, MetricBuilder.METERS); + } + + /** + * Creates a new {@link Timer} and registers it under the given name. + * + * @param name the name of the metric + * @return a new {@link Timer} + */ + public Timer timer(String name) { + return getOrAdd(name, MetricBuilder.TIMERS); + } + + /** + * Removes the metric with the given name. + * + * @param name the name of the metric + * @return whether or not the metric was removed + */ + public boolean remove(String name) { + final Metric metric = metrics.remove(name); + if (metric != null) { + onMetricRemoved(name, metric); + return true; + } + return false; + } + + /** + * Adds a {@link MetricRegistryListener} to a collection of listeners that will be notified on + * metric creation. Listeners will be notified in the order in which they are added. + *

+ * N.B.: The listener will be notified of all existing metrics when it first registers. + * + * @param listener the listener that will be notified + */ + public void addListener(MetricRegistryListener listener) { + listeners.add(listener); + + for (Map.Entry entry : metrics.entrySet()) { + notifyListenerOfAddedMetric(listener, entry.getValue(), entry.getKey()); + } + } + + /** + * Removes a {@link MetricRegistryListener} from this registry's collection of listeners. + * + * @param listener the listener that will be removed + */ + public void removeListener(MetricRegistryListener listener) { + listeners.remove(listener); + } + + public String getName() { + return name; + } + + public SortedMap getGauges() { + return getMetrics(Gauge.class); + } + + public SortedMap getCounters() { + return getMetrics(Counter.class); + } + + public SortedMap getHistograms() { + return getMetrics(Histogram.class); + } + + public SortedMap getMeters() { + return getMetrics(Meter.class); + } + + public SortedMap getTimers() { + return getMetrics(Timer.class); + } + + Clock getClock() { + return clock; + } + + @SuppressWarnings("unchecked") + private T getOrAdd(String name, MetricBuilder builder) { + final Metric metric = metrics.get(name); + if (builder.isInstance(metric)) { + return (T) metric; + } else if (metric == null) { + try { + return register(name, builder.newMetric(this)); + } catch (IllegalArgumentException e) { + final Metric added = metrics.get(name); + if (builder.isInstance(added)) { + return (T) added; + } + } + } + throw new IllegalArgumentException(name + " is already used for a different type of metric"); + } + + @SuppressWarnings("unchecked") + private SortedMap getMetrics(Class klass) { + final TreeMap timers = new TreeMap(); + for (Map.Entry entry : metrics.entrySet()) { + if (klass.isInstance(entry.getValue())) { + timers.put(entry.getKey(), (T) entry.getValue()); + } + } + return Collections.unmodifiableSortedMap(timers); + } + + private void onMetricAdded(String name, Metric metric) { + for (MetricRegistryListener listener : listeners) { + notifyListenerOfAddedMetric(listener, metric, name); + } + } + + private void notifyListenerOfAddedMetric(MetricRegistryListener listener, Metric metric, String name) { + if (metric instanceof Gauge) { + listener.onGaugeAdded(name, (Gauge) metric); + } else if (metric instanceof Counter) { + listener.onCounterAdded(name, (Counter) metric); + } else if (metric instanceof Histogram) { + listener.onHistogramAdded(name, (Histogram) metric); + } else if (metric instanceof Meter) { + listener.onMeterAdded(name, (Meter) metric); + } else if (metric instanceof Timer) { + listener.onTimerAdded(name, (Timer) metric); + } else { + throw new IllegalArgumentException("Unknown metric type: " + metric.getClass()); + } + } + + private void onMetricRemoved(String name, Metric metric) { + for (MetricRegistryListener listener : listeners) { + notifyListenerOfRemovedMetric(name, metric, listener); + } + } + + private void notifyListenerOfRemovedMetric(String name, Metric metric, MetricRegistryListener listener) { + if (metric instanceof Gauge) { + listener.onGaugeRemoved(name); + } else if (metric instanceof Counter) { + listener.onCounterRemoved(name); + } else if (metric instanceof Histogram) { + listener.onHistogramRemoved(name); + } else if (metric instanceof Meter) { + listener.onMeterRemoved(name); + } else if (metric instanceof Timer) { + listener.onTimerRemoved(name); + } else { + throw new IllegalArgumentException("Unknown metric type: " + metric.getClass()); + } + } + + /** + * A quick and easy way of capturing the notion of default metrics. + */ + private interface MetricBuilder { + final MetricBuilder COUNTERS = new MetricBuilder() { + @Override + public Counter newMetric(MetricRegistry registry) { + return new Counter(); + } + + @Override + public boolean isInstance(Metric metric) { + return Counter.class.isInstance(metric); + } + }; + + final MetricBuilder HISTOGRAMS = new MetricBuilder() { + @Override + public Histogram newMetric(MetricRegistry registry) { + return new Histogram(SampleType.BIASED); + } + + @Override + public boolean isInstance(Metric metric) { + return Histogram.class.isInstance(metric); + } + }; + + final MetricBuilder METERS = new MetricBuilder() { + @Override + public Meter newMetric(MetricRegistry registry) { + return new Meter(registry.getClock()); + } + + @Override + public boolean isInstance(Metric metric) { + return Meter.class.isInstance(metric); + } + }; + + final MetricBuilder TIMERS = new MetricBuilder() { + @Override + public Timer newMetric(MetricRegistry registry) { + return new Timer(registry.getClock()); + } + + @Override + public boolean isInstance(Metric metric) { + return Timer.class.isInstance(metric); + } + }; + + T newMetric(MetricRegistry registry); + + boolean isInstance(Metric metric); + } +} diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistryListener.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistryListener.java new file mode 100644 index 0000000000..bf0c88c92d --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistryListener.java @@ -0,0 +1,28 @@ +package com.yammer.metrics; + +import java.util.EventListener; + +/** + * Listeners for events from the registry. Listeners must be thread-safe. + */ +public interface MetricRegistryListener extends EventListener { + void onGaugeAdded(String name, Gauge gauge); + + void onGaugeRemoved(String name); + + void onCounterAdded(String name, Counter counter); + + void onCounterRemoved(String name); + + void onHistogramAdded(String name, Histogram histogram); + + void onHistogramRemoved(String name); + + void onMeterAdded(String name, Meter meter); + + void onMeterRemoved(String name); + + void onTimerAdded(String name, Timer timer); + + void onTimerRemoved(String name); +} diff --git a/metrics-core/src/main/java/com/yammer/metrics/Metrics.java b/metrics-core/src/main/java/com/yammer/metrics/Metrics.java deleted file mode 100644 index 00e1d32cd4..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/Metrics.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.yammer.metrics; - -import com.yammer.metrics.core.MetricsRegistry; -import com.yammer.metrics.reporting.JmxReporter; - -/** - * A default metrics registry. - */ -public class Metrics { - private static final MetricsRegistry DEFAULT_REGISTRY = new MetricsRegistry(); - private static final JmxReporter JMX_REPORTER = new JmxReporter(DEFAULT_REGISTRY); - - static { - JMX_REPORTER.start(); - } - - private Metrics() { /* unused */ } - - /** - * Returns the (static) default registry. - * - * @return the metrics registry - */ - public static MetricsRegistry defaultRegistry() { - return DEFAULT_REGISTRY; - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/MovingWindowSample.java b/metrics-core/src/main/java/com/yammer/metrics/MovingWindowSample.java new file mode 100644 index 0000000000..d9dcca8319 --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/MovingWindowSample.java @@ -0,0 +1,41 @@ +package com.yammer.metrics; + +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongArray; + +import static java.lang.Math.min; + +public class MovingWindowSample implements Sample { + private final AtomicLongArray window; + private final AtomicLong index; + + public MovingWindowSample(int size) { + this.window = new AtomicLongArray(size); + this.index = new AtomicLong(); + } + + @Override + public void clear() { + index.set(0); + } + + @Override + public int size() { + return (int) min(index.get(), window.length()); + } + + @Override + public void update(long value) { + final int i = (int) (index.getAndIncrement() % window.length()); + window.set(i, value); + } + + @Override + public Snapshot getSnapshot() { + final long[] values = new long[size()]; + for (int i = 0; i < values.length; i++) { + values[i] = window.get(i); + } + return new Snapshot(values); + } +} diff --git a/metrics-core/src/main/java/com/yammer/metrics/Reporter.java b/metrics-core/src/main/java/com/yammer/metrics/Reporter.java new file mode 100644 index 0000000000..9f6d36e81f --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/Reporter.java @@ -0,0 +1,11 @@ +package com.yammer.metrics; + +import java.util.SortedMap; + +public interface Reporter { + void report(SortedMap gauges, + SortedMap counters, + SortedMap histograms, + SortedMap meters, + SortedMap timers); +} diff --git a/metrics-core/src/main/java/com/yammer/metrics/stats/Sample.java b/metrics-core/src/main/java/com/yammer/metrics/Sample.java similarity index 94% rename from metrics-core/src/main/java/com/yammer/metrics/stats/Sample.java rename to metrics-core/src/main/java/com/yammer/metrics/Sample.java index 5a299ab2fa..da1dd9e157 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/stats/Sample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Sample.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.stats; +package com.yammer.metrics; /** * A statistically representative sample of a data stream. diff --git a/metrics-core/src/main/java/com/yammer/metrics/SampleType.java b/metrics-core/src/main/java/com/yammer/metrics/SampleType.java new file mode 100644 index 0000000000..ca7180d5d7 --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/SampleType.java @@ -0,0 +1,34 @@ +package com.yammer.metrics; + +/** + * The type of sampling the histogram should be performing. + */ +public enum SampleType { + /** + * Uses a uniform sample of 1028 elements, which offers a 99.9% confidence level with a 5% + * margin of error assuming a normal distribution. + */ + UNIFORM { + @Override + public Sample newSample() { + return new UniformSample(DEFAULT_SAMPLE_SIZE); + } + }, + + /** + * Uses an exponentially decaying sample of 1028 elements, which offers a 99.9% confidence + * level with a 5% margin of error assuming a normal distribution, and an alpha factor of + * 0.015, which heavily biases the sample to the past 5 minutes of measurements. + */ + BIASED { + @Override + public Sample newSample() { + return new ExponentiallyDecayingSample(DEFAULT_SAMPLE_SIZE, DEFAULT_ALPHA); + } + }; + + private static final int DEFAULT_SAMPLE_SIZE = 1028; + private static final double DEFAULT_ALPHA = 0.015; + + public abstract Sample newSample(); +} diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/Sampling.java b/metrics-core/src/main/java/com/yammer/metrics/Sampling.java similarity index 73% rename from metrics-core/src/main/java/com/yammer/metrics/core/Sampling.java rename to metrics-core/src/main/java/com/yammer/metrics/Sampling.java index 5108881e39..8cb9effed2 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/core/Sampling.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Sampling.java @@ -1,6 +1,4 @@ -package com.yammer.metrics.core; - -import com.yammer.metrics.stats.Snapshot; +package com.yammer.metrics; /** * An object which samples values. diff --git a/metrics-core/src/main/java/com/yammer/metrics/stats/Snapshot.java b/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java similarity index 93% rename from metrics-core/src/main/java/com/yammer/metrics/stats/Snapshot.java rename to metrics-core/src/main/java/com/yammer/metrics/Snapshot.java index 2df1137a18..3ed8713880 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/stats/Snapshot.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.stats; +package com.yammer.metrics; import java.io.File; import java.io.IOException; @@ -18,7 +18,7 @@ public class Snapshot { private static final double P99_Q = 0.99; private static final double P999_Q = 0.999; - private final double[] values; + private final long[] values; /** * Create a new {@link Snapshot} with the given values. @@ -27,7 +27,7 @@ public class Snapshot { */ public Snapshot(Collection values) { final Object[] copy = values.toArray(); - this.values = new double[copy.length]; + this.values = new long[copy.length]; for (int i = 0; i < copy.length; i++) { this.values[i] = (Long) copy[i]; } @@ -39,9 +39,8 @@ public Snapshot(Collection values) { * * @param values an unordered set of values in the sample */ - public Snapshot(double[] values) { - this.values = new double[values.length]; - System.arraycopy(values, 0, this.values, 0, values.length); + public Snapshot(long[] values) { + this.values = Arrays.copyOf(values, values.length); Arrays.sort(this.values); } @@ -143,7 +142,7 @@ public double get999thPercentile() { * * @return the entire set of values in the snapshot */ - public double[] getValues() { + public long[] getValues() { return Arrays.copyOf(values, values.length); } diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/Summarizable.java b/metrics-core/src/main/java/com/yammer/metrics/Summarizable.java similarity index 96% rename from metrics-core/src/main/java/com/yammer/metrics/core/Summarizable.java rename to metrics-core/src/main/java/com/yammer/metrics/Summarizable.java index b886741206..cd6836e1a1 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/core/Summarizable.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Summarizable.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.core; +package com.yammer.metrics; /** * An object which can produce statistical summaries. diff --git a/metrics-core/src/main/java/com/yammer/metrics/stats/ThreadLocalRandom.java b/metrics-core/src/main/java/com/yammer/metrics/ThreadLocalRandom.java similarity index 99% rename from metrics-core/src/main/java/com/yammer/metrics/stats/ThreadLocalRandom.java rename to metrics-core/src/main/java/com/yammer/metrics/ThreadLocalRandom.java index 21b04cbf09..876a6793e4 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/stats/ThreadLocalRandom.java +++ b/metrics-core/src/main/java/com/yammer/metrics/ThreadLocalRandom.java @@ -6,7 +6,7 @@ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/ThreadLocalRandom.java?view=markup */ -package com.yammer.metrics.stats; +package com.yammer.metrics; import java.util.Random; diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/Timer.java b/metrics-core/src/main/java/com/yammer/metrics/Timer.java similarity index 65% rename from metrics-core/src/main/java/com/yammer/metrics/core/Timer.java rename to metrics-core/src/main/java/com/yammer/metrics/Timer.java index 0bfeec8b36..b1b9111b76 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/core/Timer.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Timer.java @@ -1,7 +1,4 @@ -package com.yammer.metrics.core; - -import com.yammer.metrics.core.Histogram.SampleType; -import com.yammer.metrics.stats.Snapshot; +package com.yammer.metrics; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; @@ -11,38 +8,26 @@ * throughput statistics via {@link Meter}. */ public class Timer implements Metered, Sampling, Summarizable { - private final TimeUnit durationUnit, rateUnit; private final Meter meter; private final Histogram histogram = new Histogram(SampleType.BIASED); private final Clock clock; /** * Creates a new {@link Timer}. - * - * @param durationUnit the scale unit for this timer's duration metrics - * @param rateUnit the scale unit for this timer's rate metrics - * @param clock the clock used to calculate duration */ - Timer(TimeUnit durationUnit, TimeUnit rateUnit, Clock clock) { - this.durationUnit = durationUnit; - this.rateUnit = rateUnit; - this.meter = new Meter("calls", rateUnit, clock); - this.clock = clock; - clear(); + public Timer() { + this(Clock.defaultClock()); } /** - * Returns the timer's duration scale unit. + * Creates a new {@link Timer}. * - * @return the timer's duration scale unit + * @param clock the clock used to calculate duration */ - public TimeUnit getDurationUnit() { - return durationUnit; - } - - @Override - public TimeUnit getRateUnit() { - return rateUnit; + Timer(Clock clock) { + this.meter = new Meter(clock); + this.clock = clock; + clear(); } /** @@ -81,12 +66,13 @@ public T time(Callable event) throws Exception { } /** - * Returns a timing {@link TimerContext}, which measures an elapsed time in nanoseconds. + * Returns a timing {@link Context}, which measures an elapsed time in nanoseconds. * - * @return a new {@link TimerContext} + * @return a new {@link Context} + * @see Context */ - public TimerContext time() { - return new TimerContext(this, clock); + public Context time() { + return new Context(this, clock); } @Override @@ -121,7 +107,7 @@ public double getOneMinuteRate() { */ @Override public double getMax() { - return convertFromNS(histogram.getMax()); + return histogram.getMax(); } /** @@ -131,7 +117,7 @@ public double getMax() { */ @Override public double getMin() { - return convertFromNS(histogram.getMin()); + return histogram.getMin(); } /** @@ -141,7 +127,7 @@ public double getMin() { */ @Override public double getMean() { - return convertFromNS(histogram.getMean()); + return histogram.getMean(); } /** @@ -151,7 +137,7 @@ public double getMean() { */ @Override public double getStdDev() { - return convertFromNS(histogram.getStdDev()); + return histogram.getStdDev(); } /** @@ -161,22 +147,12 @@ public double getStdDev() { */ @Override public double getSum() { - return convertFromNS(histogram.getSum()); + return histogram.getSum(); } @Override public Snapshot getSnapshot() { - final double[] values = histogram.getSnapshot().getValues(); - final double[] converted = new double[values.length]; - for (int i = 0; i < values.length; i++) { - converted[i] = convertFromNS(values[i]); - } - return new Snapshot(converted); - } - - @Override - public String getEventType() { - return meter.getEventType(); + return new Snapshot(histogram.getSnapshot().getValues()); } private void update(long duration) { @@ -186,7 +162,30 @@ private void update(long duration) { } } - private double convertFromNS(double ns) { - return ns / TimeUnit.NANOSECONDS.convert(1, durationUnit); + /** + * A timing context. + * + * @see Timer#time() + */ + public static class Context { + private final Timer timer; + private final Clock clock; + private final long startTime; + + private Context(Timer timer, Clock clock) { + this.timer = timer; + this.clock = clock; + this.startTime = clock.getTick(); + } + + /** + * Stops recording the elapsed time, updates the timer and returns the elapsed time in + * nanoseconds. + */ + public long stop() { + final long elapsedNanos = clock.getTick() - startTime; + timer.update(elapsedNanos, TimeUnit.NANOSECONDS); + return elapsedNanos; + } } } diff --git a/metrics-core/src/main/java/com/yammer/metrics/stats/UniformSample.java b/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java similarity index 97% rename from metrics-core/src/main/java/com/yammer/metrics/stats/UniformSample.java rename to metrics-core/src/main/java/com/yammer/metrics/UniformSample.java index 64a6886410..9f27ebc9d6 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/stats/UniformSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.stats; +package com.yammer.metrics; import java.util.ArrayList; import java.util.List; @@ -74,7 +74,7 @@ private static long nextLong(long n) { @Override public Snapshot getSnapshot() { - final int s = size(); + final int s = (int) size(); final List copy = new ArrayList(s); for (int i = 0; i < s; i++) { copy.add(values.get(i)); diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/HealthCheck.java b/metrics-core/src/main/java/com/yammer/metrics/core/HealthCheck.java deleted file mode 100644 index 80b740c81f..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/core/HealthCheck.java +++ /dev/null @@ -1,202 +0,0 @@ -package com.yammer.metrics.core; - -/** - * A health check for a component of your application. - */ -public abstract class HealthCheck { - /** - * The result of a {@link HealthCheck} being run. It can be healthy (with an optional message) - * or unhealthy (with either an error message or a thrown exception). - */ - public static class Result { - private static final Result HEALTHY = new Result(true, null, null); - private static final int PRIME = 31; - - /** - * Returns a healthy {@link Result} with no additional message. - * - * @return a healthy {@link Result} with no additional message - */ - public static Result healthy() { - return HEALTHY; - } - - /** - * Returns a healthy {@link Result} with an additional message. - * - * @param message an informative message - * @return a healthy {@link Result} with an additional message - */ - public static Result healthy(String message) { - return new Result(true, message, null); - } - - /** - * Returns a healthy {@link Result} with a formatted message. - * - * Message formatting follows the same rules as - * {@link String#format(String, Object...)}. - * - * @param message a message format - * @param args the arguments apply to the message format - * @return a healthy {@link Result} with an additional message - * @see String#format(String, Object...) - */ - public static Result healthy(String message, Object... args) { - return healthy(String.format(message, args)); - } - - /** - * Returns an unhealthy {@link Result} with the given message. - * - * @param message an informative message describing how the health check failed - * @return an unhealthy {@link Result} with the given message - */ - public static Result unhealthy(String message) { - return new Result(false, message, null); - } - - /** - * Returns an unhealthy {@link Result} with a formatted message. - * - * Message formatting follows the same rules as - * {@link String#format(String, Object...)}. - * - * @param message a message format - * @param args the arguments apply to the message format - * @return an unhealthy {@link Result} with an additional message - * @see String#format(String, Object...) - */ - public static Result unhealthy(String message, Object... args) { - return unhealthy(String.format(message, args)); - } - - /** - * Returns an unhealthy {@link Result} with the given error. - * - * @param error an exception thrown during the health check - * @return an unhealthy {@link Result} with the given error - */ - public static Result unhealthy(Throwable error) { - return new Result(false, error.getMessage(), error); - } - - private final boolean healthy; - private final String message; - private final Throwable error; - - private Result(boolean isHealthy, String message, Throwable error) { - this.healthy = isHealthy; - this.message = message; - this.error = error; - } - - /** - * Returns {@code true} if the result indicates the component is healthy; {@code false} - * otherwise. - * @return {@code true} if the result indicates the component is healthy - */ - public boolean isHealthy() { - return healthy; - } - - /** - * Returns any additional message for the result, or {@code null} if the result has no - * message. - * - * @return any additional message for the result, or {@code null} - */ - public String getMessage() { - return message; - } - - /** - * Returns any exception for the result, or {@code null} if the result has no exception. - * - * @return any exception for the result, or {@code null} - */ - public Throwable getError() { - return error; - } - - @Override - public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } - final Result result = (Result) o; - return healthy == result.healthy && - !(error != null ? !error.equals(result.error) : result.error != null) && - !(message != null ? !message.equals(result.message) : result.message != null); - } - - @Override - public int hashCode() { - int result = (healthy ? 1 : 0); - result = PRIME * result + (message != null ? message.hashCode() : 0); - result = PRIME * result + (error != null ? error.hashCode() : 0); - return result; - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder("Result{isHealthy="); - builder.append(healthy); - if (message != null) { - builder.append(", message=").append(message); - } - if (error != null) { - builder.append(", error=").append(error); - } - builder.append('}'); - return builder.toString(); - } - } - - private final String name; - - /** - * Create a new {@link HealthCheck} instance with the given name. - * - * @param name the name of the health check (and, ideally, the name of the underlying - * component the health check tests) - */ - protected HealthCheck(String name) { - this.name = name; - } - - /** - * Returns the health check's name. - * - * @return the health check's name - */ - public String getName() { - return name; - } - - /** - * Perform a check of the application component. - * - * @return if the component is healthy, a healthy {@link Result}; otherwise, an unhealthy - * {@link Result} with a descriptive error message or exception - * - * @throws Exception if there is an unhandled error during the health check; this will result in - * a failed health check - */ - protected abstract Result check() throws Exception; - - /** - * Executes the health check, catching and handling any exceptions raised by {@link #check()}. - * - * @return if the component is healthy, a healthy {@link Result}; otherwise, an unhealthy - * {@link Result} with a descriptive error message or exception - */ - public Result execute() { - try { - return check(); - } catch (Error e) { - throw e; - } catch (Throwable e) { - return Result.unhealthy(e); - } - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/HealthCheckRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/core/HealthCheckRegistry.java deleted file mode 100644 index e3779842af..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/core/HealthCheckRegistry.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.yammer.metrics.core; - -import com.yammer.metrics.core.HealthCheck.Result; - -import java.util.Collections; -import java.util.Map.Entry; -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -/** - * A registry for health checks. - */ -public class HealthCheckRegistry { - private final ConcurrentMap healthChecks = new ConcurrentHashMap(); - - /** - * Registers an application {@link HealthCheck}. - * - * @param healthCheck the {@link HealthCheck} instance - */ - public void register(HealthCheck healthCheck) { - healthChecks.putIfAbsent(healthCheck.getName(), healthCheck); - } - - /** - * Unregisters the application {@link HealthCheck} with the given name. - * - * @param name the name of the {@link HealthCheck} instance - */ - public void unregister(String name) { - healthChecks.remove(name); - } - - /** - * Unregisters the given {@link HealthCheck}. - * - * @param healthCheck a {@link HealthCheck} - */ - public void unregister(HealthCheck healthCheck) { - unregister(healthCheck.getName()); - } - - /** - * Runs the registered health checks and returns a map of the results. - * - * @return a map of the health check results - */ - public SortedMap runHealthChecks() { - final SortedMap results = new TreeMap(); - for (Entry entry : healthChecks.entrySet()) { - final Result result = entry.getValue().execute(); - results.put(entry.getKey(), result); - } - return Collections.unmodifiableSortedMap(results); - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/MetricName.java b/metrics-core/src/main/java/com/yammer/metrics/core/MetricName.java deleted file mode 100644 index 76826efdd2..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/core/MetricName.java +++ /dev/null @@ -1,228 +0,0 @@ -package com.yammer.metrics.core; - -import com.yammer.metrics.annotation.ExceptionMetered; - -import java.lang.reflect.Method; - -/** - * A value class encapsulating a metric's owning class and name. - */ -public class MetricName implements Comparable { - private final String domain; - private final String type; - private final String name; - private final String scope; - - /** - * Creates a new {@link MetricName} without a scope. - * - * @param klass the {@link Class} to which the {@link Metric} belongs - * @param name the name of the {@link Metric} - */ - public MetricName(Class klass, String name) { - this(klass, name, null); - } - - /** - * Creates a new {@link MetricName} without a scope. - * - * @param domain the domain to which the {@link Metric} belongs - * @param type the type to which the {@link Metric} belongs - * @param name the name of the {@link Metric} - */ - public MetricName(String domain, String type, String name) { - this(domain, type, name, null); - } - - /** - * Creates a new {@link MetricName} without a scope. - * - * @param klass the {@link Class} to which the {@link Metric} belongs - * @param name the name of the {@link Metric} - * @param scope the scope of the {@link Metric} - */ - public MetricName(Class klass, String name, String scope) { - this(getPackageName(klass), - getClassName(klass), - name, - scope); - } - - /** - * Creates a new {@link MetricName} without a scope. - * - * @param domain the domain to which the {@link Metric} belongs - * @param type the type to which the {@link Metric} belongs - * @param name the name of the {@link Metric} - * @param scope the scope of the {@link Metric} - */ - public MetricName(String domain, String type, String name, String scope) { - if (domain == null || type == null) { - throw new IllegalArgumentException("Both domain and type need to be specified"); - } - if (name == null) { - throw new IllegalArgumentException("Name needs to be specified"); - } - this.domain = domain; - this.type = type; - this.name = name; - this.scope = scope; - } - - /** - * Returns the domain to which the {@link Metric} belongs. For class-based metrics, this will be - * the package name of the {@link Class} to which the {@link Metric} belongs. - * - * @return the domain to which the {@link Metric} belongs - */ - public String getDomain() { - return domain; - } - - /** - * Returns the type to which the {@link Metric} belongs. For class-based metrics, this will be - * the simple class name of the {@link Class} to which the {@link Metric} belongs. - * - * @return the type to which the {@link Metric} belongs - */ - public String getType() { - return type; - } - - /** - * Returns the name of the {@link Metric}. - * - * @return the name of the {@link Metric} - */ - public String getName() { - return name; - } - - /** - * Returns the scope of the {@link Metric}. - * - * @return the scope of the {@link Metric} - */ - public String getScope() { - return scope; - } - - /** - * Returns {@code true} if the {@link Metric} has a scope, {@code false} otherwise. - * - * @return {@code true} if the {@link Metric} has a scope - */ - public boolean hasScope() { - return scope != null; - } - - @Override - public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } - final MetricName that = (MetricName) o; - return domain.equals(that.domain) && - name.equals(that.name) && - type.equals(that.type) && - (scope == null ? that.scope == null : scope.equals(that.scope)); - } - - @Override - public int hashCode() { - int result = domain.hashCode(); - result = 31 * result + type.hashCode(); - result = 31 * result + name.hashCode(); - result = 31 * result + (scope != null ? scope.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return domain + '.' + type + '.' + name + (scope == null ? "" : '.' + scope); - } - - @Override - public int compareTo(MetricName o) { - int result = domain.compareTo(o.domain); - if (result != 0) { - return result; - } - - result = type.compareTo(o.type); - if (result != 0) { - return result; - } - - result = name.compareTo(o.name); - if (result != 0) { - return result; - } - - if (scope == null) { - if (o.scope != null) { - return -1; - } - return 0; - } - - if (o.scope != null) { - return scope.compareTo(o.scope); - } - return 1; - } - - private static String getPackageName(Class klass) { - return klass.getPackage() == null ? "" : klass.getPackage().getName(); - } - - private static String getClassName(Class klass) { - return klass.getSimpleName().replaceAll("\\$$", ""); - } - - private static String chooseDomain(String domain, Class klass) { - if(domain == null || domain.isEmpty()) { - domain = getPackageName(klass); - } - return domain; - } - - private static String chooseType(String type, Class klass) { - if(type == null || type.isEmpty()) { - type = getClassName(klass); - } - return type; - } - - private static String chooseName(String name, Method method) { - if(name == null || name.isEmpty()) { - name = method.getName(); - } - return name; - } - - public static MetricName forTimedMethod(Class klass, Method method, com.yammer.metrics.annotation.Timed annotation) { - return new MetricName(chooseDomain(annotation.group(), klass), - chooseType(annotation.type(), klass), - chooseName(annotation.name(), method)); - } - - public static MetricName forMeteredMethod(Class klass, Method method, com.yammer.metrics.annotation.Metered annotation) { - return new MetricName(chooseDomain(annotation.group(), klass), - chooseType(annotation.type(), klass), - chooseName(annotation.name(), method)); - } - - public static MetricName forGaugeMethod(Class klass, Method method, com.yammer.metrics.annotation.Gauge annotation) { - return new MetricName(chooseDomain(annotation.group(), klass), - chooseType(annotation.type(), klass), - chooseName(annotation.name(), method)); - } - - public static MetricName forExceptionMeteredMethod(Class klass, Method method, com.yammer.metrics.annotation.ExceptionMetered annotation) { - return new MetricName(chooseDomain(annotation.group(), klass), - chooseType(annotation.type(), klass), - annotation.name() == null || annotation.name().isEmpty() ? - method.getName() + ExceptionMetered.DEFAULT_NAME_SUFFIX : - annotation.name()); - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/MetricPredicate.java b/metrics-core/src/main/java/com/yammer/metrics/core/MetricPredicate.java deleted file mode 100644 index 9060ec8310..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/core/MetricPredicate.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.yammer.metrics.core; - -/** - * A {@link MetricPredicate} is used to determine whether a metric should be included when sorting - * and filtering metrics. This is especially useful for limited metric reporting. - */ -public interface MetricPredicate { - /** - * A predicate which matches all inputs. - */ - MetricPredicate ALL = new MetricPredicate() { - @Override - public boolean matches(MetricName name, Metric metric) { - return true; - } - }; - - /** - * Returns {@code true} if the metric matches the predicate. - * - * @param name the name of the metric - * @param metric the metric itself - * @return {@code true} if the predicate applies, {@code false} otherwise - */ - boolean matches(MetricName name, Metric metric); -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/MetricProcessor.java b/metrics-core/src/main/java/com/yammer/metrics/core/MetricProcessor.java deleted file mode 100644 index fcde224bf0..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/core/MetricProcessor.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.yammer.metrics.core; - -/** - * A processor of metric instances. - * - * @param - */ -public interface MetricProcessor { - /** - * Process the given {@link Metered} instance. - * - * @param name the name of the meter - * @param meter the meter - * @param context the context of the meter - * @throws Exception if something goes wrong - */ - void processMeter(MetricName name, Metered meter, T context) throws Exception; - - /** - * Process the given counter. - * - * @param name the name of the counter - * @param counter the counter - * @param context the context of the meter - * @throws Exception if something goes wrong - */ - void processCounter(MetricName name, Counter counter, T context) throws Exception; - - /** - * Process the given histogram. - * - * @param name the name of the histogram - * @param histogram the histogram - * @param context the context of the meter - * @throws Exception if something goes wrong - */ - void processHistogram(MetricName name, Histogram histogram, T context) throws Exception; - - /** - * Process the given timer. - * - * @param name the name of the timer - * @param timer the timer - * @param context the context of the meter - * @throws Exception if something goes wrong - */ - void processTimer(MetricName name, Timer timer, T context) throws Exception; - - /** - * Process the given gauge. - * - * @param name the name of the gauge - * @param gauge the gauge - * @param context the context of the meter - * @throws Exception if something goes wrong - */ - void processGauge(MetricName name, Gauge gauge, T context) throws Exception; -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/MetricsRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/core/MetricsRegistry.java deleted file mode 100644 index 108c3b13de..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/core/MetricsRegistry.java +++ /dev/null @@ -1,511 +0,0 @@ -package com.yammer.metrics.core; - -import com.yammer.metrics.core.Histogram.SampleType; - -import java.util.*; -import java.util.concurrent.*; - -/** - * A registry of metric instances. - */ -public class MetricsRegistry { - private static final int EXPECTED_METRIC_COUNT = 1024; - private final Clock clock; - private final ConcurrentMap metrics; - private final List listeners; - private final String name; - - /** - * Creates a new {@link MetricsRegistry}. - */ - public MetricsRegistry() { - this(Clock.defaultClock()); - } - - /** - * Creates a new {@link MetricsRegistry} with the given name. - * - * @param name the name of the registry - */ - public MetricsRegistry(String name) { - this(name, Clock.defaultClock()); - } - - /** - * Creates a new {@link MetricsRegistry} with the given {@link Clock} instance. - * - * @param clock a {@link Clock} instance - */ - public MetricsRegistry(Clock clock) { - this(null, clock); - } - - /** - * Creates a new {@link MetricsRegistry} with the given name and {@link Clock} instance. - * - * @param name the name of the registry - * @param clock a {@link Clock} instance - */ - public MetricsRegistry(String name, Clock clock) { - this.name = name; - this.clock = clock; - this.metrics = newMetricsMap(); - this.listeners = new CopyOnWriteArrayList(); - } - - /** - * Given a new {@link Gauge}, registers it under the given class and name. - * - * @param klass the class which owns the metric - * @param name the name of the metric - * @param metric the metric - * @param the type of the value returned by the metric - * @return {@code metric} - */ - public Gauge newGauge(Class klass, - String name, - Gauge metric) { - return newGauge(klass, name, null, metric); - } - - /** - * Given a new {@link Gauge}, registers it under the given class and name. - * - * @param klass the class which owns the metric - * @param name the name of the metric - * @param scope the scope of the metric - * @param metric the metric - * @param the type of the value returned by the metric - * @return {@code metric} - */ - public Gauge newGauge(Class klass, - String name, - String scope, - Gauge metric) { - return newGauge(createName(klass, name, scope), metric); - } - - /** - * Given a new {@link Gauge}, registers it under the given metric name. - * - * @param metricName the name of the metric - * @param metric the metric - * @param the type of the value returned by the metric - * @return {@code metric} - */ - public Gauge newGauge(MetricName metricName, - Gauge metric) { - return getOrAdd(metricName, metric); - } - - /** - * Creates a new {@link Counter} and registers it under the given class and name. - * - * @param klass the class which owns the metric - * @param name the name of the metric - * @return a new {@link Counter} - */ - public Counter newCounter(Class klass, - String name) { - return newCounter(klass, name, null); - } - - /** - * Creates a new {@link Counter} and registers it under the given class and name. - * - * @param klass the class which owns the metric - * @param name the name of the metric - * @param scope the scope of the metric - * @return a new {@link Counter} - */ - public Counter newCounter(Class klass, - String name, - String scope) { - return newCounter(createName(klass, name, scope)); - } - - /** - * Creates a new {@link Counter} and registers it under the given metric name. - * - * @param metricName the name of the metric - * @return a new {@link Counter} - */ - public Counter newCounter(MetricName metricName) { - return getOrAdd(metricName, new Counter()); - } - - /** - * Creates a new {@link Histogram} and registers it under the given class and name. - * - * @param klass the class which owns the metric - * @param name the name of the metric - * @param biased whether or not the histogram should be biased - * @return a new {@link Histogram} - */ - public Histogram newHistogram(Class klass, - String name, - boolean biased) { - return newHistogram(klass, name, null, biased); - } - - /** - * Creates a new {@link Histogram} and registers it under the given class, name, and scope. - * - * @param klass the class which owns the metric - * @param name the name of the metric - * @param scope the scope of the metric - * @param biased whether or not the histogram should be biased - * @return a new {@link Histogram} - */ - public Histogram newHistogram(Class klass, - String name, - String scope, - boolean biased) { - return newHistogram(createName(klass, name, scope), biased); - } - - /** - * Creates a new non-biased {@link Histogram} and registers it under the given class and name. - * - * @param klass the class which owns the metric - * @param name the name of the metric - * @return a new {@link Histogram} - */ - public Histogram newHistogram(Class klass, - String name) { - return newHistogram(klass, name, false); - } - - /** - * Creates a new non-biased {@link Histogram} and registers it under the given class, name, and - * scope. - * - * @param klass the class which owns the metric - * @param name the name of the metric - * @param scope the scope of the metric - * @return a new {@link Histogram} - */ - public Histogram newHistogram(Class klass, - String name, - String scope) { - return newHistogram(klass, name, scope, false); - } - - /** - * Creates a new {@link Histogram} and registers it under the given metric name. - * - * @param metricName the name of the metric - * @param biased whether or not the histogram should be biased - * @return a new {@link Histogram} - */ - public Histogram newHistogram(MetricName metricName, - boolean biased) { - return getOrAdd(metricName, - new Histogram(biased ? SampleType.BIASED : SampleType.UNIFORM)); - } - - /** - * Creates a new {@link Meter} and registers it under the given class and name. - * - * @param klass the class which owns the metric - * @param name the name of the metric - * @param eventType the plural name of the type of events the meter is measuring (e.g., {@code - * "requests"}) - * @param unit the rate unit of the new meter - * @return a new {@link Meter} - */ - public Meter newMeter(Class klass, - String name, - String eventType, - TimeUnit unit) { - return newMeter(klass, name, null, eventType, unit); - } - - /** - * Creates a new {@link Meter} and registers it under the given class, name, and scope. - * - * @param klass the class which owns the metric - * @param name the name of the metric - * @param scope the scope of the metric - * @param eventType the plural name of the type of events the meter is measuring (e.g., {@code - * "requests"}) - * @param unit the rate unit of the new meter - * @return a new {@link Meter} - */ - public Meter newMeter(Class klass, - String name, - String scope, - String eventType, - TimeUnit unit) { - return newMeter(createName(klass, name, scope), eventType, unit); - } - - /** - * Creates a new {@link Meter} and registers it under the given metric name. - * - * @param metricName the name of the metric - * @param eventType the plural name of the type of events the meter is measuring (e.g., {@code - * "requests"}) - * @param unit the rate unit of the new meter - * @return a new {@link Meter} - */ - public Meter newMeter(MetricName metricName, - String eventType, - TimeUnit unit) { - final Metric existingMetric = metrics.get(metricName); - if (existingMetric != null) { - return (Meter) existingMetric; - } - return getOrAdd(metricName, new Meter(eventType, unit, clock)); - } - - /** - * Creates a new {@link Timer} and registers it under the given class and name, measuring - * elapsed time in milliseconds and invocations per second. - * - * @param klass the class which owns the metric - * @param name the name of the metric - * @return a new {@link Timer} - */ - public Timer newTimer(Class klass, - String name) { - return newTimer(klass, name, null, TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - } - - /** - * Creates a new {@link Timer} and registers it under the given class and name. - * - * @param klass the class which owns the metric - * @param name the name of the metric - * @param durationUnit the duration scale unit of the new timer - * @param rateUnit the rate scale unit of the new timer - * @return a new {@link Timer} - */ - public Timer newTimer(Class klass, - String name, - TimeUnit durationUnit, - TimeUnit rateUnit) { - return newTimer(klass, name, null, durationUnit, rateUnit); - } - - /** - * Creates a new {@link Timer} and registers it under the given class, name, and scope, - * measuring elapsed time in milliseconds and invocations per second. - * - * @param klass the class which owns the metric - * @param name the name of the metric - * @param scope the scope of the metric - * @return a new {@link Timer} - */ - public Timer newTimer(Class klass, - String name, - String scope) { - return newTimer(klass, name, scope, TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - } - - /** - * Creates a new {@link Timer} and registers it under the given class, name, and scope. - * - * @param klass the class which owns the metric - * @param name the name of the metric - * @param scope the scope of the metric - * @param durationUnit the duration scale unit of the new timer - * @param rateUnit the rate scale unit of the new timer - * @return a new {@link Timer} - */ - public Timer newTimer(Class klass, - String name, - String scope, - TimeUnit durationUnit, - TimeUnit rateUnit) { - return newTimer(createName(klass, name, scope), durationUnit, rateUnit); - } - - /** - * Creates a new {@link Timer} and registers it under the given metric name. - * - * @param metricName the name of the metric - * @param durationUnit the duration scale unit of the new timer - * @param rateUnit the rate scale unit of the new timer - * @return a new {@link Timer} - */ - public Timer newTimer(MetricName metricName, - TimeUnit durationUnit, - TimeUnit rateUnit) { - final Metric existingMetric = metrics.get(metricName); - if (existingMetric != null) { - return (Timer) existingMetric; - } - return getOrAdd(metricName, - new Timer(durationUnit, rateUnit, clock)); - } - - /** - * Returns an unmodifiable map of all metrics and their names. - * - * @return an unmodifiable map of all metrics and their names - */ - public Map getAllMetrics() { - return Collections.unmodifiableMap(metrics); - } - - /** - * Returns a grouped and sorted map of all registered metrics. - * - * @return all registered metrics, grouped by name and sorted - */ - public SortedMap> getGroupedMetrics() { - return getGroupedMetrics(MetricPredicate.ALL); - } - - /** - * Returns a grouped and sorted map of all registered metrics which match then given {@link - * MetricPredicate}. - * - * @param predicate a predicate which metrics have to match to be in the results - * @return all registered metrics which match {@code predicate}, sorted by name - */ - public SortedMap> getGroupedMetrics(MetricPredicate predicate) { - final SortedMap> groups = - new TreeMap>(); - for (Map.Entry entry : metrics.entrySet()) { - final String qualifiedTypeName = entry.getKey().getDomain() + "." + entry.getKey() - .getType(); - if (predicate.matches(entry.getKey(), entry.getValue())) { - final String scopedName; - if (entry.getKey().hasScope()) { - scopedName = qualifiedTypeName + "." + entry.getKey().getScope(); - } else { - scopedName = qualifiedTypeName; - } - SortedMap group = groups.get(scopedName); - if (group == null) { - group = new TreeMap(); - groups.put(scopedName, group); - } - group.put(entry.getKey(), entry.getValue()); - } - } - return Collections.unmodifiableSortedMap(groups); - } - - /** - * Removes the metric for the given class with the given name. - * - * @param klass the klass the metric is associated with - * @param name the name of the metric - */ - public void removeMetric(Class klass, - String name) { - removeMetric(klass, name, null); - } - - /** - * Removes the metric for the given class with the given name and scope. - * - * @param klass the klass the metric is associated with - * @param name the name of the metric - * @param scope the scope of the metric - */ - public void removeMetric(Class klass, - String name, - String scope) { - removeMetric(createName(klass, name, scope)); - } - - /** - * Removes the metric with the given name. - * - * @param name the name of the metric - */ - public void removeMetric(MetricName name) { - final Metric metric = metrics.remove(name); - if (metric != null) { - notifyMetricRemoved(name); - } - } - - /** - * Adds a {@link MetricsRegistryListener} to a collection of listeners that will be notified on - * metric creation. Listeners will be notified in the order in which they are added. - *

- * N.B.: The listener will be notified of all existing metrics when it first registers. - * - * @param listener the listener that will be notified - */ - public void addListener(MetricsRegistryListener listener) { - listeners.add(listener); - for (Map.Entry entry : metrics.entrySet()) { - listener.onMetricAdded(entry.getKey(), entry.getValue()); - } - } - - /** - * Removes a {@link MetricsRegistryListener} from this registry's collection of listeners. - * - * @param listener the listener that will be removed - */ - public void removeListener(MetricsRegistryListener listener) { - listeners.remove(listener); - } - - /** - * Override to customize how {@link MetricName}s are created. - * - * @param klass the class which owns the metric - * @param name the name of the metric - * @param scope the metric's scope - * @return the metric's full name - */ - protected MetricName createName(Class klass, String name, String scope) { - return new MetricName(klass, name, scope); - } - - /** - * Returns a new {@link ConcurrentMap} implementation. Subclass this to do weird things with - * your own {@link MetricsRegistry} implementation. - * - * @return a new {@link ConcurrentMap} - */ - protected ConcurrentMap newMetricsMap() { - return new ConcurrentHashMap(EXPECTED_METRIC_COUNT); - } - - /** - * Gets any existing metric with the given name or, if none exists, adds the given metric. - * - * @param name the metric's name - * @param metric the new metric - * @param the type of the metric - * @return either the existing metric or {@code metric} - */ - @SuppressWarnings("unchecked") - protected final T getOrAdd(MetricName name, T metric) { - final Metric existingMetric = metrics.get(name); - if (existingMetric == null) { - final Metric justAddedMetric = metrics.putIfAbsent(name, metric); - if (justAddedMetric == null) { - notifyMetricAdded(name, metric); - return metric; - } - return (T) justAddedMetric; - } - return (T) existingMetric; - } - - private void notifyMetricRemoved(MetricName name) { - for (MetricsRegistryListener listener : listeners) { - listener.onMetricRemoved(name); - } - } - - private void notifyMetricAdded(MetricName name, Metric metric) { - for (MetricsRegistryListener listener : listeners) { - listener.onMetricAdded(name, metric); - } - } - - public String getName() { - return name; - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/MetricsRegistryListener.java b/metrics-core/src/main/java/com/yammer/metrics/core/MetricsRegistryListener.java deleted file mode 100644 index ea77395693..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/core/MetricsRegistryListener.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.yammer.metrics.core; - -import java.util.EventListener; - -/** - * Listeners for events from the registry. Listeners must be thread-safe. - */ -public interface MetricsRegistryListener extends EventListener { - /** - * Called when a metric has been added to the {@link MetricsRegistry}. - * - * @param name the name of the {@link Metric} - * @param metric the {@link Metric} - */ - void onMetricAdded(MetricName name, Metric metric); - - /** - * Called when a metric has been removed from the {@link MetricsRegistry}. - * - * @param name the name of the {@link com.yammer.metrics.core.Metric} - */ - void onMetricRemoved(MetricName name); -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/TimerContext.java b/metrics-core/src/main/java/com/yammer/metrics/core/TimerContext.java deleted file mode 100644 index a70e73af95..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/core/TimerContext.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.yammer.metrics.core; - -import java.util.concurrent.TimeUnit; - -/** - * A timing context. - * - * @see Timer#time() - */ -public class TimerContext { - private final Timer timer; - private final Clock clock; - private final long startTime; - - /** - * Creates a new {@link TimerContext} with the current time as its starting value and with the - * given {@link Timer}. - * - * @param timer the {@link Timer} to report the elapsed time to - */ - TimerContext(Timer timer, Clock clock) { - this.timer = timer; - this.clock = clock; - this.startTime = clock.getTick(); - } - - /** - * Stops recording the elapsed time, updates the timer and returns the elapsed time - */ - public long stop() { - final long elapsedNanos = clock.getTick() - startTime; - timer.update(elapsedNanos, TimeUnit.NANOSECONDS); - return elapsedNanos; - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/core/VirtualMachineMetrics.java b/metrics-core/src/main/java/com/yammer/metrics/core/VirtualMachineMetrics.java deleted file mode 100644 index c69dae63be..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/core/VirtualMachineMetrics.java +++ /dev/null @@ -1,492 +0,0 @@ -package com.yammer.metrics.core; - -import javax.management.*; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.lang.Thread.State; -import java.lang.management.*; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.*; -import java.util.concurrent.TimeUnit; - -/** - * A collection of Java Virtual Machine metrics. - */ -public class VirtualMachineMetrics { - private static final int MAX_STACK_TRACE_DEPTH = 100; - - private static final VirtualMachineMetrics INSTANCE = new VirtualMachineMetrics( - ManagementFactory.getMemoryMXBean(), - ManagementFactory.getMemoryPoolMXBeans(), - ManagementFactory.getOperatingSystemMXBean(), - ManagementFactory.getThreadMXBean(), - ManagementFactory.getGarbageCollectorMXBeans(), - ManagementFactory.getRuntimeMXBean(), - ManagementFactory.getPlatformMBeanServer()); - - /** - * The default instance of {@link VirtualMachineMetrics}. - * - * @return the default {@link VirtualMachineMetrics instance} - */ - public static VirtualMachineMetrics getInstance() { - return INSTANCE; - } - - /** - * Per-GC statistics. - */ - public static class GarbageCollectorStats { - private final long runs, timeMS; - - private GarbageCollectorStats(long runs, long timeMS) { - this.runs = runs; - this.timeMS = timeMS; - } - - /** - * Returns the number of times the garbage collector has run. - * - * @return the number of times the garbage collector has run - */ - public long getRuns() { - return runs; - } - - /** - * Returns the amount of time in the given unit the garbage collector has taken in total. - * - * @param unit the time unit for the return value - * @return the amount of time in the given unit the garbage collector - */ - public long getTime(TimeUnit unit) { - return unit.convert(timeMS, TimeUnit.MILLISECONDS); - } - } - - /** - * The management interface for a buffer pool, for example a pool of {@link - * java.nio.ByteBuffer#allocateDirect direct} or {@link java.nio.MappedByteBuffer mapped} - * buffers. - */ - public static class BufferPoolStats { - private final long count, memoryUsed, totalCapacity; - - private BufferPoolStats(long count, long memoryUsed, long totalCapacity) { - this.count = count; - this.memoryUsed = memoryUsed; - this.totalCapacity = totalCapacity; - } - - /** - * Returns an estimate of the number of buffers in the pool. - * - * @return An estimate of the number of buffers in this pool - */ - public long getCount() { - return count; - } - - /** - * Returns an estimate of the memory that the Java virtual machine is using for this buffer - * pool. The value returned by this method may differ from the estimate of the total {@link - * #getTotalCapacity capacity} of the buffers in this pool. This difference is explained by - * alignment, memory allocator, and other implementation specific reasons. - * - * @return An estimate of the memory that the Java virtual machine is using for this buffer - * pool in bytes, or {@code -1L} if an estimate of the memory usage is not - * available - */ - public long getMemoryUsed() { - return memoryUsed; - } - - /** - * Returns an estimate of the total capacity of the buffers in this pool. A buffer's - * capacity is the number of elements it contains and the value returned by this method is - * an estimate of the total capacity of buffers in the pool in bytes. - * - * @return An estimate of the total capacity of the buffers in this pool in bytes - */ - public long getTotalCapacity() { - return totalCapacity; - } - } - - private final MemoryMXBean memory; - private final List memoryPools; - private final OperatingSystemMXBean os; - private final ThreadMXBean threads; - private final List garbageCollectors; - private final RuntimeMXBean runtime; - private final MBeanServer mBeanServer; - - VirtualMachineMetrics(MemoryMXBean memory, - List memoryPools, - OperatingSystemMXBean os, - ThreadMXBean threads, - List garbageCollectors, - RuntimeMXBean runtime, MBeanServer mBeanServer) { - this.memory = memory; - this.memoryPools = memoryPools; - this.os = os; - this.threads = threads; - this.garbageCollectors = garbageCollectors; - this.runtime = runtime; - this.mBeanServer = mBeanServer; - } - - /** - * Returns the total initial memory of the current JVM. - * - * @return total Heap and non-heap initial JVM memory in bytes. - */ - public double getTotalInit() { - return memory.getHeapMemoryUsage().getInit() + - memory.getNonHeapMemoryUsage().getInit(); - } - - /** - * Returns the total memory currently used by the current JVM. - * - * @return total Heap and non-heap memory currently used by JVM in bytes. - */ - public double getTotalUsed() { - return memory.getHeapMemoryUsage().getUsed() + - memory.getNonHeapMemoryUsage().getUsed(); - } - - /** - * Returns the total memory currently used by the current JVM. - * - * @return total Heap and non-heap memory currently used by JVM in bytes. - */ - public double getTotalMax() { - return memory.getHeapMemoryUsage().getMax() + - memory.getNonHeapMemoryUsage().getMax(); - } - /** - * Returns the total memory committed to the JVM. - * - * @return total Heap and non-heap memory currently committed to the JVM in bytes. - */ - public double getTotalCommitted() { - return memory.getHeapMemoryUsage().getCommitted() + - memory.getNonHeapMemoryUsage().getCommitted(); - } - /** - * Returns the heap initial memory of the current JVM. - * - * @return Heap initial JVM memory in bytes. - */ - public double getHeapInit() { - return memory.getHeapMemoryUsage().getInit(); - } - /** - * Returns the heap memory currently used by the current JVM. - * - * @return Heap memory currently used by JVM in bytes. - */ - public double getHeapUsed() { - return memory.getHeapMemoryUsage().getUsed(); - } - /** - * Returns the heap memory currently used by the current JVM. - * - * @return Heap memory currently used by JVM in bytes. - */ - public double getHeapMax() { - return memory.getHeapMemoryUsage().getMax(); - } - /** - * Returns the heap memory committed to the JVM. - * - * @return Heap memory currently committed to the JVM in bytes. - */ - public double getHeapCommitted() { - return memory.getHeapMemoryUsage().getCommitted(); - } - - /** - * Returns the percentage of the JVM's heap which is being used. - * - * @return the percentage of the JVM's heap which is being used - */ - public double getHeapUsage() { - final MemoryUsage usage = memory.getHeapMemoryUsage(); - return usage.getUsed() / (double) usage.getMax(); - } - - /** - * Returns the percentage of the JVM's non-heap memory (e.g., direct buffers) which is being - * used. - * - * @return the percentage of the JVM's non-heap memory which is being used - */ - public double getNonHeapUsage() { - final MemoryUsage usage = memory.getNonHeapMemoryUsage(); - return usage.getUsed() / (double) usage.getMax(); - } - - /** - * Returns a map of memory pool names to the percentage of that pool which is being used. - * - * @return a map of memory pool names to the percentage of that pool which is being used - */ - public Map getMemoryPoolUsage() { - final Map pools = new TreeMap(); - for (MemoryPoolMXBean pool : memoryPools) { - final double max = pool.getUsage().getMax() == -1 ? - pool.getUsage().getCommitted() : - pool.getUsage().getMax(); - pools.put(pool.getName(), pool.getUsage().getUsed() / max); - } - return Collections.unmodifiableMap(pools); - } - - /** - * Returns the percentage of available file descriptors which are currently in use. - * - * @return the percentage of available file descriptors which are currently in use, or {@code - * NaN} if the running JVM does not have access to this information - */ - public double getFileDescriptorUsage() { - try { - final Method getOpenFileDescriptorCount = os.getClass().getDeclaredMethod("getOpenFileDescriptorCount"); - getOpenFileDescriptorCount.setAccessible(true); - final Long openFds = (Long) getOpenFileDescriptorCount.invoke(os); - final Method getMaxFileDescriptorCount = os.getClass().getDeclaredMethod("getMaxFileDescriptorCount"); - getMaxFileDescriptorCount.setAccessible(true); - final Long maxFds = (Long) getMaxFileDescriptorCount.invoke(os); - return openFds.doubleValue() / maxFds.doubleValue(); - } catch (NoSuchMethodException e) { - return Double.NaN; - } catch (IllegalAccessException e) { - return Double.NaN; - } catch (InvocationTargetException e) { - return Double.NaN; - } - } - - /** - * Returns the version of the currently-running jvm. - * - * @return the version of the currently-running jvm, eg "1.6.0_24" - * @see J2SE SDK/JRE Version String - * Naming Convention - */ - public String getVersion() { - return System.getProperty("java.runtime.version"); - } - - /** - * Returns the name of the currently-running jvm. - * - * @return the name of the currently-running jvm, eg "Java HotSpot(TM) Client VM" - * @see System.getProperties() - */ - public String getName() { - return System.getProperty("java.vm.name"); - } - - /** - * Returns the number of seconds the JVM process has been running. - * - * @return the number of seconds the JVM process has been running - */ - public long getUptime() { - return TimeUnit.MILLISECONDS.toSeconds(runtime.getUptime()); - } - - /** - * Returns the number of live threads (includes {@link #getDaemonThreadCount}. - * - * @return the number of live threads - */ - public int getThreadCount() { - return threads.getThreadCount(); - } - - /** - * Returns the number of live daemon threads. - * - * @return the number of live daemon threads - */ - public int getDaemonThreadCount() { - return threads.getDaemonThreadCount(); - } - - /** - * Returns a map of garbage collector names to garbage collector information. - * - * @return a map of garbage collector names to garbage collector information - */ - public Map getGarbageCollectors() { - final Map stats = new HashMap(); - for (GarbageCollectorMXBean gc : garbageCollectors) { - stats.put(gc.getName(), - new GarbageCollectorStats(gc.getCollectionCount(), - gc.getCollectionTime())); - } - return Collections.unmodifiableMap(stats); - } - - /** - * Returns a set of strings describing deadlocked threads, if any are deadlocked. - * - * @return a set of any deadlocked threads - */ - public Set getDeadlockedThreads() { - final long[] threadIds = threads.findDeadlockedThreads(); - if (threadIds != null) { - final Set threads = new HashSet(); - for (ThreadInfo info : this.threads.getThreadInfo(threadIds, MAX_STACK_TRACE_DEPTH)) { - final StringBuilder stackTrace = new StringBuilder(); - for (StackTraceElement element : info.getStackTrace()) { - stackTrace.append("\t at ").append(element.toString()).append('\n'); - } - - threads.add( - String.format( - "%s locked on %s (owned by %s):\n%s", - info.getThreadName(), info.getLockName(), - info.getLockOwnerName(), - stackTrace.toString() - ) - ); - } - return Collections.unmodifiableSet(threads); - } - return Collections.emptySet(); - } - - /** - * Returns a map of thread states to the percentage of all threads which are in that state. - * - * @return a map of thread states to percentages - */ - public Map getThreadStatePercentages() { - final Map conditions = new HashMap(); - for (State state : State.values()) { - conditions.put(state, 0.0); - } - - final long[] allThreadIds = threads.getAllThreadIds(); - final ThreadInfo[] allThreads = threads.getThreadInfo(allThreadIds); - int liveCount = 0; - for (ThreadInfo info : allThreads) { - if (info != null) { - final State state = info.getThreadState(); - conditions.put(state, conditions.get(state) + 1); - liveCount++; - } - } - for (State state : new ArrayList(conditions.keySet())) { - conditions.put(state, conditions.get(state) / liveCount); - } - - return Collections.unmodifiableMap(conditions); - } - - /** - * Dumps all of the threads' current information to an output stream. - * - * @param out an output stream - */ - public void getThreadDump(OutputStream out) { - final ThreadInfo[] threads = this.threads.dumpAllThreads(true, true); - final PrintWriter writer = new PrintWriter(out, true); - - for (int ti = threads.length - 1; ti >= 0; ti--) { - final ThreadInfo t = threads[ti]; - writer.printf("%s id=%d state=%s", - t.getThreadName(), - t.getThreadId(), - t.getThreadState()); - final LockInfo lock = t.getLockInfo(); - if (lock != null && t.getThreadState() != Thread.State.BLOCKED) { - writer.printf("\n - waiting on <0x%08x> (a %s)", - lock.getIdentityHashCode(), - lock.getClassName()); - writer.printf("\n - locked <0x%08x> (a %s)", - lock.getIdentityHashCode(), - lock.getClassName()); - } else if (lock != null && t.getThreadState() == Thread.State.BLOCKED) { - writer.printf("\n - waiting to lock <0x%08x> (a %s)", - lock.getIdentityHashCode(), - lock.getClassName()); - } - - if (t.isSuspended()) { - writer.print(" (suspended)"); - } - - if (t.isInNative()) { - writer.print(" (running in native)"); - } - - writer.println(); - if (t.getLockOwnerName() != null) { - writer.printf(" owned by %s id=%d\n", t.getLockOwnerName(), t.getLockOwnerId()); - } - - final StackTraceElement[] elements = t.getStackTrace(); - final MonitorInfo[] monitors = t.getLockedMonitors(); - - for (int i = 0; i < elements.length; i++) { - final StackTraceElement element = elements[i]; - writer.printf(" at %s\n", element); - for (int j = 1; j < monitors.length; j++) { - final MonitorInfo monitor = monitors[j]; - if (monitor.getLockedStackDepth() == i) { - writer.printf(" - locked %s\n", monitor); - } - } - } - writer.println(); - - final LockInfo[] locks = t.getLockedSynchronizers(); - if (locks.length > 0) { - writer.printf(" Locked synchronizers: count = %d\n", locks.length); - for (LockInfo l : locks) { - writer.printf(" - %s\n", l); - } - writer.println(); - } - } - - writer.println(); - writer.flush(); - } - - public Map getBufferPoolStats() { - try { - final String[] attributes = { "Count", "MemoryUsed", "TotalCapacity" }; - - final ObjectName direct = new ObjectName("java.nio:type=BufferPool,name=direct"); - final ObjectName mapped = new ObjectName("java.nio:type=BufferPool,name=mapped"); - - final AttributeList directAttributes = mBeanServer.getAttributes(direct, attributes); - final AttributeList mappedAttributes = mBeanServer.getAttributes(mapped, attributes); - - final Map stats = new TreeMap(); - - final BufferPoolStats directStats = new BufferPoolStats((Long) ((Attribute) directAttributes.get(0)).getValue(), - (Long) ((Attribute) directAttributes.get(1)).getValue(), - (Long) ((Attribute) directAttributes.get(2)).getValue()); - - stats.put("direct", directStats); - - final BufferPoolStats mappedStats = new BufferPoolStats((Long) ((Attribute) mappedAttributes.get(0)).getValue(), - (Long) ((Attribute) mappedAttributes.get(1)).getValue(), - (Long) ((Attribute) mappedAttributes.get(2)).getValue()); - - stats.put("mapped", mappedStats); - - return Collections.unmodifiableMap(stats); - } catch (JMException e) { - return Collections.emptyMap(); - } - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/reporting/AbstractReporter.java b/metrics-core/src/main/java/com/yammer/metrics/reporting/AbstractReporter.java deleted file mode 100644 index dffa66e574..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/reporting/AbstractReporter.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.yammer.metrics.reporting; - -import com.yammer.metrics.core.MetricsRegistry; - -/** - * The base class for all metric reporters. - */ -public abstract class AbstractReporter { - private final MetricsRegistry metricsRegistry; - - /** - * Creates a new {@link AbstractReporter} instance. - * - * @param registry the {@link MetricsRegistry} containing the metrics this reporter will - * report - */ - protected AbstractReporter(MetricsRegistry registry) { - this.metricsRegistry = registry; - } - - /** - * Stops the reporter and closes any internal resources. - */ - public void shutdown() { - // nothing to do here - } - - /** - * Returns the reporter's {@link MetricsRegistry}. - * - * @return the reporter's {@link MetricsRegistry} - */ - protected MetricsRegistry getMetricsRegistry() { - return metricsRegistry; - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/reporting/ConsoleReporter.java b/metrics-core/src/main/java/com/yammer/metrics/reporting/ConsoleReporter.java deleted file mode 100644 index c0eae7e361..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/reporting/ConsoleReporter.java +++ /dev/null @@ -1,239 +0,0 @@ -package com.yammer.metrics.reporting; - -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.*; -import com.yammer.metrics.stats.Snapshot; -import com.yammer.metrics.core.MetricPredicate; - -import java.io.PrintStream; -import java.text.DateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.Map.Entry; -import java.util.SortedMap; -import java.util.TimeZone; -import java.util.concurrent.TimeUnit; - -/** - * A simple reporters which prints out application metrics to a {@link PrintStream} periodically. - */ -public class ConsoleReporter extends AbstractPollingReporter implements - MetricProcessor { - private static final int CONSOLE_WIDTH = 80; - - /** - * Enables the console reporter for the default metrics registry, and causes it to print to - * STDOUT with the specified period. - * - * @param period the period between successive outputs - * @param unit the time unit of {@code period} - */ - public static void enable(long period, TimeUnit unit) { - enable(Metrics.defaultRegistry(), period, unit); - } - - /** - * Enables the console reporter for the given metrics registry, and causes it to print to STDOUT - * with the specified period and unrestricted output. - * - * @param metricsRegistry the metrics registry - * @param period the period between successive outputs - * @param unit the time unit of {@code period} - */ - public static void enable(MetricsRegistry metricsRegistry, long period, TimeUnit unit) { - final ConsoleReporter reporter = new ConsoleReporter(metricsRegistry, - System.out, - MetricPredicate.ALL); - reporter.start(period, unit); - } - - private final PrintStream out; - private final MetricPredicate predicate; - private final Clock clock; - private final TimeZone timeZone; - private final Locale locale; - - /** - * Creates a new {@link ConsoleReporter} for the default metrics registry, with unrestricted - * output. - * - * @param out the {@link PrintStream} to which output will be written - */ - public ConsoleReporter(PrintStream out) { - this(Metrics.defaultRegistry(), out, MetricPredicate.ALL); - } - - /** - * Creates a new {@link ConsoleReporter} for a given metrics registry. - * - * @param metricsRegistry the metrics registry - * @param out the {@link PrintStream} to which output will be written - * @param predicate the {@link MetricPredicate} used to determine whether a metric will be - * output - */ - public ConsoleReporter(MetricsRegistry metricsRegistry, PrintStream out, MetricPredicate predicate) { - this(metricsRegistry, out, predicate, Clock.defaultClock(), TimeZone.getDefault()); - } - - /** - * Creates a new {@link ConsoleReporter} for a given metrics registry. - * - * @param metricsRegistry the metrics registry - * @param out the {@link PrintStream} to which output will be written - * @param predicate the {@link MetricPredicate} used to determine whether a metric will be - * output - * @param clock the {@link Clock} used to print time - * @param timeZone the {@link TimeZone} used to print time - */ - public ConsoleReporter(MetricsRegistry metricsRegistry, - PrintStream out, - MetricPredicate predicate, - Clock clock, - TimeZone timeZone) { - this(metricsRegistry, out, predicate, clock, timeZone, Locale.getDefault()); - } - - /** - * Creates a new {@link ConsoleReporter} for a given metrics registry. - * - * @param metricsRegistry the metrics registry - * @param out the {@link PrintStream} to which output will be written - * @param predicate the {@link MetricPredicate} used to determine whether a metric will be - * output - * @param clock the {@link com.yammer.metrics.core.Clock} used to print time - * @param timeZone the {@link TimeZone} used to print time - * @param locale the {@link Locale} used to print values - */ - public ConsoleReporter(MetricsRegistry metricsRegistry, - PrintStream out, - MetricPredicate predicate, - Clock clock, - TimeZone timeZone, Locale locale) { - super(metricsRegistry, "console-reporter"); - this.out = out; - this.predicate = predicate; - this.clock = clock; - this.timeZone = timeZone; - this.locale = locale; - } - - @Override - public void run() { - try { - final DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT, - DateFormat.MEDIUM, - locale); - final MetricDispatcher dispatcher = new MetricDispatcher(); - format.setTimeZone(timeZone); - final String dateTime = format.format(new Date(clock.getTime())); - out.print(dateTime); - out.print(' '); - for (int i = 0; i < (CONSOLE_WIDTH - dateTime.length() - 1); i++) { - out.print('='); - } - out.println(); - for (Entry> entry : getMetricsRegistry().getGroupedMetrics( - predicate).entrySet()) { - out.print(entry.getKey()); - out.println(':'); - for (Entry subEntry : entry.getValue().entrySet()) { - out.print(" "); - out.print(subEntry.getKey().getName()); - out.println(':'); - dispatcher.dispatch(subEntry.getValue(), subEntry.getKey(), this, out); - out.println(); - } - out.println(); - } - out.println(); - out.flush(); - } catch (Exception e) { - e.printStackTrace(out); - } - } - - @Override - public void processGauge(MetricName name, Gauge gauge, PrintStream stream) { - stream.printf(locale, " value = %s\n", gauge.getValue()); - } - - @Override - public void processCounter(MetricName name, Counter counter, PrintStream stream) { - stream.printf(locale, " count = %d\n", counter.getCount()); - } - - @Override - public void processMeter(MetricName name, Metered meter, PrintStream stream) { - final String unit = abbrev(meter.getRateUnit()); - stream.printf(locale, " count = %d\n", meter.getCount()); - stream.printf(locale, " mean rate = %2.2f %s/%s\n", - meter.getMeanRate(), - meter.getEventType(), - unit); - stream.printf(locale, " 1-minute rate = %2.2f %s/%s\n", - meter.getOneMinuteRate(), - meter.getEventType(), - unit); - stream.printf(locale, " 5-minute rate = %2.2f %s/%s\n", - meter.getFiveMinuteRate(), - meter.getEventType(), - unit); - stream.printf(locale, " 15-minute rate = %2.2f %s/%s\n", - meter.getFifteenMinuteRate(), - meter.getEventType(), - unit); - } - - @Override - public void processHistogram(MetricName name, Histogram histogram, PrintStream stream) { - final Snapshot snapshot = histogram.getSnapshot(); - stream.printf(locale, " min = %2.2f\n", histogram.getMin()); - stream.printf(locale, " max = %2.2f\n", histogram.getMax()); - stream.printf(locale, " mean = %2.2f\n", histogram.getMean()); - stream.printf(locale, " stddev = %2.2f\n", histogram.getStdDev()); - stream.printf(locale, " median = %2.2f\n", snapshot.getMedian()); - stream.printf(locale, " 75%% <= %2.2f\n", snapshot.get75thPercentile()); - stream.printf(locale, " 95%% <= %2.2f\n", snapshot.get95thPercentile()); - stream.printf(locale, " 98%% <= %2.2f\n", snapshot.get98thPercentile()); - stream.printf(locale, " 99%% <= %2.2f\n", snapshot.get99thPercentile()); - stream.printf(locale, " 99.9%% <= %2.2f\n", snapshot.get999thPercentile()); - } - - @Override - public void processTimer(MetricName name, Timer timer, PrintStream stream) { - processMeter(name, timer, stream); - final String durationUnit = abbrev(timer.getDurationUnit()); - final Snapshot snapshot = timer.getSnapshot(); - stream.printf(locale, " min = %2.2f%s\n", timer.getMin(), durationUnit); - stream.printf(locale, " max = %2.2f%s\n", timer.getMax(), durationUnit); - stream.printf(locale, " mean = %2.2f%s\n", timer.getMean(), durationUnit); - stream.printf(locale, " stddev = %2.2f%s\n", timer.getStdDev(), durationUnit); - stream.printf(locale, " median = %2.2f%s\n", snapshot.getMedian(), durationUnit); - stream.printf(locale, " 75%% <= %2.2f%s\n", snapshot.get75thPercentile(), durationUnit); - stream.printf(locale, " 95%% <= %2.2f%s\n", snapshot.get95thPercentile(), durationUnit); - stream.printf(locale, " 98%% <= %2.2f%s\n", snapshot.get98thPercentile(), durationUnit); - stream.printf(locale, " 99%% <= %2.2f%s\n", snapshot.get99thPercentile(), durationUnit); - stream.printf(locale, " 99.9%% <= %2.2f%s\n", snapshot.get999thPercentile(), durationUnit); - } - - private String abbrev(TimeUnit unit) { - switch (unit) { - case NANOSECONDS: - return "ns"; - case MICROSECONDS: - return "us"; - case MILLISECONDS: - return "ms"; - case SECONDS: - return "s"; - case MINUTES: - return "m"; - case HOURS: - return "h"; - case DAYS: - return "d"; - default: - throw new IllegalArgumentException("Unrecognized TimeUnit: " + unit); - } - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/reporting/CsvReporter.java b/metrics-core/src/main/java/com/yammer/metrics/reporting/CsvReporter.java deleted file mode 100644 index 951e8f5446..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/reporting/CsvReporter.java +++ /dev/null @@ -1,278 +0,0 @@ -package com.yammer.metrics.reporting; - -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.*; -import com.yammer.metrics.stats.Snapshot; -import com.yammer.metrics.core.MetricPredicate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -/** - * A reporter which periodically appends data from each metric to a metric-specific CSV file in - * an output directory. - */ -public class CsvReporter extends AbstractPollingReporter implements - MetricProcessor { - - private static final Logger LOGGER = LoggerFactory.getLogger(CsvReporter.class); - - /** - * Enables the CSV reporter for the default metrics registry, and causes it to write to files in - * {@code outputDir} with the specified period. - * - * @param outputDir the directory in which {@code .csv} files will be created - * @param period the period between successive outputs - * @param unit the time unit of {@code period} - */ - public static void enable(File outputDir, long period, TimeUnit unit) { - enable(Metrics.defaultRegistry(), outputDir, period, unit); - } - - /** - * Enables the CSV reporter for the given metrics registry, and causes it to write to files in - * {@code outputDir} with the specified period. - * - * @param metricsRegistry the metrics registry - * @param outputDir the directory in which {@code .csv} files will be created - * @param period the period between successive outputs - * @param unit the time unit of {@code period} - */ - public static void enable(MetricsRegistry metricsRegistry, File outputDir, long period, TimeUnit unit) { - final CsvReporter reporter = new CsvReporter(metricsRegistry, outputDir); - reporter.start(period, unit); - } - - /** - * The context used to output metrics. - */ - public interface Context { - /** - * Returns an open {@link PrintStream} for the metric with {@code header} already written - * to it. - * - * @param header the CSV header - * @return an open {@link PrintStream} - * @throws IOException if there is an error opening the stream or writing to it - */ - PrintStream getStream(String header) throws IOException; - } - - private final MetricPredicate predicate; - private final File outputDir; - private final Map streamMap; - private final Clock clock; - private long startTime; - - /** - * Creates a new {@link CsvReporter} which will write all metrics from the given - * {@link MetricsRegistry} to CSV files in the given output directory. - * - * @param outputDir the directory to which files will be written - * @param metricsRegistry the {@link MetricsRegistry} containing the metrics this reporter - * will report - */ - public CsvReporter(MetricsRegistry metricsRegistry, File outputDir) { - this(metricsRegistry, MetricPredicate.ALL, outputDir); - } - - /** - * Creates a new {@link CsvReporter} which will write metrics from the given - * {@link MetricsRegistry} which match the given {@link MetricPredicate} to CSV files in the - * given output directory. - * - * @param metricsRegistry the {@link MetricsRegistry} containing the metrics this reporter - * will report - * @param predicate the {@link MetricPredicate} which metrics are required to match - * before being written to files - * @param outputDir the directory to which files will be written - */ - public CsvReporter(MetricsRegistry metricsRegistry, - MetricPredicate predicate, - File outputDir) { - this(metricsRegistry, predicate, outputDir, Clock.defaultClock()); - } - - /** - * Creates a new {@link CsvReporter} which will write metrics from the given - * {@link MetricsRegistry} which match the given {@link MetricPredicate} to CSV files in the - * given output directory. - * - * @param metricsRegistry the {@link MetricsRegistry} containing the metrics this reporter - * will report - * @param predicate the {@link MetricPredicate} which metrics are required to match - * before being written to files - * @param outputDir the directory to which files will be written - * @param clock the clock used to measure time - */ - public CsvReporter(MetricsRegistry metricsRegistry, - MetricPredicate predicate, - File outputDir, - Clock clock) { - super(metricsRegistry, "csv-reporter"); - if (outputDir.exists() && !outputDir.isDirectory()) { - throw new IllegalArgumentException(outputDir + " is not a directory"); - } - this.outputDir = outputDir; - this.predicate = predicate; - this.streamMap = new HashMap(); - this.startTime = 0L; - this.clock = clock; - } - - /** - * Returns an opened {@link PrintStream} for the given {@link MetricName} which outputs data - * to a metric-specific {@code .csv} file in the output directory. - * - * @param metricName the name of the metric - * @return an opened {@link PrintStream} specific to {@code metricName} - * @throws IOException if there is an error opening the stream - */ - protected PrintStream createStreamForMetric(MetricName metricName) throws IOException { - final File newFile = new File(outputDir, metricName.toString() + ".csv"); - if (newFile.createNewFile()) { - return new PrintStream(new FileOutputStream(newFile)); - } - throw new IOException("Unable to create " + newFile); - } - - @Override - public void run() { - final long time = TimeUnit.MILLISECONDS.toSeconds(clock.getTime() - startTime); - final Set> metrics = getMetricsRegistry().getAllMetrics().entrySet(); - final MetricDispatcher dispatcher = new MetricDispatcher(); - try { - for (Entry entry : metrics) { - final MetricName metricName = entry.getKey(); - final Metric metric = entry.getValue(); - if (predicate.matches(metricName, metric)) { - final Context context = new Context() { - @Override - public PrintStream getStream(String header) throws IOException { - final PrintStream stream = getPrintStream(metricName, header); - stream.print(time); - stream.print(','); - return stream; - } - - }; - dispatcher.dispatch(entry.getValue(), entry.getKey(), this, context); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public void processMeter(MetricName name, Metered meter, Context context) throws IOException { - final PrintStream stream = context.getStream( - "# time,count,1 min rate,mean rate,5 min rate,15 min rate"); - stream.append(new StringBuilder() - .append(meter.getCount()).append(',') - .append(meter.getOneMinuteRate()).append(',') - .append(meter.getMeanRate()).append(',') - .append(meter.getFiveMinuteRate()).append(',') - .append(meter.getFifteenMinuteRate()).toString()) - .println(); - stream.flush(); - } - - @Override - public void processCounter(MetricName name, Counter counter, Context context) throws IOException { - final PrintStream stream = context.getStream("# time,count"); - stream.println(counter.getCount()); - stream.flush(); - } - - @Override - public void processHistogram(MetricName name, Histogram histogram, Context context) throws IOException { - final PrintStream stream = context.getStream("# time,min,max,mean,median,stddev,95%,99%,99.9%"); - final Snapshot snapshot = histogram.getSnapshot(); - stream.append(new StringBuilder() - .append(histogram.getMin()).append(',') - .append(histogram.getMax()).append(',') - .append(histogram.getMean()).append(',') - .append(snapshot.getMedian()).append(',') - .append(histogram.getStdDev()).append(',') - .append(snapshot.get95thPercentile()).append(',') - .append(snapshot.get99thPercentile()).append(',') - .append(snapshot.get999thPercentile()).toString()) - .println(); - stream.println(); - stream.flush(); - } - - @Override - public void processTimer(MetricName name, Timer timer, Context context) throws IOException { - final PrintStream stream = context.getStream("# time,count,1 min rate,mean rate,5 min rate,15 min rate,min,max,mean,median,stddev,95%,99%,99.9%"); - final Snapshot snapshot = timer.getSnapshot(); - stream.append(new StringBuilder() - .append(timer.getCount()).append(',') - .append(timer.getOneMinuteRate()).append(',') - .append(timer.getMeanRate()).append(',') - .append(timer.getFiveMinuteRate()).append(',') - .append(timer.getFifteenMinuteRate()).append(',') - .append(timer.getMin()).append(',') - .append(timer.getMax()).append(',') - .append(timer.getMean()).append(',') - .append(snapshot.getMedian()).append(',') - .append(timer.getStdDev()).append(',') - .append(snapshot.get95thPercentile()).append(',') - .append(snapshot.get99thPercentile()).append(',') - .append(snapshot.get999thPercentile()).toString()) - .println(); - stream.flush(); - } - - @Override - public void processGauge(MetricName name, Gauge gauge, Context context) throws IOException { - final PrintStream stream = context.getStream("# time,value"); - stream.println(gauge.getValue()); - stream.flush(); - } - - @Override - public void start(long period, TimeUnit unit) { - this.startTime = clock.getTime(); - super.start(period, unit); - } - - @Override - public void shutdown() { - try { - super.shutdown(); - } finally { - for (PrintStream out : streamMap.values()) { - try { - out.close(); - } catch (Throwable t) { - LOGGER.warn("Failed to close stream", t); - } - } - } - } - - private PrintStream getPrintStream(MetricName metricName, String header) - throws IOException { - PrintStream stream; - synchronized (streamMap) { - stream = streamMap.get(metricName); - if (stream == null) { - stream = createStreamForMetric(metricName); - streamMap.put(metricName, stream); - stream.println(header); - } - } - return stream; - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/reporting/JmxReporter.java b/metrics-core/src/main/java/com/yammer/metrics/reporting/JmxReporter.java deleted file mode 100644 index 5d4df655cc..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/reporting/JmxReporter.java +++ /dev/null @@ -1,475 +0,0 @@ -package com.yammer.metrics.reporting; - -import com.yammer.metrics.core.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.management.*; -import java.lang.management.ManagementFactory; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; -import static javax.management.ObjectName.quote; - -/** - * A reporter which exposes application metric as JMX MBeans. - */ -public class JmxReporter extends AbstractReporter implements MetricsRegistryListener, - MetricProcessor { - - private static final Logger LOGGER = LoggerFactory.getLogger(JmxReporter.class); - - // CHECKSTYLE:OFF - @SuppressWarnings("UnusedDeclaration") - public interface MetricMBean { - ObjectName objectName(); - } - // CHECKSTYLE:ON - - - private abstract static class AbstractBean implements MetricMBean { - private final ObjectName objectName; - - protected AbstractBean(ObjectName objectName) { - this.objectName = objectName; - } - - @Override - public ObjectName objectName() { - return objectName; - } - } - - // CHECKSTYLE:OFF - @SuppressWarnings("UnusedDeclaration") - public interface GaugeMBean extends MetricMBean { - Object getValue(); - } - // CHECKSTYLE:ON - - - private static class Gauge extends AbstractBean implements GaugeMBean { - private final com.yammer.metrics.core.Gauge metric; - - private Gauge(com.yammer.metrics.core.Gauge metric, ObjectName objectName) { - super(objectName); - this.metric = metric; - } - - @Override - public Object getValue() { - return metric.getValue(); - } - } - - // CHECKSTYLE:OFF - @SuppressWarnings("UnusedDeclaration") - public interface CounterMBean extends MetricMBean { - long getCount(); - } - // CHECKSTYLE:ON - - - private static class Counter extends AbstractBean implements CounterMBean { - private final com.yammer.metrics.core.Counter metric; - - private Counter(com.yammer.metrics.core.Counter metric, ObjectName objectName) { - super(objectName); - this.metric = metric; - } - - @Override - public long getCount() { - return metric.getCount(); - } - } - - //CHECKSTYLE:OFF - @SuppressWarnings("UnusedDeclaration") - public interface MeterMBean extends MetricMBean { - long getCount(); - - String getEventType(); - - TimeUnit getRateUnit(); - - double getMeanRate(); - - double getOneMinuteRate(); - - double getFiveMinuteRate(); - - double getFifteenMinuteRate(); - } - //CHECKSTYLE:ON - - private static class Meter extends AbstractBean implements MeterMBean { - private final Metered metric; - - private Meter(Metered metric, ObjectName objectName) { - super(objectName); - this.metric = metric; - } - - @Override - public long getCount() { - return metric.getCount(); - } - - @Override - public String getEventType() { - return metric.getEventType(); - } - - @Override - public TimeUnit getRateUnit() { - return metric.getRateUnit(); - } - - @Override - public double getMeanRate() { - return metric.getMeanRate(); - } - - @Override - public double getOneMinuteRate() { - return metric.getOneMinuteRate(); - } - - @Override - public double getFiveMinuteRate() { - return metric.getFiveMinuteRate(); - } - - @Override - public double getFifteenMinuteRate() { - return metric.getFifteenMinuteRate(); - } - } - - // CHECKSTYLE:OFF - @SuppressWarnings("UnusedDeclaration") - public interface HistogramMBean extends MetricMBean { - long getCount(); - - double getMin(); - - double getMax(); - - double getMean(); - - double getStdDev(); - - double get50thPercentile(); - - double get75thPercentile(); - - double get95thPercentile(); - - double get98thPercentile(); - - double get99thPercentile(); - - double get999thPercentile(); - - double[] values(); - } - // CHECKSTYLE:ON - - private static class Histogram implements HistogramMBean { - private final ObjectName objectName; - private final com.yammer.metrics.core.Histogram metric; - - private Histogram(com.yammer.metrics.core.Histogram metric, ObjectName objectName) { - this.metric = metric; - this.objectName = objectName; - } - - @Override - public ObjectName objectName() { - return objectName; - } - - @Override - public double get50thPercentile() { - return metric.getSnapshot().getMedian(); - } - - @Override - public long getCount() { - return metric.getCount(); - } - - @Override - public double getMin() { - return metric.getMin(); - } - - @Override - public double getMax() { - return metric.getMax(); - } - - @Override - public double getMean() { - return metric.getMean(); - } - - @Override - public double getStdDev() { - return metric.getStdDev(); - } - - @Override - public double get75thPercentile() { - return metric.getSnapshot().get75thPercentile(); - } - - @Override - public double get95thPercentile() { - return metric.getSnapshot().get95thPercentile(); - } - - @Override - public double get98thPercentile() { - return metric.getSnapshot().get98thPercentile(); - } - - @Override - public double get99thPercentile() { - return metric.getSnapshot().get99thPercentile(); - } - - @Override - public double get999thPercentile() { - return metric.getSnapshot().get999thPercentile(); - } - - @Override - public double[] values() { - return metric.getSnapshot().getValues(); - } - } - - // CHECKSTYLE:OFF - @SuppressWarnings("UnusedDeclaration") - public interface TimerMBean extends MeterMBean, HistogramMBean { - TimeUnit getLatencyUnit(); - } - // CHECKSTYLE:ON - - static class Timer extends Meter implements TimerMBean { - private final com.yammer.metrics.core.Timer metric; - - private Timer(com.yammer.metrics.core.Timer metric, ObjectName objectName) { - super(metric, objectName); - this.metric = metric; - } - - @Override - public double get50thPercentile() { - return metric.getSnapshot().getMedian(); - } - - @Override - public TimeUnit getLatencyUnit() { - return metric.getDurationUnit(); - } - - @Override - public double getMin() { - return metric.getMin(); - } - - @Override - public double getMax() { - return metric.getMax(); - } - - @Override - public double getMean() { - return metric.getMean(); - } - - @Override - public double getStdDev() { - return metric.getStdDev(); - } - - @Override - public double get75thPercentile() { - return metric.getSnapshot().get75thPercentile(); - } - - @Override - public double get95thPercentile() { - return metric.getSnapshot().get95thPercentile(); - } - - @Override - public double get98thPercentile() { - return metric.getSnapshot().get98thPercentile(); - } - - @Override - public double get99thPercentile() { - return metric.getSnapshot().get99thPercentile(); - } - - @Override - public double get999thPercentile() { - return metric.getSnapshot().get999thPercentile(); - } - - @Override - public double[] values() { - return metric.getSnapshot().getValues(); - } - } - - static final class Context { - private final MetricName metricName; - private final ObjectName objectName; - - public Context(final MetricName metricName, final ObjectName objectName) { - this.metricName = metricName; - this.objectName = objectName; - } - - MetricName getMetricName() { - return metricName; - } - - ObjectName getObjectName() { - return objectName; - } - } - - private final Map registeredBeans; - private final String registryName; - private final MBeanServer server; - private final MetricDispatcher dispatcher; - - /** - * Creates a new {@link JmxReporter} for the given registry. - * - * @param registry a {@link MetricsRegistry} - */ - public JmxReporter(MetricsRegistry registry) { - super(registry); - this.registryName = registry.getName(); - this.registeredBeans = new ConcurrentHashMap(100); - this.server = ManagementFactory.getPlatformMBeanServer(); - this.dispatcher = new MetricDispatcher(); - } - - @Override - public void onMetricAdded(MetricName name, Metric metric) { - if (metric != null) { - try { - dispatcher.dispatch(metric, name, this, new Context(name, createObjectName(name))); - } catch (Exception e) { - LOGGER.warn("Error processing " + name, e); - } - } - } - - private ObjectName createObjectName(MetricName name) throws MalformedObjectNameException { - final StringBuilder nameBuilder = new StringBuilder(); - nameBuilder.append(name.getDomain()); - nameBuilder.append(":type="); - nameBuilder.append(quote(name.getType())); - if (name.hasScope()) { - nameBuilder.append(",scope="); - nameBuilder.append(quote(name.getScope())); - } - if (!name.getName().isEmpty()) { - nameBuilder.append(",name="); - nameBuilder.append(quote(name.getName())); - } - if (registryName != null) { - nameBuilder.append(",registry="); - nameBuilder.append(quote(registryName)); - } - return new ObjectName(nameBuilder.toString()); - } - - @Override - public void onMetricRemoved(MetricName name) { - final ObjectName objectName = registeredBeans.remove(name); - if (objectName != null) { - unregisterBean(objectName); - } - } - - @Override - public void processMeter(MetricName name, Metered meter, Context context) throws Exception { - registerBean(context.getMetricName(), new Meter(meter, context.getObjectName()), - context.getObjectName()); - } - - @Override - public void processCounter(MetricName name, com.yammer.metrics.core.Counter counter, Context context) throws Exception { - registerBean(context.getMetricName(), - new Counter(counter, context.getObjectName()), - context.getObjectName()); - } - - @Override - public void processHistogram(MetricName name, com.yammer.metrics.core.Histogram histogram, Context context) throws Exception { - registerBean(context.getMetricName(), - new Histogram(histogram, context.getObjectName()), - context.getObjectName()); - } - - @Override - public void processTimer(MetricName name, com.yammer.metrics.core.Timer timer, Context context) throws Exception { - registerBean(context.getMetricName(), new Timer(timer, context.getObjectName()), - context.getObjectName()); - } - - @Override - public void processGauge(MetricName name, com.yammer.metrics.core.Gauge gauge, Context context) throws Exception { - registerBean(context.getMetricName(), new Gauge(gauge, context.getObjectName()), - context.getObjectName()); - } - - @Override - public void shutdown() { - getMetricsRegistry().removeListener(this); - for (ObjectName name : registeredBeans.values()) { - unregisterBean(name); - } - registeredBeans.clear(); - } - - /** - * Starts the reporter. - */ - public final void start() { - getMetricsRegistry().addListener(this); - } - - private void registerBean(MetricName name, MetricMBean bean, ObjectName objectName) - throws MBeanRegistrationException, OperationsException { - - if ( server.isRegistered(objectName) ){ - server.unregisterMBean(objectName); - } - server.registerMBean(bean, objectName); - registeredBeans.put(name, objectName); - } - - private void unregisterBean(ObjectName name) { - try { - server.unregisterMBean(name); - } catch (InstanceNotFoundException e) { - // This is often thrown when the process is shutting down. An application with lots of - // metrics will often begin unregistering metrics *after* JMX itself has cleared, - // resulting in a huge dump of exceptions as the process is exiting. - LOGGER.trace("Error unregistering " + name, e); - } catch (MBeanRegistrationException e) { - LOGGER.debug("Error unregistering " + name, e); - } - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/reporting/MetricDispatcher.java b/metrics-core/src/main/java/com/yammer/metrics/reporting/MetricDispatcher.java deleted file mode 100644 index 1e01ae7495..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/reporting/MetricDispatcher.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.yammer.metrics.reporting; - -import com.yammer.metrics.core.*; - -public class MetricDispatcher { - public void dispatch(Metric metric, MetricName name, MetricProcessor processor, T context) throws Exception { - if (metric instanceof Gauge) { - processor.processGauge(name, (Gauge) metric, context); - } else if (metric instanceof Counter) { - processor.processCounter(name, (Counter) metric, context); - } else if (metric instanceof Meter) { - processor.processMeter(name, (Meter) metric, context); - } else if (metric instanceof Histogram) { - processor.processHistogram(name, (Histogram) metric, context); - } else if (metric instanceof Timer) { - processor.processTimer(name, (Timer) metric, context); - } else { - throw new IllegalArgumentException("Unable to dispatch " + metric); - } - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/util/AtomicGauge.java b/metrics-core/src/main/java/com/yammer/metrics/util/AtomicGauge.java deleted file mode 100644 index 09c75da6a8..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/util/AtomicGauge.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.yammer.metrics.util; - -import com.yammer.metrics.core.Gauge; - -public class AtomicGauge extends Gauge { - private volatile T value = null; - - public void setValue(T value) { - this.value = value; - } - - @Override - public T getValue() { - return value; - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/util/DeadlockHealthCheck.java b/metrics-core/src/main/java/com/yammer/metrics/util/DeadlockHealthCheck.java deleted file mode 100644 index d92d58c036..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/util/DeadlockHealthCheck.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.yammer.metrics.util; - -import com.yammer.metrics.core.HealthCheck; -import com.yammer.metrics.core.VirtualMachineMetrics; - -import java.util.Set; - -/** - * A {@link HealthCheck} implementation which returns a list of deadlocked threads, if any. - */ -public class DeadlockHealthCheck extends HealthCheck { - private final VirtualMachineMetrics vm; - - /** - * Creates a new {@link DeadlockHealthCheck} with the given {@link VirtualMachineMetrics} - * instance. - * - * @param vm a {@link VirtualMachineMetrics} instance - */ - public DeadlockHealthCheck(VirtualMachineMetrics vm) { - super("deadlocks"); - this.vm = vm; - } - - /** - * Creates a new {@link DeadlockHealthCheck}. - */ - @SuppressWarnings("UnusedDeclaration") - public DeadlockHealthCheck() { - this(VirtualMachineMetrics.getInstance()); - } - - @Override - protected Result check() throws Exception { - final Set threads = vm.getDeadlockedThreads(); - if (threads.isEmpty()) { - return Result.healthy(); - } - - final StringBuilder builder = new StringBuilder("Deadlocked threads detected:\n"); - for (String thread : threads) { - builder.append(thread).append('\n'); - } - return Result.unhealthy(builder.toString()); - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/util/DeathRattleExceptionHandler.java b/metrics-core/src/main/java/com/yammer/metrics/util/DeathRattleExceptionHandler.java deleted file mode 100644 index 7dd9ea65be..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/util/DeathRattleExceptionHandler.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.yammer.metrics.util; - -import com.yammer.metrics.core.Counter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * When a thread throws an Exception that was not caught, a DeathRattleExceptionHandler will - * increment a counter signalling a thread has died and print out the name and stack trace of the - * thread. - *

- * This makes it easy to build alerts on unexpected Thread deaths and fine-grained use quickens - * debugging in production. - *

- * You can also set a DeathRattleExceptionHandler as the default exception handler on all threads, - * allowing you to get information on Threads you do not have direct control over. - *

- * Usage is straightforward: - *

- *


- * Counter c = Metrics.newCounter(MyRunnable.class, "thread-deaths");
- * Thread.UncaughtExceptionHandler exHandler = new DeathRattleExceptionHandler(c);
- * final Thread myThread = new Thread(myRunnable, "MyRunnable");
- * myThread.setUncaughtExceptionHandler(exHandler);
- * 
- *

- * Setting the global default exception handler should be done first, like so: - *

- *


- * Counter c = Metrics.newCounter(MyMainClass.class, "unhandled-thread-deaths");
- * Thread.UncaughtExceptionHandler ohNoIDidntKnowAboutThis = new DeathRattleExceptionHandler(c);
- * Thread.setDefaultUncaughtExceptionHandler(ohNoIDidntKnowAboutThis);
- * 
- */ -public class DeathRattleExceptionHandler implements Thread.UncaughtExceptionHandler { - private static final Logger LOGGER = LoggerFactory.getLogger(DeathRattleExceptionHandler.class); - - private final Counter counter; - - /** - * Creates a new {@link DeathRattleExceptionHandler} with the given {@link Counter}. - * - * @param counter the {@link Counter} which will be used to record the number of uncaught - * exceptions - */ - public DeathRattleExceptionHandler(Counter counter) { - this.counter = counter; - } - - @Override - public void uncaughtException(Thread t, Throwable e) { - counter.inc(); - LOGGER.error("Uncaught exception on thread " + t, e); - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/util/JmxGauge.java b/metrics-core/src/main/java/com/yammer/metrics/util/JmxGauge.java deleted file mode 100644 index c42c9924af..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/util/JmxGauge.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.yammer.metrics.util; - -import com.yammer.metrics.core.Gauge; - -import javax.management.MBeanServer; -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; -import java.lang.management.ManagementFactory; - -/** - * A gauge which exposes an attribute of a JMX MBean. - */ -public class JmxGauge extends Gauge { - private static final MBeanServer SERVER = ManagementFactory.getPlatformMBeanServer(); - private final ObjectName objectName; - private final String attribute; - - /** - * Creates a new {@link JmxGauge} for the given attribute of the given MBean. - * - * @param objectName the string value of the MBean's {@link ObjectName} - * @param attribute the MBean attribute's name - * - * @throws MalformedObjectNameException if {@code objectName} is malformed - */ - public JmxGauge(String objectName, String attribute) throws MalformedObjectNameException { - this(new ObjectName(objectName), attribute); - } - - /** - * Creates a new {@link JmxGauge} for the given attribute of the given MBean. - * - * @param objectName the MBean's {@link ObjectName} - * @param attribute the MBean attribute's name - */ - public JmxGauge(ObjectName objectName, String attribute) { - this.objectName = objectName; - this.attribute = attribute; - } - - @Override - public Object getValue() { - try { - return SERVER.getAttribute(objectName, attribute); - } catch (Exception e) { - throw new RuntimeException(e); - } - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/util/PercentGauge.java b/metrics-core/src/main/java/com/yammer/metrics/util/PercentGauge.java deleted file mode 100644 index ee3bc33919..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/util/PercentGauge.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.yammer.metrics.util; - -/** - * A {@link RatioGauge} extension which returns a percentage, not a ratio. - */ -public abstract class PercentGauge extends RatioGauge { - private static final int ONE_HUNDRED = 100; - - @Override - public Double getValue() { - return super.getValue() * ONE_HUNDRED; - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/util/RatioGauge.java b/metrics-core/src/main/java/com/yammer/metrics/util/RatioGauge.java deleted file mode 100644 index d53d1d75c6..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/util/RatioGauge.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.yammer.metrics.util; - -import com.yammer.metrics.core.Gauge; - -import static java.lang.Double.isInfinite; -import static java.lang.Double.isNaN; - -/** - * A gauge which measures the ratio of one value to another. - *

- * If the denominator is zero, not a number, or infinite, the resulting ratio is not a number. - */ -public abstract class RatioGauge extends Gauge { - /** - * Returns the numerator (the value on the top half of the fraction or the left-hand side of the - * ratio). - * - * @return the numerator - */ - protected abstract double getNumerator(); - - /** - * Returns the denominator (the value on the bottom half of the fraction or the right-hand side - * of the ratio). - * - * @return the denominator - */ - protected abstract double getDenominator(); - - @Override - public Double getValue() { - final double d = getDenominator(); - if (isNaN(d) || isInfinite(d) || d == 0.0) { - return Double.NaN; - } - return getNumerator() / d; - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/util/ToggleGauge.java b/metrics-core/src/main/java/com/yammer/metrics/util/ToggleGauge.java deleted file mode 100644 index 418a09ea3c..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/util/ToggleGauge.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.yammer.metrics.util; - -import com.yammer.metrics.core.Gauge; - -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Returns a {@code 1} the first time it's called, a {@code 0} every time after that. - */ -public class ToggleGauge extends Gauge { - private final AtomicInteger value = new AtomicInteger(1); - - @Override - public Integer getValue() { - try { - return value.get(); - } finally { - this.value.set(0); - } - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/core/VMMFactory.java b/metrics-core/src/test/java/com/yammer/metrics/core/VMMFactory.java deleted file mode 100644 index 16a4d8b85f..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/core/VMMFactory.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.yammer.metrics.core; - -import javax.management.MBeanServer; -import java.lang.management.*; -import java.util.List; - -public class VMMFactory { - private VMMFactory() {} - - public static VirtualMachineMetrics build(MemoryMXBean memoryMXBean, - List memoryPoolMXBeans, - OperatingSystemMXBean operatingSystemMXBean, - ThreadMXBean threadMXBean, - List garbageCollectorMXBeans, - RuntimeMXBean runtimeMXBean, - MBeanServer mBeanServer) { - return new VirtualMachineMetrics(memoryMXBean, - memoryPoolMXBeans, - operatingSystemMXBean, - threadMXBean, - garbageCollectorMXBeans, - runtimeMXBean, - mBeanServer); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/core/tests/ClockTest.java b/metrics-core/src/test/java/com/yammer/metrics/core/tests/ClockTest.java deleted file mode 100644 index 217c5a4c04..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/core/tests/ClockTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.yammer.metrics.core.tests; - -import com.yammer.metrics.core.Clock; -import org.junit.Test; - -import java.lang.management.ManagementFactory; - -import static org.hamcrest.Matchers.closeTo; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -public class ClockTest { - @Test - public void cpuTimeClock() throws Exception { - final Clock.CpuTimeClock clock = new Clock.CpuTimeClock(); - - assertThat((double) clock.getTime(), - is(closeTo(System.currentTimeMillis(), 100))); - - assertThat((double) clock.getTick(), - is(closeTo(ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime(), 1000000))); - } - - @Test - public void userTimeClock() throws Exception { - final Clock.UserTimeClock clock = new Clock.UserTimeClock(); - - assertThat((double) clock.getTime(), - is(closeTo(System.currentTimeMillis(), 100))); - - assertThat((double) clock.getTick(), - is(closeTo(System.nanoTime(), 100000))); - } - - @Test - public void defaultsToUserTime() throws Exception { - assertThat(Clock.defaultClock(), - is(instanceOf(Clock.UserTimeClock.class))); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/core/tests/CounterTest.java b/metrics-core/src/test/java/com/yammer/metrics/core/tests/CounterTest.java deleted file mode 100644 index dff922dbfb..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/core/tests/CounterTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.yammer.metrics.core.tests; - -import com.yammer.metrics.core.Counter; -import com.yammer.metrics.core.MetricsRegistry; -import org.junit.Test; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -public class CounterTest { - private final MetricsRegistry registry = new MetricsRegistry(); - private final Counter counter = registry.newCounter(CounterTest.class, "counter"); - - @Test - public void startsAtZero() throws Exception { - assertThat("the counter's initial value is zero", - counter.getCount(), - is(0L)); - } - - @Test - public void incrementsByOne() throws Exception { - counter.inc(); - - assertThat("the counter's value after being incremented is one", - counter.getCount(), - is(1L)); - } - - @Test - public void incrementsByAnArbitraryDelta() throws Exception { - counter.inc(12); - - assertThat("the counter's value after being incremented by 12 is 12", - counter.getCount(), - is(12L)); - } - - @Test - public void decrementsByOne() throws Exception { - counter.dec(); - - assertThat("the counter's value after being decremented is negative one", - counter.getCount(), - is(-1L)); - } - - @Test - public void decrementsByAnArbitraryDelta() throws Exception { - counter.dec(12); - - assertThat("the counter's value after being decremented by 12 is -12", - counter.getCount(), - is(-12L)); - } - - @Test - public void isZeroAfterBeingCleared() throws Exception { - counter.inc(3); - counter.clear(); - - assertThat("the counter's value after being cleared is zero", - counter.getCount(), - is(0L)); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/core/tests/GaugeTest.java b/metrics-core/src/test/java/com/yammer/metrics/core/tests/GaugeTest.java deleted file mode 100644 index 024e2382af..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/core/tests/GaugeTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.yammer.metrics.core.tests; - -import com.yammer.metrics.core.Gauge; -import org.junit.Test; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -public class GaugeTest { - final Gauge gauge = new Gauge() { - @Override - public String getValue() { - return "woo"; - } - }; - - @Test - public void returnsAValue() throws Exception { - assertThat("a gauge returns a value", - gauge.getValue(), - is("woo")); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/core/tests/HealthCheckRegistryTest.java b/metrics-core/src/test/java/com/yammer/metrics/core/tests/HealthCheckRegistryTest.java deleted file mode 100644 index e53b592df7..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/core/tests/HealthCheckRegistryTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.yammer.metrics.core.tests; - -import com.yammer.metrics.core.HealthCheck; -import com.yammer.metrics.core.HealthCheckRegistry; -import org.junit.Before; -import org.junit.Test; - -import java.util.Map; - -import static com.yammer.metrics.core.HealthCheck.Result; -import static org.hamcrest.Matchers.hasEntry; -import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class HealthCheckRegistryTest { - private final HealthCheckRegistry registry = new HealthCheckRegistry(); - - private final HealthCheck hc1 = mock(HealthCheck.class); - private final HealthCheck hc2 = mock(HealthCheck.class); - - private final Result r1 = mock(Result.class); - private final Result r2 = mock(Result.class); - - @Before - public void setUp() throws Exception { - when(hc1.getName()).thenReturn("hc1"); - when(hc1.execute()).thenReturn(r1); - - when(hc2.getName()).thenReturn("hc2"); - when(hc2.execute()).thenReturn(r2); - - registry.register(hc1); - registry.register(hc2); - } - - @Test - public void runsRegisteredHealthChecks() throws Exception { - final Map results = registry.runHealthChecks(); - - assertThat(results, - hasEntry("hc1", r1)); - - assertThat(results, - hasEntry("hc2", r2)); - } - - @Test - public void removesRegisteredHealthChecks() throws Exception { - registry.unregister(hc1); - - final Map results = registry.runHealthChecks(); - - assertThat(results, - not(hasEntry("hc1", r1))); - - assertThat(results, - hasEntry("hc2", r2)); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/core/tests/HealthCheckTest.java b/metrics-core/src/test/java/com/yammer/metrics/core/tests/HealthCheckTest.java deleted file mode 100644 index 85faafb4aa..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/core/tests/HealthCheckTest.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.yammer.metrics.core.tests; - -import com.yammer.metrics.core.HealthCheck; -import org.junit.Test; - -import static com.yammer.metrics.core.HealthCheck.Result; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class HealthCheckTest { - private static class ExampleHealthCheck extends HealthCheck { - private final HealthCheck underlying; - - private ExampleHealthCheck(HealthCheck underlying) { - super("example"); - this.underlying = underlying; - } - - @Override - protected Result check() throws Exception { - return underlying.execute(); - } - } - - private final HealthCheck underlying = mock(HealthCheck.class); - private final HealthCheck healthCheck = new ExampleHealthCheck(underlying); - - @Test - public void hasAName() throws Exception { - assertThat(healthCheck.getName(), - is("example")); - } - - @Test - public void canHaveHealthyResults() throws Exception { - final Result result = Result.healthy(); - - assertThat(result.isHealthy(), - is(true)); - - assertThat(result.getMessage(), - is(nullValue())); - - assertThat(result.getError(), - is(nullValue())); - } - - @Test - public void canHaveHealthyResultsWithMessages() throws Exception { - final Result result = Result.healthy("woo"); - - assertThat(result.isHealthy(), - is(true)); - - assertThat(result.getMessage(), - is("woo")); - - assertThat(result.getError(), - is(nullValue())); - } - - @Test - public void canHaveHealthyResultsWithFormattedMessages() throws Exception { - final Result result = Result.healthy("foo %s", "bar"); - - assertThat(result.isHealthy(), - is(true)); - - assertThat(result.getMessage(), - is("foo bar")); - - assertThat(result.getError(), - is(nullValue())); - } - - @Test - public void canHaveUnhealthyResults() throws Exception { - final Result result = Result.unhealthy("bad"); - - assertThat(result.isHealthy(), - is(false)); - - assertThat(result.getMessage(), - is("bad")); - - assertThat(result.getError(), - is(nullValue())); - } - - @Test - public void canHaveUnhealthyResultsWithFormattedMessages() throws Exception { - final Result result = Result.unhealthy("foo %s %d", "bar", 123); - - assertThat(result.isHealthy(), - is(false)); - - assertThat(result.getMessage(), - is("foo bar 123")); - - assertThat(result.getError(), - is(nullValue())); - } - - @Test - public void canHaveUnhealthyResultsWithExceptions() throws Exception { - final RuntimeException e = mock(RuntimeException.class); - when(e.getMessage()).thenReturn("oh noes"); - - final Result result = Result.unhealthy(e); - - assertThat(result.isHealthy(), - is(false)); - - assertThat(result.getMessage(), - is("oh noes")); - - assertThat(result.getError(), - is((Throwable) e)); - } - - @Test - public void returnsResultsWhenExecuted() throws Exception { - final Result result = mock(Result.class); - when(underlying.execute()).thenReturn(result); - - assertThat(healthCheck.execute(), - is(result)); - } - - @Test - public void wrapsExceptionsWhenExecuted() throws Exception { - final RuntimeException e = mock(RuntimeException.class); - when(e.getMessage()).thenReturn("oh noes"); - - when(underlying.execute()).thenThrow(e); - - assertThat(healthCheck.execute(), - is(Result.unhealthy(e))); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/core/tests/HistogramTest.java b/metrics-core/src/test/java/com/yammer/metrics/core/tests/HistogramTest.java deleted file mode 100644 index ffe7965b1d..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/core/tests/HistogramTest.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.yammer.metrics.core.tests; - -import com.yammer.metrics.core.Histogram; -import com.yammer.metrics.core.MetricsRegistry; -import com.yammer.metrics.stats.Snapshot; -import org.junit.Test; - -import static org.hamcrest.Matchers.closeTo; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -public class HistogramTest { - private final MetricsRegistry registry = new MetricsRegistry(); - private final Histogram histogram = registry.newHistogram(HistogramTest.class, "histogram", false); - - @Test - public void anEmptyHistogram() throws Exception { - assertThat("the histogram has a count of zero", - histogram.getCount(), - is(0L)); - - assertThat("the histogram has a max of zero", - histogram.getMax(), - is(closeTo(0.0, 0.0001))); - - assertThat("the histogram has a min of zero", - histogram.getMin(), - is(closeTo(0.0, 0.0001))); - - assertThat("the histogram has a mean of zero", - histogram.getMean(), - is(closeTo(0.0, 0.0001))); - - assertThat("the histogram has a standard deviation of zero", - histogram.getStdDev(), - is(closeTo(0.0, 0.0001))); - - assertThat("the histogram has a sum of zero", - histogram.getSum(), - is(closeTo(0.0, 0.0001))); - - final Snapshot snapshot = histogram.getSnapshot(); - - assertThat("the histogram has a median of zero", - snapshot.getMedian(), - is(closeTo(0.0, 0.0001))); - - assertThat("the histogram has a 75th percentile of zero", - snapshot.get75thPercentile(), - is(closeTo(0.0, 0.0001))); - - assertThat("the histogram has a 99th percentile of zero", - snapshot.get99thPercentile(), - is(closeTo(0.0, 0.0001))); - - assertThat("the histogram is empty", - snapshot.size(), - is(0)); - } - - @Test - public void aHistogramWith1000Elements() throws Exception { - for (int i = 1; i <= 1000; i++) { - histogram.update(i); - } - - assertThat("the histogram has a count of 1000", - histogram.getCount(), - is(1000L)); - - assertThat("the histogram has a max of 1000", - histogram.getMax(), - is(closeTo(1000.0, 0.0001))); - - assertThat("the histogram has a min of 1", - histogram.getMin(), - is(closeTo(1.0, 0.0001))); - - assertThat("the histogram has a mean of 500.5", - histogram.getMean(), - is(closeTo(500.5, 0.0001))); - - assertThat("the histogram has a standard deviation of 288.82", - histogram.getStdDev(), - is(closeTo(288.8194360957494, 0.0001))); - - assertThat("the histogram has a sum of 500500", - histogram.getSum(), - is(closeTo(500500, 0.1))); - - final Snapshot snapshot = histogram.getSnapshot(); - - assertThat("the histogram has a median of 500.5", - snapshot.getMedian(), - is(closeTo(500.5, 0.0001))); - - assertThat("the histogram has a 75th percentile of 750.75", - snapshot.get75thPercentile(), - is(closeTo(750.75, 0.0001))); - - assertThat("the histogram has a 99th percentile of 990.99", - snapshot.get99thPercentile(), - is(closeTo(990.99, 0.0001))); - - assertThat("the histogram has 1000 values", - snapshot.size(), - is(1000)); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/core/tests/MeterTest.java b/metrics-core/src/test/java/com/yammer/metrics/core/tests/MeterTest.java deleted file mode 100644 index 3711648c5b..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/core/tests/MeterTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.yammer.metrics.core.tests; - -import com.yammer.metrics.core.Meter; -import com.yammer.metrics.core.MetricsRegistry; -import org.junit.Test; - -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.Matchers.closeTo; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -public class MeterTest { - private final MetricsRegistry registry = new MetricsRegistry(); - private final Meter meter = registry.newMeter(MeterTest.class, "things", "thing", TimeUnit.SECONDS); - - @Test - public void aBlankMeter() throws Exception { - assertThat("the meter has a count of zero", - meter.getCount(), - is(0L)); - - assertThat("the meter has a mean rate of zero", - meter.getMeanRate(), - is(closeTo(0.0, 0.001))); - } - - @Test - public void aMeterWithThreeEvents() throws Exception { - meter.mark(3); - - assertThat("the meter has a count of three", - meter.getCount(), - is(3L)); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/core/tests/MetricNameTest.java b/metrics-core/src/test/java/com/yammer/metrics/core/tests/MetricNameTest.java deleted file mode 100644 index ff9e52f34f..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/core/tests/MetricNameTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.yammer.metrics.core.tests; - -import com.yammer.metrics.core.MetricName; -import org.junit.Test; - -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; - -public class MetricNameTest { - private final MetricName name = new MetricName("group", "type", "name", "scope"); - - @Test - public void hasAGroup() throws Exception { - assertThat(name.getDomain(), - is("group")); - } - - @Test - public void hasAType() throws Exception { - assertThat(name.getType(), - is("type")); - } - - @Test - public void hasAName() throws Exception { - assertThat(name.getName(), - is("name")); - } - - @Test - public void hasAScope() throws Exception { - assertThat(name.getScope(), - is("scope")); - - assertThat(name.hasScope(), - is(true)); - } - - @Test - public void isHumanReadable() throws Exception { - assertThat(name.toString(), - is("group.type.name.scope")); - } - - @Test - public void createsNamesForSimpleMetrics() throws Exception { - final MetricName simple = new MetricName(MetricNameTest.class, "name"); - - assertThat("it uses the package name as the group", - simple.getDomain(), - is("com.yammer.metrics.core.tests")); - - assertThat("it uses the class name as the type", - simple.getType(), - is("MetricNameTest")); - - assertThat("it doesn't have a scope", - simple.hasScope(), - is(false)); - - assertThat("it has a name", - simple.getName(), - is("name")); - } - - @Test - public void createsNamesForScopedMetrics() throws Exception { - final MetricName scoped = new MetricName(MetricNameTest.class, "name", "scope"); - - assertThat("it uses the package name as the group", - scoped.getDomain(), - is("com.yammer.metrics.core.tests")); - - assertThat("it uses the class name as the type", - scoped.getType(), - is("MetricNameTest")); - - assertThat("it has a scope", - scoped.getScope(), - is("scope")); - - assertThat("it has a name", - scoped.getName(), - is("name")); - } - - @Test - public void hasAWorkingEqualsImplementation() throws Exception { - assertThat(name, - is(equalTo(name))); - - assertThat(name, - is(not(equalTo(null)))); - - assertThat(name, - is(not(equalTo((Object) "")))); - - assertThat(name, - is(equalTo(new MetricName("group", "type", "name", "scope")))); - } - - @Test - public void hasAWorkingHashCodeImplementation() throws Exception { - assertThat(new MetricName("group", "type", "name", "scope").hashCode(), - is(equalTo(new MetricName("group", "type", "name", "scope").hashCode()))); - - assertThat(new MetricName("group", "type", "name", "scope").hashCode(), - is(not(equalTo(new MetricName("group", "type", "name", "scope2").hashCode())))); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/core/tests/MetricsRegistryTest.java b/metrics-core/src/test/java/com/yammer/metrics/core/tests/MetricsRegistryTest.java deleted file mode 100644 index fcc250c3fe..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/core/tests/MetricsRegistryTest.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.yammer.metrics.core.tests; - -import com.yammer.metrics.core.*; -import org.junit.Test; -import org.mockito.InOrder; - -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.*; - -public class MetricsRegistryTest { - private final MetricsRegistry registry = new MetricsRegistry(); - - @Test - public void sortingMetricNamesSortsThemByClassThenScopeThenName() throws Exception { - final MetricName one = new MetricName(Object.class, "one"); - final MetricName two = new MetricName(Object.class, "two"); - final MetricName three = new MetricName(String.class, "three"); - - final Counter mOne = registry.newCounter(Object.class, "one"); - final Counter mTwo = registry.newCounter(Object.class, "two"); - final Counter mThree = registry.newCounter(String.class, "three"); - - final SortedMap> sortedMetrics = new TreeMap>(); - final TreeMap objectMetrics = new TreeMap(); - objectMetrics.put(one, mOne); - objectMetrics.put(two, mTwo); - sortedMetrics.put(Object.class.getCanonicalName(), objectMetrics); - - final TreeMap stringMetrics = new TreeMap(); - stringMetrics.put(three, mThree); - sortedMetrics.put(String.class.getCanonicalName(), stringMetrics); - - assertThat(registry.getGroupedMetrics(), - is(sortedMetrics)); - } - - @Test - public void listenersRegisterNewMetrics() throws Exception { - final MetricsRegistryListener listener = mock(MetricsRegistryListener.class); - registry.addListener(listener); - - final Gauge gauge = mock(Gauge.class); - registry.newGauge(MetricsRegistryTest.class, "gauge", gauge); - final Counter counter = registry.newCounter(MetricsRegistryTest.class, "counter"); - final Histogram histogram = registry.newHistogram(MetricsRegistryTest.class, "histogram"); - final Meter meter = registry.newMeter(MetricsRegistryTest.class, - "meter", - "things", - TimeUnit.SECONDS); - final Timer timer = registry.newTimer(MetricsRegistryTest.class, "timer"); - - verify(listener).onMetricAdded(new MetricName(MetricsRegistryTest.class, "gauge"), gauge); - - verify(listener).onMetricAdded(new MetricName(MetricsRegistryTest.class, "counter"), counter); - - verify(listener).onMetricAdded(new MetricName(MetricsRegistryTest.class, "histogram"), histogram); - - verify(listener).onMetricAdded(new MetricName(MetricsRegistryTest.class, "meter"), meter); - - verify(listener).onMetricAdded(new MetricName(MetricsRegistryTest.class, "timer"), timer); - } - - @Test - public void removedListenersDoNotReceiveEvents() throws Exception { - final MetricsRegistryListener listener = mock(MetricsRegistryListener.class); - registry.addListener(listener); - - final Counter counter1 = registry.newCounter(MetricsRegistryTest.class, "counter1"); - - registry.removeListener(listener); - - final Counter counter2 = registry.newCounter(MetricsRegistryTest.class, "counter2"); - - verify(listener).onMetricAdded(new MetricName(MetricsRegistryTest.class, "counter1"), counter1); - - verify(listener, never()).onMetricAdded(new MetricName(MetricsRegistryTest.class, "counter2"), counter2); - } - - @Test - public void metricsCanBeRemoved() throws Exception { - final MetricsRegistryListener listener = mock(MetricsRegistryListener.class); - registry.addListener(listener); - - final MetricName name = new MetricName(MetricsRegistryTest.class, "counter1"); - - final Counter counter1 = registry.newCounter(MetricsRegistryTest.class, "counter1"); - registry.removeMetric(MetricsRegistryTest.class, "counter1"); - - final InOrder inOrder = inOrder(listener); - inOrder.verify(listener).onMetricAdded(name, counter1); - inOrder.verify(listener).onMetricRemoved(name); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/core/tests/TimerTest.java b/metrics-core/src/test/java/com/yammer/metrics/core/tests/TimerTest.java deleted file mode 100644 index df883a3b3a..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/core/tests/TimerTest.java +++ /dev/null @@ -1,190 +0,0 @@ -package com.yammer.metrics.core.tests; - -import com.yammer.metrics.core.Clock; -import com.yammer.metrics.core.MetricsRegistry; -import com.yammer.metrics.core.Timer; -import com.yammer.metrics.stats.Snapshot; -import org.junit.Test; - -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.Matchers.closeTo; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -public class TimerTest { - private final MetricsRegistry registry = new MetricsRegistry(new Clock() { - // a mock clock that increments its ticker by 50msec per call - private long val = 0; - - @Override - public long getTick() { - return val += 50000000; - } - }); - private final Timer timer = registry.newTimer(TimerTest.class, "timer"); - - @Test - public void hasADurationUnit() throws Exception { - assertThat("the timer has a duration unit", - timer.getDurationUnit(), - is(TimeUnit.MILLISECONDS)); - } - - @Test - public void hasARateUnit() throws Exception { - assertThat("the timer has a rate unit", - timer.getRateUnit(), - is(TimeUnit.SECONDS)); - } - - @Test - public void aBlankTimer() throws Exception { - assertThat("the timer has a count of zero", - timer.getCount(), - is(0L)); - - assertThat("the timer has a max duration of zero", - timer.getMax(), - is(closeTo(0.0, 0.001))); - - assertThat("the timer has a min duration of zero", - timer.getMin(), - is(closeTo(0.0, 0.001))); - - assertThat("the timer has a mean duration of zero", - timer.getMean(), - is(closeTo(0.0, 0.001))); - - assertThat("the timer has a duration standard deviation of zero", - timer.getStdDev(), - is(closeTo(0.0, 0.001))); - - final Snapshot snapshot = timer.getSnapshot(); - - assertThat("the timer has a median duration of zero", - snapshot.getMedian(), - is(closeTo(0.0, 0.001))); - - assertThat("the timer has a 75th percentile duration of zero", - snapshot.get75thPercentile(), - is(closeTo(0.0, 0.001))); - - assertThat("the timer has a 99th percentile duration of zero", - snapshot.get99thPercentile(), - is(closeTo(0.0, 0.001))); - - assertThat("the timer has a mean rate of zero", - timer.getMeanRate(), - is(closeTo(0.0, 0.001))); - - assertThat("the timer has a one-minute rate of zero", - timer.getOneMinuteRate(), - is(closeTo(0.0, 0.001))); - - assertThat("the timer has a five-minute rate of zero", - timer.getFiveMinuteRate(), - is(closeTo(0.0, 0.001))); - - assertThat("the timer has a fifteen-minute rate of zero", - timer.getFifteenMinuteRate(), - is(closeTo(0.0, 0.001))); - - assertThat("the timer has no values", - timer.getSnapshot().size(), - is(0)); - } - - @Test - public void timingASeriesOfEvents() throws Exception { - timer.update(10, TimeUnit.MILLISECONDS); - timer.update(20, TimeUnit.MILLISECONDS); - timer.update(20, TimeUnit.MILLISECONDS); - timer.update(30, TimeUnit.MILLISECONDS); - timer.update(40, TimeUnit.MILLISECONDS); - - assertThat("the timer has a count of 5", - timer.getCount(), - is(5L)); - - assertThat("the timer has a max duration of 40", - timer.getMax(), - is(closeTo(40.0, 0.001))); - - assertThat("the timer has a min duration of 10", - timer.getMin(), - is(closeTo(10.0, 0.001))); - - assertThat("the timer has a mean duration of 24", - timer.getMean(), - is(closeTo(24.0, 0.001))); - - assertThat("the timer has a duration standard deviation of zero", - timer.getStdDev(), - is(closeTo(11.401, 0.001))); - - final Snapshot snapshot = timer.getSnapshot(); - - assertThat("the timer has a median duration of 20", - snapshot.getMedian(), - is(closeTo(20.0, 0.001))); - - assertThat("the timer has a 75th percentile duration of 35", - snapshot.get75thPercentile(), - is(closeTo(35.0, 0.001))); - - assertThat("the timer has a 99th percentile duration of 40", - snapshot.get99thPercentile(), - is(closeTo(40.0, 0.001))); - - assertThat("the timer has no values", - timer.getSnapshot().getValues(), - is(new double[]{10.0, 20.0, 20.0, 30.0, 40.0})); - } - - @Test - public void timingVariantValues() throws Exception { - timer.update(Long.MAX_VALUE, TimeUnit.NANOSECONDS); - timer.update(0, TimeUnit.NANOSECONDS); - - assertThat("the timer has an accurate standard deviation", - timer.getStdDev(), - is(closeTo(6.521908912666392E12, 0.001))); - } - - @Test - public void timingCallableInstances() throws Exception { - final String value = timer.time(new Callable() { - @Override - public String call() throws Exception { - return "one"; - } - }); - - assertThat("the timer has a count of 1", - timer.getCount(), - is(1L)); - - assertThat("returns the result of the callable", - value, - is("one")); - - assertThat("records the duration of the Callable#call()", - timer.getMax(), - is(closeTo(50.0, 0.001))); - } - - @Test - public void timingContexts() throws Exception { - timer.time().stop(); - - assertThat("the timer has a count of 1", - timer.getCount(), - is(1L)); - - assertThat("records the duration of the context", - timer.getMax(), - is(closeTo(50.0, 0.001))); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/core/tests/VirtualMachineMetricsTest.java b/metrics-core/src/test/java/com/yammer/metrics/core/tests/VirtualMachineMetricsTest.java deleted file mode 100644 index bd5a37741e..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/core/tests/VirtualMachineMetricsTest.java +++ /dev/null @@ -1,268 +0,0 @@ -package com.yammer.metrics.core.tests; - -import com.sun.management.UnixOperatingSystemMXBean; -import com.yammer.metrics.core.VMMFactory; -import com.yammer.metrics.core.VirtualMachineMetrics; -import com.yammer.metrics.core.VirtualMachineMetrics.GarbageCollectorStats; -import org.junit.Before; -import org.junit.Test; - -import javax.management.*; -import java.lang.management.*; -import java.lang.reflect.InvocationTargetException; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import static java.util.Arrays.asList; -import static org.hamcrest.Matchers.hasEntry; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class VirtualMachineMetricsTest { - private final MemoryUsage heap = new MemoryUsage(1, 10, 100, 1000); - private final MemoryUsage nonHeap = new MemoryUsage(2, 20, 200, 2000); - private final MemoryMXBean memory = mock(MemoryMXBean.class); - private final MemoryPoolMXBean pool1 = mock(MemoryPoolMXBean.class); - private final MemoryPoolMXBean pool2 = mock(MemoryPoolMXBean.class); - private final MemoryUsage pool1Usage = heap; - private final MemoryUsage pool2Usage = nonHeap; - private final List memoryPools = asList(pool1, - pool2); - private final UnixOperatingSystemMXBean os = mock(UnixOperatingSystemMXBean.class); - private final ThreadMXBean threads = mock(ThreadMXBean.class); - private final GarbageCollectorMXBean gc1 = mock(GarbageCollectorMXBean.class); - private final GarbageCollectorMXBean gc2 = mock(GarbageCollectorMXBean.class); - private final List garbageCollectors = asList(gc1, gc2); - private final RuntimeMXBean runtime = mock(RuntimeMXBean.class); - private final MBeanServer mBeanServer = mock(MBeanServer.class); - - private final VirtualMachineMetrics vmm = VMMFactory.build(memory, - memoryPools, - os, - threads, - garbageCollectors, - runtime, - mBeanServer); - - @Before - public void setUp() throws Exception { - when(memory.getHeapMemoryUsage()).thenReturn(heap); - when(memory.getNonHeapMemoryUsage()).thenReturn(nonHeap); - - when(pool1.getUsage()).thenReturn(pool1Usage); - when(pool1.getName()).thenReturn("pool1"); - - when(pool2.getUsage()).thenReturn(pool2Usage); - when(pool2.getName()).thenReturn("pool2"); - - when(os.getOpenFileDescriptorCount()).thenReturn(50L); - when(os.getMaxFileDescriptorCount()).thenReturn(1000L); - - when(runtime.getUptime()).thenReturn(11000L); - - when(threads.getThreadCount()).thenReturn(52); - when(threads.getDaemonThreadCount()).thenReturn(22); - - when(gc1.getName()).thenReturn("gc1"); - when(gc1.getCollectionCount()).thenReturn(1L); - when(gc1.getCollectionTime()).thenReturn(10L); - - when(gc2.getName()).thenReturn("gc2"); - when(gc2.getCollectionCount()).thenReturn(2L); - when(gc2.getCollectionTime()).thenReturn(20L); - } - - @Test - public void calculatesTotalInit() throws Exception { - assertThat(vmm.getTotalInit(), - is(3.0)); - } - - @Test - public void calculatesTotalUsed() throws Exception { - assertThat(vmm.getTotalUsed(), - is(30.0)); - } - - @Test - public void calculatesTotalMax() throws Exception { - assertThat(vmm.getTotalMax(), - is(3000.0)); - } - - @Test - public void calculatesTotalCommitted() throws Exception { - assertThat(vmm.getTotalCommitted(), - is(300.0)); - } - - @Test - public void calculatesHeapInit() throws Exception { - assertThat(vmm.getHeapInit(), - is(1.0)); - } - - @Test - public void calculatesHeapUsed() throws Exception { - assertThat(vmm.getHeapUsed(), - is(10.0)); - } - - @Test - public void calculatesHeapCommitted() throws Exception { - assertThat(vmm.getHeapCommitted(), - is(100.0)); - } - - @Test - public void calculatesHeapMax() throws Exception { - assertThat(vmm.getHeapMax(), - is(1000.0)); - } - - @Test - public void calculatesNonHeapUsage() throws Exception { - assertThat(vmm.getNonHeapUsage(), - is(0.01)); - } - - @Test - public void calculatesMemoryPoolUsage() throws Exception { - final Map usages = vmm.getMemoryPoolUsage(); - - assertThat(usages, - hasEntry("pool1", 0.01)); - - assertThat(usages, - hasEntry("pool2", 0.01)); - } - - @Test - public void calculatesFileDescriptorUsage() throws Exception { - assertThat(vmm.getFileDescriptorUsage(), - is(0.05)); - } - - @Test - @SuppressWarnings("unchecked") - public void fdCalculationHandlesNonUnixSystems() throws Exception { - when(os.getOpenFileDescriptorCount()).thenThrow(NoSuchMethodException.class); - - assertThat(vmm.getFileDescriptorUsage(), - is(Double.NaN)); - } - - @Test - @SuppressWarnings("unchecked") - public void fdCalculationHandlesSecuredSystems() throws Exception { - when(os.getOpenFileDescriptorCount()).thenThrow(IllegalAccessException.class); - - assertThat(vmm.getFileDescriptorUsage(), - is(Double.NaN)); - } - - @Test - @SuppressWarnings("unchecked") - public void fdCalculationHandlesWeirdSystems() throws Exception { - when(os.getOpenFileDescriptorCount()).thenThrow(InvocationTargetException.class); - - assertThat(vmm.getFileDescriptorUsage(), - is(Double.NaN)); - } - - @Test - public void fetchesTheVMName() throws Exception { - // this is ugly, but I'd rather not dick with the system properties - assertThat(vmm.getName(), - is(System.getProperty("java.vm.name"))); - } - - @Test - public void calculatesTheUptimeInSeconds() throws Exception { - assertThat(vmm.getUptime(), - is(11L)); - } - - @Test - public void calculatesTheThreadCount() throws Exception { - assertThat(vmm.getThreadCount(), - is(52)); - } - - @Test - public void calculatesTheDaemonThreadCount() throws Exception { - assertThat(vmm.getDaemonThreadCount(), - is(22)); - } - - @Test - public void calculatesGcStats() throws Exception { - final Map stats = vmm.getGarbageCollectors(); - - assertThat(stats.get("gc1").getRuns(), - is(1L)); - - assertThat(stats.get("gc1").getTime(TimeUnit.MILLISECONDS), - is(10L)); - - assertThat(stats.get("gc2").getRuns(), - is(2L)); - - assertThat(stats.get("gc2").getTime(TimeUnit.MILLISECONDS), - is(20L)); - } - - @Test - public void handlesMissingBufferPools() throws Exception { - when(mBeanServer.getAttributes(any(ObjectName.class), any(String[].class))).thenThrow(new InstanceNotFoundException("OH NO")); - - assertThat(vmm.getBufferPoolStats().isEmpty(), - is(true)); - } - - @Test - public void handlesMappedAndDirectBufferPools() throws Exception { - final String[] attributes = { "Count", "MemoryUsed", "TotalCapacity" }; - - final ObjectName direct = new ObjectName("java.nio:type=BufferPool,name=direct"); - final ObjectName mapped = new ObjectName("java.nio:type=BufferPool,name=mapped"); - - final AttributeList directAttributes = new AttributeList(); - directAttributes.add(new Attribute("Count", 100L)); - directAttributes.add(new Attribute("MemoryUsed", 200L)); - directAttributes.add(new Attribute("TotalCapacity", 300L)); - - final AttributeList mappedAttributes = new AttributeList(); - mappedAttributes.add(new Attribute("Count", 1000L)); - mappedAttributes.add(new Attribute("MemoryUsed", 2000L)); - mappedAttributes.add(new Attribute("TotalCapacity", 3000L)); - - when(mBeanServer.getAttributes(direct, attributes)).thenReturn(directAttributes); - when(mBeanServer.getAttributes(mapped, attributes)).thenReturn(mappedAttributes); - - assertThat(vmm.getBufferPoolStats().get("direct").getCount(), - is(100L)); - - assertThat(vmm.getBufferPoolStats().get("direct").getMemoryUsed(), - is(200L)); - - assertThat(vmm.getBufferPoolStats().get("direct").getTotalCapacity(), - is(300L)); - - assertThat(vmm.getBufferPoolStats().get("mapped").getCount(), - is(1000L)); - - assertThat(vmm.getBufferPoolStats().get("mapped").getMemoryUsed(), - is(2000L)); - - assertThat(vmm.getBufferPoolStats().get("mapped").getTotalCapacity(), - is(3000L)); - } - - // TODO: 1/13/12 -- test thread state percentages - // TODO: 1/13/12 -- test thread dumps -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/examples/DirectoryLister.java b/metrics-core/src/test/java/com/yammer/metrics/examples/DirectoryLister.java deleted file mode 100644 index 8380c32024..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/examples/DirectoryLister.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.yammer.metrics.examples; - -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Counter; -import com.yammer.metrics.core.Meter; -import com.yammer.metrics.core.MetricsRegistry; -import com.yammer.metrics.core.Timer; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; - -public class DirectoryLister { - private final MetricsRegistry registry = Metrics.defaultRegistry(); - private final Counter counter = registry.newCounter(getClass(), "directories"); - private final Meter meter = registry.newMeter(getClass(), "files", "files", TimeUnit.SECONDS); - private final Timer timer = registry.newTimer(getClass(), - "directory-listing", - TimeUnit.MILLISECONDS, - TimeUnit.SECONDS); - private final File directory; - - public DirectoryLister(File directory) { - this.directory = directory; - } - - public List list() throws Exception { - counter.inc(); - final File[] list = timer.time(new Callable() { - @Override - public File[] call() throws Exception { - return directory.listFiles(); - } - }); - counter.dec(); - - if (list == null) { - return Collections.emptyList(); - } - - final List result = new ArrayList(list.length); - for (File file : list) { - meter.mark(); - result.add(file); - } - return result; - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/examples/ExampleRunner.java b/metrics-core/src/test/java/com/yammer/metrics/examples/ExampleRunner.java deleted file mode 100644 index 5b59fa39ab..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/examples/ExampleRunner.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.yammer.metrics.examples; - -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Counter; -import com.yammer.metrics.core.Histogram; -import com.yammer.metrics.core.MetricsRegistry; -import com.yammer.metrics.reporting.ConsoleReporter; - -import java.io.File; -import java.util.List; -import java.util.concurrent.*; - -public class ExampleRunner { - private static final int WORKER_COUNT = 10; - private static final BlockingQueue JOBS = new LinkedBlockingQueue(); - private static final ExecutorService POOL = Executors.newFixedThreadPool(WORKER_COUNT); - private static final MetricsRegistry REGISTRY = Metrics.defaultRegistry(); - private static final Counter QUEUE_DEPTH = REGISTRY.newCounter(ExampleRunner.class, "queue-depth"); - private static final Histogram DIRECTORY_SIZE = REGISTRY.newHistogram(ExampleRunner.class, "directory-size", false); - - public static class Job implements Runnable { - @Override - public void run() { - try { - while (!Thread.interrupted()) { - final File file = JOBS.poll(1, TimeUnit.MINUTES); - QUEUE_DEPTH.dec(); - if (file.isDirectory()) { - final List contents = new DirectoryLister(file).list(); - DIRECTORY_SIZE.update(contents.size()); - QUEUE_DEPTH.inc(contents.size()); - JOBS.addAll(contents); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - - public static void main(String[] args) throws Exception { - ConsoleReporter.enable(10, TimeUnit.SECONDS); - - System.err.println("Scanning all files on your hard drive..."); - - JOBS.add(new File("/")); - QUEUE_DEPTH.inc(); - for (int i = 0; i < WORKER_COUNT; i++) { - POOL.submit(new Job()); - } - - POOL.awaitTermination(10, TimeUnit.DAYS); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/reporting/tests/AbstractPollingReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/reporting/tests/AbstractPollingReporterTest.java deleted file mode 100644 index 71b7b5ad0f..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/reporting/tests/AbstractPollingReporterTest.java +++ /dev/null @@ -1,219 +0,0 @@ -package com.yammer.metrics.reporting.tests; - -import com.yammer.metrics.core.*; -import com.yammer.metrics.reporting.AbstractPollingReporter; -import com.yammer.metrics.stats.Snapshot; -import org.junit.Before; -import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.io.ByteArrayOutputStream; -import java.io.OutputStream; -import java.util.Arrays; -import java.util.Random; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.*; - -public abstract class AbstractPollingReporterTest { - - protected final Clock clock = mock(Clock.class); - protected AbstractPollingReporter reporter; - protected ByteArrayOutputStream out; - protected TestMetricsRegistry registry; - - @Before - public void init() throws Exception { - when(clock.getTick()).thenReturn(1234L); - when(clock.getTime()).thenReturn(5678L); - registry = new TestMetricsRegistry(); - out = new ByteArrayOutputStream(); - reporter = createReporter(registry, out, clock); - } - - protected static class TestMetricsRegistry extends MetricsRegistry { - public T add(MetricName name, T metric) { - return getOrAdd(name, metric); - } - } - - protected void assertReporterOutput(Callable action, String... expected) throws Exception { - // Invoke the callable to trigger (ie, mark()/inc()/etc) and return the metric - final T metric = action.call(); - try { - // Add the metric to the registry, run the reporter and flush the result - registry.add(new MetricName(Object.class, "metric"), metric); - reporter.run(); - out.flush(); - final String[] lines = out.toString().split("\r?\n|\r"); - // Assertions: first check that the line count matches then compare line by line ignoring leading and trailing whitespace - assertEquals("Line count mismatch, was:\n" + Arrays.toString(lines) + "\nexpected:\n" + Arrays - .toString(expected) + "\n", expected.length, - lines.length); - for (int i = 0; i < lines.length; i++) { - if (!expected[i].trim().equals(lines[i].trim())) { - System.err.println("Failure comparing line " + (1 + i)); - System.err.println("Was: '" + lines[i] + "'"); - System.err.println("Expected: '" + expected[i] + "'\n"); - } - assertEquals(expected[i].trim(), lines[i].trim()); - } - } finally { - reporter.shutdown(); - } - } - - protected abstract AbstractPollingReporter createReporter(MetricsRegistry registry, OutputStream out, Clock clock) throws Exception; - - @Test - public final void counter() throws Exception { - final long count = new Random().nextInt(Integer.MAX_VALUE); - assertReporterOutput( - new Callable() { - @Override - public Counter call() throws Exception { - return createCounter(count); - } - }, - expectedCounterResult(count)); - } - - @Test - public final void histogram() throws Exception { - assertReporterOutput( - new Callable() { - @Override - public Histogram call() throws Exception { - return createHistogram(); - } - }, - expectedHistogramResult()); - } - - @Test - public final void meter() throws Exception { - assertReporterOutput( - new Callable() { - @Override - public Meter call() throws Exception { - return createMeter(); - } - }, - expectedMeterResult()); - } - - @Test - public final void timer() throws Exception { - assertReporterOutput( - new Callable() { - @Override - public Timer call() throws Exception { - return createTimer(); - } - }, - expectedTimerResult()); - } - - @Test - public final void gauge() throws Exception { - final String value = "gaugeValue"; - assertReporterOutput( - new Callable>() { - @Override - public Gauge call() throws Exception { - return createGauge(); - } - }, - expectedGaugeResult(value)); - } - - static Counter createCounter(long count) throws Exception { - final Counter mock = mock(Counter.class); - when(mock.getCount()).thenReturn(count); - return mock; - } - - static Histogram createHistogram() throws Exception { - final Histogram mock = mock(Histogram.class); - setupSummarizableMock(mock); - setupSamplingMock(mock); - return mock; - } - - - static Gauge createGauge() throws Exception { - @SuppressWarnings("unchecked") - final Gauge mock = mock(Gauge.class); - when(mock.getValue()).thenReturn("gaugeValue"); - return mock; - } - - - static Timer createTimer() throws Exception { - final Timer mock = mock(Timer.class); - when(mock.getDurationUnit()).thenReturn(TimeUnit.MILLISECONDS); - setupSummarizableMock(mock); - setupMeteredMock(mock); - setupSamplingMock(mock); - return mock; - } - - static Meter createMeter() throws Exception { - final Meter mock = mock(Meter.class); - setupMeteredMock(mock); - return mock; - } - - static abstract class MetricsProcessorAction implements Answer { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - @SuppressWarnings("unchecked") - final MetricProcessor processor = (MetricProcessor) invocation.getArguments()[0]; - final MetricName name = (MetricName) invocation.getArguments()[1]; - final Object context = invocation.getArguments()[2]; - delegateToProcessor(processor, name, context); - return null; - } - - abstract void delegateToProcessor(MetricProcessor processor, MetricName name, Object context) throws Exception; - } - - static void setupSummarizableMock(Summarizable summarizable) { - when(summarizable.getMin()).thenReturn(1d); - when(summarizable.getMax()).thenReturn(3d); - when(summarizable.getMean()).thenReturn(2d); - when(summarizable.getStdDev()).thenReturn(1.5d); - } - - static void setupMeteredMock(Metered metered) { - when(metered.getCount()).thenReturn(1L); - when(metered.getOneMinuteRate()).thenReturn(1d); - when(metered.getFiveMinuteRate()).thenReturn(5d); - when(metered.getFifteenMinuteRate()).thenReturn(15d); - when(metered.getMeanRate()).thenReturn(2d); - when(metered.getEventType()).thenReturn("eventType"); - when(metered.getRateUnit()).thenReturn(TimeUnit.SECONDS); - } - - static void setupSamplingMock(Sampling sampling) { - final double[] values = new double[1000]; - for (int i = 0; i < values.length; i++) { - values[i] = i / 1000.0; - } - when(sampling.getSnapshot()).thenReturn(new Snapshot(values)); - } - - public abstract String[] expectedGaugeResult(String value); - - public abstract String[] expectedTimerResult(); - - public abstract String[] expectedMeterResult(); - - public abstract String[] expectedHistogramResult(); - - public abstract String[] expectedCounterResult(long count); - -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/reporting/tests/ConsoleReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/reporting/tests/ConsoleReporterTest.java deleted file mode 100644 index de35a8864b..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/reporting/tests/ConsoleReporterTest.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.yammer.metrics.reporting.tests; - -import com.yammer.metrics.core.Clock; -import com.yammer.metrics.core.MetricPredicate; -import com.yammer.metrics.core.MetricsRegistry; -import com.yammer.metrics.reporting.AbstractPollingReporter; -import com.yammer.metrics.reporting.ConsoleReporter; -import org.junit.Test; - -import java.io.OutputStream; -import java.io.PrintStream; -import java.util.Locale; -import java.util.TimeZone; -import java.util.concurrent.TimeUnit; - -import static org.junit.Assert.fail; - -public class ConsoleReporterTest extends AbstractPollingReporterTest { - - @Override - protected AbstractPollingReporter createReporter(MetricsRegistry registry, OutputStream out, Clock clock) { - return new ConsoleReporter(registry, - new PrintStream(out), - MetricPredicate.ALL, - clock, - TimeZone.getTimeZone("UTC"), - Locale.US); - } - - @Override - public String[] expectedCounterResult(long count) { - return new String[]{ - "1/1/70 12:00:05 AM =============================================================", - "java.lang.Object:", - "metric:", - "count = " + count - }; - } - - @Override - public String[] expectedHistogramResult() { - return new String[]{ - "1/1/70 12:00:05 AM =============================================================", - "java.lang.Object:", - "metric:", - "min = 1.00", - "max = 3.00", - "mean = 2.00", - "stddev = 1.50", - "median = 0.50", - "75% <= 0.75", - "95% <= 0.95", - "98% <= 0.98", - "99% <= 0.99", - "99.9% <= 1.00" - }; - } - - @Override - public String[] expectedMeterResult() { - return new String[]{ - "1/1/70 12:00:05 AM =============================================================", - "java.lang.Object:", - "metric:", - "count = 1", - "mean rate = 2.00 eventType/s", - "1-minute rate = 1.00 eventType/s", - "5-minute rate = 5.00 eventType/s", - "15-minute rate = 15.00 eventType/s" - }; - } - - @Override - public String[] expectedTimerResult() { - return new String[]{ - "1/1/70 12:00:05 AM =============================================================", - "java.lang.Object:", "" + - "metric:", - "count = 1", - "mean rate = 2.00 eventType/s", - "1-minute rate = 1.00 eventType/s", - "5-minute rate = 5.00 eventType/s", - "15-minute rate = 15.00 eventType/s", - "min = 1.00ms", - "max = 3.00ms", - "mean = 2.00ms", - "stddev = 1.50ms", - "median = 0.50ms", - "75% <= 0.75ms", - "95% <= 0.95ms", - "98% <= 0.98ms", - "99% <= 0.99ms", - "99.9% <= 1.00ms" - }; - } - - @Override - public String[] expectedGaugeResult(String value) { - return new String[]{ - "1/1/70 12:00:05 AM =============================================================", - "java.lang.Object:", - "metric:", - String.format("value = %s", value) - }; - } - - @Test - public void givenShutdownReporterWhenCreatingNewReporterExpectSuccess() { - try { - final ConsoleReporter reporter1 = new ConsoleReporter(System.out); - reporter1.start(1, TimeUnit.SECONDS); - reporter1.shutdown(); - final ConsoleReporter reporter2 = new ConsoleReporter(System.out); - reporter2.start(1, TimeUnit.SECONDS); - reporter2.shutdown(); - } catch (Exception e) { - e.printStackTrace(); - fail("should be able to start and shutdown reporters"); - } - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/reporting/tests/CsvReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/reporting/tests/CsvReporterTest.java deleted file mode 100644 index 9b9e4eb81f..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/reporting/tests/CsvReporterTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.yammer.metrics.reporting.tests; - -import com.yammer.metrics.core.Clock; -import com.yammer.metrics.core.MetricName; -import com.yammer.metrics.core.MetricPredicate; -import com.yammer.metrics.core.MetricsRegistry; -import com.yammer.metrics.reporting.AbstractPollingReporter; -import com.yammer.metrics.reporting.CsvReporter; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintStream; - -public class CsvReporterTest extends AbstractPollingReporterTest { - - @Override - protected AbstractPollingReporter createReporter(MetricsRegistry registry, final OutputStream out, Clock clock) throws Exception { - return new CsvReporter(registry, MetricPredicate.ALL, new File("/tmp"), clock) { - @Override - protected PrintStream createStreamForMetric(MetricName metricName) throws IOException { - return new PrintStream(out); - } - }; - } - - @Override - public String[] expectedCounterResult(long count) { - return new String[]{"# time,count", String.format("5,%s\n", count)}; - } - - @Override - public String[] expectedHistogramResult() { - return new String[]{"# time,min,max,mean,median,stddev,95%,99%,99.9%", - "5,1.0,3.0,2.0,0.4995,1.5,0.9499499999999999,0.98999,0.998999\n"}; - } - - @Override - public String[] expectedMeterResult() { - return new String[]{"# time,count,1 min rate,mean rate,5 min rate,15 min rate", - "5,1,1.0,2.0,5.0,15.0\n"}; - } - - @Override - public String[] expectedTimerResult() { - return new String[]{"# time,count,1 min rate,mean rate,5 min rate,15 min rate,min,max,mean,median,stddev,95%,99%,99.9%", - "5,1,1.0,2.0,5.0,15.0,1.0,3.0,2.0,0.4995,1.5,0.9499499999999999,0.98999,0.998999\n"}; - } - - @Override - public String[] expectedGaugeResult(String value) { - return new String[]{"# time,value", String.format("5,%s\n", value)}; - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/stats/tests/EWMATest.java b/metrics-core/src/test/java/com/yammer/metrics/stats/tests/EWMATest.java deleted file mode 100644 index 16b4de813b..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/stats/tests/EWMATest.java +++ /dev/null @@ -1,322 +0,0 @@ -package com.yammer.metrics.stats.tests; - -import com.yammer.metrics.stats.EWMA; -import org.junit.Test; - -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.Matchers.closeTo; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -public class EWMATest { - @Test - public void aOneMinuteEWMAWithAValueOfThree() throws Exception { - final EWMA ewma = EWMA.oneMinuteEWMA(); - ewma.update(3); - ewma.tick(); - - assertThat("the EWMA has a rate of 0.6 events/sec after the first tick", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.6, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.22072766 events/sec after 1 minute", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.22072766, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.08120117 events/sec after 2 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.08120117, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.02987224 events/sec after 3 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.02987224, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.01098938 events/sec after 4 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.01098938, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.00404277 events/sec after 5 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.00404277, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.00148725 events/sec after 6 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.00148725, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.00054713 events/sec after 7 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.00054713, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.00020128 events/sec after 8 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.00020128, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.00007405 events/sec after 9 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.00007405, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.00002724 events/sec after 10 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.00002724, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.00001002 events/sec after 11 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.00001002, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.00000369 events/sec after 12 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.00000369, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.00000136 events/sec after 13 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.00000136, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.00000050 events/sec after 14 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.00000050, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.00000018 events/sec after 15 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.00000018, 0.000001))); - } - - @Test - public void aFiveMinuteEWMAWithAValueOfThree() throws Exception { - final EWMA ewma = EWMA.fiveMinuteEWMA(); - ewma.update(3); - ewma.tick(); - - assertThat("the EWMA has a rate of 0.6 events/sec after the first tick", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.6, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.49123845 events/sec after 1 minute", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.49123845, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.40219203 events/sec after 2 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.40219203, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.32928698 events/sec after 3 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.32928698, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.26959738 events/sec after 4 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.26959738, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.22072766 events/sec after 5 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.22072766, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.18071653 events/sec after 6 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.18071653, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.14795818 events/sec after 7 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.14795818, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.12113791 events/sec after 8 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.12113791, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.09917933 events/sec after 9 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.09917933, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.08120117 events/sec after 10 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.08120117, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.06648190 events/sec after 11 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.06648190, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.05443077 events/sec after 12 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.05443077, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.04456415 events/sec after 13 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.04456415, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.03648604 events/sec after 14 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.03648604, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.02987224 events/sec after 15 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.02987224, 0.000001))); - } - - @Test - public void aFifteenMinuteEWMAWithAValueOfThree() throws Exception { - final EWMA ewma = EWMA.fifteenMinuteEWMA(); - ewma.update(3); - ewma.tick(); - - assertThat("the EWMA has a rate of 0.6 events/sec after the first tick", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.6, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.56130419 events/sec after 1 minute", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.56130419, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.52510399 events/sec after 2 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.52510399, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.49123845 events/sec after 3 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.49123845, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.45955700 events/sec after 4 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.45955700, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.42991879 events/sec after 5 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.42991879, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.40219203 events/sec after 6 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.40219203, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.37625345 events/sec after 7 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.37625345, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.35198773 events/sec after 8 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.35198773, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.32928698 events/sec after 9 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.32928698, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.30805027 events/sec after 10 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.30805027, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.28818318 events/sec after 11 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.28818318, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.26959738 events/sec after 12 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.26959738, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.25221023 events/sec after 13 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.25221023, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.23594443 events/sec after 14 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.23594443, 0.000001))); - - elapseMinute(ewma); - - assertThat("the EWMA has a rate of 0.22072766 events/sec after 15 minutes", - ewma.getRate(TimeUnit.SECONDS), - is(closeTo(0.22072766, 0.000001))); - } - - - private void elapseMinute(EWMA ewma) { - for (int i = 1; i <= 12; i++) { - ewma.tick(); - } - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/stats/tests/UniformSampleTest.java b/metrics-core/src/test/java/com/yammer/metrics/stats/tests/UniformSampleTest.java deleted file mode 100644 index f7e3bf74c5..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/stats/tests/UniformSampleTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.yammer.metrics.stats.tests; - -import com.yammer.metrics.stats.Snapshot; -import com.yammer.metrics.stats.UniformSample; -import org.junit.Test; - -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertThat; - -public class UniformSampleTest { - @Test - @SuppressWarnings("unchecked") - public void aSampleOf100OutOf1000Elements() throws Exception { - final UniformSample sample = new UniformSample(100); - for (int i = 0; i < 1000; i++) { - sample.update(i); - } - - final Snapshot snapshot = sample.getSnapshot(); - - assertThat("the sample has a size of 100", - sample.size(), - is(100)); - - assertThat("the sample has 100 elements", - snapshot.size(), - is(100)); - - for (double i : snapshot.getValues()) { - assertThat("the sample only contains elements from the population", - i, - is(allOf( - lessThan(1000.0), - greaterThanOrEqualTo(0.0) - ))); - } - } - -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/AbstractPollingReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/AbstractPollingReporterTest.java new file mode 100644 index 0000000000..41c9cd5b0b --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/AbstractPollingReporterTest.java @@ -0,0 +1,66 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.*; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.concurrent.TimeUnit; + +import static org.mockito.Mockito.*; + +public class AbstractPollingReporterTest { + private final Gauge gauge = mock(Gauge.class); + private final Counter counter = mock(Counter.class); + private final Histogram histogram = mock(Histogram.class); + private final Meter meter = mock(Meter.class); + private final Timer timer = mock(Timer.class); + + private final MetricRegistry registry = new MetricRegistry("test"); + private final Reporter underlying = mock(Reporter.class); + private final AbstractPollingReporter reporter = new AbstractPollingReporter(registry, "example") { + @Override + public void report(SortedMap gauges, + SortedMap counters, + SortedMap histograms, + SortedMap meters, + SortedMap timers) { + underlying.report(gauges, counters, histograms, meters, timers); + } + }; + + @Before + public void setUp() throws Exception { + registry.register("gauge", gauge); + registry.register("counter", counter); + registry.register("histogram", histogram); + registry.register("meter", meter); + registry.register("timer", timer); + + reporter.start(100, TimeUnit.MILLISECONDS); + } + + @After + public void tearDown() throws Exception { + reporter.stop(); + } + + @Test + public void pollsPeriodically() throws Exception { + verify(underlying, timeout(250).times(2)).report( + map("gauge", gauge), + map("counter", counter), + map("histogram", histogram), + map("meter", meter), + map("timer", timer) + ); + } + + private SortedMap map(String name, T value) { + final SortedMap map = new TreeMap(); + map.put(name, value); + return map; + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/ClockTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/ClockTest.java new file mode 100644 index 0000000000..d1b16bc94c --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/ClockTest.java @@ -0,0 +1,43 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.Clock; +import org.junit.Test; + +import java.lang.management.ManagementFactory; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.fest.assertions.data.Offset.offset; + +public class ClockTest { + @Test + public void cpuTimeClock() throws Exception { + final Clock.CpuTimeClock clock = new Clock.CpuTimeClock(); + + assertThat((double) clock.getTime()) + .isEqualTo(System.currentTimeMillis(), + offset(100.0)); + + assertThat((double) clock.getTick()) + .isEqualTo(ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime(), + offset(1000000.0)); + } + + @Test + public void userTimeClock() throws Exception { + final Clock.UserTimeClock clock = new Clock.UserTimeClock(); + + assertThat((double) clock.getTime()) + .isEqualTo(System.currentTimeMillis(), + offset(100.0)); + + assertThat((double) clock.getTick()) + .isEqualTo(System.nanoTime(), + offset(100000.0)); + } + + @Test + public void defaultsToUserTime() throws Exception { + assertThat(Clock.defaultClock()) + .isInstanceOf(Clock.UserTimeClock.class); + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/CounterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/CounterTest.java new file mode 100644 index 0000000000..e75daaeae1 --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/CounterTest.java @@ -0,0 +1,57 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.Counter; +import org.junit.Test; + +import static org.fest.assertions.api.Assertions.assertThat; + +public class CounterTest { + private final Counter counter = new Counter(); + + @Test + public void startsAtZero() throws Exception { + assertThat(counter.getCount()) + .isZero(); + } + + @Test + public void incrementsByOne() throws Exception { + counter.inc(); + + assertThat(counter.getCount()) + .isEqualTo(1); + } + + @Test + public void incrementsByAnArbitraryDelta() throws Exception { + counter.inc(12); + + assertThat(counter.getCount()) + .isEqualTo(12); + } + + @Test + public void decrementsByOne() throws Exception { + counter.dec(); + + assertThat(counter.getCount()) + .isEqualTo(-1); + } + + @Test + public void decrementsByAnArbitraryDelta() throws Exception { + counter.dec(12); + + assertThat(counter.getCount()) + .isEqualTo(-12); + } + + @Test + public void isZeroAfterBeingCleared() throws Exception { + counter.inc(3); + counter.clear(); + + assertThat(counter.getCount()) + .isZero(); + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/EWMATest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/EWMATest.java new file mode 100644 index 0000000000..5a283277c4 --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/EWMATest.java @@ -0,0 +1,225 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.EWMA; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.fest.assertions.data.Offset.offset; + +public class EWMATest { + @Test + public void aOneMinuteEWMAWithAValueOfThree() throws Exception { + final EWMA ewma = EWMA.oneMinuteEWMA(); + ewma.update(3); + ewma.tick(); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.6, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.22072766, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.08120117, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.02987224, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.01098938, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00404277, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00148725, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00054713, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00020128, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00007405, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00002724, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00001002, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00000369, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00000136, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00000050, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00000018, offset(0.000001)); + } + + @Test + public void aFiveMinuteEWMAWithAValueOfThree() throws Exception { + final EWMA ewma = EWMA.fiveMinuteEWMA(); + ewma.update(3); + ewma.tick(); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.6, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.49123845, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.40219203, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.32928698, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.26959738, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.22072766, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.18071653, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.14795818, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.12113791, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.09917933, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.08120117, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.06648190, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.05443077, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.04456415, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.03648604, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.02987224, offset(0.000001)); + } + + @Test + public void aFifteenMinuteEWMAWithAValueOfThree() throws Exception { + final EWMA ewma = EWMA.fifteenMinuteEWMA(); + ewma.update(3); + ewma.tick(); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.6, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.56130419, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.52510399, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.49123845, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.45955700, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.42991879, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.40219203, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.37625345, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.35198773, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.32928698, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.30805027, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.28818318, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.26959738, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.25221023, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.23594443, offset(0.000001)); + + elapseMinute(ewma); + + assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.22072766, offset(0.000001)); + } + + + private void elapseMinute(EWMA ewma) { + for (int i = 1; i <= 12; i++) { + ewma.tick(); + } + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/stats/tests/ExponentiallyDecayingSampleTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/ExponentiallyDecayingSampleTest.java similarity index 52% rename from metrics-core/src/test/java/com/yammer/metrics/stats/tests/ExponentiallyDecayingSampleTest.java rename to metrics-core/src/test/java/com/yammer/metrics/tests/ExponentiallyDecayingSampleTest.java index 7e5761d15f..39ccfcef26 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/stats/tests/ExponentiallyDecayingSampleTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/ExponentiallyDecayingSampleTest.java @@ -1,46 +1,34 @@ -package com.yammer.metrics.stats.tests; +package com.yammer.metrics.tests; -import com.yammer.metrics.core.Clock; -import com.yammer.metrics.stats.ExponentiallyDecayingSample; -import com.yammer.metrics.stats.Snapshot; +import com.yammer.metrics.Clock; +import com.yammer.metrics.ExponentiallyDecayingSample; +import com.yammer.metrics.Snapshot; import org.junit.Test; import java.util.concurrent.TimeUnit; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertThat; +import static org.fest.assertions.api.Assertions.assertThat; public class ExponentiallyDecayingSampleTest { @Test - @SuppressWarnings("unchecked") public void aSampleOf100OutOf1000Elements() throws Exception { final ExponentiallyDecayingSample sample = new ExponentiallyDecayingSample(100, 0.99); for (int i = 0; i < 1000; i++) { sample.update(i); } - assertThat("the sample has a size of 100", - sample.size(), - is(100)); + assertThat(sample.size()) + .isEqualTo(100); final Snapshot snapshot = sample.getSnapshot(); - assertThat("the sample has 100 elements", - snapshot.size(), - is(100)); - - for (double i : snapshot.getValues()) { - assertThat("the sample only contains elements from the population", - i, - is(allOf( - lessThan(1000.0), - greaterThanOrEqualTo(0.0) - ))); - } + assertThat(snapshot.size()) + .isEqualTo(100); + + assertAllValuesBetween(sample, 0, 1000); } @Test - @SuppressWarnings("unchecked") public void aSampleOf100OutOf10Elements() throws Exception { final ExponentiallyDecayingSample sample = new ExponentiallyDecayingSample(100, 0.99); for (int i = 0; i < 10; i++) { @@ -49,26 +37,16 @@ public void aSampleOf100OutOf10Elements() throws Exception { final Snapshot snapshot = sample.getSnapshot(); - assertThat("the sample has a size of 10", - snapshot.size(), - is(10)); - - assertThat("the sample has 10 elements", - snapshot.size(), - is(10)); - - for (double i : snapshot.getValues()) { - assertThat("the sample only contains elements from the population", - i, - is(allOf( - lessThan(10.0), - greaterThanOrEqualTo(0.0) - ))); - } + assertThat(snapshot.size()) + .isEqualTo(10); + + assertThat(snapshot.size()) + .isEqualTo(10); + + assertAllValuesBetween(sample, 0, 10); } @Test - @SuppressWarnings("unchecked") public void aHeavilyBiasedSampleOf100OutOf1000Elements() throws Exception { final ExponentiallyDecayingSample sample = new ExponentiallyDecayingSample(1000, 0.01); for (int i = 0; i < 100; i++) { @@ -76,24 +54,15 @@ public void aHeavilyBiasedSampleOf100OutOf1000Elements() throws Exception { } - assertThat("the sample has a size of 100", - sample.size(), - is(100)); + assertThat(sample.size()) + .isEqualTo(100); final Snapshot snapshot = sample.getSnapshot(); - assertThat("the sample has 100 elements", - snapshot.size(), - is(100)); - - for (double i : snapshot.getValues()) { - assertThat("the sample only contains elements from the population", - i, - is(allOf( - lessThan(100.0), - greaterThanOrEqualTo(0.0) - ))); - } + assertThat(snapshot.size()) + .isEqualTo(100); + + assertAllValuesBetween(sample, 0, 100); } @Test @@ -108,7 +77,8 @@ public void longPeriodsOfInactivityShouldNotCorruptSamplingState() { sample.update(1000 + i); clock.addMillis(100); } - assertThat("the sample has 10 elements", sample.getSnapshot().size(), is(10)); + assertThat(sample.getSnapshot().size()) + .isEqualTo(10); assertAllValuesBetween(sample, 1000, 2000); // wait for 15 hours and add another value. @@ -117,7 +87,8 @@ public void longPeriodsOfInactivityShouldNotCorruptSamplingState() { // zero after rescale. clock.addHours(15); sample.update(2000); - assertThat("the sample has 2 elements", sample.getSnapshot().size(), is(2)); + assertThat(sample.getSnapshot().size()) + .isEqualTo(2); assertAllValuesBetween(sample, 1000, 3000); @@ -126,24 +97,18 @@ public void longPeriodsOfInactivityShouldNotCorruptSamplingState() { sample.update(3000 + i); clock.addMillis(100); } - assertThat("the sample has 10 elements", sample.getSnapshot().size(), is(10)); + assertThat(sample.getSnapshot().size()) + .isEqualTo(10); assertAllValuesBetween(sample, 3000, 4000); - - } - @SuppressWarnings("unchecked") - private void assertAllValuesBetween(ExponentiallyDecayingSample sample, + private static void assertAllValuesBetween(ExponentiallyDecayingSample sample, double min, double max) { for (double i : sample.getSnapshot().getValues()) { - assertThat("the sample only contains elements from the population", - i, - is(allOf( - lessThan(max), - greaterThanOrEqualTo(min) - ))); + assertThat(i) + .isLessThan(max) + .isGreaterThanOrEqualTo(min); } - } class ManualClock extends Clock { @@ -166,7 +131,5 @@ public long getTick() { public long getTime() { return TimeUnit.NANOSECONDS.toMillis(ticksInNanos); } - } - } diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/HealthChecksTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/HealthChecksTest.java deleted file mode 100644 index 0433c8393d..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/HealthChecksTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.yammer.metrics.tests; - -import com.yammer.metrics.HealthChecks; -import com.yammer.metrics.core.HealthCheck; -import com.yammer.metrics.core.HealthCheckRegistry; -import org.junit.Before; -import org.junit.Test; - -import java.util.Map; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -public class HealthChecksTest { - private final HealthCheckRegistry registry = HealthChecks.defaultRegistry(); - - private static class ExampleHealthCheck extends HealthCheck { - private ExampleHealthCheck() { - super("example"); - } - - @Override - protected Result check() throws Exception { - return Result.healthy("whee"); - } - } - - @Before - public void setUp() throws Exception { - registry.register(new ExampleHealthCheck()); - } - - @Test - public void runsRegisteredHealthChecks() throws Exception { - final Map results = registry.runHealthChecks(); - - assertThat(results.get("example"), - is(HealthCheck.Result.healthy("whee"))); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java new file mode 100644 index 0000000000..7c19bea1f4 --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java @@ -0,0 +1,72 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.Snapshot; +import com.yammer.metrics.Histogram; +import com.yammer.metrics.MetricRegistry; +import org.junit.Test; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.fest.assertions.api.Assertions.offset; + +public class HistogramTest { + private final MetricRegistry registry = new MetricRegistry("test"); + private final Histogram histogram = registry.histogram("histogram"); + + @Test + public void anEmptyHistogram() throws Exception { + assertThat(histogram.getCount()) + .isEqualTo(0L); + + assertThat(histogram.getMax()).isEqualTo(0.0, offset(0.0001)); + + assertThat(histogram.getMin()).isEqualTo(0.0, offset(0.0001)); + + assertThat(histogram.getMean()).isEqualTo(0.0, offset(0.0001)); + + assertThat(histogram.getStdDev()).isEqualTo(0.0, offset(0.0001)); + + assertThat(histogram.getSum()).isEqualTo(0.0, offset(0.0001)); + + final Snapshot snapshot = histogram.getSnapshot(); + + assertThat(snapshot.getMedian()).isEqualTo(0.0, offset(0.0001)); + + assertThat(snapshot.get75thPercentile()).isEqualTo(0.0, offset(0.0001)); + + assertThat(snapshot.get99thPercentile()).isEqualTo(0.0, offset(0.0001)); + + assertThat(snapshot.size()) + .isEqualTo(0); + } + + @Test + public void aHistogramWith1000Elements() throws Exception { + for (int i = 1; i <= 1000; i++) { + histogram.update(i); + } + + assertThat(histogram.getCount()) + .isEqualTo(1000L); + + assertThat(histogram.getMax()).isEqualTo(1000.0, offset(0.0001)); + + assertThat(histogram.getMin()).isEqualTo(1.0, offset(0.0001)); + + assertThat(histogram.getMean()).isEqualTo(500.5, offset(0.0001)); + + assertThat(histogram.getStdDev()).isEqualTo(288.8194360957494, offset(0.0001)); + + assertThat(histogram.getSum()).isEqualTo(500500, offset(0.1)); + + final Snapshot snapshot = histogram.getSnapshot(); + + assertThat(snapshot.getMedian()).isEqualTo(500.5, offset(0.0001)); + + assertThat(snapshot.get75thPercentile()).isEqualTo(750.75, offset(0.0001)); + + assertThat(snapshot.get99thPercentile()).isEqualTo(990.99, offset(0.0001)); + + assertThat(snapshot.size()) + .isEqualTo(1000); + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MeterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/MeterTest.java new file mode 100644 index 0000000000..8d68910984 --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/MeterTest.java @@ -0,0 +1,30 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.Meter; +import com.yammer.metrics.MetricRegistry; +import org.junit.Test; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.fest.assertions.api.Assertions.offset; + +public class MeterTest { + private final MetricRegistry registry = new MetricRegistry("test"); + private final Meter meter = registry.meter("things"); + + @Test + public void aBlankMeter() throws Exception { + assertThat(meter.getCount()) + .isZero(); + + assertThat(meter.getMeanRate()) + .isEqualTo(0.0, offset(0.001)); + } + + @Test + public void aMeterWithThreeEvents() throws Exception { + meter.mark(3); + + assertThat(meter.getCount()) + .isEqualTo(3); + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java new file mode 100644 index 0000000000..c05e0f0de5 --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java @@ -0,0 +1,232 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.*; +import org.junit.Before; +import org.junit.Test; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.fest.assertions.data.MapEntry.entry; +import static org.mockito.Mockito.*; + +public class MetricRegistryTest { + private final MetricRegistryListener listener = mock(MetricRegistryListener.class); + private final MetricRegistry registry = new MetricRegistry("name"); + @SuppressWarnings("unchecked") + private final Gauge gauge = mock(Gauge.class); + private final Counter counter = mock(Counter.class); + private final Histogram histogram = mock(Histogram.class); + private final Meter meter = mock(Meter.class); + private final Timer timer = mock(Timer.class); + + @Before + public void setUp() throws Exception { + registry.addListener(listener); + } + + @Test + public void hasAName() throws Exception { + assertThat(registry.getName()) + .isEqualTo("name"); + } + + @Test + public void registeringAGaugeTriggersANotification() throws Exception { + assertThat(registry.register("thing", gauge)) + .isEqualTo(gauge); + + verify(listener).onGaugeAdded("thing", gauge); + } + + @Test + public void removingAGaugeTriggersANotification() throws Exception { + registry.register("thing", gauge); + + assertThat(registry.remove("thing")) + .isTrue(); + + verify(listener).onGaugeRemoved("thing"); + } + + @Test + public void registeringACounterTriggersANotification() throws Exception { + assertThat(registry.register("thing", counter)) + .isEqualTo(counter); + + verify(listener).onCounterAdded("thing", counter); + } + + @Test + public void accessingACounterRegistersAndReusesTheCounter() throws Exception { + final Counter counter1 = registry.counter("thing"); + final Counter counter2 = registry.counter("thing"); + + assertThat(counter1) + .isSameAs(counter2); + + verify(listener).onCounterAdded("thing", counter1); + } + + @Test + public void removingACounterTriggersANotification() throws Exception { + registry.register("thing", counter); + + assertThat(registry.remove("thing")) + .isTrue(); + + verify(listener).onCounterRemoved("thing"); + } + + @Test + public void registeringAHistogramTriggersANotification() throws Exception { + assertThat(registry.register("thing", histogram)) + .isEqualTo(histogram); + + verify(listener).onHistogramAdded("thing", histogram); + } + + @Test + public void accessingAHistogramRegistersAndReusesIt() throws Exception { + final Histogram histogram1 = registry.histogram("thing"); + final Histogram histogram2 = registry.histogram("thing"); + + assertThat(histogram1) + .isSameAs(histogram2); + + verify(listener).onHistogramAdded("thing", histogram1); + } + + @Test + public void removingAHistogramTriggersANotification() throws Exception { + registry.register("thing", histogram); + + assertThat(registry.remove("thing")) + .isTrue(); + + verify(listener).onHistogramRemoved("thing"); + } + + @Test + public void registeringAMeterTriggersANotification() throws Exception { + assertThat(registry.register("thing", meter)) + .isEqualTo(meter); + + verify(listener).onMeterAdded("thing", meter); + } + + @Test + public void accessingAMeterRegistersAndReusesIt() throws Exception { + final Meter meter1 = registry.meter("thing"); + final Meter meter2 = registry.meter("thing"); + + assertThat(meter1) + .isSameAs(meter2); + + verify(listener).onMeterAdded("thing", meter1); + } + + @Test + public void removingAMeterTriggersANotification() throws Exception { + registry.register("thing", meter); + + assertThat(registry.remove("thing")) + .isTrue(); + + verify(listener).onMeterRemoved("thing"); + } + + @Test + public void registeringATimerTriggersANotification() throws Exception { + assertThat(registry.register("thing", timer)) + .isEqualTo(timer); + + verify(listener).onTimerAdded("thing", timer); + } + + @Test + public void accessingATimerRegistersAndReusesIt() throws Exception { + final Timer timer1 = registry.timer("thing"); + final Timer timer2 = registry.timer("thing"); + + assertThat(timer1) + .isSameAs(timer2); + + verify(listener).onTimerAdded("thing", timer1); + } + + @Test + public void removingATimerTriggersANotification() throws Exception { + registry.register("thing", timer); + + assertThat(registry.remove("thing")) + .isTrue(); + + verify(listener).onTimerRemoved("thing"); + } + + @Test + public void addingAListenerWithExistingMetricsCatchesItUp() throws Exception { + registry.register("gauge", gauge); + registry.register("counter", counter); + registry.register("histogram", histogram); + registry.register("meter", meter); + registry.register("timer", timer); + + final MetricRegistryListener other = mock(MetricRegistryListener.class); + registry.addListener(other); + + verify(other).onGaugeAdded("gauge", gauge); + verify(other).onCounterAdded("counter", counter); + verify(other).onHistogramAdded("histogram", histogram); + verify(other).onMeterAdded("meter", meter); + verify(other).onTimerAdded("timer", timer); + } + + @Test + public void aRemovedListenerDoesNotReceiveUpdates() throws Exception { + registry.register("gauge", gauge); + registry.removeListener(listener); + registry.register("gauge2", gauge); + + verify(listener, never()).onGaugeAdded("gauge2", gauge); + } + + @Test + public void hasAMapOfRegisteredGauges() throws Exception { + registry.register("gauge", gauge); + + assertThat(registry.getGauges()) + .contains(entry("gauge", gauge)); + } + + @Test + public void hasAMapOfRegisteredCounters() throws Exception { + registry.register("counter", counter); + + assertThat(registry.getCounters()) + .contains(entry("counter", counter)); + } + + @Test + public void hasAMapOfRegisteredHistograms() throws Exception { + registry.register("histogram", histogram); + + assertThat(registry.getHistograms()) + .contains(entry("histogram", histogram)); + } + + @Test + public void hasAMapOfRegisteredMeters() throws Exception { + registry.register("meter", meter); + + assertThat(registry.getMeters()) + .contains(entry("meter", meter)); + } + + @Test + public void hasAMapOfRegisteredTimers() throws Exception { + registry.register("timer", timer); + + assertThat(registry.getTimers()) + .contains(entry("timer", timer)); + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MovingWindowSampleTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/MovingWindowSampleTest.java new file mode 100644 index 0000000000..20529aa5e2 --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/MovingWindowSampleTest.java @@ -0,0 +1,30 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.MovingWindowSample; +import org.junit.Test; + +import static org.fest.assertions.api.Assertions.assertThat; + +public class MovingWindowSampleTest { + private final MovingWindowSample sample = new MovingWindowSample(3); + + @Test + public void handlesSmallSamples() throws Exception { + sample.update(1); + sample.update(2); + + assertThat(sample.getSnapshot().getValues()) + .containsOnly(1, 2); + } + + @Test + public void onlyKeepsTheMostRecentFromBigSamples() throws Exception { + sample.update(1); + sample.update(2); + sample.update(3); + sample.update(4); + + assertThat(sample.getSnapshot().getValues()) + .containsOnly(2, 3, 4); + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/stats/tests/SnapshotTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java similarity index 54% rename from metrics-core/src/test/java/com/yammer/metrics/stats/tests/SnapshotTest.java rename to metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java index b42e686f02..29a8163ca4 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/stats/tests/SnapshotTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java @@ -1,87 +1,80 @@ -package com.yammer.metrics.stats.tests; +package com.yammer.metrics.tests; -import com.yammer.metrics.stats.Snapshot; +import com.yammer.metrics.Snapshot; import org.junit.Test; import java.util.ArrayList; import java.util.List; import static java.util.Arrays.asList; -import static org.hamcrest.Matchers.closeTo; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; +import static org.fest.assertions.api.Assertions.assertThat; +import static org.fest.assertions.api.Assertions.offset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; public class SnapshotTest { - private final Snapshot snapshot = new Snapshot(new double[]{5, 1, 2, 3, 4}); + private final Snapshot snapshot = new Snapshot(new long[]{5, 1, 2, 3, 4}); @Test public void smallQuantilesAreTheFirstValue() throws Exception { - assertThat(snapshot.getValue(0.0), - is(closeTo(1, 0.1))); + assertThat(snapshot.getValue(0.0)) + .isEqualTo(1, offset(0.1)); } @Test public void bigQuantilesAreTheLastValue() throws Exception { - assertThat(snapshot.getValue(1.0), - is(closeTo(5, 0.1))); + assertThat(snapshot.getValue(1.0)) + .isEqualTo(5, offset(0.1)); } @Test public void hasAMedian() throws Exception { - assertThat(snapshot.getMedian(), - is(closeTo(3, 0.1))); + assertThat(snapshot.getMedian()).isEqualTo(3, offset(0.1)); } @Test public void hasAp75() throws Exception { - assertThat(snapshot.get75thPercentile(), - is(closeTo(4.5, 0.1))); + assertThat(snapshot.get75thPercentile()).isEqualTo(4.5, offset(0.1)); } @Test public void hasAp95() throws Exception { - assertThat(snapshot.get95thPercentile(), - is(closeTo(5.0, 0.1))); + assertThat(snapshot.get95thPercentile()).isEqualTo(5.0, offset(0.1)); } @Test public void hasAp98() throws Exception { - assertThat(snapshot.get98thPercentile(), - is(closeTo(5.0, 0.1))); + assertThat(snapshot.get98thPercentile()).isEqualTo(5.0, offset(0.1)); } @Test public void hasAp99() throws Exception { - assertThat(snapshot.get99thPercentile(), - is(closeTo(5.0, 0.1))); + assertThat(snapshot.get99thPercentile()).isEqualTo(5.0, offset(0.1)); } @Test public void hasAp999() throws Exception { - assertThat(snapshot.get999thPercentile(), - is(closeTo(5.0, 0.1))); + assertThat(snapshot.get999thPercentile()).isEqualTo(5.0, offset(0.1)); } @Test public void hasValues() throws Exception { - assertThat(snapshot.getValues(), - is(new double[]{1, 2, 3, 4, 5})); + assertThat(snapshot.getValues()) + .containsOnly(1, 2, 3, 4, 5); } @Test public void hasASize() throws Exception { - assertThat(snapshot.size(), - is(5)); + assertThat(snapshot.size()) + .isEqualTo(5); } @Test public void canAlsoBeCreatedFromACollectionOfLongs() throws Exception { final Snapshot other = new Snapshot(asList(5L, 1L, 2L, 3L, 4L)); - assertThat(other.getValues(), - is(new double[]{1.0, 2.0, 3.0, 4.0, 5.0})); + assertThat(other.getValues()) + .containsOnly(1, 2, 3, 4, 5); } @Test @@ -96,8 +89,8 @@ public void worksWithUnderestimatedCollections() throws Exception { final Snapshot other = new Snapshot(longs); - assertThat(other.getValues(), - is(new double[]{ 1.0, 2.0, 3.0, 4.0, 5.0 })); + assertThat(other.getValues()) + .containsOnly(1, 2, 3, 4, 5); } @Test @@ -112,7 +105,7 @@ public void worksWithOverestimatedCollections() throws Exception { final Snapshot other = new Snapshot(longs); - assertThat(other.getValues(), - is(new double[]{ 1.0, 2.0, 3.0, 4.0, 5.0 })); + assertThat(other.getValues()) + .containsOnly(1, 2, 3, 4, 5); } } diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java new file mode 100644 index 0000000000..f7efac1a6a --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java @@ -0,0 +1,127 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.MetricRegistry; +import com.yammer.metrics.Snapshot; +import com.yammer.metrics.Timer; +import com.yammer.metrics.Clock; +import org.junit.Test; + +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.fest.assertions.api.Assertions.offset; + +public class TimerTest { + private final MetricRegistry registry = new MetricRegistry("test", new Clock() { + // a mock clock that increments its ticker by 50msec per call + private long val = 0; + + @Override + public long getTick() { + return val += 50000000; + } + }); + private final Timer timer = registry.timer("timer"); + + @Test + public void aBlankTimer() throws Exception { + assertThat(timer.getCount()) + .isZero(); + + assertThat(timer.getMax()).isEqualTo(0.0, offset(0.001)); + + assertThat(timer.getMin()).isEqualTo(0.0, offset(0.001)); + + assertThat(timer.getMean()).isEqualTo(0.0, offset(0.001)); + + assertThat(timer.getStdDev()).isEqualTo(0.0, offset(0.001)); + + final Snapshot snapshot = timer.getSnapshot(); + + assertThat(snapshot.getMedian()).isEqualTo(0.0, offset(0.001)); + + assertThat(snapshot.get75thPercentile()).isEqualTo(0.0, offset(0.001)); + + assertThat(snapshot.get99thPercentile()).isEqualTo(0.0, offset(0.001)); + + assertThat(timer.getMeanRate()).isEqualTo(0.0, offset(0.001)); + + assertThat(timer.getOneMinuteRate()).isEqualTo(0.0, offset(0.001)); + + assertThat(timer.getFiveMinuteRate()).isEqualTo(0.0, offset(0.001)); + + assertThat(timer.getFifteenMinuteRate()).isEqualTo(0.0, offset(0.001)); + + assertThat(timer.getSnapshot().size()) + .isZero(); + } + + @Test + public void timingASeriesOfEvents() throws Exception { + timer.update(10, TimeUnit.MILLISECONDS); + timer.update(20, TimeUnit.MILLISECONDS); + timer.update(20, TimeUnit.MILLISECONDS); + timer.update(30, TimeUnit.MILLISECONDS); + timer.update(40, TimeUnit.MILLISECONDS); + + assertThat(timer.getCount()) + .isEqualTo(5); + + assertThat(timer.getMax()).isEqualTo(4e7, offset(0.001)); + + assertThat(timer.getMin()).isEqualTo(1e7, offset(0.001)); + + assertThat(timer.getMean()).isEqualTo(2.4e7, offset(0.001)); + + assertThat(timer.getStdDev()).isEqualTo(1.14e7, offset(10000.0)); + + final Snapshot snapshot = timer.getSnapshot(); + + assertThat(snapshot.getMedian()).isEqualTo(2e7, offset(0.001)); + + assertThat(snapshot.get75thPercentile()).isEqualTo(3.5e7, offset(0.001)); + + assertThat(snapshot.get99thPercentile()).isEqualTo(4e7, offset(0.001)); + + assertThat(timer.getSnapshot().getValues()) + .containsOnly(10000000, 20000000, 20000000, 30000000, 40000000); + } + + @Test + public void timingVariantValues() throws Exception { + timer.update(Long.MAX_VALUE, TimeUnit.NANOSECONDS); + timer.update(0, TimeUnit.NANOSECONDS); + + assertThat(timer.getStdDev()) + .isEqualTo(6.521908912666392E18, offset(0.001)); + } + + @Test + public void timingCallableInstances() throws Exception { + final String value = timer.time(new Callable() { + @Override + public String call() throws Exception { + return "one"; + } + }); + + assertThat(timer.getCount()) + .isEqualTo(1); + + assertThat(value) + .isEqualTo("one"); + + assertThat(timer.getMax()).isEqualTo(5e7, offset(0.001)); + } + + @Test + public void timingContexts() throws Exception { + timer.time().stop(); + + assertThat(timer.getCount()) + .isEqualTo(1); + + assertThat(timer.getMax()).isEqualTo(5e7, offset(0.001)); + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/UniformSampleTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/UniformSampleTest.java new file mode 100644 index 0000000000..9c5923d225 --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/UniformSampleTest.java @@ -0,0 +1,33 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.UniformSample; +import com.yammer.metrics.Snapshot; +import org.junit.Test; + +import static org.fest.assertions.api.Assertions.assertThat; + +public class UniformSampleTest { + @Test + @SuppressWarnings("unchecked") + public void aSampleOf100OutOf1000Elements() throws Exception { + final UniformSample sample = new UniformSample(100); + for (int i = 0; i < 1000; i++) { + sample.update(i); + } + + final Snapshot snapshot = sample.getSnapshot(); + + assertThat(sample.size()) + .isEqualTo(100); + + assertThat(snapshot.size()) + .isEqualTo(100); + + for (double i : snapshot.getValues()) { + assertThat(i) + .isLessThan(1000) + .isGreaterThanOrEqualTo(0); + } + } + +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/util/tests/DeadlockHealthCheckTest.java b/metrics-core/src/test/java/com/yammer/metrics/util/tests/DeadlockHealthCheckTest.java deleted file mode 100644 index 5618db3b86..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/util/tests/DeadlockHealthCheckTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.yammer.metrics.util.tests; - -import com.yammer.metrics.util.DeadlockHealthCheck; -import com.yammer.metrics.core.HealthCheck; -import com.yammer.metrics.core.VirtualMachineMetrics; -import org.junit.Test; - -import java.util.HashSet; -import java.util.Set; - -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class DeadlockHealthCheckTest { - private final VirtualMachineMetrics vm = mock(VirtualMachineMetrics.class); - private final DeadlockHealthCheck healthCheck = new DeadlockHealthCheck(vm); - - @Test - public void hasAName() throws Exception { - assertThat(healthCheck.getName(), - is("deadlocks")); - } - - @Test - public void returnsHealthyIfNoDeadlocks() throws Exception { - when(vm.getDeadlockedThreads()).thenReturn(new HashSet()); - - assertThat(healthCheck.execute(), - is(HealthCheck.Result.healthy())); - } - - @Test - public void returnsUnhealthyIfDeadlocks() throws Exception { - final Set threads = new HashSet(); - threads.add("thread1"); - threads.add("thread2"); - - when(vm.getDeadlockedThreads()).thenReturn(threads); - - assertThat(healthCheck.execute(), - is(HealthCheck.Result.unhealthy("Deadlocked threads detected:\n" + - "thread1\n" + - "thread2\n"))); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/util/tests/DeathRattleExceptionHandlerTest.java b/metrics-core/src/test/java/com/yammer/metrics/util/tests/DeathRattleExceptionHandlerTest.java deleted file mode 100644 index be1324090e..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/util/tests/DeathRattleExceptionHandlerTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.yammer.metrics.util.tests; - -import com.yammer.metrics.core.Counter; -import com.yammer.metrics.util.DeathRattleExceptionHandler; -import org.junit.Test; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -public class DeathRattleExceptionHandlerTest { - private final Counter counter = mock(Counter.class); - private final DeathRattleExceptionHandler handler = new DeathRattleExceptionHandler(counter); - - @Test - public void incrementsTheCounterWhenAnExceptionIsThrown() throws Exception { - final Throwable e = new Throwable(); - - handler.uncaughtException(Thread.currentThread(), e); - - verify(counter).inc(); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/util/tests/JmxGaugeTest.java b/metrics-core/src/test/java/com/yammer/metrics/util/tests/JmxGaugeTest.java deleted file mode 100644 index c6c4bc583f..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/util/tests/JmxGaugeTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.yammer.metrics.util.tests; - -import com.yammer.metrics.util.JmxGauge; -import org.junit.Before; -import org.junit.Test; - -import static java.lang.management.ManagementFactory.getCompilationMXBean; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -public class JmxGaugeTest { - private JmxGauge gauge; - - @Before - public void setUp() throws Exception { - this.gauge = new JmxGauge("java.lang:type=Compilation", - "CompilationTimeMonitoringSupported"); - } - - @Test - public void queriesJmxForGaugeValues() throws Exception { - assertThat(gauge.getValue(), - is((Object) getCompilationMXBean().isCompilationTimeMonitoringSupported())); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/util/tests/PercentGaugeTest.java b/metrics-core/src/test/java/com/yammer/metrics/util/tests/PercentGaugeTest.java deleted file mode 100644 index a39c411d08..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/util/tests/PercentGaugeTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.yammer.metrics.util.tests; - -import com.yammer.metrics.util.PercentGauge; -import org.junit.Test; - -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; - -public class PercentGaugeTest { - @Test - public void returnsAPercentage() throws Exception { - final PercentGauge gauge = new PercentGauge() { - @Override - protected double getNumerator() { - return 2; - } - - @Override - protected double getDenominator() { - return 4; - } - }; - - assertThat(gauge.getValue(), - is(50.0)); - } - - @Test - public void handlesNaN() throws Exception { - final PercentGauge gauge = new PercentGauge() { - @Override - protected double getNumerator() { - return 2; - } - - @Override - protected double getDenominator() { - return 0; - } - }; - - assertThat(gauge.getValue(), - is(Double.NaN)); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/util/tests/RatioGaugeTest.java b/metrics-core/src/test/java/com/yammer/metrics/util/tests/RatioGaugeTest.java deleted file mode 100644 index 2bb6bd2598..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/util/tests/RatioGaugeTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.yammer.metrics.util.tests; - -import com.yammer.metrics.util.RatioGauge; -import org.junit.Test; - -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; - -public class RatioGaugeTest { - @Test - public void calculatesTheRatioOfTheNumeratorToTheDenominator() throws Exception { - final RatioGauge regular = new RatioGauge() { - @Override - protected double getNumerator() { - return 2; - } - - @Override - protected double getDenominator() { - return 4; - } - }; - - assertThat(regular.getValue(), - is(0.5)); - } - - @Test - public void handlesDivideByZeroIssues() throws Exception { - final RatioGauge divByZero = new RatioGauge() { - @Override - protected double getNumerator() { - return 100; - } - - @Override - protected double getDenominator() { - return 0; - } - }; - - assertThat(divByZero.getValue(), - is(Double.NaN)); - } - - @Test - public void handlesInfiniteDenominators() throws Exception { - final RatioGauge infinite = new RatioGauge() { - @Override - protected double getNumerator() { - return 10; - } - - @Override - protected double getDenominator() { - return Double.POSITIVE_INFINITY; - } - }; - - assertThat(infinite.getValue(), - is(Double.NaN)); - } - - @Test - public void handlesNaNDenominators() throws Exception { - final RatioGauge nan = new RatioGauge() { - @Override - protected double getNumerator() { - return 10; - } - - @Override - protected double getDenominator() { - return Double.NaN; - } - }; - - assertThat(nan.getValue(), - is(Double.NaN)); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/util/tests/ToggleGaugeTest.java b/metrics-core/src/test/java/com/yammer/metrics/util/tests/ToggleGaugeTest.java deleted file mode 100644 index 1a5da8116c..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/util/tests/ToggleGaugeTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.yammer.metrics.util.tests; - -import com.yammer.metrics.util.ToggleGauge; -import org.junit.Test; - -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; - -public class ToggleGaugeTest { - private final ToggleGauge toggle = new ToggleGauge(); - - @Test - public void returnsOneThenZero() throws Exception { - assertThat(toggle.getValue(), - is(1)); - - assertThat(toggle.getValue(), - is(0)); - - assertThat(toggle.getValue(), - is(0)); - - assertThat(toggle.getValue(), - is(0)); - } -} diff --git a/metrics-core/src/test/resources/recency-bias-graph.r b/metrics-core/src/test/resources/recency-bias-graph.r deleted file mode 100755 index fc9c4b29cd..0000000000 --- a/metrics-core/src/test/resources/recency-bias-graph.r +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env Rscript --vanilla -if (!library("getopt", character.only = TRUE, logical.return = TRUE)) { - install.packages("getopt", repos = "http://lib.stat.cmu.edu/R/CRAN") -} -require("getopt") - -# Setup parameters for the script -params = matrix(c( - 'help', 'h', 0, "logical", - 'input', 'i', 2, "character" - ), ncol=4, byrow=TRUE) - -# Parse the parameters -opt = getopt(params) - -data <- read.csv(file=opt$input,head=TRUE,sep=",") -plot(data$t, data$exponential.mean, "l", xlab="Time", ylab="Mean", col="tomato") -lines(data$expected.exponential.mean, col="tomato4") -lines(data$uniform.mean, col="violetred") -lines(data$expected.uniform.mean, col="violetred4") diff --git a/metrics-ehcache/pom.xml b/metrics-ehcache/pom.xml index 7d6e3b7e28..2eabedb31e 100644 --- a/metrics-ehcache/pom.xml +++ b/metrics-ehcache/pom.xml @@ -16,7 +16,7 @@ com.yammer.metrics metrics-core - ${project.version} + 2.2.0 net.sf.ehcache diff --git a/metrics-ganglia/pom.xml b/metrics-ganglia/pom.xml index e178683033..8d3f8d993e 100644 --- a/metrics-ganglia/pom.xml +++ b/metrics-ganglia/pom.xml @@ -18,26 +18,15 @@ com.yammer.metrics metrics-core - ${project.version} - - - org.slf4j - slf4j-api - ${slf4j.version} + 2.2.0 com.yammer.metrics metrics-core - ${project.version} + 2.2.0 test-jar test - - org.slf4j - slf4j-jdk14 - ${slf4j.version} - test - commons-io commons-io diff --git a/metrics-graphite/pom.xml b/metrics-graphite/pom.xml index fd1e1009b2..29a05d7d2b 100644 --- a/metrics-graphite/pom.xml +++ b/metrics-graphite/pom.xml @@ -18,25 +18,14 @@ com.yammer.metrics metrics-core - ${project.version} + 2.2.0 jar compile - - org.slf4j - slf4j-api - ${slf4j.version} - - - org.slf4j - slf4j-jdk14 - ${slf4j.version} - test - com.yammer.metrics metrics-core - ${project.version} + 2.2.0 test-jar test diff --git a/metrics-healthchecks/pom.xml b/metrics-healthchecks/pom.xml new file mode 100644 index 0000000000..e598732d52 --- /dev/null +++ b/metrics-healthchecks/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + + com.yammer.metrics + metrics-parent + 3.0.0-SNAPSHOT + + + metrics-healthchecks + Metrics Health Checks + bundle + diff --git a/metrics-httpclient/pom.xml b/metrics-httpclient/pom.xml index 6edf6d1b40..41cae7e594 100644 --- a/metrics-httpclient/pom.xml +++ b/metrics-httpclient/pom.xml @@ -16,7 +16,7 @@ com.yammer.metrics metrics-core - ${project.version} + 2.2.0 org.apache.httpcomponents diff --git a/metrics-jdbi/pom.xml b/metrics-jdbi/pom.xml index c324e192bf..f27103399e 100644 --- a/metrics-jdbi/pom.xml +++ b/metrics-jdbi/pom.xml @@ -16,7 +16,7 @@ com.yammer.metrics metrics-core - ${project.version} + 2.2.0 org.jdbi diff --git a/metrics-jersey/pom.xml b/metrics-jersey/pom.xml index 6aa2a30d1e..87c1b69c92 100644 --- a/metrics-jersey/pom.xml +++ b/metrics-jersey/pom.xml @@ -24,12 +24,12 @@ com.yammer.metrics metrics-core - ${project.version} + 2.2.0 com.yammer.metrics metrics-annotation - ${project.version} + 2.2.0 com.sun.jersey diff --git a/metrics-jetty/pom.xml b/metrics-jetty/pom.xml index 444d63662d..257b1b2035 100644 --- a/metrics-jetty/pom.xml +++ b/metrics-jetty/pom.xml @@ -16,7 +16,7 @@ com.yammer.metrics metrics-core - ${project.version} + 2.2.0 org.eclipse.jetty diff --git a/metrics-log4j/pom.xml b/metrics-log4j/pom.xml index 078abcb5b2..4c79b9a932 100644 --- a/metrics-log4j/pom.xml +++ b/metrics-log4j/pom.xml @@ -16,7 +16,7 @@ com.yammer.metrics metrics-core - ${project.version} + 2.2.0 log4j diff --git a/metrics-logback/pom.xml b/metrics-logback/pom.xml index 978ab0e3fa..1461a4dc7f 100644 --- a/metrics-logback/pom.xml +++ b/metrics-logback/pom.xml @@ -20,7 +20,7 @@ com.yammer.metrics metrics-core - ${project.version} + 2.2.0 ch.qos.logback diff --git a/metrics-servlet/pom.xml b/metrics-servlet/pom.xml index 9be4fa0bd7..a70574966d 100644 --- a/metrics-servlet/pom.xml +++ b/metrics-servlet/pom.xml @@ -16,7 +16,7 @@ com.yammer.metrics metrics-core - ${project.version} + 2.2.0 javax.servlet @@ -38,7 +38,7 @@ com.yammer.metrics metrics-jetty - ${project.version} + 2.2.0 test diff --git a/metrics-web/pom.xml b/metrics-web/pom.xml index 476ca261e6..9ad99d7723 100644 --- a/metrics-web/pom.xml +++ b/metrics-web/pom.xml @@ -18,7 +18,7 @@ com.yammer.metrics metrics-core - ${project.version} + 2.2.0 javax.servlet diff --git a/pom.xml b/pom.xml index 1a0016b0f6..ea5f18f48a 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ UTF-8 UTF-8 2.5 - 1.6.6 + 1.7.2 2.0.5 @@ -84,12 +84,23 @@ + + org.slf4j + slf4j-api + ${slf4j.version} + junit - junit-dep + junit 4.10 test + + org.easytesting + fest-assert-core + 2.0M10 + test + org.hamcrest hamcrest-all @@ -99,7 +110,13 @@ org.mockito mockito-all - 1.9.0 + 1.9.5 + test + + + org.slf4j + slf4j-simple + ${slf4j.version} test From f3a07bcd473c204ad6accf3e023b374854d2cfff Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 10 Mar 2013 12:57:22 -0700 Subject: [PATCH 0003/2558] Added metrics-healthchecks. --- .../yammer/metrics/health/HealthCheck.java | 179 ++++++++++++++++++ .../metrics/health/HealthCheckRegistry.java | 85 +++++++++ .../health/tests/HealthCheckRegistryTest.java | 75 ++++++++ .../metrics/health/tests/HealthCheckTest.java | 133 +++++++++++++ pom.xml | 1 + 5 files changed, 473 insertions(+) create mode 100644 metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheck.java create mode 100644 metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java create mode 100644 metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckRegistryTest.java create mode 100644 metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckTest.java diff --git a/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheck.java b/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheck.java new file mode 100644 index 0000000000..9845e0bb96 --- /dev/null +++ b/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheck.java @@ -0,0 +1,179 @@ +package com.yammer.metrics.health; + +/** + * A health check for a component of your application. + */ +public abstract class HealthCheck { + /** + * The result of a {@link HealthCheck} being run. It can be healthy (with an optional message) + * or unhealthy (with either an error message or a thrown exception). + */ + public static class Result { + private static final Result HEALTHY = new Result(true, null, null); + private static final int PRIME = 31; + + /** + * Returns a healthy {@link Result} with no additional message. + * + * @return a healthy {@link Result} with no additional message + */ + public static Result healthy() { + return HEALTHY; + } + + /** + * Returns a healthy {@link Result} with an additional message. + * + * @param message an informative message + * @return a healthy {@link Result} with an additional message + */ + public static Result healthy(String message) { + return new Result(true, message, null); + } + + /** + * Returns a healthy {@link Result} with a formatted message. + *

+ * Message formatting follows the same rules as {@link String#format(String, Object...)}. + * + * @param message a message format + * @param args the arguments apply to the message format + * @return a healthy {@link Result} with an additional message + * @see String#format(String, Object...) + */ + public static Result healthy(String message, Object... args) { + return healthy(String.format(message, args)); + } + + /** + * Returns an unhealthy {@link Result} with the given message. + * + * @param message an informative message describing how the health check failed + * @return an unhealthy {@link Result} with the given message + */ + public static Result unhealthy(String message) { + return new Result(false, message, null); + } + + /** + * Returns an unhealthy {@link Result} with a formatted message. + *

+ * Message formatting follows the same rules as {@link String#format(String, Object...)}. + * + * @param message a message format + * @param args the arguments apply to the message format + * @return an unhealthy {@link Result} with an additional message + * @see String#format(String, Object...) + */ + public static Result unhealthy(String message, Object... args) { + return unhealthy(String.format(message, args)); + } + + /** + * Returns an unhealthy {@link Result} with the given error. + * + * @param error an exception thrown during the health check + * @return an unhealthy {@link Result} with the given error + */ + public static Result unhealthy(Throwable error) { + return new Result(false, error.getMessage(), error); + } + + private final boolean healthy; + private final String message; + private final Throwable error; + + private Result(boolean isHealthy, String message, Throwable error) { + this.healthy = isHealthy; + this.message = message; + this.error = error; + } + + /** + * Returns {@code true} if the result indicates the component is healthy; {@code false} + * otherwise. + * + * @return {@code true} if the result indicates the component is healthy + */ + public boolean isHealthy() { + return healthy; + } + + /** + * Returns any additional message for the result, or {@code null} if the result has no + * message. + * + * @return any additional message for the result, or {@code null} + */ + public String getMessage() { + return message; + } + + /** + * Returns any exception for the result, or {@code null} if the result has no exception. + * + * @return any exception for the result, or {@code null} + */ + public Throwable getError() { + return error; + } + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + final Result result = (Result) o; + return healthy == result.healthy && + !(error != null ? !error.equals(result.error) : result.error != null) && + !(message != null ? !message.equals(result.message) : result.message != null); + } + + @Override + public int hashCode() { + int result = (healthy ? 1 : 0); + result = PRIME * result + (message != null ? message.hashCode() : 0); + result = PRIME * result + (error != null ? error.hashCode() : 0); + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder("Result{isHealthy="); + builder.append(healthy); + if (message != null) { + builder.append(", message=").append(message); + } + if (error != null) { + builder.append(", error=").append(error); + } + builder.append('}'); + return builder.toString(); + } + } + + /** + * Perform a check of the application component. + * + * @return if the component is healthy, a healthy {@link Result}; otherwise, an unhealthy {@link + * Result} with a descriptive error message or exception + * @throws Exception if there is an unhandled error during the health check; this will result in + * a failed health check + */ + protected abstract Result check() throws Exception; + + /** + * Executes the health check, catching and handling any exceptions raised by {@link #check()}. + * + * @return if the component is healthy, a healthy {@link Result}; otherwise, an unhealthy {@link + * Result} with a descriptive error message or exception + */ + public Result execute() { + try { + return check(); + } catch (Error e) { + throw e; + } catch (Throwable e) { + return Result.unhealthy(e); + } + } +} diff --git a/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java b/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java new file mode 100644 index 0000000000..3ef58da383 --- /dev/null +++ b/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java @@ -0,0 +1,85 @@ +package com.yammer.metrics.health; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; +import java.util.concurrent.*; + +import static com.yammer.metrics.health.HealthCheck.Result; + +/** + * A registry for health checks. + */ +public class HealthCheckRegistry { + private static final Logger LOGGER = LoggerFactory.getLogger(HealthCheckRegistry.class); + + private final ConcurrentMap healthChecks; + + /** + * Creates a new {@link HealthCheckRegistry}. + */ + public HealthCheckRegistry() { + this.healthChecks = new ConcurrentHashMap(); + } + + /** + * Registers an application {@link HealthCheck}. + * + * @param name the name of the health check + * @param healthCheck the {@link HealthCheck} instance + */ + public void register(String name, HealthCheck healthCheck) { + healthChecks.putIfAbsent(name, healthCheck); + } + + /** + * Unregisters the application {@link HealthCheck} with the given name. + * + * @param name the name of the {@link HealthCheck} instance + */ + public void unregister(String name) { + healthChecks.remove(name); + } + + /** + * Runs the registered health checks and returns a map of the results. + * + * @return a map of the health check results + */ + public SortedMap runHealthChecks() { + final SortedMap results = new TreeMap(); + for (Map.Entry entry : healthChecks.entrySet()) { + final Result result = entry.getValue().execute(); + results.put(entry.getKey(), result); + } + return Collections.unmodifiableSortedMap(results); + } + + /** + * Runs the registered health checks in parallel and returns a map of the results. + * + * @return a map of the health check results + */ + public SortedMap runHealthChecks(ExecutorService executor) { + final Map> futures = new HashMap>(); + for (final Map.Entry entry : healthChecks.entrySet()) { + futures.put(entry.getKey(), executor.submit(new Callable() { + @Override + public Result call() throws Exception { + return entry.getValue().execute(); + } + })); + } + + final SortedMap results = new TreeMap(); + for (Map.Entry> entry : futures.entrySet()) { + try { + results.put(entry.getKey(), entry.getValue().get()); + } catch (Exception e) { + LOGGER.warn("Error executing health check {}", entry.getKey(), e); + } + } + return Collections.unmodifiableSortedMap(results); + } +} diff --git a/metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckRegistryTest.java b/metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckRegistryTest.java new file mode 100644 index 0000000000..4af55f6c31 --- /dev/null +++ b/metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckRegistryTest.java @@ -0,0 +1,75 @@ +package com.yammer.metrics.health.tests; + +import com.yammer.metrics.health.HealthCheck; +import com.yammer.metrics.health.HealthCheckRegistry; +import org.junit.Before; +import org.junit.Test; + +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.fest.assertions.api.Assertions.entry; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class HealthCheckRegistryTest { + private final HealthCheckRegistry registry = new HealthCheckRegistry(); + + private final HealthCheck hc1 = mock(HealthCheck.class); + private final HealthCheck hc2 = mock(HealthCheck.class); + + private final HealthCheck.Result r1 = mock(HealthCheck.Result.class); + private final HealthCheck.Result r2 = mock(HealthCheck.Result.class); + + @Before + public void setUp() throws Exception { + when(hc1.execute()).thenReturn(r1); + + when(hc2.execute()).thenReturn(r2); + + registry.register("hc1", hc1); + registry.register("hc2", hc2); + } + + @Test + public void runsRegisteredHealthChecks() throws Exception { + final Map results = registry.runHealthChecks(); + + assertThat(results) + .contains(entry("hc1", r1)); + + assertThat(results) + .contains(entry("hc2", r2)); + } + + @Test + public void runsRegisteredHealthChecksInParallel() throws Exception { + final ExecutorService executor = Executors.newFixedThreadPool(10); + final Map results = registry.runHealthChecks(executor); + + executor.shutdown(); + executor.awaitTermination(1, TimeUnit.SECONDS); + + assertThat(results) + .contains(entry("hc1", r1)); + + assertThat(results) + .contains(entry("hc2", r2)); + } + + @Test + public void removesRegisteredHealthChecks() throws Exception { + registry.unregister("hc1"); + + final Map results = registry.runHealthChecks(); + + assertThat(results) + .doesNotContainKey("hc1"); + + assertThat(results) + .containsKey("hc2"); + } +} diff --git a/metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckTest.java b/metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckTest.java new file mode 100644 index 0000000000..f56f9c7712 --- /dev/null +++ b/metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckTest.java @@ -0,0 +1,133 @@ +package com.yammer.metrics.health.tests; + +import com.yammer.metrics.health.HealthCheck; +import org.junit.Test; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class HealthCheckTest { + private static class ExampleHealthCheck extends HealthCheck { + private final HealthCheck underlying; + + private ExampleHealthCheck(HealthCheck underlying) { + this.underlying = underlying; + } + + @Override + protected Result check() throws Exception { + return underlying.execute(); + } + } + + private final HealthCheck underlying = mock(HealthCheck.class); + private final HealthCheck healthCheck = new ExampleHealthCheck(underlying); + + @Test + public void canHaveHealthyResults() throws Exception { + final HealthCheck.Result result = HealthCheck.Result.healthy(); + + assertThat(result.isHealthy()) + .isTrue(); + + assertThat(result.getMessage()) + .isNull(); + + assertThat(result.getError()) + .isNull(); + } + + @Test + public void canHaveHealthyResultsWithMessages() throws Exception { + final HealthCheck.Result result = HealthCheck.Result.healthy("woo"); + + assertThat(result.isHealthy()) + .isTrue(); + + assertThat(result.getMessage()) + .isEqualTo("woo"); + + assertThat(result.getError()) + .isNull(); + } + + @Test + public void canHaveHealthyResultsWithFormattedMessages() throws Exception { + final HealthCheck.Result result = HealthCheck.Result.healthy("foo %s", "bar"); + + assertThat(result.isHealthy()) + .isTrue(); + + assertThat(result.getMessage()) + .isEqualTo("foo bar"); + + assertThat(result.getError()) + .isNull(); + } + + @Test + public void canHaveUnhealthyResults() throws Exception { + final HealthCheck.Result result = HealthCheck.Result.unhealthy("bad"); + + assertThat(result.isHealthy()) + .isFalse(); + + assertThat(result.getMessage()) + .isEqualTo("bad"); + + assertThat(result.getError()) + .isNull(); + } + + @Test + public void canHaveUnhealthyResultsWithFormattedMessages() throws Exception { + final HealthCheck.Result result = HealthCheck.Result.unhealthy("foo %s %d", "bar", 123); + + assertThat(result.isHealthy()) + .isFalse(); + + assertThat(result.getMessage()) + .isEqualTo("foo bar 123"); + + assertThat(result.getError()) + .isNull(); + } + + @Test + public void canHaveUnhealthyResultsWithExceptions() throws Exception { + final RuntimeException e = mock(RuntimeException.class); + when(e.getMessage()).thenReturn("oh noes"); + + final HealthCheck.Result result = HealthCheck.Result.unhealthy(e); + + assertThat(result.isHealthy()) + .isFalse(); + + assertThat(result.getMessage()) + .isEqualTo("oh noes"); + + assertThat(result.getError()) + .isEqualTo(e); + } + + @Test + public void returnsResultsWhenExecuted() throws Exception { + final HealthCheck.Result result = mock(HealthCheck.Result.class); + when(underlying.execute()).thenReturn(result); + + assertThat(healthCheck.execute()) + .isEqualTo(result); + } + + @Test + public void wrapsExceptionsWhenExecuted() throws Exception { + final RuntimeException e = mock(RuntimeException.class); + when(e.getMessage()).thenReturn("oh noes"); + + when(underlying.execute()).thenThrow(e); + + assertThat(healthCheck.execute()) + .isEqualTo(HealthCheck.Result.unhealthy(e)); + } +} diff --git a/pom.xml b/pom.xml index ea5f18f48a..f1abb7b760 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ metrics-annotation metrics-core + metrics-healthchecks metrics-ehcache metrics-ganglia metrics-graphite From 7af6fdf7e8e7f8a85b39d0309d11be8775effa1f Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 10 Mar 2013 13:33:56 -0700 Subject: [PATCH 0004/2558] Bump the various modules back to 2.2 API. --- .../metrics/ehcache/InstrumentedEhcache.java | 36 ++++----- .../tests/ConfigInstrumentedEhcacheTest.java | 4 +- .../tests/InstrumentedEhcacheTest.java | 4 +- .../metrics/ganglia/GangliaReporter.java | 65 +++++++-------- .../metrics/graphite/GraphiteReporter.java | 51 ++++++------ .../InstrumentedClientConnManager.java | 8 +- .../InstrumentedTimingCollectorTest.java | 22 ++--- ...umentedResourceMethodDispatchProvider.java | 35 ++++---- .../jersey/tests/MetricsJerseyTest.java | 8 +- .../tests/SingletonMetricsJerseyTest.java | 8 +- .../metrics/jetty/InstrumentedHandler.java | 24 +++--- .../jetty/InstrumentedQueuedThreadPool.java | 4 +- .../metrics/servlet/MetricsServlet.java | 81 +++++++++---------- .../metrics/servlet/ThreadDumpServlet.java | 2 +- .../servlet/experiments/ExampleServer.java | 2 +- .../servlet/tests/MetricsServletTest.java | 46 +++++------ .../servlet/tests/ThreadDumpServletTest.java | 2 +- 17 files changed, 200 insertions(+), 202 deletions(-) diff --git a/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcache.java b/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcache.java index e779125c08..c27a8cb560 100644 --- a/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcache.java +++ b/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcache.java @@ -221,7 +221,7 @@ public static Ehcache instrument(MetricsRegistry registry, final Ehcache cache) registry.newGauge(cache.getClass(), "hits", cache.getName(), new Gauge() { @Override - public Long getValue() { + public Long value() { return cache.getStatistics().getCacheHits(); } }); @@ -231,7 +231,7 @@ public Long getValue() { cache.getName(), new Gauge() { @Override - public Long getValue() { + public Long value() { return cache.getStatistics().getInMemoryHits(); } }); @@ -241,7 +241,7 @@ public Long getValue() { cache.getName(), new Gauge() { @Override - public Long getValue() { + public Long value() { return cache.getStatistics().getOffHeapHits(); } }); @@ -251,14 +251,14 @@ public Long getValue() { cache.getName(), new Gauge() { @Override - public Long getValue() { + public Long value() { return cache.getStatistics().getOnDiskHits(); } }); registry.newGauge(cache.getClass(), "misses", cache.getName(), new Gauge() { @Override - public Long getValue() { + public Long value() { return cache.getStatistics().getCacheMisses(); } }); @@ -268,7 +268,7 @@ public Long getValue() { cache.getName(), new Gauge() { @Override - public Long getValue() { + public Long value() { return cache.getStatistics().getInMemoryMisses(); } }); @@ -278,7 +278,7 @@ public Long getValue() { cache.getName(), new Gauge() { @Override - public Long getValue() { + public Long value() { return cache.getStatistics().getOffHeapMisses(); } }); @@ -288,14 +288,14 @@ public Long getValue() { cache.getName(), new Gauge() { @Override - public Long getValue() { + public Long value() { return cache.getStatistics().getOnDiskMisses(); } }); registry.newGauge(cache.getClass(), "objects", cache.getName(), new Gauge() { @Override - public Long getValue() { + public Long value() { return cache.getStatistics().getObjectCount(); } }); @@ -305,7 +305,7 @@ public Long getValue() { cache.getName(), new Gauge() { @Override - public Long getValue() { + public Long value() { return cache.getStatistics().getMemoryStoreObjectCount(); } }); @@ -315,7 +315,7 @@ public Long getValue() { cache.getName(), new Gauge() { @Override - public Long getValue() { + public Long value() { return cache.getStatistics().getOffHeapStoreObjectCount(); } }); @@ -325,7 +325,7 @@ public Long getValue() { cache.getName(), new Gauge() { @Override - public Long getValue() { + public Long value() { return cache.getStatistics().getDiskStoreObjectCount(); } }); @@ -335,7 +335,7 @@ public Long getValue() { cache.getName(), new Gauge() { @Override - public Float getValue() { + public Float value() { return cache.getStatistics().getAverageGetTime(); } }); @@ -345,7 +345,7 @@ public Float getValue() { cache.getName(), new Gauge() { @Override - public Long getValue() { + public Long value() { return cache.getStatistics().getAverageSearchTime(); } }); @@ -355,7 +355,7 @@ public Long getValue() { cache.getName(), new Gauge() { @Override - public Long getValue() { + public Long value() { return cache.getStatistics().getEvictionCount(); } }); @@ -365,7 +365,7 @@ public Long getValue() { cache.getName(), new Gauge() { @Override - public Long getValue() { + public Long value() { return cache.getStatistics().getSearchesPerSecond(); } }); @@ -375,7 +375,7 @@ public Long getValue() { cache.getName(), new Gauge() { @Override - public Long getValue() { + public Long value() { return cache.getStatistics().getWriterQueueSize(); } }); @@ -385,7 +385,7 @@ public Long getValue() { cache.getName(), new Gauge() { @Override - public String getValue() { + public String value() { return cache.getStatistics() .getStatisticsAccuracyDescription(); } diff --git a/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/ConfigInstrumentedEhcacheTest.java b/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/ConfigInstrumentedEhcacheTest.java index 249731c897..e260f8e2f6 100644 --- a/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/ConfigInstrumentedEhcacheTest.java +++ b/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/ConfigInstrumentedEhcacheTest.java @@ -37,7 +37,7 @@ public void measuresGets() throws Exception { TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - assertThat(gets.getCount(), is(1L)); + assertThat(gets.count(), is(1L)); } @@ -52,7 +52,7 @@ public void measuresPuts() throws Exception { TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - assertThat(puts.getCount(), is(1L)); + assertThat(puts.count(), is(1L)); } diff --git a/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/InstrumentedEhcacheTest.java b/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/InstrumentedEhcacheTest.java index 0a272980e6..dac6f846a5 100644 --- a/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/InstrumentedEhcacheTest.java +++ b/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/InstrumentedEhcacheTest.java @@ -40,7 +40,7 @@ public void measuresGetsAndPuts() throws Exception { TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - assertThat(gets.getCount(), + assertThat(gets.count(), is(1L)); final Timer puts = Metrics.defaultRegistry().newTimer(Cache.class, @@ -49,7 +49,7 @@ public void measuresGetsAndPuts() throws Exception { TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - assertThat(puts.getCount(), + assertThat(puts.count(), is(1L)); } } diff --git a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java index b0f6a03ad8..2e2cfb4c8f 100644 --- a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java +++ b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java @@ -3,7 +3,6 @@ import com.yammer.metrics.Metrics; import com.yammer.metrics.core.*; import com.yammer.metrics.reporting.AbstractPollingReporter; -import com.yammer.metrics.reporting.MetricDispatcher; import com.yammer.metrics.stats.Snapshot; import com.yammer.metrics.core.MetricPredicate; import org.slf4j.Logger; @@ -40,7 +39,6 @@ public class GangliaReporter extends AbstractPollingReporter implements MetricPr private final MetricPredicate predicate; private final VirtualMachineMetrics vm; private final Locale locale = Locale.US; - private final MetricDispatcher dispatcher = new MetricDispatcher(); private String hostLabel; private String groupPrefix = ""; private boolean compressPackageNames; @@ -264,13 +262,12 @@ public void run() { } private void printRegularMetrics() { - for (Map.Entry> entry : getMetricsRegistry().getGroupedMetrics( - predicate).entrySet()) { + for (Map.Entry> entry : getMetricsRegistry().groupedMetrics(predicate).entrySet()) { for (Map.Entry subEntry : entry.getValue().entrySet()) { final Metric metric = subEntry.getValue(); if (metric != null) { try { - dispatcher.dispatch(subEntry.getValue(), subEntry.getKey(), this, null); + metric.processWith(this, subEntry.getKey(), null); } catch (Exception ignored) { LOG.error("Error printing regular metrics:", ignored); } @@ -341,7 +338,7 @@ protected void sendMetricData(String hostName, String metricType, String metricN @Override public void processGauge(MetricName name, Gauge gauge, String x) throws IOException { - final Object value = gauge.getValue(); + final Object value = gauge.value(); final Class klass = value.getClass(); final String type; @@ -355,7 +352,7 @@ public void processGauge(MetricName name, Gauge gauge, String x) throws IOExc sendToGanglia(sanitizeName(name), type, - String.format(locale, "%s", gauge.getValue()), + String.format(locale, "%s", gauge.value()), "gauge"); } @@ -363,21 +360,21 @@ public void processGauge(MetricName name, Gauge gauge, String x) throws IOExc public void processCounter(MetricName name, Counter counter, String x) throws IOException { sendToGanglia(sanitizeName(name), GANGLIA_INT_TYPE, - String.format(locale, "%d", counter.getCount()), + String.format(locale, "%d", counter.count()), "counter"); } @Override public void processMeter(MetricName name, Metered meter, String x) throws IOException { final String sanitizedName = sanitizeName(name); - final String rateUnits = meter.getRateUnit().name(); + final String rateUnits = meter.rateUnit().name(); final String rateUnit = rateUnits.substring(0, rateUnits.length() - 1).toLowerCase(Locale.US); - final String unit = meter.getEventType() + '/' + rateUnit; - printLongField(sanitizedName + ".count", meter.getCount(), "metered", meter.getEventType()); - printDoubleField(sanitizedName + ".meanRate", meter.getMeanRate(), "metered", unit); - printDoubleField(sanitizedName + ".1MinuteRate", meter.getOneMinuteRate(), "metered", unit); - printDoubleField(sanitizedName + ".5MinuteRate", meter.getFiveMinuteRate(), "metered", unit); - printDoubleField(sanitizedName + ".15MinuteRate", meter.getFifteenMinuteRate(), "metered", unit); + final String unit = meter.eventType() + '/' + rateUnit; + printLongField(sanitizedName + ".count", meter.count(), "metered", meter.eventType()); + printDoubleField(sanitizedName + ".meanRate", meter.meanRate(), "metered", unit); + printDoubleField(sanitizedName + ".1MinuteRate", meter.oneMinuteRate(), "metered", unit); + printDoubleField(sanitizedName + ".5MinuteRate", meter.fiveMinuteRate(), "metered", unit); + printDoubleField(sanitizedName + ".15MinuteRate", meter.fifteenMinuteRate(), "metered", unit); } @Override @@ -385,10 +382,10 @@ public void processHistogram(MetricName name, Histogram histogram, String x) thr final String sanitizedName = sanitizeName(name); final Snapshot snapshot = histogram.getSnapshot(); // TODO: what units make sense for histograms? should we add event type to the Histogram metric? - printDoubleField(sanitizedName + ".min", histogram.getMin(), "histo"); - printDoubleField(sanitizedName + ".max", histogram.getMax(), "histo"); - printDoubleField(sanitizedName + ".mean", histogram.getMean(), "histo"); - printDoubleField(sanitizedName + ".stddev", histogram.getStdDev(), "histo"); + printDoubleField(sanitizedName + ".min", histogram.min(), "histo"); + printDoubleField(sanitizedName + ".max", histogram.max(), "histo"); + printDoubleField(sanitizedName + ".mean", histogram.mean(), "histo"); + printDoubleField(sanitizedName + ".stddev", histogram.stdDev(), "histo"); printDoubleField(sanitizedName + ".median", snapshot.getMedian(), "histo"); printDoubleField(sanitizedName + ".75percentile", snapshot.get75thPercentile(), "histo"); printDoubleField(sanitizedName + ".95percentile", snapshot.get95thPercentile(), "histo"); @@ -402,11 +399,11 @@ public void processTimer(MetricName name, Timer timer, String x) throws IOExcept processMeter(name, timer, x); final String sanitizedName = sanitizeName(name); final Snapshot snapshot = timer.getSnapshot(); - final String durationUnit = timer.getDurationUnit().name(); - printDoubleField(sanitizedName + ".min", timer.getMin(), "timer", durationUnit); - printDoubleField(sanitizedName + ".max", timer.getMax(), "timer", durationUnit); - printDoubleField(sanitizedName + ".mean", timer.getMean(), "timer", durationUnit); - printDoubleField(sanitizedName + ".stddev", timer.getStdDev(), "timer", durationUnit); + final String durationUnit = timer.durationUnit().name(); + printDoubleField(sanitizedName + ".min", timer.min(), "timer", durationUnit); + printDoubleField(sanitizedName + ".max", timer.max(), "timer", durationUnit); + printDoubleField(sanitizedName + ".mean", timer.mean(), "timer", durationUnit); + printDoubleField(sanitizedName + ".stddev", timer.stdDev(), "timer", durationUnit); printDoubleField(sanitizedName + ".median", snapshot.getMedian(), "timer", durationUnit); printDoubleField(sanitizedName + ".75percentile", snapshot.get75thPercentile(), "timer", durationUnit); printDoubleField(sanitizedName + ".95percentile", snapshot.get95thPercentile(), "timer", durationUnit); @@ -437,26 +434,26 @@ private void printLongField(String name, long value, String groupName, String un } private void printVmMetrics() { - printDoubleField("jvm.memory.heap_usage", vm.getHeapUsage(), "jvm"); - printDoubleField("jvm.memory.non_heap_usage", vm.getNonHeapUsage(), "jvm"); - for (Map.Entry pool : vm.getMemoryPoolUsage().entrySet()) { + printDoubleField("jvm.memory.heap_usage", vm.heapUsage(), "jvm"); + printDoubleField("jvm.memory.non_heap_usage", vm.nonHeapUsage(), "jvm"); + for (Map.Entry pool : vm.memoryPoolUsage().entrySet()) { printDoubleField("jvm.memory.memory_pool_usages." + pool.getKey(), pool.getValue(), "jvm"); } - printDoubleField("jvm.daemon_thread_count", vm.getDaemonThreadCount(), "jvm"); - printDoubleField("jvm.thread_count", vm.getThreadCount(), "jvm"); - printDoubleField("jvm.uptime", vm.getUptime(), "jvm"); - printDoubleField("jvm.fd_usage", vm.getFileDescriptorUsage(), "jvm"); + printDoubleField("jvm.daemon_thread_count", vm.daemonThreadCount(), "jvm"); + printDoubleField("jvm.thread_count", vm.threadCount(), "jvm"); + printDoubleField("jvm.uptime", vm.uptime(), "jvm"); + printDoubleField("jvm.fd_usage", vm.fileDescriptorUsage(), "jvm"); - for (Map.Entry entry : vm.getThreadStatePercentages().entrySet()) { + for (Map.Entry entry : vm.threadStatePercentages().entrySet()) { printDoubleField("jvm.thread-states." + entry.getKey().toString().toLowerCase(), entry.getValue(), "jvm"); } - for (Map.Entry entry : vm.getGarbageCollectors().entrySet()) { + for (Map.Entry entry : vm.garbageCollectors().entrySet()) { printLongField("jvm.gc." + entry.getKey() + ".time", entry.getValue().getTime(TimeUnit.MILLISECONDS), "jvm"); @@ -483,7 +480,7 @@ protected String sanitizeName(MetricName name) { if (name == null) { return ""; } - final String qualifiedTypeName = name.getDomain() + "." + name.getType() + "." + name.getName(); + final String qualifiedTypeName = name.getGroup() + "." + name.getType() + "." + name.getName(); final String metricName = name.hasScope() ? qualifiedTypeName + '.' + name.getScope() : qualifiedTypeName; final StringBuilder sb = new StringBuilder(); for (int i = 0; i < metricName.length(); i++) { diff --git a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java index 502268b3a3..96ba962498 100644 --- a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java +++ b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java @@ -3,7 +3,6 @@ import com.yammer.metrics.Metrics; import com.yammer.metrics.core.*; import com.yammer.metrics.reporting.AbstractPollingReporter; -import com.yammer.metrics.reporting.MetricDispatcher; import com.yammer.metrics.stats.Snapshot; import com.yammer.metrics.core.MetricPredicate; import org.slf4j.Logger; @@ -30,7 +29,6 @@ public class GraphiteReporter extends AbstractPollingReporter implements MetricP protected final String prefix; protected final MetricPredicate predicate; protected final Locale locale = Locale.US; - protected final MetricDispatcher dispatcher = new MetricDispatcher(); protected final Clock clock; protected final SocketProvider socketProvider; protected final VirtualMachineMetrics vm; @@ -209,7 +207,7 @@ public void run() { socket = this.socketProvider.get(); writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); - final long epoch = clock.getTime() / 1000; + final long epoch = clock.time() / 1000; if (this.printVMMetrics) { printVmMetrics(epoch); } @@ -241,13 +239,12 @@ public void run() { } protected void printRegularMetrics(final Long epoch) { - for (Entry> entry : getMetricsRegistry().getGroupedMetrics( - predicate).entrySet()) { + for (Entry> entry : getMetricsRegistry().groupedMetrics(predicate).entrySet()) { for (Entry subEntry : entry.getValue().entrySet()) { final Metric metric = subEntry.getValue(); if (metric != null) { try { - dispatcher.dispatch(subEntry.getValue(), subEntry.getKey(), this, epoch); + metric.processWith(this, subEntry.getKey(), epoch); } catch (Exception ignored) { LOG.error("Error printing regular metrics:", ignored); } @@ -287,7 +284,7 @@ protected void sendToGraphite(long timestamp, String name, String value) { protected String sanitizeName(MetricName name) { final StringBuilder sb = new StringBuilder() - .append(name.getDomain()) + .append(name.getGroup()) .append('.') .append(name.getType()) .append('.'); @@ -304,22 +301,22 @@ protected String sanitizeString(String s) { @Override public void processGauge(MetricName name, Gauge gauge, Long epoch) throws IOException { - sendObjToGraphite(epoch, sanitizeName(name), "value", gauge.getValue()); + sendObjToGraphite(epoch, sanitizeName(name), "value", gauge.value()); } @Override public void processCounter(MetricName name, Counter counter, Long epoch) throws IOException { - sendInt(epoch, sanitizeName(name), "count", counter.getCount()); + sendInt(epoch, sanitizeName(name), "count", counter.count()); } @Override public void processMeter(MetricName name, Metered meter, Long epoch) throws IOException { final String sanitizedName = sanitizeName(name); - sendInt(epoch, sanitizedName, "count", meter.getCount()); - sendFloat(epoch, sanitizedName, "meanRate", meter.getMeanRate()); - sendFloat(epoch, sanitizedName, "1MinuteRate", meter.getOneMinuteRate()); - sendFloat(epoch, sanitizedName, "5MinuteRate", meter.getFiveMinuteRate()); - sendFloat(epoch, sanitizedName, "15MinuteRate", meter.getFifteenMinuteRate()); + sendInt(epoch, sanitizedName, "count", meter.count()); + sendFloat(epoch, sanitizedName, "meanRate", meter.meanRate()); + sendFloat(epoch, sanitizedName, "1MinuteRate", meter.oneMinuteRate()); + sendFloat(epoch, sanitizedName, "5MinuteRate", meter.fiveMinuteRate()); + sendFloat(epoch, sanitizedName, "15MinuteRate", meter.fifteenMinuteRate()); } @Override @@ -338,10 +335,10 @@ public void processTimer(MetricName name, Timer timer, Long epoch) throws IOExce } protected void sendSummarizable(long epoch, String sanitizedName, Summarizable metric) throws IOException { - sendFloat(epoch, sanitizedName, "min", metric.getMin()); - sendFloat(epoch, sanitizedName, "max", metric.getMax()); - sendFloat(epoch, sanitizedName, "mean", metric.getMean()); - sendFloat(epoch, sanitizedName, "stddev", metric.getStdDev()); + sendFloat(epoch, sanitizedName, "min", metric.min()); + sendFloat(epoch, sanitizedName, "max", metric.max()); + sendFloat(epoch, sanitizedName, "mean", metric.mean()); + sendFloat(epoch, sanitizedName, "stddev", metric.stdDev()); } protected void sendSampling(long epoch, String sanitizedName, Sampling metric) throws IOException { @@ -355,22 +352,22 @@ protected void sendSampling(long epoch, String sanitizedName, Sampling metric) t } protected void printVmMetrics(long epoch) { - sendFloat(epoch, "jvm.memory", "heap_usage", vm.getHeapUsage()); - sendFloat(epoch, "jvm.memory", "non_heap_usage", vm.getNonHeapUsage()); - for (Entry pool : vm.getMemoryPoolUsage().entrySet()) { + sendFloat(epoch, "jvm.memory", "heap_usage", vm.heapUsage()); + sendFloat(epoch, "jvm.memory", "non_heap_usage", vm.nonHeapUsage()); + for (Entry pool : vm.memoryPoolUsage().entrySet()) { sendFloat(epoch, "jvm.memory.memory_pool_usages", sanitizeString(pool.getKey()), pool.getValue()); } - sendInt(epoch, "jvm", "daemon_thread_count", vm.getDaemonThreadCount()); - sendInt(epoch, "jvm", "thread_count", vm.getThreadCount()); - sendInt(epoch, "jvm", "uptime", vm.getUptime()); - sendFloat(epoch, "jvm", "fd_usage", vm.getFileDescriptorUsage()); + sendInt(epoch, "jvm", "daemon_thread_count", vm.daemonThreadCount()); + sendInt(epoch, "jvm", "thread_count", vm.threadCount()); + sendInt(epoch, "jvm", "uptime", vm.uptime()); + sendFloat(epoch, "jvm", "fd_usage", vm.fileDescriptorUsage()); - for (Entry entry : vm.getThreadStatePercentages().entrySet()) { + for (Entry entry : vm.threadStatePercentages().entrySet()) { sendFloat(epoch, "jvm.thread-states", entry.getKey().toString().toLowerCase(), entry.getValue()); } - for (Entry entry : vm.getGarbageCollectors().entrySet()) { + for (Entry entry : vm.garbageCollectors().entrySet()) { final String name = "jvm.gc." + sanitizeString(entry.getKey()); sendInt(epoch, name, "time", entry.getValue().getTime(TimeUnit.MILLISECONDS)); sendInt(epoch, name, "runs", entry.getValue().getRuns()); diff --git a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedClientConnManager.java b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedClientConnManager.java index 43fe0574ac..c23a2e6e08 100644 --- a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedClientConnManager.java +++ b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedClientConnManager.java @@ -47,7 +47,7 @@ public InstrumentedClientConnManager(MetricsRegistry metricsRegistry, "available-connections", new Gauge() { @Override - public Integer getValue() { + public Integer value() { // this acquires a lock on the connection pool; remove if contention sucks return getTotalStats().getAvailable(); } @@ -56,7 +56,7 @@ public Integer getValue() { "leased-connections", new Gauge() { @Override - public Integer getValue() { + public Integer value() { // this acquires a lock on the connection pool; remove if contention sucks return getTotalStats().getLeased(); } @@ -65,7 +65,7 @@ public Integer getValue() { "max-connections", new Gauge() { @Override - public Integer getValue() { + public Integer value() { // this acquires a lock on the connection pool; remove if contention sucks return getTotalStats().getMax(); } @@ -74,7 +74,7 @@ public Integer getValue() { "pending-connections", new Gauge() { @Override - public Integer getValue() { + public Integer value() { // this acquires a lock on the connection pool; remove if contention sucks return getTotalStats().getPending(); } diff --git a/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java b/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java index 25a008b209..dbee94d75b 100644 --- a/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java +++ b/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java @@ -39,7 +39,7 @@ public void updatesTimerForSqlObjects() throws Exception { assertThat(name, is(new MetricName(getClass(), "updatesTimerForSqlObjects"))); - assertThat(timer.getMax(), + assertThat(timer.max(), is(closeTo(1000.0, 1))); } @@ -58,7 +58,7 @@ public void updatesTimerForSqlObjectsWithoutMethod() throws Exception { assertThat(name, is(new MetricName(getClass(), "SELECT_1"))); - assertThat(timer.getMax(), + assertThat(timer.max(), is(closeTo(1000.0, 1))); } @@ -76,7 +76,7 @@ public void updatesTimerForRawSql() throws Exception { assertThat(name, is(new MetricName("sql", "raw", "SELECT_1"))); - assertThat(timer.getMax(), + assertThat(timer.max(), is(closeTo(2000.0, 1))); } @@ -93,7 +93,7 @@ public void updatesTimerForNoRawSql() throws Exception { assertThat(name, is(new MetricName("sql", "empty", ""))); - assertThat(timer.getMax(), + assertThat(timer.max(), is(closeTo(2000.0, 1))); } @@ -111,7 +111,7 @@ public void updatesTimerForNonSqlishRawSql() throws Exception { assertThat(name, is(new MetricName("sql", "raw", "don_t_know_what_it_is_but_it_s_not_SQL"))); - assertThat(timer.getMax(), + assertThat(timer.max(), is(closeTo(3000.0, 1))); } @@ -131,7 +131,7 @@ public void updatesTimerForContextClass() throws Exception { assertThat(name, is(new MetricName(getClass(), "updatesTimerForContextClass"))); - assertThat(timer.getMax(), + assertThat(timer.max(), is(closeTo(3000.0, 1))); } @@ -151,7 +151,7 @@ public void updatesTimerForTemplateFile() throws Exception { assertThat(name, is(new MetricName("foo", "bar", "updatesTimerForTemplateFile"))); - assertThat(timer.getMax(), + assertThat(timer.max(), is(closeTo(4000.0, 1))); } @@ -171,7 +171,7 @@ public void updatesTimerForContextGroupAndName() throws Exception { assertThat(name, is(new MetricName("my-group", "updatesTimerForContextGroupAndName", ""))); - assertThat(timer.getMax(), + assertThat(timer.max(), is(closeTo(4000.0, 1))); } @@ -192,7 +192,7 @@ public void updatesTimerForContextGroupTypeAndName() throws Exception { assertThat(name, is(new MetricName("my-group", "my-type", "updatesTimerForContextGroupTypeAndName"))); - assertThat(timer.getMax(), + assertThat(timer.max(), is(closeTo(5000.0, 1))); } @@ -212,7 +212,7 @@ public void updatesTimerForShortSqlObjectStrategy() throws Exception { assertThat(name, is(new MetricName("jdbi", getClass().getSimpleName(), "updatesTimerForShortSqlObjectStrategy"))); - assertThat(timer.getMax(), + assertThat(timer.max(), is(closeTo(1000.0, 1))); } @@ -232,7 +232,7 @@ public void updatesTimerForShortContextClassStrategy() throws Exception { assertThat(name, is(new MetricName("jdbi", getClass().getSimpleName(), "updatesTimerForShortContextClassStrategy"))); - assertThat(timer.getMax(), + assertThat(timer.max(), is(closeTo(3000.0, 1))); } } diff --git a/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java b/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java index c74b1c4410..476af1962a 100644 --- a/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java +++ b/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java @@ -103,31 +103,36 @@ public RequestDispatcher create(AbstractResourceMethod method) { if (method.getMethod().isAnnotationPresent(Timed.class)) { final Timed annotation = method.getMethod().getAnnotation(Timed.class); - final MetricName name = MetricName.forTimedMethod(method.getDeclaringResource() - .getResourceClass(), - method.getMethod(), - annotation); - final Timer timer = registry.newTimer(name, annotation.durationUnit(), annotation.rateUnit()); + final Class klass = method.getDeclaringResource().getResourceClass(); + final String group = MetricName.chooseGroup(annotation.group(), klass); + final String type = MetricName.chooseType(annotation.type(), klass); + final String name = MetricName.chooseName(annotation.name(), method.getMethod()); + final MetricName metricName = new MetricName(group, type, name); + final Timer timer = registry.newTimer(metricName, annotation.durationUnit(), annotation.rateUnit()); dispatcher = new TimedRequestDispatcher(dispatcher, timer); } if (method.getMethod().isAnnotationPresent(Metered.class)) { final Metered annotation = method.getMethod().getAnnotation(Metered.class); - final MetricName name = MetricName.forMeteredMethod(method.getDeclaringResource() - .getResourceClass(), - method.getMethod(), - annotation); - final Meter meter = registry.newMeter(name, annotation.eventType(), annotation.rateUnit()); + Class klass = method.getDeclaringResource().getResourceClass(); + String group = MetricName.chooseGroup(annotation.group(), klass); + String type = MetricName.chooseType(annotation.type(), klass); + String name = MetricName.chooseName(annotation.name(), method.getMethod()); + MetricName metricName = new MetricName(group, type, name); + final Meter meter = registry.newMeter(metricName, annotation.eventType(), annotation.rateUnit()); dispatcher = new MeteredRequestDispatcher(dispatcher, meter); } if (method.getMethod().isAnnotationPresent(ExceptionMetered.class)) { final ExceptionMetered annotation = method.getMethod().getAnnotation(ExceptionMetered.class); - final MetricName name = MetricName.forExceptionMeteredMethod(method.getDeclaringResource() - .getResourceClass(), - method.getMethod(), - annotation); - final Meter meter = registry.newMeter(name, annotation.eventType(), annotation.rateUnit()); + Class klass = method.getDeclaringResource().getResourceClass(); + String group = MetricName.chooseGroup(annotation.group(), klass); + String type = MetricName.chooseType(annotation.type(), klass); + String name = annotation.name() == null || annotation.name().equals("") ? + method.getMethod().getName() + ExceptionMetered.DEFAULT_NAME_SUFFIX : annotation + .name(); + MetricName metricName = new MetricName(group, type, name); + final Meter meter = registry.newMeter(metricName, annotation.eventType(), annotation.rateUnit()); dispatcher = new ExceptionMeteredRequestDispatcher(dispatcher, meter, annotation.cause()); } diff --git a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/MetricsJerseyTest.java b/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/MetricsJerseyTest.java index 0b3fe42093..49db785f51 100644 --- a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/MetricsJerseyTest.java +++ b/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/MetricsJerseyTest.java @@ -40,7 +40,7 @@ public void timedMethodsAreTimed() { is("yay")); final Timer timer = Metrics.defaultRegistry().newTimer(InstrumentedResource.class, "timed"); - assertThat(timer.getCount(), + assertThat(timer.count(), is(1L)); } @@ -50,7 +50,7 @@ public void meteredMethodsAreMetered() { is("woo")); final Meter meter = Metrics.defaultRegistry().newMeter(InstrumentedResource.class, "metered", "blah", TimeUnit.SECONDS); - assertThat(meter.getCount(), + assertThat(meter.count(), is(1L)); } @@ -61,7 +61,7 @@ public void exceptionMeteredMethodsAreExceptionMetered() { assertThat(resource().path("exception-metered").get(String.class), is("fuh")); - assertThat(meter.getCount(), + assertThat(meter.count(), is(0L)); try { @@ -72,7 +72,7 @@ public void exceptionMeteredMethodsAreExceptionMetered() { is(instanceOf(IOException.class))); } - assertThat(meter.getCount(), + assertThat(meter.count(), is(1L)); } } diff --git a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/SingletonMetricsJerseyTest.java b/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/SingletonMetricsJerseyTest.java index e9716b2a3a..02f4b4ca55 100644 --- a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/SingletonMetricsJerseyTest.java +++ b/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/SingletonMetricsJerseyTest.java @@ -63,7 +63,7 @@ public void timedMethodsAreTimed() { is("yay")); final Timer timer = registry.newTimer(InstrumentedResource.class, "timed"); - assertThat(timer.getCount(), + assertThat(timer.count(), is(1L)); } @@ -73,7 +73,7 @@ public void meteredMethodsAreMetered() { is("woo")); final Meter meter = registry.newMeter(InstrumentedResource.class, "metered", "blah", TimeUnit.SECONDS); - assertThat(meter.getCount(), + assertThat(meter.count(), is(1L)); } @@ -84,7 +84,7 @@ public void exceptionMeteredMethodsAreExceptionMetered() { assertThat(resource().path("exception-metered").get(String.class), is("fuh")); - assertThat(meter.getCount(), + assertThat(meter.count(), is(0L)); try { @@ -95,7 +95,7 @@ public void exceptionMeteredMethodsAreExceptionMetered() { is(instanceOf(IOException.class))); } - assertThat(meter.getCount(), + assertThat(meter.count(), is(1L)); } } diff --git a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedHandler.java b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedHandler.java index 9a84da958c..7e9aec6e44 100644 --- a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedHandler.java +++ b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedHandler.java @@ -84,72 +84,72 @@ public InstrumentedHandler(Handler underlying, MetricsRegistry registry) { registry.newGauge(underlying.getClass(), "percent-4xx-1m", new RatioGauge() { @Override protected double getNumerator() { - return responses[3].getOneMinuteRate(); + return responses[3].oneMinuteRate(); } @Override protected double getDenominator() { - return requests.getOneMinuteRate(); + return requests.oneMinuteRate(); } }); registry.newGauge(underlying.getClass(), "percent-4xx-5m", new RatioGauge() { @Override protected double getNumerator() { - return responses[3].getFiveMinuteRate(); + return responses[3].fiveMinuteRate(); } @Override protected double getDenominator() { - return requests.getFiveMinuteRate(); + return requests.fiveMinuteRate(); } }); registry.newGauge(underlying.getClass(), "percent-4xx-15m", new RatioGauge() { @Override protected double getNumerator() { - return responses[3].getFifteenMinuteRate(); + return responses[3].fifteenMinuteRate(); } @Override protected double getDenominator() { - return requests.getFifteenMinuteRate(); + return requests.fifteenMinuteRate(); } }); registry.newGauge(underlying.getClass(), "percent-5xx-1m", new RatioGauge() { @Override protected double getNumerator() { - return responses[4].getOneMinuteRate(); + return responses[4].oneMinuteRate(); } @Override protected double getDenominator() { - return requests.getOneMinuteRate(); + return requests.oneMinuteRate(); } }); registry.newGauge(underlying.getClass(), "percent-5xx-5m", new RatioGauge() { @Override protected double getNumerator() { - return responses[4].getFiveMinuteRate(); + return responses[4].fiveMinuteRate(); } @Override protected double getDenominator() { - return requests.getFiveMinuteRate(); + return requests.fiveMinuteRate(); } }); registry.newGauge(underlying.getClass(), "percent-5xx-15m", new RatioGauge() { @Override protected double getNumerator() { - return responses[4].getFifteenMinuteRate(); + return responses[4].fifteenMinuteRate(); } @Override protected double getDenominator() { - return requests.getFifteenMinuteRate(); + return requests.fifteenMinuteRate(); } }); diff --git a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedQueuedThreadPool.java b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedQueuedThreadPool.java index 97f07824c3..deffa2009c 100644 --- a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedQueuedThreadPool.java +++ b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedQueuedThreadPool.java @@ -27,13 +27,13 @@ protected double getDenominator() { }); registry.newGauge(QueuedThreadPool.class, "active-threads", new Gauge() { @Override - public Integer getValue() { + public Integer value() { return getThreads(); } }); registry.newGauge(QueuedThreadPool.class, "idle-threads", new Gauge() { @Override - public Integer getValue() { + public Integer value() { return getIdleThreads(); } }); diff --git a/metrics-servlet/src/main/java/com/yammer/metrics/servlet/MetricsServlet.java b/metrics-servlet/src/main/java/com/yammer/metrics/servlet/MetricsServlet.java index 56b4e9f4f2..7ad8494dca 100644 --- a/metrics-servlet/src/main/java/com/yammer/metrics/servlet/MetricsServlet.java +++ b/metrics-servlet/src/main/java/com/yammer/metrics/servlet/MetricsServlet.java @@ -6,7 +6,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.yammer.metrics.Metrics; import com.yammer.metrics.core.*; -import com.yammer.metrics.reporting.MetricDispatcher; import com.yammer.metrics.stats.Snapshot; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -189,30 +188,30 @@ private void writeVmMetrics(JsonGenerator json) throws IOException { json.writeFieldName("vm"); json.writeStartObject(); { - json.writeStringField("name", vm.getName()); - json.writeStringField("version", vm.getVersion()); + json.writeStringField("name", vm.name()); + json.writeStringField("version", vm.version()); } json.writeEndObject(); json.writeFieldName("memory"); json.writeStartObject(); { - json.writeNumberField("totalInit", vm.getTotalInit()); - json.writeNumberField("totalUsed", vm.getTotalUsed()); - json.writeNumberField("totalMax", vm.getTotalMax()); - json.writeNumberField("totalCommitted", vm.getTotalCommitted()); - - json.writeNumberField("heapInit", vm.getHeapInit()); - json.writeNumberField("heapUsed", vm.getHeapUsed()); - json.writeNumberField("heapMax", vm.getHeapMax()); - json.writeNumberField("heapCommitted", vm.getHeapCommitted()); - - json.writeNumberField("heap_usage", vm.getHeapUsage()); - json.writeNumberField("non_heap_usage", vm.getNonHeapUsage()); + json.writeNumberField("totalInit", vm.totalInit()); + json.writeNumberField("totalUsed", vm.totalUsed()); + json.writeNumberField("totalMax", vm.totalMax()); + json.writeNumberField("totalCommitted", vm.totalCommitted()); + + json.writeNumberField("heapInit", vm.heapInit()); + json.writeNumberField("heapUsed", vm.heapUsed()); + json.writeNumberField("heapMax", vm.heapMax()); + json.writeNumberField("heapCommitted", vm.heapCommitted()); + + json.writeNumberField("heap_usage", vm.heapUsage()); + json.writeNumberField("non_heap_usage", vm.nonHeapUsage()); json.writeFieldName("memory_pool_usages"); json.writeStartObject(); { - for (Map.Entry pool : vm.getMemoryPoolUsage().entrySet()) { + for (Map.Entry pool : vm.memoryPoolUsage().entrySet()) { json.writeNumberField(pool.getKey(), pool.getValue()); } } @@ -247,16 +246,16 @@ private void writeVmMetrics(JsonGenerator json) throws IOException { } - json.writeNumberField("daemon_thread_count", vm.getDaemonThreadCount()); - json.writeNumberField("thread_count", vm.getThreadCount()); - json.writeNumberField("current_time", clock.getTime()); - json.writeNumberField("uptime", vm.getUptime()); - json.writeNumberField("fd_usage", vm.getFileDescriptorUsage()); + json.writeNumberField("daemon_thread_count", vm.daemonThreadCount()); + json.writeNumberField("thread_count", vm.threadCount()); + json.writeNumberField("current_time", clock.time()); + json.writeNumberField("uptime", vm.uptime()); + json.writeNumberField("fd_usage", vm.fileDescriptorUsage()); json.writeFieldName("thread-states"); json.writeStartObject(); { - for (Map.Entry entry : vm.getThreadStatePercentages() + for (Map.Entry entry : vm.threadStatePercentages() .entrySet()) { json.writeNumberField(entry.getKey().toString().toLowerCase(), entry.getValue()); @@ -267,7 +266,7 @@ private void writeVmMetrics(JsonGenerator json) throws IOException { json.writeFieldName("garbage-collectors"); json.writeStartObject(); { - for (Map.Entry entry : vm.getGarbageCollectors() + for (Map.Entry entry : vm.garbageCollectors() .entrySet()) { json.writeFieldName(entry.getKey()); json.writeStartObject(); @@ -285,8 +284,8 @@ private void writeVmMetrics(JsonGenerator json) throws IOException { } public void writeRegularMetrics(JsonGenerator json, String classPrefix, boolean showFullSamples) throws IOException { - final MetricDispatcher dispatcher = new MetricDispatcher(); - for (Map.Entry> entry : registry.getGroupedMetrics().entrySet()) { + final Context context = new Context(json, showFullSamples); + for (Map.Entry> entry : registry.groupedMetrics().entrySet()) { if (classPrefix == null || entry.getKey().startsWith(classPrefix)) { json.writeFieldName(entry.getKey()); json.writeStartObject(); @@ -294,7 +293,7 @@ public void writeRegularMetrics(JsonGenerator json, String classPrefix, boolean for (Map.Entry subEntry : entry.getValue().entrySet()) { json.writeFieldName(subEntry.getKey().getName()); try { - dispatcher.dispatch(subEntry.getValue(), subEntry.getKey(), this, new Context(json, showFullSamples)); + subEntry.getValue().processWith(this, subEntry.getKey(), context); } catch (Exception e) { LOGGER.warn("Error writing out " + subEntry.getKey(), e); } @@ -311,7 +310,7 @@ public void processHistogram(MetricName name, Histogram histogram, Context conte json.writeStartObject(); { json.writeStringField("type", "histogram"); - json.writeNumberField("count", histogram.getCount()); + json.writeNumberField("count", histogram.count()); writeSummarizable(histogram, json); writeSampling(histogram, json); @@ -328,7 +327,7 @@ public void processCounter(MetricName name, Counter counter, Context context) th json.writeStartObject(); { json.writeStringField("type", "counter"); - json.writeNumberField("count", counter.getCount()); + json.writeNumberField("count", counter.count()); } json.writeEndObject(); } @@ -350,7 +349,7 @@ public void processMeter(MetricName name, Metered meter, Context context) throws json.writeStartObject(); { json.writeStringField("type", "meter"); - json.writeStringField("event_type", meter.getEventType()); + json.writeStringField("event_type", meter.eventType()); writeMeteredFields(meter, json); } json.writeEndObject(); @@ -365,7 +364,7 @@ public void processTimer(MetricName name, Timer timer, Context context) throws E json.writeFieldName("duration"); json.writeStartObject(); { - json.writeStringField("unit", timer.getDurationUnit().toString().toLowerCase()); + json.writeStringField("unit", timer.durationUnit().toString().toLowerCase()); writeSummarizable(timer, json); writeSampling(timer, json); if (context.showFullSamples) { @@ -386,7 +385,7 @@ public void processTimer(MetricName name, Timer timer, Context context) throws E private static Object evaluateGauge(Gauge gauge) { try { - return gauge.getValue(); + return gauge.value(); } catch (RuntimeException e) { LOGGER.warn("Error evaluating gauge", e); return "error reading gauge: " + e.getMessage(); @@ -394,10 +393,10 @@ private static Object evaluateGauge(Gauge gauge) { } private static void writeSummarizable(Summarizable metric, JsonGenerator json) throws IOException { - json.writeNumberField("min", metric.getMin()); - json.writeNumberField("max", metric.getMax()); - json.writeNumberField("mean", metric.getMean()); - json.writeNumberField("std_dev", metric.getStdDev()); + json.writeNumberField("min", metric.min()); + json.writeNumberField("max", metric.max()); + json.writeNumberField("mean", metric.mean()); + json.writeNumberField("std_dev", metric.stdDev()); } private static void writeSampling(Sampling metric, JsonGenerator json) throws IOException { @@ -411,11 +410,11 @@ private static void writeSampling(Sampling metric, JsonGenerator json) throws IO } private static void writeMeteredFields(Metered metered, JsonGenerator json) throws IOException { - json.writeStringField("unit", metered.getRateUnit().toString().toLowerCase()); - json.writeNumberField("count", metered.getCount()); - json.writeNumberField("mean", metered.getMeanRate()); - json.writeNumberField("m1", metered.getOneMinuteRate()); - json.writeNumberField("m5", metered.getFiveMinuteRate()); - json.writeNumberField("m15", metered.getFifteenMinuteRate()); + json.writeStringField("unit", metered.rateUnit().toString().toLowerCase()); + json.writeNumberField("count", metered.count()); + json.writeNumberField("mean", metered.meanRate()); + json.writeNumberField("m1", metered.oneMinuteRate()); + json.writeNumberField("m5", metered.fiveMinuteRate()); + json.writeNumberField("m15", metered.fifteenMinuteRate()); } } diff --git a/metrics-servlet/src/main/java/com/yammer/metrics/servlet/ThreadDumpServlet.java b/metrics-servlet/src/main/java/com/yammer/metrics/servlet/ThreadDumpServlet.java index ec4c99e8b0..556a16c370 100644 --- a/metrics-servlet/src/main/java/com/yammer/metrics/servlet/ThreadDumpServlet.java +++ b/metrics-servlet/src/main/java/com/yammer/metrics/servlet/ThreadDumpServlet.java @@ -43,7 +43,7 @@ protected void doGet(HttpServletRequest req, resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); final OutputStream output = resp.getOutputStream(); try { - vm.getThreadDump(output); + vm.threadDump(output); } finally { output.close(); } diff --git a/metrics-servlet/src/test/java/com/yammer/metrics/servlet/experiments/ExampleServer.java b/metrics-servlet/src/test/java/com/yammer/metrics/servlet/experiments/ExampleServer.java index 843342f968..c1fa50dc2c 100644 --- a/metrics-servlet/src/test/java/com/yammer/metrics/servlet/experiments/ExampleServer.java +++ b/metrics-servlet/src/test/java/com/yammer/metrics/servlet/experiments/ExampleServer.java @@ -22,7 +22,7 @@ public class ExampleServer { static { Metrics.defaultRegistry().newGauge(ExampleServer.class, "boo", new Gauge() { @Override - public Integer getValue() { + public Integer value() { throw new RuntimeException("asplode!"); } }); diff --git a/metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/MetricsServletTest.java b/metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/MetricsServletTest.java index f88c96adbe..a38c6d4566 100644 --- a/metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/MetricsServletTest.java +++ b/metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/MetricsServletTest.java @@ -40,7 +40,7 @@ public class MetricsServletTest { @Before public void setUp() throws Exception { - when(clock.getTime()).thenReturn(12345678L); + when(clock.time()).thenReturn(12345678L); when(request.getMethod()).thenReturn("GET"); @@ -53,33 +53,33 @@ public void setUp() throws Exception { @Test public void generatesVirtualMachineMetrics() throws Exception { - when(vm.getName()).thenReturn("vm"); - when(vm.getVersion()).thenReturn("version"); - when(vm.getTotalInit()).thenReturn(1.0); - when(vm.getTotalUsed()).thenReturn(2.0); - when(vm.getTotalMax()).thenReturn(3.0); - when(vm.getTotalCommitted()).thenReturn(4.0); - when(vm.getHeapInit()).thenReturn(5.0); - when(vm.getHeapUsed()).thenReturn(6.0); - when(vm.getHeapMax()).thenReturn(7.0); - when(vm.getHeapCommitted()).thenReturn(8.0); + when(vm.name()).thenReturn("vm"); + when(vm.version()).thenReturn("version"); + when(vm.totalInit()).thenReturn(1.0); + when(vm.totalUsed()).thenReturn(2.0); + when(vm.totalMax()).thenReturn(3.0); + when(vm.totalCommitted()).thenReturn(4.0); + when(vm.heapInit()).thenReturn(5.0); + when(vm.heapUsed()).thenReturn(6.0); + when(vm.heapMax()).thenReturn(7.0); + when(vm.heapCommitted()).thenReturn(8.0); final Map pools = new TreeMap(); pools.put("one", 100.0); pools.put("two", 200.0); - when(vm.getMemoryPoolUsage()).thenReturn(pools); + when(vm.memoryPoolUsage()).thenReturn(pools); - when(vm.getDaemonThreadCount()).thenReturn(300); - when(vm.getThreadCount()).thenReturn(400); + when(vm.daemonThreadCount()).thenReturn(300); + when(vm.threadCount()).thenReturn(400); - when(vm.getHeapUsage()).thenReturn(34.0); - when(vm.getNonHeapUsage()).thenReturn(37.0); - when(vm.getUptime()).thenReturn(9991L); - when(vm.getFileDescriptorUsage()).thenReturn(0.222); + when(vm.heapUsage()).thenReturn(34.0); + when(vm.nonHeapUsage()).thenReturn(37.0); + when(vm.uptime()).thenReturn(9991L); + when(vm.fileDescriptorUsage()).thenReturn(0.222); final Map threads = new TreeMap(); threads.put(Thread.State.BLOCKED, 0.33); - when(vm.getThreadStatePercentages()).thenReturn(threads); + when(vm.threadStatePercentages()).thenReturn(threads); final Map gcs = new TreeMap(); @@ -88,7 +88,7 @@ public void generatesVirtualMachineMetrics() throws Exception { when(gc.getTime(TimeUnit.MILLISECONDS)).thenReturn(40L); when(gc.getRuns()).thenReturn(20L); gcs.put("one", gc); - when(vm.getGarbageCollectors()).thenReturn(gcs); + when(vm.garbageCollectors()).thenReturn(gcs); final VirtualMachineMetrics.BufferPoolStats direct = mock(VirtualMachineMetrics.BufferPoolStats.class); when(direct.getCount()).thenReturn(1L); @@ -130,7 +130,7 @@ public void generatesVirtualMachineMetrics() throws Exception { public void generatesGauges() throws Exception { registry.newGauge(MetricsServletTest.class, "gauge", new Gauge() { @Override - public Double getValue() { + public Double value() { return 22.2; } }); @@ -168,7 +168,7 @@ public void generatesHistograms() throws Exception { @Test public void generatesMeters() throws Exception { - when(clock.getTick()).thenReturn(100000L, 110000L); + when(clock.tick()).thenReturn(100000L, 110000L); registry.newMeter(MetricsServletTest.class, "meter", "things", TimeUnit.SECONDS) .mark(12); @@ -184,7 +184,7 @@ public void generatesMeters() throws Exception { @Test public void generatesTimers() throws Exception { - when(clock.getTick()).thenReturn(100000L, 110000L); + when(clock.tick()).thenReturn(100000L, 110000L); registry.newTimer(MetricsServletTest.class, "timer").update(100, TimeUnit.MILLISECONDS); diff --git a/metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/ThreadDumpServletTest.java b/metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/ThreadDumpServletTest.java index 2147605379..e95bf9e18e 100644 --- a/metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/ThreadDumpServletTest.java +++ b/metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/ThreadDumpServletTest.java @@ -35,7 +35,7 @@ public void printsAThreadDumpOnGET() throws Exception { final InOrder inOrder = inOrder(response, output, vm); inOrder.verify(response).setStatus(200); inOrder.verify(response).setContentType("text/plain"); - inOrder.verify(vm).getThreadDump(output); + inOrder.verify(vm).threadDump(output); inOrder.verify(output).close(); } } From c7a6219d28f5bd60474c78243842a46bf1db1ce7 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 10 Mar 2013 13:49:11 -0700 Subject: [PATCH 0005/2558] Add metrics-jvm. Not at all ready for prime time yet. --- metrics-jvm/pom.xml | 22 + .../metrics/jvm/BufferPoolGaugeMap.java | 38 ++ .../metrics/jvm/ThreadStatesGaugeMap.java | 45 ++ .../metrics/jvm/VirtualMachineMetrics.java | 507 ++++++++++++++++++ pom.xml | 1 + 5 files changed, 613 insertions(+) create mode 100644 metrics-jvm/pom.xml create mode 100644 metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolGaugeMap.java create mode 100644 metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeMap.java create mode 100644 metrics-jvm/src/main/java/com/yammer/metrics/jvm/VirtualMachineMetrics.java diff --git a/metrics-jvm/pom.xml b/metrics-jvm/pom.xml new file mode 100644 index 0000000000..7b5cd8cd3d --- /dev/null +++ b/metrics-jvm/pom.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + + com.yammer.metrics + metrics-parent + 3.0.0-SNAPSHOT + + + metrics-jvm + + + + com.yammer.metrics + metrics-core + ${project.version} + + + diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolGaugeMap.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolGaugeMap.java new file mode 100644 index 0000000000..2960f3196e --- /dev/null +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolGaugeMap.java @@ -0,0 +1,38 @@ +package com.yammer.metrics.jvm; + +import com.yammer.metrics.Gauge; +import com.yammer.metrics.JmxAttributeGauge; + +import javax.management.*; +import java.util.*; + +public class BufferPoolGaugeMap { + private static final String[] ATTRIBUTES = { "Count", "MemoryUsed", "TotalCapacity" }; + private static final String[] NAMES = { "count", "used", "capacity" }; + private static final String[] POOLS = { "direct", "mapped" }; + + private final MBeanServer mBeanServer; + + public BufferPoolGaugeMap(MBeanServer mBeanServer) { + this.mBeanServer = mBeanServer; + } + + public Map> getGauges() { + final Map> gauges = new HashMap>(); + for (String pool : POOLS) { + for (int i = 0; i < ATTRIBUTES.length; i++) { + final String attribute = ATTRIBUTES[i]; + final String name = NAMES[i]; + try { + final ObjectName on = new ObjectName("java.nio:type=BufferPool,name=" + pool); + mBeanServer.getMBeanInfo(on); + gauges.put("jvm.buffers." + pool + "." + name, + new JmxAttributeGauge(mBeanServer, on, attribute)); + } catch (JMException ignored) { + + } + } + } + return Collections.unmodifiableMap(gauges); + } +} diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeMap.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeMap.java new file mode 100644 index 0000000000..b236cad113 --- /dev/null +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeMap.java @@ -0,0 +1,45 @@ +package com.yammer.metrics.jvm; + +import com.yammer.metrics.Gauge; + +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class ThreadStatesGaugeMap { + private final ThreadMXBean threads; + + public ThreadStatesGaugeMap(ThreadMXBean threads) { + this.threads = threads; + } + + public Map> getGauges() { + final Map> gauges = new HashMap>(); + for (final Thread.State state : Thread.State.values()) { + gauges.put("jvm.thread-states." + state.toString().toLowerCase(), + new Gauge() { + @Override + public Object getValue() { + return getThreadCount(state); + } + }); + } + return Collections.unmodifiableMap(gauges); + } + + private int getThreadCount(Thread.State state) { + final ThreadInfo[] allThreads = threads.getThreadInfo(threads.getAllThreadIds()); + int count = 0; + for (ThreadInfo info : allThreads) { + if (info != null) { + if (info.getThreadState() == state) { + count++; + } + } + } + + return count; + } +} diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/VirtualMachineMetrics.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/VirtualMachineMetrics.java new file mode 100644 index 0000000000..321a699829 --- /dev/null +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/VirtualMachineMetrics.java @@ -0,0 +1,507 @@ +package com.yammer.metrics.jvm; + +import javax.management.*; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.lang.Thread.State; +import java.lang.management.*; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * A collection of Java Virtual Machine metrics. + */ +public class VirtualMachineMetrics { + private static final int MAX_STACK_TRACE_DEPTH = 100; + + private static final VirtualMachineMetrics INSTANCE = new VirtualMachineMetrics( + ManagementFactory.getMemoryMXBean(), + ManagementFactory.getMemoryPoolMXBeans(), + ManagementFactory.getOperatingSystemMXBean(), + ManagementFactory.getThreadMXBean(), + ManagementFactory.getGarbageCollectorMXBeans(), + ManagementFactory.getRuntimeMXBean(), + ManagementFactory.getPlatformMBeanServer()); + + /** + * The default instance of {@link VirtualMachineMetrics}. + * + * @return the default {@link VirtualMachineMetrics instance} + */ + public static VirtualMachineMetrics getInstance() { + return INSTANCE; + } + + /** + * Per-GC statistics. + */ + public static class GarbageCollectorStats { + private final long runs, timeMS; + + private GarbageCollectorStats(long runs, long timeMS) { + this.runs = runs; + this.timeMS = timeMS; + } + + /** + * Returns the number of times the garbage collector has run. + * + * @return the number of times the garbage collector has run + */ + public long getRuns() { + return runs; + } + + /** + * Returns the amount of time in the given unit the garbage collector has taken in total. + * + * @param unit the time unit for the return value + * @return the amount of time in the given unit the garbage collector + */ + public long getTime(TimeUnit unit) { + return unit.convert(timeMS, TimeUnit.MILLISECONDS); + } + } + + /** + * The management interface for a buffer pool, for example a pool of {@link + * java.nio.ByteBuffer#allocateDirect direct} or {@link java.nio.MappedByteBuffer mapped} + * buffers. + */ + public static class BufferPoolStats { + private final long count, memoryUsed, totalCapacity; + + private BufferPoolStats(long count, long memoryUsed, long totalCapacity) { + this.count = count; + this.memoryUsed = memoryUsed; + this.totalCapacity = totalCapacity; + } + + /** + * Returns an estimate of the number of buffers in the pool. + * + * @return An estimate of the number of buffers in this pool + */ + public long getCount() { + return count; + } + + /** + * Returns an estimate of the memory that the Java virtual machine is using for this buffer + * pool. The value returned by this method may differ from the estimate of the total {@link + * #getTotalCapacity capacity} of the buffers in this pool. This difference is explained by + * alignment, memory allocator, and other implementation specific reasons. + * + * @return An estimate of the memory that the Java virtual machine is using for this buffer + * pool in bytes, or {@code -1L} if an estimate of the memory usage is not + * available + */ + public long getMemoryUsed() { + return memoryUsed; + } + + /** + * Returns an estimate of the total capacity of the buffers in this pool. A buffer's + * capacity is the number of elements it contains and the value returned by this method is + * an estimate of the total capacity of buffers in the pool in bytes. + * + * @return An estimate of the total capacity of the buffers in this pool in bytes + */ + public long getTotalCapacity() { + return totalCapacity; + } + } + + private final MemoryMXBean memory; + private final List memoryPools; + private final OperatingSystemMXBean os; + private final ThreadMXBean threads; + private final List garbageCollectors; + private final RuntimeMXBean runtime; + private final MBeanServer mBeanServer; + + VirtualMachineMetrics(MemoryMXBean memory, + List memoryPools, + OperatingSystemMXBean os, + ThreadMXBean threads, + List garbageCollectors, + RuntimeMXBean runtime, MBeanServer mBeanServer) { + this.memory = memory; + this.memoryPools = memoryPools; + this.os = os; + this.threads = threads; + this.garbageCollectors = garbageCollectors; + this.runtime = runtime; + this.mBeanServer = mBeanServer; + } + + /** + * Returns the total initial memory of the current JVM. + * + * @return total Heap and non-heap initial JVM memory in bytes. + */ + public double getTotalInit() { + return memory.getHeapMemoryUsage().getInit() + + memory.getNonHeapMemoryUsage().getInit(); + } + + /** + * Returns the total memory currently used by the current JVM. + * + * @return total Heap and non-heap memory currently used by JVM in bytes. + */ + public double getTotalUsed() { + return memory.getHeapMemoryUsage().getUsed() + + memory.getNonHeapMemoryUsage().getUsed(); + } + + /** + * Returns the total memory currently used by the current JVM. + * + * @return total Heap and non-heap memory currently used by JVM in bytes. + */ + public double getTotalMax() { + return memory.getHeapMemoryUsage().getMax() + + memory.getNonHeapMemoryUsage().getMax(); + } + + /** + * Returns the total memory committed to the JVM. + * + * @return total Heap and non-heap memory currently committed to the JVM in bytes. + */ + public double getTotalCommitted() { + return memory.getHeapMemoryUsage().getCommitted() + + memory.getNonHeapMemoryUsage().getCommitted(); + } + + /** + * Returns the heap initial memory of the current JVM. + * + * @return Heap initial JVM memory in bytes. + */ + public double getHeapInit() { + return memory.getHeapMemoryUsage().getInit(); + } + + /** + * Returns the heap memory currently used by the current JVM. + * + * @return Heap memory currently used by JVM in bytes. + */ + public double getHeapUsed() { + return memory.getHeapMemoryUsage().getUsed(); + } + + /** + * Returns the heap memory currently used by the current JVM. + * + * @return Heap memory currently used by JVM in bytes. + */ + public double getHeapMax() { + return memory.getHeapMemoryUsage().getMax(); + } + + /** + * Returns the heap memory committed to the JVM. + * + * @return Heap memory currently committed to the JVM in bytes. + */ + public double getHeapCommitted() { + return memory.getHeapMemoryUsage().getCommitted(); + } + + /** + * Returns the percentage of the JVM's heap which is being used. + * + * @return the percentage of the JVM's heap which is being used + */ + public double getHeapUsage() { + final MemoryUsage usage = memory.getHeapMemoryUsage(); + return usage.getUsed() / (double) usage.getMax(); + } + + /** + * Returns the percentage of the JVM's non-heap memory (e.g., direct buffers) which is being + * used. + * + * @return the percentage of the JVM's non-heap memory which is being used + */ + public double getNonHeapUsage() { + final MemoryUsage usage = memory.getNonHeapMemoryUsage(); + return usage.getUsed() / (double) usage.getMax(); + } + + /** + * Returns a map of memory pool names to the percentage of that pool which is being used. + * + * @return a map of memory pool names to the percentage of that pool which is being used + */ + public Map getMemoryPoolUsage() { + final Map pools = new TreeMap(); + for (MemoryPoolMXBean pool : memoryPools) { + final double max = pool.getUsage().getMax() == -1 ? + pool.getUsage().getCommitted() : + pool.getUsage().getMax(); + pools.put(pool.getName(), pool.getUsage().getUsed() / max); + } + return Collections.unmodifiableMap(pools); + } + + /** + * Returns the percentage of available file descriptors which are currently in use. + * + * @return the percentage of available file descriptors which are currently in use, or {@code + * NaN} if the running JVM does not have access to this information + */ + public double getFileDescriptorUsage() { + try { + final Method getOpenFileDescriptorCount = os.getClass() + .getDeclaredMethod( + "getOpenFileDescriptorCount"); + getOpenFileDescriptorCount.setAccessible(true); + final Long openFds = (Long) getOpenFileDescriptorCount.invoke(os); + final Method getMaxFileDescriptorCount = os.getClass() + .getDeclaredMethod( + "getMaxFileDescriptorCount"); + getMaxFileDescriptorCount.setAccessible(true); + final Long maxFds = (Long) getMaxFileDescriptorCount.invoke(os); + return openFds.doubleValue() / maxFds.doubleValue(); + } catch (NoSuchMethodException e) { + return Double.NaN; + } catch (IllegalAccessException e) { + return Double.NaN; + } catch (InvocationTargetException e) { + return Double.NaN; + } + } + + /** + * Returns the version of the currently-running jvm. + * + * @return the version of the currently-running jvm, eg "1.6.0_24" + * @see J2SE SDK/JRE Version String + * Naming Convention + */ + public String getVersion() { + return System.getProperty("java.runtime.version"); + } + + /** + * Returns the name of the currently-running jvm. + * + * @return the name of the currently-running jvm, eg "Java HotSpot(TM) Client VM" + * @see System.getProperties() + */ + public String getName() { + return System.getProperty("java.vm.name"); + } + + /** + * Returns the number of seconds the JVM process has been running. + * + * @return the number of seconds the JVM process has been running + */ + public long getUptime() { + return TimeUnit.MILLISECONDS.toSeconds(runtime.getUptime()); + } + + /** + * Returns the number of live threads (includes {@link #getDaemonThreadCount}. + * + * @return the number of live threads + */ + public int getThreadCount() { + return threads.getThreadCount(); + } + + /** + * Returns the number of live daemon threads. + * + * @return the number of live daemon threads + */ + public int getDaemonThreadCount() { + return threads.getDaemonThreadCount(); + } + + /** + * Returns a map of garbage collector names to garbage collector information. + * + * @return a map of garbage collector names to garbage collector information + */ + public Map getGarbageCollectors() { + final Map stats = new HashMap(); + for (GarbageCollectorMXBean gc : garbageCollectors) { + stats.put(gc.getName(), + new GarbageCollectorStats(gc.getCollectionCount(), + gc.getCollectionTime())); + } + return Collections.unmodifiableMap(stats); + } + + /** + * Returns a set of strings describing deadlocked threads, if any are deadlocked. + * + * @return a set of any deadlocked threads + */ + public Set getDeadlockedThreads() { + final long[] threadIds = threads.findDeadlockedThreads(); + if (threadIds != null) { + final Set threads = new HashSet(); + for (ThreadInfo info : this.threads.getThreadInfo(threadIds, MAX_STACK_TRACE_DEPTH)) { + final StringBuilder stackTrace = new StringBuilder(); + for (StackTraceElement element : info.getStackTrace()) { + stackTrace.append("\t at ").append(element.toString()).append('\n'); + } + + threads.add( + String.format( + "%s locked on %s (owned by %s):\n%s", + info.getThreadName(), info.getLockName(), + info.getLockOwnerName(), + stackTrace.toString() + ) + ); + } + return Collections.unmodifiableSet(threads); + } + return Collections.emptySet(); + } + + /** + * Returns a map of thread states to the percentage of all threads which are in that state. + * + * @return a map of thread states to percentages + */ + public Map getThreadStatePercentages() { + final Map conditions = new HashMap(); + for (State state : State.values()) { + conditions.put(state, 0.0); + } + + final long[] allThreadIds = threads.getAllThreadIds(); + final ThreadInfo[] allThreads = threads.getThreadInfo(allThreadIds); + int liveCount = 0; + for (ThreadInfo info : allThreads) { + if (info != null) { + final State state = info.getThreadState(); + conditions.put(state, conditions.get(state) + 1); + liveCount++; + } + } + for (State state : new ArrayList(conditions.keySet())) { + conditions.put(state, conditions.get(state) / liveCount); + } + + return Collections.unmodifiableMap(conditions); + } + + /** + * Dumps all of the threads' current information to an output stream. + * + * @param out an output stream + */ + public void getThreadDump(OutputStream out) { + final ThreadInfo[] threads = this.threads.dumpAllThreads(true, true); + final PrintWriter writer = new PrintWriter(out, true); + + for (int ti = threads.length - 1; ti >= 0; ti--) { + final ThreadInfo t = threads[ti]; + writer.printf("%s id=%d state=%s", + t.getThreadName(), + t.getThreadId(), + t.getThreadState()); + final LockInfo lock = t.getLockInfo(); + if (lock != null && t.getThreadState() != Thread.State.BLOCKED) { + writer.printf("\n - waiting on <0x%08x> (a %s)", + lock.getIdentityHashCode(), + lock.getClassName()); + writer.printf("\n - locked <0x%08x> (a %s)", + lock.getIdentityHashCode(), + lock.getClassName()); + } else if (lock != null && t.getThreadState() == Thread.State.BLOCKED) { + writer.printf("\n - waiting to lock <0x%08x> (a %s)", + lock.getIdentityHashCode(), + lock.getClassName()); + } + + if (t.isSuspended()) { + writer.print(" (suspended)"); + } + + if (t.isInNative()) { + writer.print(" (running in native)"); + } + + writer.println(); + if (t.getLockOwnerName() != null) { + writer.printf(" owned by %s id=%d\n", t.getLockOwnerName(), t.getLockOwnerId()); + } + + final StackTraceElement[] elements = t.getStackTrace(); + final MonitorInfo[] monitors = t.getLockedMonitors(); + + for (int i = 0; i < elements.length; i++) { + final StackTraceElement element = elements[i]; + writer.printf(" at %s\n", element); + for (int j = 1; j < monitors.length; j++) { + final MonitorInfo monitor = monitors[j]; + if (monitor.getLockedStackDepth() == i) { + writer.printf(" - locked %s\n", monitor); + } + } + } + writer.println(); + + final LockInfo[] locks = t.getLockedSynchronizers(); + if (locks.length > 0) { + writer.printf(" Locked synchronizers: count = %d\n", locks.length); + for (LockInfo l : locks) { + writer.printf(" - %s\n", l); + } + writer.println(); + } + } + + writer.println(); + writer.flush(); + } + + public Map getBufferPoolStats() { + try { + final String[] attributes = { "Count", "MemoryUsed", "TotalCapacity" }; + + final ObjectName direct = new ObjectName("java.nio:type=BufferPool,name=direct"); + final ObjectName mapped = new ObjectName("java.nio:type=BufferPool,name=mapped"); + + final AttributeList directAttributes = mBeanServer.getAttributes(direct, attributes); + final AttributeList mappedAttributes = mBeanServer.getAttributes(mapped, attributes); + + final Map stats = new TreeMap(); + + final BufferPoolStats directStats = new BufferPoolStats((Long) ((Attribute) directAttributes + .get(0)).getValue(), + (Long) ((Attribute) directAttributes + .get(1)).getValue(), + (Long) ((Attribute) directAttributes + .get(2)).getValue()); + + stats.put("direct", directStats); + + final BufferPoolStats mappedStats = new BufferPoolStats((Long) ((Attribute) mappedAttributes + .get(0)).getValue(), + (Long) ((Attribute) mappedAttributes + .get(1)).getValue(), + (Long) ((Attribute) mappedAttributes + .get(2)).getValue()); + + stats.put("mapped", mappedStats); + + return Collections.unmodifiableMap(stats); + } catch (JMException e) { + return Collections.emptyMap(); + } + } +} diff --git a/pom.xml b/pom.xml index f1abb7b760..8be55ad249 100644 --- a/pom.xml +++ b/pom.xml @@ -26,6 +26,7 @@ metrics-jdbi metrics-jersey metrics-jetty + metrics-jvm metrics-log4j metrics-logback metrics-servlet From 0ae1fc0292fa2f7509ab7f237f6a752e316a0d86 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 10 Mar 2013 14:01:59 -0700 Subject: [PATCH 0006/2558] Added builders for metric names. --- .../com/yammer/metrics/MetricRegistry.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java index 8c50e652ef..b02e98952b 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -9,6 +9,28 @@ * A registry of metric instances. */ public class MetricRegistry { + public static String name(String name, String... names) { + final StringBuilder builder = new StringBuilder(); + append(builder, name); + for (String s : names) { + append(builder, s); + } + return builder.toString(); + } + + public static String name(Class klass, String... names) { + return name(klass.getCanonicalName(), names); + } + + private static void append(StringBuilder builder, String part) { + if (part != null && !part.isEmpty()) { + if (builder.length() > 0) { + builder.append('.'); + } + builder.append(part); + } + } + private final Clock clock; private final ConcurrentMap metrics; private final List listeners; From facd352da9d65d7b9f15c269eb645252aa1d5c03 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 10 Mar 2013 14:02:10 -0700 Subject: [PATCH 0007/2558] Move metrics-web to 3.0. --- metrics-web/pom.xml | 2 +- .../metrics/web/WebappMetricsFilter.java | 52 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/metrics-web/pom.xml b/metrics-web/pom.xml index 9ad99d7723..476ca261e6 100644 --- a/metrics-web/pom.xml +++ b/metrics-web/pom.xml @@ -18,7 +18,7 @@ com.yammer.metrics metrics-core - 2.2.0 + ${project.version} javax.servlet diff --git a/metrics-web/src/main/java/com/yammer/metrics/web/WebappMetricsFilter.java b/metrics-web/src/main/java/com/yammer/metrics/web/WebappMetricsFilter.java index 4b4205ab49..f252cf6763 100644 --- a/metrics-web/src/main/java/com/yammer/metrics/web/WebappMetricsFilter.java +++ b/metrics-web/src/main/java/com/yammer/metrics/web/WebappMetricsFilter.java @@ -1,7 +1,9 @@ package com.yammer.metrics.web; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.*; +import com.yammer.metrics.Counter; +import com.yammer.metrics.Meter; +import com.yammer.metrics.MetricRegistry; +import com.yammer.metrics.Timer; import javax.servlet.*; import javax.servlet.http.HttpServletResponse; @@ -11,7 +13,8 @@ import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; + +import static com.yammer.metrics.MetricRegistry.name; /** * {@link Filter} implementation which captures request information and a breakdown of the response @@ -32,13 +35,15 @@ public abstract class WebappMetricsFilter implements Filter { /** * Creates a new instance of the filter. * - * @param registryAttribute the attribute used to look up the metrics registry in the servlet context + * @param registryAttribute the attribute used to look up the metrics registry in the + * servlet context * @param meterNamesByStatusCode A map, keyed by status code, of meter names that we are * interested in. * @param otherMetricName The name used for the catch-all meter. */ - public WebappMetricsFilter(String registryAttribute, Map meterNamesByStatusCode, - String otherMetricName) { + protected WebappMetricsFilter(String registryAttribute, + Map meterNamesByStatusCode, + String otherMetricName) { this.registryAttribute = registryAttribute; this.otherMetricName = otherMetricName; this.meterNamesByStatusCode = meterNamesByStatusCode; @@ -46,37 +51,32 @@ public WebappMetricsFilter(String registryAttribute, Map meterN @Override public void init(FilterConfig filterConfig) throws ServletException { - final MetricsRegistry metricsRegistry = getMetricsFactory(filterConfig); + final MetricRegistry metricsRegistry = getMetricsFactory(filterConfig); this.metersByStatusCode = new ConcurrentHashMap(meterNamesByStatusCode .size()); for (Entry entry : meterNamesByStatusCode.entrySet()) { metersByStatusCode.put(entry.getKey(), - metricsRegistry.newMeter(WebappMetricsFilter.class, - entry.getValue(), - "responses", - TimeUnit.SECONDS)); + metricsRegistry.meter(name(WebappMetricsFilter.class, entry.getValue()))); } - this.otherMeter = metricsRegistry.newMeter(WebappMetricsFilter.class, - otherMetricName, - "responses", - TimeUnit.SECONDS); - this.activeRequests = metricsRegistry.newCounter(WebappMetricsFilter.class, "activeRequests"); - this.requestTimer = metricsRegistry.newTimer(WebappMetricsFilter.class, - "requests", - TimeUnit.MILLISECONDS, - TimeUnit.SECONDS); + this.otherMeter = metricsRegistry.meter(name(WebappMetricsFilter.class, + otherMetricName)); + this.activeRequests = metricsRegistry.counter(name(WebappMetricsFilter.class, + "activeRequests")); + this.requestTimer = metricsRegistry.timer(name(WebappMetricsFilter.class, + "requests")); } - private MetricsRegistry getMetricsFactory(FilterConfig filterConfig) { - final MetricsRegistry metricsRegistry; + private MetricRegistry getMetricsFactory(FilterConfig filterConfig) { + final MetricRegistry metricsRegistry; final Object o = filterConfig.getServletContext().getAttribute(this.registryAttribute); - if (o instanceof MetricsRegistry) { - metricsRegistry = (MetricsRegistry) o; + if (o instanceof MetricRegistry) { + metricsRegistry = (MetricRegistry) o; } else { - metricsRegistry = Metrics.defaultRegistry(); + // TODO: 3/10/13 -- figure out how to coordinate on registry names + metricsRegistry = new MetricRegistry("web"); } return metricsRegistry; } @@ -93,7 +93,7 @@ public void doFilter(ServletRequest request, final StatusExposingServletResponse wrappedResponse = new StatusExposingServletResponse((HttpServletResponse) response); activeRequests.inc(); - final TimerContext context = requestTimer.time(); + final Timer.Context context = requestTimer.time(); try { chain.doFilter(request, wrappedResponse); } finally { From 50bcd332ff8be4a4bb5e2d80c1a6e58417a582bb Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 10 Mar 2013 14:16:29 -0700 Subject: [PATCH 0008/2558] Bring annotations around. --- .../metrics/annotation/ExceptionMetered.java | 31 ++++--------------- .../com/yammer/metrics/annotation/Gauge.java | 18 +++++------ .../yammer/metrics/annotation/Metered.java | 25 +++------------ .../com/yammer/metrics/annotation/Timed.java | 26 +++------------- 4 files changed, 23 insertions(+), 77 deletions(-) diff --git a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/ExceptionMetered.java b/metrics-annotation/src/main/java/com/yammer/metrics/annotation/ExceptionMetered.java index 83b391b4e9..81cfd17fa9 100644 --- a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/ExceptionMetered.java +++ b/metrics-annotation/src/main/java/com/yammer/metrics/annotation/ExceptionMetered.java @@ -4,15 +4,13 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.util.concurrent.TimeUnit; /** - * An annotation for marking a method of a Guice-provided object as metered. + * An annotation for marking a method of an annotated object as metered. *

* Given a method like this: *


- *     \@ExceptionMetered(name = "fancyName", eventType = "namings", rateUnit = TimeUnit.SECONDS,
- * cause=IllegalArgumentException.class)
+ *     \@ExceptionMetered(name = "fancyName", cause=IllegalArgumentException.class)
  *     public String fancyName(String name) {
  *         return "Sir Captain " + name;
  *     }
@@ -45,19 +43,7 @@
     /**
      * The default suffix for meter names.
      */
-    String DEFAULT_NAME_SUFFIX = "Exceptions";
-
-    /**
-     * The group of the timer. If not specified, the meter will be given a group based on
-     * the package.
-     */
-    String group() default "";
-
-    /**
-     * The type of the timer. If not specified the meter will be given a type based on
-     * the class name.
-     */
-    String type() default "";
+    String DEFAULT_NAME_SUFFIX = "exceptions";
 
     /**
      * The name of the meter. If not specified, the meter will be given a name based on the method
@@ -66,15 +52,10 @@
     String name() default "";
 
     /**
-     * The name of the type of events the meter is measuring. The event type defaults to
-     * "exceptions".
-     */
-    String eventType() default "exceptions";
-
-    /**
-     * The time unit of the meter's rate. Defaults to Seconds.
+     * If {@code true}, use the given name an as absolute name. If {@code false}, use the given name
+     * relative to the annotated class.
      */
-    TimeUnit rateUnit() default TimeUnit.SECONDS;
+    boolean absolute() default false;
 
     /**
      * The type of exceptions that the meter will catch and count.
diff --git a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Gauge.java b/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Gauge.java
index ffe8c3df47..f4ded680bd 100644
--- a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Gauge.java
+++ b/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Gauge.java
@@ -6,7 +6,7 @@
 import java.lang.annotation.Target;
 
 /**
- * An annotation for marking a method of a Guice-provided object as a gauge.
+ * An annotation for marking a method of an annotated object as a gauge.
  * 

* Given a method like this: *


@@ -20,20 +20,16 @@
  * annotated method's return value as its value.
  */
 @Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD, ElementType.FIELD})
+@Target({ ElementType.METHOD, ElementType.FIELD })
 public @interface Gauge {
     /**
-     * The gauge's group.
-     */
-    String group() default "";
-
-    /**
-     * The gauge's type.
+     * The gauge's name.
      */
-    String type() default "";
+    String name() default "";
 
     /**
-     * The gauge's name.
+     * If {@code true}, use the given name an as absolute name. If {@code false}, use the given name
+     * relative to the annotated class.
      */
-    String name() default "";
+    boolean absolute() default false;
 }
diff --git a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Metered.java b/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Metered.java
index d5087125a9..5243cbc40b 100644
--- a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Metered.java
+++ b/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Metered.java
@@ -4,14 +4,13 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
-import java.util.concurrent.TimeUnit;
 
 /**
- * An annotation for marking a method of a Guice-provided object as metered.
+ * An annotation for marking a method of an annotated object as metered.
  * 

* Given a method like this: *


- *     \@Metered(name = "fancyName", eventType = "namings", rateUnit = TimeUnit.SECONDS)
+ *     \@Metered(name = "fancyName")
  *     public String fancyName(String name) {
  *         return "Sir Captain " + name;
  *     }
@@ -23,28 +22,14 @@
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.METHOD)
 public @interface Metered {
-    /**
-     * The group of the timer.
-     */
-    String group() default "";
-
-    /**
-     * The type of the timer.
-     */
-    String type() default "";
-
     /**
      * The name of the meter.
      */
     String name() default "";
 
     /**
-     * The name of the type of events the meter is measuring.
-     */
-    String eventType() default "calls";
-
-    /**
-     * The time unit of the meter's rate.
+     * If {@code true}, use the given name an as absolute name. If {@code false}, use the given name
+     * relative to the annotated class.
      */
-    TimeUnit rateUnit() default TimeUnit.SECONDS;
+    boolean absolute() default false;
 }
diff --git a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Timed.java b/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Timed.java
index 551e3fd8b2..aa1f36cac3 100644
--- a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Timed.java
+++ b/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Timed.java
@@ -4,15 +4,13 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
-import java.util.concurrent.TimeUnit;
 
 /**
- * An annotation for marking a method of a Guice-provided object as timed.
+ * An annotation for marking a method of an annotated object as timed.
  * 

* Given a method like this: *


- *     \@Timed(name = "fancyName", rateUnit = TimeUnit.SECONDS, durationUnit =
- * TimeUnit.MICROSECONDS)
+ *     \@Timed(name = "fancyName")
  *     public String fancyName(String name) {
  *         return "Sir Captain " + name;
  *     }
@@ -24,28 +22,14 @@
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.METHOD)
 public @interface Timed {
-    /**
-     * The group of the timer.
-     */
-    String group() default "";
-
-    /**
-     * The type of the timer.
-     */
-    String type() default "";
-
     /**
      * The name of the timer.
      */
     String name() default "";
 
     /**
-     * The time unit of the timer's rate.
-     */
-    TimeUnit rateUnit() default TimeUnit.SECONDS;
-
-    /**
-     * The time unit of the timer's duration.
+     * If {@code true}, use the given name an as absolute name. If {@code false}, use the given name
+     * relative to the annotated class.
      */
-    TimeUnit durationUnit() default TimeUnit.MILLISECONDS;
+    boolean absolute() default false;
 }

From eb40138a662754e1d968e9e3d24270d0926acdad Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Sun, 10 Mar 2013 14:33:19 -0700
Subject: [PATCH 0009/2558] Revamp metrics-jersey.

---
 metrics-jersey/pom.xml                        |  4 +-
 ...rumentedResourceMethodDispatchAdapter.java | 18 ++---
 ...umentedResourceMethodDispatchProvider.java | 79 ++++++++++---------
 .../jersey/tests/MetricsJerseyTest.java       | 78 ------------------
 .../tests/SingletonMetricsJerseyTest.java     | 76 ++++++++----------
 5 files changed, 81 insertions(+), 174 deletions(-)
 delete mode 100644 metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/MetricsJerseyTest.java

diff --git a/metrics-jersey/pom.xml b/metrics-jersey/pom.xml
index 87c1b69c92..6aa2a30d1e 100644
--- a/metrics-jersey/pom.xml
+++ b/metrics-jersey/pom.xml
@@ -24,12 +24,12 @@
         
             com.yammer.metrics
             metrics-core
-            2.2.0
+            ${project.version}
         
         
             com.yammer.metrics
             metrics-annotation
-            2.2.0
+            ${project.version}
         
         
             com.sun.jersey
diff --git a/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java b/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java
index 801495faf0..e9cd1f2f36 100644
--- a/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java
+++ b/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java
@@ -2,8 +2,7 @@
 
 import com.sun.jersey.spi.container.ResourceMethodDispatchAdapter;
 import com.sun.jersey.spi.container.ResourceMethodDispatchProvider;
-import com.yammer.metrics.Metrics;
-import com.yammer.metrics.core.MetricsRegistry;
+import com.yammer.metrics.MetricRegistry;
 
 import javax.ws.rs.ext.Provider;
 
@@ -13,16 +12,9 @@
  */
 @Provider
 public class InstrumentedResourceMethodDispatchAdapter implements ResourceMethodDispatchAdapter {
-    private MetricsRegistry registry;
+    // TODO: 3/10/13  -- figure out how to coordinate on registry names
 
-    /**
-     * Construct a resource method dispatch adapter using the default
-     * metrics registry
-     *
-     */
-    public InstrumentedResourceMethodDispatchAdapter() {
-        this(Metrics.defaultRegistry());
-    }
+    private MetricRegistry registry;
 
     /**
      * Construct a resource method dispatch adapter using the given
@@ -31,9 +23,9 @@ public InstrumentedResourceMethodDispatchAdapter() {
      * When using this constructor, the {@link InstrumentedResourceMethodDispatchAdapter}
      * should be added to a Jersey {@code ResourceConfig} as a singleton
      *
-     * @param registry a {@link MetricsRegistry}
+     * @param registry a {@link MetricRegistry}
      */
-    public InstrumentedResourceMethodDispatchAdapter( MetricsRegistry registry ) {
+    public InstrumentedResourceMethodDispatchAdapter(MetricRegistry registry) {
         this.registry = registry;
     }
 
diff --git a/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java b/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java
index 476af1962a..85182d63be 100644
--- a/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java
+++ b/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java
@@ -4,13 +4,14 @@
 import com.sun.jersey.api.model.AbstractResourceMethod;
 import com.sun.jersey.spi.container.ResourceMethodDispatchProvider;
 import com.sun.jersey.spi.dispatch.RequestDispatcher;
+import com.yammer.metrics.Meter;
+import com.yammer.metrics.MetricRegistry;
+import com.yammer.metrics.Timer;
 import com.yammer.metrics.annotation.ExceptionMetered;
 import com.yammer.metrics.annotation.Metered;
 import com.yammer.metrics.annotation.Timed;
-import com.yammer.metrics.core.*;
-import sun.misc.Unsafe;
 
-import java.lang.reflect.Field;
+import static com.yammer.metrics.MetricRegistry.name;
 
 class InstrumentedResourceMethodDispatchProvider implements ResourceMethodDispatchProvider {
     private static class TimedRequestDispatcher implements RequestDispatcher {
@@ -24,7 +25,7 @@ private TimedRequestDispatcher(RequestDispatcher underlying, Timer timer) {
 
         @Override
         public void dispatch(Object resource, HttpContext httpContext) {
-            final TimerContext context = timer.time();
+            final Timer.Context context = timer.time();
             try {
                 underlying.dispatch(resource, httpContext);
             } finally {
@@ -71,25 +72,25 @@ public void dispatch(Object resource, HttpContext httpContext) {
                         (e.getCause() != null && exceptionClass.isAssignableFrom(e.getCause().getClass()))) {
                     meter.mark();
                 }
-                getUnsafe().throwException(e);
+                InstrumentedResourceMethodDispatchProvider.throwUnchecked(e);
             }
         }
     }
 
-    private static Unsafe getUnsafe() {
-        try {
-            final Field field = Unsafe.class.getDeclaredField("theUnsafe");
-            field.setAccessible(true);
-            return (Unsafe) field.get(null);
-        } catch (Exception ex) {
-            throw new RuntimeException("can't get Unsafe instance", ex);
-        }
+    /*
+     * A dirty hack to allow us to throw exceptions of any type without bringing down the unsafe
+     * thunder.
+     */
+    @SuppressWarnings("unchecked")
+    private static  void throwUnchecked(Throwable e) throws T {
+        throw (T) e;
     }
 
     private final ResourceMethodDispatchProvider provider;
-    private final MetricsRegistry registry;
+    private final MetricRegistry registry;
 
-    public InstrumentedResourceMethodDispatchProvider(ResourceMethodDispatchProvider provider, MetricsRegistry registry) {
+    public InstrumentedResourceMethodDispatchProvider(ResourceMethodDispatchProvider provider,
+                                                      MetricRegistry registry) {
         this.provider = provider;
         this.registry = registry;
     }
@@ -103,39 +104,43 @@ public RequestDispatcher create(AbstractResourceMethod method) {
 
         if (method.getMethod().isAnnotationPresent(Timed.class)) {
             final Timed annotation = method.getMethod().getAnnotation(Timed.class);
-            final Class klass = method.getDeclaringResource().getResourceClass();
-            final String group = MetricName.chooseGroup(annotation.group(), klass);
-            final String type = MetricName.chooseType(annotation.type(), klass);
-            final String name = MetricName.chooseName(annotation.name(), method.getMethod());
-            final MetricName metricName = new MetricName(group, type, name);
-            final Timer timer = registry.newTimer(metricName, annotation.durationUnit(), annotation.rateUnit());
+            final String name = chooseName(annotation.name(), annotation.absolute(), method);
+            final Timer timer = registry.timer(name);
             dispatcher = new TimedRequestDispatcher(dispatcher, timer);
         }
 
         if (method.getMethod().isAnnotationPresent(Metered.class)) {
             final Metered annotation = method.getMethod().getAnnotation(Metered.class);
-            Class klass = method.getDeclaringResource().getResourceClass();
-            String group = MetricName.chooseGroup(annotation.group(), klass);
-            String type = MetricName.chooseType(annotation.type(), klass);
-            String name = MetricName.chooseName(annotation.name(), method.getMethod());
-            MetricName metricName = new MetricName(group, type, name);
-            final Meter meter = registry.newMeter(metricName, annotation.eventType(), annotation.rateUnit());
+            final String name = chooseName(annotation.name(), annotation.absolute(), method);
+            final Meter meter = registry.meter(name);
             dispatcher = new MeteredRequestDispatcher(dispatcher, meter);
         }
 
         if (method.getMethod().isAnnotationPresent(ExceptionMetered.class)) {
-            final ExceptionMetered annotation = method.getMethod().getAnnotation(ExceptionMetered.class);
-            Class klass = method.getDeclaringResource().getResourceClass();
-            String group = MetricName.chooseGroup(annotation.group(), klass);
-            String type = MetricName.chooseType(annotation.type(), klass);
-            String name = annotation.name() == null || annotation.name().equals("") ?
-                    method.getMethod().getName() + ExceptionMetered.DEFAULT_NAME_SUFFIX : annotation
-                    .name();
-            MetricName metricName = new MetricName(group, type, name);
-            final Meter meter = registry.newMeter(metricName, annotation.eventType(), annotation.rateUnit());
-            dispatcher = new ExceptionMeteredRequestDispatcher(dispatcher, meter, annotation.cause());
+            final ExceptionMetered annotation = method.getMethod()
+                                                      .getAnnotation(ExceptionMetered.class);
+            final String name = chooseName(annotation.name(),
+                                           annotation.absolute(),
+                                           method,
+                                           ExceptionMetered.DEFAULT_NAME_SUFFIX);
+            final Meter meter = registry.meter(name);
+            dispatcher = new ExceptionMeteredRequestDispatcher(dispatcher,
+                                                               meter,
+                                                               annotation.cause());
         }
 
         return dispatcher;
     }
+
+    private String chooseName(String explicitName, boolean absolute, AbstractResourceMethod method, String... suffixes) {
+        if (explicitName != null && !explicitName.isEmpty()) {
+            if (absolute) {
+                return explicitName;
+            }
+            return name(method.getDeclaringResource().getResourceClass(), explicitName);
+        }
+        return name(name(method.getDeclaringResource().getResourceClass(),
+                         method.getMethod().getName()),
+                    suffixes);
+    }
 }
diff --git a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/MetricsJerseyTest.java b/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/MetricsJerseyTest.java
deleted file mode 100644
index 49db785f51..0000000000
--- a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/MetricsJerseyTest.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package com.yammer.metrics.jersey.tests;
-
-import com.sun.jersey.api.container.MappableContainerException;
-import com.sun.jersey.test.framework.AppDescriptor;
-import com.sun.jersey.test.framework.JerseyTest;
-import com.sun.jersey.test.framework.LowLevelAppDescriptor;
-import com.yammer.metrics.Metrics;
-import com.yammer.metrics.core.Meter;
-import com.yammer.metrics.core.Timer;
-import com.yammer.metrics.jersey.InstrumentedResourceMethodDispatchAdapter;
-import com.yammer.metrics.jersey.tests.resources.InstrumentedResource;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import static org.hamcrest.Matchers.instanceOf;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-
-public class MetricsJerseyTest extends JerseyTest {
-    static {
-        Logger.getLogger("com.sun.jersey").setLevel(Level.OFF);
-    }
-
-    @Override
-    protected AppDescriptor configure() {
-        return new LowLevelAppDescriptor.Builder(
-                InstrumentedResourceMethodDispatchAdapter.class,
-                InstrumentedResource.class
-        ).build();
-    }
-
-    @Test
-    public void timedMethodsAreTimed() {
-        assertThat(resource().path("timed").get(String.class),
-                   is("yay"));
-
-        final Timer timer = Metrics.defaultRegistry().newTimer(InstrumentedResource.class, "timed");
-        assertThat(timer.count(),
-                   is(1L));
-    }
-
-    @Test
-    public void meteredMethodsAreMetered() {
-        assertThat(resource().path("metered").get(String.class),
-                   is("woo"));
-
-        final Meter meter = Metrics.defaultRegistry().newMeter(InstrumentedResource.class, "metered", "blah", TimeUnit.SECONDS);
-        assertThat(meter.count(),
-                   is(1L));
-    }
-
-    @Test
-    public void exceptionMeteredMethodsAreExceptionMetered() {
-        final Meter meter = Metrics.defaultRegistry().newMeter(InstrumentedResource.class, "exceptionMeteredExceptions", "blah", TimeUnit.SECONDS);
-        
-        assertThat(resource().path("exception-metered").get(String.class),
-                   is("fuh"));
-
-        assertThat(meter.count(),
-                   is(0L));
-        
-        try {
-            resource().path("exception-metered").queryParam("splode", "true").get(String.class);
-            fail("should have thrown a MappableContainerException, but didn't");
-        } catch (MappableContainerException e) {
-            assertThat(e.getCause(),
-                       is(instanceOf(IOException.class)));
-        }
-
-        assertThat(meter.count(),
-                   is(1L));
-    }
-}
diff --git a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/SingletonMetricsJerseyTest.java b/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/SingletonMetricsJerseyTest.java
index 02f4b4ca55..9a6fa356b0 100644
--- a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/SingletonMetricsJerseyTest.java
+++ b/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/SingletonMetricsJerseyTest.java
@@ -5,25 +5,20 @@
 import com.sun.jersey.test.framework.AppDescriptor;
 import com.sun.jersey.test.framework.JerseyTest;
 import com.sun.jersey.test.framework.LowLevelAppDescriptor;
-import com.yammer.metrics.Metrics;
-import com.yammer.metrics.core.Meter;
-import com.yammer.metrics.core.MetricsRegistry;
-import com.yammer.metrics.core.Timer;
+import com.yammer.metrics.Meter;
+import com.yammer.metrics.MetricRegistry;
+import com.yammer.metrics.Timer;
 import com.yammer.metrics.jersey.InstrumentedResourceMethodDispatchAdapter;
 import com.yammer.metrics.jersey.tests.resources.InstrumentedResource;
 import org.junit.Test;
 
 import java.io.IOException;
-import java.util.concurrent.TimeUnit;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import static org.hamcrest.Matchers.instanceOf;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.sameInstance;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
+import static com.yammer.metrics.MetricRegistry.name;
+import static org.fest.assertions.api.Assertions.assertThat;
+import static org.fest.assertions.api.Assertions.failBecauseExceptionWasNotThrown;
 
 /**
  * Tests importing {@link InstrumentedResourceMethodDispatchAdapter} as a singleton
@@ -34,11 +29,11 @@ public class SingletonMetricsJerseyTest extends JerseyTest {
         Logger.getLogger("com.sun.jersey").setLevel(Level.OFF);
     }
 
-    private MetricsRegistry registry;
+    private MetricRegistry registry;
 
     @Override
     protected AppDescriptor configure() {
-        this.registry = new MetricsRegistry();
+        this.registry = new MetricRegistry("test");
 
         final DefaultResourceConfig config = new DefaultResourceConfig();
         config.getSingletons().add(new InstrumentedResourceMethodDispatchAdapter(registry));
@@ -47,55 +42,48 @@ protected AppDescriptor configure() {
         return new LowLevelAppDescriptor.Builder(config).build();
     }
 
-    @Test
-    public void registryIsNotDefault() {
-        final Timer timer1 = registry.newTimer(InstrumentedResource.class, "timed");
-        final Timer timer2 = registry.newTimer(InstrumentedResource.class, "timed");
-        final Timer timer3 = Metrics.defaultRegistry().newTimer(InstrumentedResource.class, "timed");
-
-        assertThat(timer1, sameInstance(timer2));
-        assertThat(timer1, not(sameInstance(timer3)));
-    }
-
     @Test
     public void timedMethodsAreTimed() {
-        assertThat(resource().path("timed").get(String.class),
-                   is("yay"));
+        assertThat(resource().path("timed").get(String.class))
+                .isEqualTo("yay");
 
-        final Timer timer = registry.newTimer(InstrumentedResource.class, "timed");
-        assertThat(timer.count(),
-                   is(1L));
+        final Timer timer = registry.timer(name(InstrumentedResource.class, "timed"));
+
+        assertThat(timer.getCount())
+                .isEqualTo(1);
     }
 
     @Test
     public void meteredMethodsAreMetered() {
-        assertThat(resource().path("metered").get(String.class),
-                   is("woo"));
+        assertThat(resource().path("metered").get(String.class))
+                .isEqualTo("woo");
 
-        final Meter meter = registry.newMeter(InstrumentedResource.class, "metered", "blah", TimeUnit.SECONDS);
-        assertThat(meter.count(),
-                   is(1L));
+        final Meter meter = registry.meter(name(InstrumentedResource.class, "metered"));
+        assertThat(meter.getCount())
+                .isEqualTo(1);
     }
 
     @Test
     public void exceptionMeteredMethodsAreExceptionMetered() {
-        final Meter meter = registry.newMeter(InstrumentedResource.class, "exceptionMeteredExceptions", "blah", TimeUnit.SECONDS);
-        
-        assertThat(resource().path("exception-metered").get(String.class),
-                   is("fuh"));
+        final Meter meter = registry.meter(name(InstrumentedResource.class,
+                                                "exceptionMetered",
+                                                "exceptions"));
+
+        assertThat(resource().path("exception-metered").get(String.class))
+                .isEqualTo("fuh");
 
-        assertThat(meter.count(),
-                   is(0L));
+        assertThat(meter.getCount())
+                .isZero();
         
         try {
             resource().path("exception-metered").queryParam("splode", "true").get(String.class);
-            fail("should have thrown a MappableContainerException, but didn't");
+            failBecauseExceptionWasNotThrown(MappableContainerException.class);
         } catch (MappableContainerException e) {
-            assertThat(e.getCause(),
-                       is(instanceOf(IOException.class)));
+            assertThat(e.getCause())
+                    .isInstanceOf(IOException.class);
         }
 
-        assertThat(meter.count(),
-                   is(1L));
+        assertThat(meter.getCount())
+                .isEqualTo(1);
     }
 }

From 923d7bdc00aaf87890e1820f7e97845e399e4501 Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Sun, 10 Mar 2013 14:42:08 -0700
Subject: [PATCH 0010/2558] Revamp metrics-httpclient.

---
 metrics-httpclient/pom.xml                    |  2 +-
 .../InstrumentedClientConnManager.java        | 46 ++++++++-----------
 .../httpclient/InstrumentedHttpClient.java    | 20 ++++----
 .../InstrumentedRequestDirector.java          | 33 ++++++-------
 .../tests/InstrumentedHttpClientTest.java     |  4 +-
 5 files changed, 48 insertions(+), 57 deletions(-)

diff --git a/metrics-httpclient/pom.xml b/metrics-httpclient/pom.xml
index 41cae7e594..6edf6d1b40 100644
--- a/metrics-httpclient/pom.xml
+++ b/metrics-httpclient/pom.xml
@@ -16,7 +16,7 @@
         
             com.yammer.metrics
             metrics-core
-            2.2.0
+            ${project.version}
         
         
             org.apache.httpcomponents
diff --git a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedClientConnManager.java b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedClientConnManager.java
index c23a2e6e08..d530e0c9f5 100644
--- a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedClientConnManager.java
+++ b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedClientConnManager.java
@@ -1,8 +1,7 @@
 package com.yammer.metrics.httpclient;
 
-import com.yammer.metrics.Metrics;
-import com.yammer.metrics.core.Gauge;
-import com.yammer.metrics.core.MetricsRegistry;
+import com.yammer.metrics.Gauge;
+import com.yammer.metrics.MetricRegistry;
 import org.apache.http.conn.ClientConnectionManager;
 import org.apache.http.conn.DnsResolver;
 import org.apache.http.conn.scheme.SchemeRegistry;
@@ -12,69 +11,62 @@
 
 import java.util.concurrent.TimeUnit;
 
+import static com.yammer.metrics.MetricRegistry.name;
+
 /**
  * A {@link ClientConnectionManager} which monitors the number of open connections.
  */
 public class InstrumentedClientConnManager extends PoolingClientConnectionManager {
-    public InstrumentedClientConnManager() {
-        this(SchemeRegistryFactory.createDefault());
-    }
-
-    public InstrumentedClientConnManager(SchemeRegistry registry) {
-        this(registry, -1, TimeUnit.MILLISECONDS);
+    public InstrumentedClientConnManager(MetricRegistry metricRegistry) {
+        this(metricRegistry, SchemeRegistryFactory.createDefault());
     }
 
-    public InstrumentedClientConnManager(SchemeRegistry registry,
-                                         long connTTL,
-                                         TimeUnit connTTLTimeUnit) {
-        this(Metrics.defaultRegistry(), registry, connTTL, connTTLTimeUnit);
+    public InstrumentedClientConnManager(MetricRegistry metricsRegistry,
+                                         SchemeRegistry registry) {
+        this(metricsRegistry, registry, -1, TimeUnit.MILLISECONDS);
     }
 
-    public InstrumentedClientConnManager(MetricsRegistry metricsRegistry,
+    public InstrumentedClientConnManager(MetricRegistry metricsRegistry,
                                          SchemeRegistry registry,
                                          long connTTL,
                                          TimeUnit connTTLTimeUnit) {
         this(metricsRegistry, registry, connTTL, connTTLTimeUnit, new SystemDefaultDnsResolver());
     }
 
-    public InstrumentedClientConnManager(MetricsRegistry metricsRegistry,
+    public InstrumentedClientConnManager(MetricRegistry metricsRegistry,
                                          SchemeRegistry schemeRegistry,
                                          long connTTL,
                                          TimeUnit connTTLTimeUnit,
                                          DnsResolver dnsResolver) {
         super(schemeRegistry, connTTL, connTTLTimeUnit, dnsResolver);
-        metricsRegistry.newGauge(ClientConnectionManager.class,
-                                 "available-connections",
+        metricsRegistry.register(name(ClientConnectionManager.class, "available-connections"),
                                  new Gauge() {
                                      @Override
-                                     public Integer value() {
+                                     public Integer getValue() {
                                          // this acquires a lock on the connection pool; remove if contention sucks
                                          return getTotalStats().getAvailable();
                                      }
                                  });
-        metricsRegistry.newGauge(ClientConnectionManager.class,
-                                 "leased-connections",
+        metricsRegistry.register(name(ClientConnectionManager.class, "leased-connections"),
                                  new Gauge() {
                                      @Override
-                                     public Integer value() {
+                                     public Integer getValue() {
                                          // this acquires a lock on the connection pool; remove if contention sucks
                                          return getTotalStats().getLeased();
                                      }
                                  });
-        metricsRegistry.newGauge(ClientConnectionManager.class,
-                                 "max-connections",
+        metricsRegistry.register(name(ClientConnectionManager.class, "max-connections"),
                                  new Gauge() {
                                      @Override
-                                     public Integer value() {
+                                     public Integer getValue() {
                                          // this acquires a lock on the connection pool; remove if contention sucks
                                          return getTotalStats().getMax();
                                      }
                                  });
-        metricsRegistry.newGauge(ClientConnectionManager.class,
-                                 "pending-connections",
+        metricsRegistry.register(name(ClientConnectionManager.class, "pending-connections"),
                                  new Gauge() {
                                      @Override
-                                     public Integer value() {
+                                     public Integer getValue() {
                                          // this acquires a lock on the connection pool; remove if contention sucks
                                          return getTotalStats().getPending();
                                      }
diff --git a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedHttpClient.java b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedHttpClient.java
index f22f8e6ed0..ceeb1f7136 100644
--- a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedHttpClient.java
+++ b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedHttpClient.java
@@ -1,7 +1,6 @@
 package com.yammer.metrics.httpclient;
 
-import com.yammer.metrics.Metrics;
-import com.yammer.metrics.core.MetricsRegistry;
+import com.yammer.metrics.MetricRegistry;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.http.ConnectionReuseStrategy;
@@ -17,25 +16,22 @@
 public class InstrumentedHttpClient extends DefaultHttpClient {
     private final Log log = LogFactory.getLog(getClass());
 
-    private final MetricsRegistry registry;
+    private final MetricRegistry registry;
 
-    public InstrumentedHttpClient(MetricsRegistry registry,
+    public InstrumentedHttpClient(MetricRegistry registry,
                                   InstrumentedClientConnManager manager,
                                   HttpParams params) {
         super(manager, params);
         this.registry = registry;
     }
 
-    public InstrumentedHttpClient(InstrumentedClientConnManager manager, HttpParams params) {
-        this(Metrics.defaultRegistry(), manager, params);
-    }
-
-    public InstrumentedHttpClient(HttpParams params) {
-        this(new InstrumentedClientConnManager(), params);
+    public InstrumentedHttpClient(MetricRegistry registry,
+                                  HttpParams params) {
+        this(registry, new InstrumentedClientConnManager(registry), params);
     }
 
-    public InstrumentedHttpClient() {
-        this(null);
+    public InstrumentedHttpClient(MetricRegistry registry) {
+        this(registry, new InstrumentedClientConnManager(registry), null);
     }
 
     @Override
diff --git a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedRequestDirector.java b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedRequestDirector.java
index 9c3feb441f..686f437f12 100644
--- a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedRequestDirector.java
+++ b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedRequestDirector.java
@@ -1,8 +1,7 @@
 package com.yammer.metrics.httpclient;
 
-import com.yammer.metrics.core.MetricsRegistry;
-import com.yammer.metrics.core.Timer;
-import com.yammer.metrics.core.TimerContext;
+import com.yammer.metrics.MetricRegistry;
+import com.yammer.metrics.Timer;
 import org.apache.commons.logging.Log;
 import org.apache.http.*;
 import org.apache.http.client.*;
@@ -17,6 +16,8 @@
 
 import java.io.IOException;
 
+import static com.yammer.metrics.MetricRegistry.name;
+
 class InstrumentedRequestDirector extends DefaultRequestDirector {
     private final static String GET = "GET", POST = "POST", HEAD = "HEAD", PUT = "PUT",
             OPTIONS = "OPTIONS", DELETE = "DELETE", TRACE = "TRACE",
@@ -34,7 +35,7 @@ class InstrumentedRequestDirector extends DefaultRequestDirector {
     private final Timer patchTimer;
     private final Timer otherTimer;
 
-    InstrumentedRequestDirector(MetricsRegistry registry,
+    InstrumentedRequestDirector(MetricRegistry registry,
                                 Log log,
                                 HttpRequestExecutor requestExec,
                                 ClientConnectionManager conman,
@@ -61,22 +62,22 @@ class InstrumentedRequestDirector extends DefaultRequestDirector {
               proxyAuthStrategy,
               userTokenHandler,
               params);
-        getTimer = registry.newTimer(HttpClient.class, "get-requests");
-        postTimer = registry.newTimer(HttpClient.class, "post-requests");
-        headTimer = registry.newTimer(HttpClient.class, "head-requests");
-        putTimer = registry.newTimer(HttpClient.class, "put-requests");
-        deleteTimer = registry.newTimer(HttpClient.class, "delete-requests");
-        optionsTimer = registry.newTimer(HttpClient.class, "options-requests");
-        traceTimer = registry.newTimer(HttpClient.class, "trace-requests");
-        connectTimer = registry.newTimer(HttpClient.class, "connect-requests");
-        moveTimer = registry.newTimer(HttpClient.class, "move-requests");
-        patchTimer = registry.newTimer(HttpClient.class, "patch-requests");
-        otherTimer = registry.newTimer(HttpClient.class, "other-requests");
+        getTimer = registry.timer(name(HttpClient.class, "get-requests"));
+        postTimer = registry.timer(name(HttpClient.class, "post-requests"));
+        headTimer = registry.timer(name(HttpClient.class, "head-requests"));
+        putTimer = registry.timer(name(HttpClient.class, "put-requests"));
+        deleteTimer = registry.timer(name(HttpClient.class, "delete-requests"));
+        optionsTimer = registry.timer(name(HttpClient.class, "options-requests"));
+        traceTimer = registry.timer(name(HttpClient.class, "trace-requests"));
+        connectTimer = registry.timer(name(HttpClient.class, "connect-requests"));
+        moveTimer = registry.timer(name(HttpClient.class, "move-requests"));
+        patchTimer = registry.timer(name(HttpClient.class, "patch-requests"));
+        otherTimer = registry.timer(name(HttpClient.class, "other-requests"));
     }
 
     @Override
     public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) throws HttpException, IOException {
-        final TimerContext timerContext = timer(request).time();
+        final Timer.Context timerContext = timer(request).time();
         try {
             return super.execute(target, request, context);
         } finally {
diff --git a/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/tests/InstrumentedHttpClientTest.java b/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/tests/InstrumentedHttpClientTest.java
index 33dac579d6..645e77b288 100644
--- a/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/tests/InstrumentedHttpClientTest.java
+++ b/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/tests/InstrumentedHttpClientTest.java
@@ -1,5 +1,6 @@
 package com.yammer.metrics.httpclient.tests;
 
+import com.yammer.metrics.MetricRegistry;
 import com.yammer.metrics.httpclient.InstrumentedClientConnManager;
 import com.yammer.metrics.httpclient.InstrumentedHttpClient;
 import org.apache.http.client.HttpClient;
@@ -10,7 +11,8 @@
 import static org.junit.Assert.assertThat;
 
 public class InstrumentedHttpClientTest {
-    private final HttpClient client = new InstrumentedHttpClient();
+    private final MetricRegistry registry = new MetricRegistry("test");
+    private final HttpClient client = new InstrumentedHttpClient(registry);
 
     @Test
     public void hasAnInstrumentedConnectionManager() throws Exception {

From b6ca8bf0bbfc3ad3bcc4923491c5024cd4a6195c Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Sun, 10 Mar 2013 14:46:33 -0700
Subject: [PATCH 0011/2558] Upgrade some dependencies.

---
 metrics-httpclient/pom.xml | 2 +-
 metrics-jersey/pom.xml     | 2 +-
 metrics-jetty/pom.xml      | 2 +-
 metrics-logback/pom.xml    | 2 +-
 metrics-servlet/pom.xml    | 2 +-
 pom.xml                    | 4 ++--
 6 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/metrics-httpclient/pom.xml b/metrics-httpclient/pom.xml
index 6edf6d1b40..4320b872b7 100644
--- a/metrics-httpclient/pom.xml
+++ b/metrics-httpclient/pom.xml
@@ -21,7 +21,7 @@
         
             org.apache.httpcomponents
             httpclient
-            4.2.1
+            4.2.3
         
     
 
diff --git a/metrics-jersey/pom.xml b/metrics-jersey/pom.xml
index 6aa2a30d1e..144893d64a 100644
--- a/metrics-jersey/pom.xml
+++ b/metrics-jersey/pom.xml
@@ -17,7 +17,7 @@
     
 
     
-        1.13
+        1.17.1
     
 
     
diff --git a/metrics-jetty/pom.xml b/metrics-jetty/pom.xml
index 257b1b2035..f0e21d7798 100644
--- a/metrics-jetty/pom.xml
+++ b/metrics-jetty/pom.xml
@@ -21,7 +21,7 @@
         
             org.eclipse.jetty
             jetty-server
-            8.1.5.v20120716
+            8.1.9.v20130131
         
     
 
diff --git a/metrics-logback/pom.xml b/metrics-logback/pom.xml
index 1461a4dc7f..71743c0ff8 100644
--- a/metrics-logback/pom.xml
+++ b/metrics-logback/pom.xml
@@ -13,7 +13,7 @@
     bundle
 
     
-        1.0.6
+        1.0.9
     
 
     
diff --git a/metrics-servlet/pom.xml b/metrics-servlet/pom.xml
index a70574966d..db5d61eada 100644
--- a/metrics-servlet/pom.xml
+++ b/metrics-servlet/pom.xml
@@ -32,7 +32,7 @@
         
             org.eclipse.jetty
             jetty-servlet
-            8.1.5.v20120716
+            8.1.9.v20130131
             test
         
         
diff --git a/pom.xml b/pom.xml
index 8be55ad249..fa62414bdb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,7 +38,7 @@
         UTF-8
         2.5
         1.7.2
-        2.0.5
+        2.1.4
     
 
     
@@ -94,7 +94,7 @@
         
             junit
             junit
-            4.10
+            4.11
             test
         
         

From d05996c8cb297bd1e6f307d9cc77820c9b6f9322 Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Sun, 10 Mar 2013 14:48:55 -0700
Subject: [PATCH 0012/2558] Stop grouping metrics by type in JMX.

---
 .../src/main/java/com/yammer/metrics/JmxReporter.java    | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java
index ed421cf4d6..a734117002 100644
--- a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java
+++ b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java
@@ -4,7 +4,6 @@
 import org.slf4j.LoggerFactory;
 
 import javax.management.*;
-import java.util.Hashtable;
 
 // TODO: 3/10/13  -- write tests
 // TODO: 3/10/13  -- write docs
@@ -433,15 +432,11 @@ public void onTimerRemoved(String name) {
         }
 
         private ObjectName createName(String type, String name) {
-            final Hashtable props = new Hashtable();
-            props.put("type", type);
-            props.put("name", name);
             try {
-                return new ObjectName(this.name, props);
+                return new ObjectName(this.name, "name", name);
             } catch (MalformedObjectNameException e) {
-                props.put("name", ObjectName.quote(name));
                 try {
-                    return new ObjectName(this.name, props);
+                    return new ObjectName(this.name, "name", ObjectName.quote(name));
                 } catch (MalformedObjectNameException e1) {
                     LOGGER.warn("Unable to register {} {}", type, name, e1);
                     throw new RuntimeException(e1);

From 7cd4c74fe0912a3d4282047c67954e0abe6a15a7 Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Sun, 10 Mar 2013 15:01:52 -0700
Subject: [PATCH 0013/2558] Revamp metrics-ehcache.

---
 metrics-ehcache/pom.xml                       |   4 +-
 .../metrics/ehcache/InstrumentedEhcache.java  | 412 ++++++------------
 .../ehcache/InstrumentedEhcacheFactory.java   |  31 +-
 .../tests/ConfigInstrumentedEhcacheTest.java  | 101 ++---
 .../tests/InstrumentedEhcacheTest.java        |  33 +-
 .../src/test/resources/ehcache.xml            |  20 +-
 6 files changed, 224 insertions(+), 377 deletions(-)

diff --git a/metrics-ehcache/pom.xml b/metrics-ehcache/pom.xml
index 2eabedb31e..87f1ec583b 100644
--- a/metrics-ehcache/pom.xml
+++ b/metrics-ehcache/pom.xml
@@ -16,12 +16,12 @@
         
             com.yammer.metrics
             metrics-core
-            2.2.0
+            ${project.version}
         
         
             net.sf.ehcache
             ehcache-core
-            2.6.0
+            2.6.5
         
     
 
diff --git a/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcache.java b/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcache.java
index c27a8cb560..a01e069971 100644
--- a/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcache.java
+++ b/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcache.java
@@ -1,10 +1,8 @@
 package com.yammer.metrics.ehcache;
 
-import com.yammer.metrics.Metrics;
-import com.yammer.metrics.core.Gauge;
-import com.yammer.metrics.core.MetricsRegistry;
-import com.yammer.metrics.core.Timer;
-import com.yammer.metrics.core.TimerContext;
+import com.yammer.metrics.Gauge;
+import com.yammer.metrics.MetricRegistry;
+import com.yammer.metrics.Timer;
 import net.sf.ehcache.CacheException;
 import net.sf.ehcache.Ehcache;
 import net.sf.ehcache.Element;
@@ -12,7 +10,8 @@
 import net.sf.ehcache.constructs.EhcacheDecoratorAdapter;
 
 import java.io.Serializable;
-import java.util.concurrent.TimeUnit;
+
+import static com.yammer.metrics.MetricRegistry.name;
 
 /**
  * An instrumented {@link Ehcache} instance.
@@ -111,300 +110,173 @@ public class InstrumentedEhcache extends EhcacheDecoratorAdapter {
      * level of "none."
      *
      * @param cache       an {@link Ehcache} instance
+     * @param registry    a {@link MetricRegistry}
      * @return an instrumented decorator for {@code cache}
      * @see Statistics
      */
-    public static Ehcache instrument(Ehcache cache) {
-        return instrument(Metrics.defaultRegistry(), cache);
-    }
-
-    /**
-     * Instruments the given {@link Ehcache} instance with get and put timers
-     * and a set of gauges for Ehcache's built-in statistics:
-     * 

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
{@code hits}The number of times a requested item was found in the - * cache.
{@code in-memory-hits}Number of times a requested item was found in the memory - * store.
{@code off-heap-hits}Number of times a requested item was found in the off-heap - * store.
{@code on-disk-hits}Number of times a requested item was found in the disk - * store.
{@code misses}Number of times a requested item was not found in the - * cache.
{@code in-memory-misses}Number of times a requested item was not found in the memory - * store.
{@code off-heap-misses}Number of times a requested item was not found in the - * off-heap store.
{@code on-disk-misses}Number of times a requested item was not found in the disk - * store.
{@code objects}Number of elements stored in the cache.
{@code in-memory-objects}Number of objects in the memory store.
{@code off-heap-objects}Number of objects in the off-heap store.
{@code on-disk-objects}Number of objects in the disk store.
{@code mean-get-time}The average get time. Because ehcache support JDK1.4.2, each - * get time uses {@link System#currentTimeMillis()}, rather than - * nanoseconds. The accuracy is thus limited.
{@code mean-search-time}The average execution time (in milliseconds) within the last - * sample period.
{@code eviction-count}The number of cache evictions, since the cache was created, - * or statistics were cleared.
{@code searches-per-second}The number of search executions that have completed in the - * last second.
{@code accuracy}A human readable description of the accuracy setting. One of - * "None", "Best Effort" or "Guaranteed".
- * - * N.B.: This enables Ehcache's sampling statistics with an accuracy - * level of "none." - * - * @param cache an {@link Ehcache} instance - * @param registry a {@link MetricsRegistry} - * @return an instrumented decorator for {@code cache} - * @see Statistics - */ - public static Ehcache instrument(MetricsRegistry registry, final Ehcache cache) { + public static Ehcache instrument(MetricRegistry registry, final Ehcache cache) { cache.setSampledStatisticsEnabled(true); cache.setStatisticsAccuracy(Statistics.STATISTICS_ACCURACY_NONE); - registry.newGauge(cache.getClass(), "hits", cache.getName(), new Gauge() { - @Override - public Long value() { - return cache.getStatistics().getCacheHits(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "hits"), + new Gauge() { + @Override + public Long getValue() { + return cache.getStatistics().getCacheHits(); + } + }); - registry.newGauge(cache.getClass(), - "in-memory-hits", - cache.getName(), - new Gauge() { - @Override - public Long value() { - return cache.getStatistics().getInMemoryHits(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "in-memory-hits"), + new Gauge() { + @Override + public Long getValue() { + return cache.getStatistics().getInMemoryHits(); + } + }); - registry.newGauge(cache.getClass(), - "off-heap-hits", - cache.getName(), - new Gauge() { - @Override - public Long value() { - return cache.getStatistics().getOffHeapHits(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "off-heap-hits"), + new Gauge() { + @Override + public Long getValue() { + return cache.getStatistics().getOffHeapHits(); + } + }); - registry.newGauge(cache.getClass(), - "on-disk-hits", - cache.getName(), - new Gauge() { - @Override - public Long value() { - return cache.getStatistics().getOnDiskHits(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "on-disk-hits"), + new Gauge() { + @Override + public Long getValue() { + return cache.getStatistics().getOnDiskHits(); + } + }); - registry.newGauge(cache.getClass(), "misses", cache.getName(), new Gauge() { - @Override - public Long value() { - return cache.getStatistics().getCacheMisses(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "misses"), + new Gauge() { + @Override + public Long getValue() { + return cache.getStatistics().getCacheMisses(); + } + }); - registry.newGauge(cache.getClass(), - "in-memory-misses", - cache.getName(), - new Gauge() { - @Override - public Long value() { - return cache.getStatistics().getInMemoryMisses(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "in-memory-misses"), + new Gauge() { + @Override + public Long getValue() { + return cache.getStatistics().getInMemoryMisses(); + } + }); - registry.newGauge(cache.getClass(), - "off-heap-misses", - cache.getName(), - new Gauge() { - @Override - public Long value() { - return cache.getStatistics().getOffHeapMisses(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "off-heap-misses"), + new Gauge() { + @Override + public Long getValue() { + return cache.getStatistics().getOffHeapMisses(); + } + }); - registry.newGauge(cache.getClass(), - "on-disk-misses", - cache.getName(), - new Gauge() { - @Override - public Long value() { - return cache.getStatistics().getOnDiskMisses(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "on-disk-misses"), + new Gauge() { + @Override + public Long getValue() { + return cache.getStatistics().getOnDiskMisses(); + } + }); - registry.newGauge(cache.getClass(), "objects", cache.getName(), new Gauge() { - @Override - public Long value() { - return cache.getStatistics().getObjectCount(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "objects"), + new Gauge() { + @Override + public Long getValue() { + return cache.getStatistics().getObjectCount(); + } + }); - registry.newGauge(cache.getClass(), - "in-memory-objects", - cache.getName(), - new Gauge() { - @Override - public Long value() { - return cache.getStatistics().getMemoryStoreObjectCount(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "in-memory-objects"), + new Gauge() { + @Override + public Long getValue() { + return cache.getStatistics().getMemoryStoreObjectCount(); + } + }); - registry.newGauge(cache.getClass(), - "off-heap-objects", - cache.getName(), - new Gauge() { - @Override - public Long value() { - return cache.getStatistics().getOffHeapStoreObjectCount(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "off-heap-objects"), + new Gauge() { + @Override + public Long getValue() { + return cache.getStatistics().getOffHeapStoreObjectCount(); + } + }); - registry.newGauge(cache.getClass(), - "on-disk-objects", - cache.getName(), - new Gauge() { - @Override - public Long value() { - return cache.getStatistics().getDiskStoreObjectCount(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "on-disk-objects"), + new Gauge() { + @Override + public Long getValue() { + return cache.getStatistics().getDiskStoreObjectCount(); + } + }); - registry.newGauge(cache.getClass(), - "mean-get-time", - cache.getName(), - new Gauge() { - @Override - public Float value() { - return cache.getStatistics().getAverageGetTime(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "mean-get-time"), + new Gauge() { + @Override + public Float getValue() { + return cache.getStatistics().getAverageGetTime(); + } + }); - registry.newGauge(cache.getClass(), - "mean-search-time", - cache.getName(), - new Gauge() { - @Override - public Long value() { - return cache.getStatistics().getAverageSearchTime(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "mean-search-time"), + new Gauge() { + @Override + public Long getValue() { + return cache.getStatistics().getAverageSearchTime(); + } + }); - registry.newGauge(cache.getClass(), - "eviction-count", - cache.getName(), - new Gauge() { - @Override - public Long value() { - return cache.getStatistics().getEvictionCount(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "eviction-count"), + new Gauge() { + @Override + public Long getValue() { + return cache.getStatistics().getEvictionCount(); + } + }); - registry.newGauge(cache.getClass(), - "searches-per-second", - cache.getName(), - new Gauge() { - @Override - public Long value() { - return cache.getStatistics().getSearchesPerSecond(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "searches-per-second"), + new Gauge() { + @Override + public Long getValue() { + return cache.getStatistics().getSearchesPerSecond(); + } + }); - registry.newGauge(cache.getClass(), - "writer-queue-size", - cache.getName(), - new Gauge() { - @Override - public Long value() { - return cache.getStatistics().getWriterQueueSize(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "writer-queue-size"), + new Gauge() { + @Override + public Long getValue() { + return cache.getStatistics().getWriterQueueSize(); + } + }); - registry.newGauge(cache.getClass(), - "accuracy", - cache.getName(), - new Gauge() { - @Override - public String value() { - return cache.getStatistics() - .getStatisticsAccuracyDescription(); - } - }); + registry.register(name(cache.getClass(), cache.getName(), "accuracy"), + new Gauge() { + @Override + public String getValue() { + return cache.getStatistics() + .getStatisticsAccuracyDescription(); + } + }); return new InstrumentedEhcache(registry, cache); } private final Timer getTimer, putTimer; - private InstrumentedEhcache(MetricsRegistry registry, Ehcache cache) { + private InstrumentedEhcache(MetricRegistry registry, Ehcache cache) { super(cache); - this.getTimer = registry.newTimer(cache.getClass(), "get", cache.getName(), TimeUnit.MICROSECONDS, TimeUnit.SECONDS); - this.putTimer = registry.newTimer(cache.getClass(), "put", cache.getName(), TimeUnit.MICROSECONDS, TimeUnit.SECONDS); + this.getTimer = registry.timer(name(cache.getClass(), cache.getName(), "gets")); + this.putTimer = registry.timer(name(cache.getClass(), cache.getName(), "puts")); } @Override public Element get(Object key) throws IllegalStateException, CacheException { - final TimerContext ctx = getTimer.time(); + final Timer.Context ctx = getTimer.time(); try { return underlyingCache.get(key); } finally { @@ -414,7 +286,7 @@ public Element get(Object key) throws IllegalStateException, CacheException { @Override public Element get(Serializable key) throws IllegalStateException, CacheException { - final TimerContext ctx = getTimer.time(); + final Timer.Context ctx = getTimer.time(); try { return underlyingCache.get(key); } finally { @@ -424,7 +296,7 @@ public Element get(Serializable key) throws IllegalStateException, CacheExceptio @Override public void put(Element element) throws IllegalArgumentException, IllegalStateException, CacheException { - final TimerContext ctx = putTimer.time(); + final Timer.Context ctx = putTimer.time(); try { underlyingCache.put(element); } finally { @@ -434,7 +306,7 @@ public void put(Element element) throws IllegalArgumentException, IllegalStateEx @Override public void put(Element element, boolean doNotNotifyCacheReplicators) throws IllegalArgumentException, IllegalStateException, CacheException { - final TimerContext ctx = putTimer.time(); + final Timer.Context ctx = putTimer.time(); try { underlyingCache.put(element, doNotNotifyCacheReplicators); } finally { @@ -444,7 +316,7 @@ public void put(Element element, boolean doNotNotifyCacheReplicators) throws Ill @Override public Element putIfAbsent(Element element) throws NullPointerException { - final TimerContext ctx = putTimer.time(); + final Timer.Context ctx = putTimer.time(); try { return underlyingCache.putIfAbsent(element); } finally { diff --git a/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcacheFactory.java b/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcacheFactory.java index aef4bc2602..38eb03e614 100644 --- a/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcacheFactory.java +++ b/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcacheFactory.java @@ -1,20 +1,17 @@ package com.yammer.metrics.ehcache; -import net.sf.ehcache.Ehcache; -import net.sf.ehcache.constructs.CacheDecoratorFactory; +// TODO: 3/10/13 -- figure out how to coordinate on registry names -import java.util.Properties; - -public class InstrumentedEhcacheFactory extends CacheDecoratorFactory { - - @Override - public Ehcache createDecoratedEhcache(Ehcache cache, Properties properties) { - return InstrumentedEhcache.instrument(cache); - } - - @Override - public Ehcache createDefaultDecoratedEhcache(Ehcache cache, Properties properties) { - return InstrumentedEhcache.instrument(cache); - } - -} +//public class InstrumentedEhcacheFactory extends CacheDecoratorFactory { +// +// @Override +// public Ehcache createDecoratedEhcache(Ehcache cache, Properties properties) { +// return InstrumentedEhcache.instrument(cache); +// } +// +// @Override +// public Ehcache createDefaultDecoratedEhcache(Ehcache cache, Properties properties) { +// return InstrumentedEhcache.instrument(cache); +// } +// +//} diff --git a/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/ConfigInstrumentedEhcacheTest.java b/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/ConfigInstrumentedEhcacheTest.java index e260f8e2f6..929fecca93 100644 --- a/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/ConfigInstrumentedEhcacheTest.java +++ b/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/ConfigInstrumentedEhcacheTest.java @@ -1,59 +1,46 @@ package com.yammer.metrics.ehcache.tests; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Timer; -import net.sf.ehcache.Cache; -import net.sf.ehcache.CacheManager; -import net.sf.ehcache.Ehcache; -import net.sf.ehcache.Element; -import org.junit.Before; -import org.junit.Test; - -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; - -public class ConfigInstrumentedEhcacheTest { - - private static final CacheManager MANAGER = CacheManager.create(); - - private Ehcache cache; - - @Before - public void setUp() throws Exception { - cache = MANAGER.getEhcache("test-config"); - if (cache == null) fail("Cache is not set correctly"); - } - - @Test - public void measuresGets() throws Exception { - cache.get("woo"); - - final Timer gets = Metrics.defaultRegistry().newTimer(Cache.class, - "get", - "test-config", - TimeUnit.MILLISECONDS, - TimeUnit.SECONDS); - - assertThat(gets.count(), is(1L)); - - } - - @Test - public void measuresPuts() throws Exception { - - cache.put(new Element("woo", "whee")); - - final Timer puts = Metrics.defaultRegistry().newTimer(Cache.class, - "put", - "test-config", - TimeUnit.MILLISECONDS, - TimeUnit.SECONDS); - - assertThat(puts.count(), is(1L)); - - } - -} +// TODO: 3/10/13 -- figure out how to coordinate on registry names + +//public class ConfigInstrumentedEhcacheTest { +// +// private static final CacheManager MANAGER = CacheManager.create(); +// +// private Ehcache cache; +// +// @Before +// public void setUp() throws Exception { +// cache = MANAGER.getEhcache("test-config"); +// if (cache == null) fail("Cache is not set correctly"); +// } +// +// @Test +// public void measuresGets() throws Exception { +// cache.get("woo"); +// +// final Timer gets = Metrics.defaultRegistry().newTimer(Cache.class, +// "get", +// "test-config", +// TimeUnit.MILLISECONDS, +// TimeUnit.SECONDS); +// +// assertThat(gets.count(), is(1L)); +// +// } +// +// @Test +// public void measuresPuts() throws Exception { +// +// cache.put(new Element("woo", "whee")); +// +// final Timer puts = Metrics.defaultRegistry().newTimer(Cache.class, +// "put", +// "test-config", +// TimeUnit.MILLISECONDS, +// TimeUnit.SECONDS); +// +// assertThat(puts.count(), is(1L)); +// +// } +// +//} diff --git a/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/InstrumentedEhcacheTest.java b/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/InstrumentedEhcacheTest.java index dac6f846a5..79925e1341 100644 --- a/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/InstrumentedEhcacheTest.java +++ b/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/InstrumentedEhcacheTest.java @@ -1,7 +1,7 @@ package com.yammer.metrics.ehcache.tests; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Timer; +import com.yammer.metrics.MetricRegistry; +import com.yammer.metrics.Timer; import com.yammer.metrics.ehcache.InstrumentedEhcache; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; @@ -11,21 +11,20 @@ import org.junit.Before; import org.junit.Test; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; +import static com.yammer.metrics.MetricRegistry.name; +import static org.fest.assertions.api.Assertions.assertThat; public class InstrumentedEhcacheTest { private static final CacheManager MANAGER = CacheManager.create(); + private final MetricRegistry registry = new MetricRegistry("cache"); private Ehcache cache; @Before public void setUp() throws Exception { final Cache c = new Cache(new CacheConfiguration("test", 100)); MANAGER.addCache(c); - this.cache = InstrumentedEhcache.instrument(c); + this.cache = InstrumentedEhcache.instrument(registry, c); } @Test @@ -34,22 +33,14 @@ public void measuresGetsAndPuts() throws Exception { cache.put(new Element("woo", "whee")); - final Timer gets = Metrics.defaultRegistry().newTimer(Cache.class, - "get", - "test", - TimeUnit.MILLISECONDS, - TimeUnit.SECONDS); + final Timer gets = registry.timer(name(Cache.class, "test", "gets")); - assertThat(gets.count(), - is(1L)); + assertThat(gets.getCount()) + .isEqualTo(1); - final Timer puts = Metrics.defaultRegistry().newTimer(Cache.class, - "put", - "test", - TimeUnit.MILLISECONDS, - TimeUnit.SECONDS); + final Timer puts = registry.timer(name(Cache.class, "test", "puts")); - assertThat(puts.count(), - is(1L)); + assertThat(puts.getCount()) + .isEqualTo(1); } } diff --git a/metrics-ehcache/src/test/resources/ehcache.xml b/metrics-ehcache/src/test/resources/ehcache.xml index 5c043af573..1adbd8730c 100644 --- a/metrics-ehcache/src/test/resources/ehcache.xml +++ b/metrics-ehcache/src/test/resources/ehcache.xml @@ -3,18 +3,18 @@ xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false" monitoring="autodetect" dynamicConfig="true"> - + + + + + + + + - + - + Date: Sun, 10 Mar 2013 16:42:00 -0700 Subject: [PATCH 0014/2558] Don't unnecessarily convert longs to doubles. --- .../java/com/yammer/metrics/Histogram.java | 12 ++-- .../java/com/yammer/metrics/Summarizable.java | 7 +-- .../main/java/com/yammer/metrics/Timer.java | 6 +- .../yammer/metrics/tests/HistogramTest.java | 39 ++++++++---- .../com/yammer/metrics/tests/TimerTest.java | 60 ++++++++++++------- 5 files changed, 78 insertions(+), 46 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/Histogram.java b/metrics-core/src/main/java/com/yammer/metrics/Histogram.java index 95993c8aa3..1b72084b81 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Histogram.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Histogram.java @@ -89,22 +89,22 @@ public long getCount() { * @see com.yammer.metrics.Summarizable#max() */ @Override - public double getMax() { + public long getMax() { if (getCount() > 0) { return max.get(); } - return 0.0; + return 0; } /* (non-Javadoc) * @see com.yammer.metrics.Summarizable#min() */ @Override - public double getMin() { + public long getMin() { if (getCount() > 0) { return min.get(); } - return 0.0; + return 0; } /* (non-Javadoc) @@ -133,8 +133,8 @@ public double getStdDev() { * @see com.yammer.metrics.Summarizable#sum() */ @Override - public double getSum() { - return (double) sum.get(); + public long getSum() { + return sum.get(); } @Override diff --git a/metrics-core/src/main/java/com/yammer/metrics/Summarizable.java b/metrics-core/src/main/java/com/yammer/metrics/Summarizable.java index cd6836e1a1..acc281eecb 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Summarizable.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Summarizable.java @@ -9,14 +9,14 @@ public interface Summarizable { * * @return the largest recorded value */ - double getMax(); + long getMax(); /** * Returns the smallest recorded value. * * @return the smallest recorded value */ - double getMin(); + long getMin(); /** * Returns the arithmetic mean of all recorded values. @@ -37,6 +37,5 @@ public interface Summarizable { * * @return the sum of all recorded values */ - double getSum(); - + long getSum(); } diff --git a/metrics-core/src/main/java/com/yammer/metrics/Timer.java b/metrics-core/src/main/java/com/yammer/metrics/Timer.java index b1b9111b76..848e1fd43b 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Timer.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Timer.java @@ -106,7 +106,7 @@ public double getOneMinuteRate() { * @return the longest recorded duration */ @Override - public double getMax() { + public long getMax() { return histogram.getMax(); } @@ -116,7 +116,7 @@ public double getMax() { * @return the shortest recorded duration */ @Override - public double getMin() { + public long getMin() { return histogram.getMin(); } @@ -146,7 +146,7 @@ public double getStdDev() { * @return the sum of all recorded durations */ @Override - public double getSum() { + public long getSum() { return histogram.getSum(); } diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java index 7c19bea1f4..cf20e1083e 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java @@ -17,23 +17,31 @@ public void anEmptyHistogram() throws Exception { assertThat(histogram.getCount()) .isEqualTo(0L); - assertThat(histogram.getMax()).isEqualTo(0.0, offset(0.0001)); + assertThat(histogram.getMax()) + .isEqualTo(0); - assertThat(histogram.getMin()).isEqualTo(0.0, offset(0.0001)); + assertThat(histogram.getMin()) + .isEqualTo(0); - assertThat(histogram.getMean()).isEqualTo(0.0, offset(0.0001)); + assertThat(histogram.getMean()) + .isEqualTo(0.0, offset(0.0001)); - assertThat(histogram.getStdDev()).isEqualTo(0.0, offset(0.0001)); + assertThat(histogram.getStdDev()) + .isEqualTo(0.0, offset(0.0001)); - assertThat(histogram.getSum()).isEqualTo(0.0, offset(0.0001)); + assertThat(histogram.getSum()) + .isEqualTo(0); final Snapshot snapshot = histogram.getSnapshot(); - assertThat(snapshot.getMedian()).isEqualTo(0.0, offset(0.0001)); + assertThat(snapshot.getMedian()) + .isEqualTo(0.0, offset(0.0001)); - assertThat(snapshot.get75thPercentile()).isEqualTo(0.0, offset(0.0001)); + assertThat(snapshot.get75thPercentile()) + .isEqualTo(0.0, offset(0.0001)); - assertThat(snapshot.get99thPercentile()).isEqualTo(0.0, offset(0.0001)); + assertThat(snapshot.get99thPercentile()) + .isEqualTo(0.0, offset(0.0001)); assertThat(snapshot.size()) .isEqualTo(0); @@ -48,15 +56,20 @@ public void aHistogramWith1000Elements() throws Exception { assertThat(histogram.getCount()) .isEqualTo(1000L); - assertThat(histogram.getMax()).isEqualTo(1000.0, offset(0.0001)); + assertThat(histogram.getMax()) + .isEqualTo(1000); - assertThat(histogram.getMin()).isEqualTo(1.0, offset(0.0001)); + assertThat(histogram.getMin()) + .isEqualTo(1); - assertThat(histogram.getMean()).isEqualTo(500.5, offset(0.0001)); + assertThat(histogram.getMean()) + .isEqualTo(500.5, offset(0.0001)); - assertThat(histogram.getStdDev()).isEqualTo(288.8194360957494, offset(0.0001)); + assertThat(histogram.getStdDev()) + .isEqualTo(288.8194360957494, offset(0.0001)); - assertThat(histogram.getSum()).isEqualTo(500500, offset(0.1)); + assertThat(histogram.getSum()) + .isEqualTo(500500); final Snapshot snapshot = histogram.getSnapshot(); diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java index f7efac1a6a..1a4be59070 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java @@ -29,29 +29,40 @@ public void aBlankTimer() throws Exception { assertThat(timer.getCount()) .isZero(); - assertThat(timer.getMax()).isEqualTo(0.0, offset(0.001)); + assertThat(timer.getMax()) + .isEqualTo(0); - assertThat(timer.getMin()).isEqualTo(0.0, offset(0.001)); + assertThat(timer.getMin()) + .isEqualTo(0); - assertThat(timer.getMean()).isEqualTo(0.0, offset(0.001)); + assertThat(timer.getMean()) + .isEqualTo(0.0, offset(0.001)); - assertThat(timer.getStdDev()).isEqualTo(0.0, offset(0.001)); + assertThat(timer.getStdDev()) + .isEqualTo(0.0, offset(0.001)); final Snapshot snapshot = timer.getSnapshot(); - assertThat(snapshot.getMedian()).isEqualTo(0.0, offset(0.001)); + assertThat(snapshot.getMedian()) + .isEqualTo(0.0, offset(0.001)); - assertThat(snapshot.get75thPercentile()).isEqualTo(0.0, offset(0.001)); + assertThat(snapshot.get75thPercentile()) + .isEqualTo(0.0, offset(0.001)); - assertThat(snapshot.get99thPercentile()).isEqualTo(0.0, offset(0.001)); + assertThat(snapshot.get99thPercentile()) + .isEqualTo(0.0, offset(0.001)); - assertThat(timer.getMeanRate()).isEqualTo(0.0, offset(0.001)); + assertThat(timer.getMeanRate()) + .isEqualTo(0.0, offset(0.001)); - assertThat(timer.getOneMinuteRate()).isEqualTo(0.0, offset(0.001)); + assertThat(timer.getOneMinuteRate()) + .isEqualTo(0.0, offset(0.001)); - assertThat(timer.getFiveMinuteRate()).isEqualTo(0.0, offset(0.001)); + assertThat(timer.getFiveMinuteRate()) + .isEqualTo(0.0, offset(0.001)); - assertThat(timer.getFifteenMinuteRate()).isEqualTo(0.0, offset(0.001)); + assertThat(timer.getFifteenMinuteRate()) + .isEqualTo(0.0, offset(0.001)); assertThat(timer.getSnapshot().size()) .isZero(); @@ -68,21 +79,28 @@ public void timingASeriesOfEvents() throws Exception { assertThat(timer.getCount()) .isEqualTo(5); - assertThat(timer.getMax()).isEqualTo(4e7, offset(0.001)); + assertThat(timer.getMax()) + .isEqualTo(40000000); - assertThat(timer.getMin()).isEqualTo(1e7, offset(0.001)); + assertThat(timer.getMin()) + .isEqualTo(10000000); - assertThat(timer.getMean()).isEqualTo(2.4e7, offset(0.001)); + assertThat(timer.getMean()) + .isEqualTo(24000000, offset(0.001)); - assertThat(timer.getStdDev()).isEqualTo(1.14e7, offset(10000.0)); + assertThat(timer.getStdDev()) + .isEqualTo(11400000, offset(10000.0)); final Snapshot snapshot = timer.getSnapshot(); - assertThat(snapshot.getMedian()).isEqualTo(2e7, offset(0.001)); + assertThat(snapshot.getMedian()) + .isEqualTo(20000000, offset(0.001)); - assertThat(snapshot.get75thPercentile()).isEqualTo(3.5e7, offset(0.001)); + assertThat(snapshot.get75thPercentile()) + .isEqualTo(35000000, offset(0.001)); - assertThat(snapshot.get99thPercentile()).isEqualTo(4e7, offset(0.001)); + assertThat(snapshot.get99thPercentile()) + .isEqualTo(40000000, offset(0.001)); assertThat(timer.getSnapshot().getValues()) .containsOnly(10000000, 20000000, 20000000, 30000000, 40000000); @@ -112,7 +130,8 @@ public String call() throws Exception { assertThat(value) .isEqualTo("one"); - assertThat(timer.getMax()).isEqualTo(5e7, offset(0.001)); + assertThat(timer.getMax()) + .isEqualTo(50000000); } @Test @@ -122,6 +141,7 @@ public void timingContexts() throws Exception { assertThat(timer.getCount()) .isEqualTo(1); - assertThat(timer.getMax()).isEqualTo(5e7, offset(0.001)); + assertThat(timer.getMax()) + .isEqualTo(50000000); } } From e10034ceb32f1bf8b6aec6d54081157208ea28ca Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 10 Mar 2013 22:43:22 -0700 Subject: [PATCH 0015/2558] Added LoggerReporter. --- .../com/yammer/metrics/LoggerReporter.java | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 metrics-core/src/main/java/com/yammer/metrics/LoggerReporter.java diff --git a/metrics-core/src/main/java/com/yammer/metrics/LoggerReporter.java b/metrics-core/src/main/java/com/yammer/metrics/LoggerReporter.java new file mode 100644 index 0000000000..342876b166 --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/LoggerReporter.java @@ -0,0 +1,129 @@ +package com.yammer.metrics; + +import org.slf4j.Logger; +import org.slf4j.Marker; + +import java.util.Map.Entry; +import java.util.SortedMap; + +/** + * Metrics reporter class for logging metrics values to a SLF4J {@link Logger} periodically, similar + * to how {@link ConsoleReporter} or {@link CsvReporter} function, but using the SLF4J framework + * instead. It also supports specifying a {@link Marker} instance that can be used by custom + * appenders and filters for the bound logging toolkit to further process metrics reports. + */ +public class LoggerReporter extends AbstractPollingReporter { + + private Logger logger; + private Marker marker; + + /** + * Construct a new SLF4J reporter. + * + * @param registry Metrics registry to report from. + * @param logger SLF4J {@link Logger} instance to send metrics reports to + */ + public LoggerReporter(MetricRegistry registry, Logger logger) { + this(registry, logger, null); + } + + /** + * Construct a new SLF4J reporter. + * + * @param registry Metrics registry to report from. + * @param logger SLF4J {@link Logger} instance to send metrics reports to + * @param marker SLF4J {@link Marker} instance to log with metrics class, or null if + * none. + */ + public LoggerReporter(MetricRegistry registry, Logger logger, Marker marker) { + super(registry, "logger-reporter"); + this.logger = logger; + this.marker = marker; + } + + @Override + public void report(SortedMap gauges, + SortedMap counters, + SortedMap histograms, + SortedMap meters, + SortedMap timers) { + for (Entry entry : gauges.entrySet()) { + logGauge(entry.getKey(), entry.getValue()); + } + + for (Entry entry : counters.entrySet()) { + logCounter(entry.getKey(), entry.getValue()); + } + + for (Entry entry : histograms.entrySet()) { + logHistogram(entry.getKey(), entry.getValue()); + } + + for (Entry entry : meters.entrySet()) { + logMeter(entry.getKey(), entry.getValue()); + } + + for (Entry entry : timers.entrySet()) { + logTimer(entry.getKey(), entry.getValue()); + } + } + + private void logTimer(String name, Timer timer) { + final Snapshot snapshot = timer.getSnapshot(); + logger.info(marker, + "type=TIMER, name={}, count={}, min={}, max={}, mean={}, stddev={}, median={}, p75={}, p95={}, p98={}, p999={}, mean_rate={}, m1={}, m5={}, m15={}", + name, + timer.getCount(), + timer.getMin(), + timer.getMax(), + timer.getMean(), + timer.getStdDev(), + snapshot.getMedian(), + snapshot.get75thPercentile(), + snapshot.get95thPercentile(), + snapshot.get98thPercentile(), + snapshot.get99thPercentile(), + snapshot.get999thPercentile(), + timer.getMeanRate(), + timer.getOneMinuteRate(), + timer.getFiveMinuteRate(), + timer.getFifteenMinuteRate()); + } + + private void logMeter(String name, Meter meter) { + logger.info(marker, + "type=METER, name={}, count={}, mean_rate={}, m1={}, m5={}, m15={}", + name, + meter.getCount(), + meter.getMeanRate(), + meter.getOneMinuteRate(), + meter.getFiveMinuteRate(), + meter.getFifteenMinuteRate()); + } + + private void logHistogram(String name, Histogram histogram) { + final Snapshot snapshot = histogram.getSnapshot(); + logger.info(marker, + "type=HISTOGRAM, name={}, count={}, min={}, max={}, mean={}, stddev={}, median={}, p75={}, p95={}, p98={}, p999={}", + name, + histogram.getCount(), + histogram.getMin(), + histogram.getMax(), + histogram.getMean(), + histogram.getStdDev(), + snapshot.getMedian(), + snapshot.get75thPercentile(), + snapshot.get95thPercentile(), + snapshot.get98thPercentile(), + snapshot.get99thPercentile(), + snapshot.get999thPercentile()); + } + + private void logCounter(String name, Counter counter) { + logger.info(marker, "type=COUNTER, name={}, count={} ", name, counter.getCount()); + } + + private void logGauge(String name, Gauge gauge) { + logger.info(marker, "type=GAUGE, name={}, value={}", name, gauge.getValue()); + } +} From c516ee0b857a5ab332e03dc86555aede6f4214f2 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 10 Mar 2013 22:55:56 -0700 Subject: [PATCH 0016/2558] Added getNames to HealthCheckRegistry and MetricRegistry. --- .../src/main/java/com/yammer/metrics/MetricRegistry.java | 4 ++++ .../com/yammer/metrics/health/HealthCheckRegistry.java | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java index b02e98952b..5d68d8fef6 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -165,6 +165,10 @@ public String getName() { return name; } + public SortedSet getNames() { + return Collections.unmodifiableSortedSet(new TreeSet(metrics.keySet())); + } + public SortedMap getGauges() { return getMetrics(Gauge.class); } diff --git a/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java b/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java index 3ef58da383..37e93ef749 100644 --- a/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java +++ b/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java @@ -42,6 +42,15 @@ public void unregister(String name) { healthChecks.remove(name); } + /** + * Returns a set of the names of all registered health checks. + * + * @return the names of all registered health checks + */ + public SortedSet getNames() { + return Collections.unmodifiableSortedSet(new TreeSet(healthChecks.keySet())); + } + /** * Runs the registered health checks and returns a map of the results. * From 33c0f4c8574c4bcaabccd91dc3ba5ae1323fca76 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 10 Mar 2013 22:57:22 -0700 Subject: [PATCH 0017/2558] Added runHealthCheck to HealthCheckRegistry. --- .../com/yammer/metrics/health/HealthCheckRegistry.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java b/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java index 37e93ef749..75cae7c84f 100644 --- a/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java +++ b/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java @@ -51,6 +51,14 @@ public SortedSet getNames() { return Collections.unmodifiableSortedSet(new TreeSet(healthChecks.keySet())); } + public HealthCheck.Result runHealthCheck(String name) { + final HealthCheck healthCheck = healthChecks.get(name); + if (healthCheck == null) { + throw new NoSuchElementException("No health check named " + name + " exists"); + } + return healthCheck.execute(); + } + /** * Runs the registered health checks and returns a map of the results. * From 7680221b8c651a336f824c102161afe79e2f5787 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 10 Mar 2013 23:11:07 -0700 Subject: [PATCH 0018/2558] Remove all the clear methods. --- .../main/java/com/yammer/metrics/Counter.java | 7 ----- .../java/com/yammer/metrics/Histogram.java | 29 +++++++------------ .../main/java/com/yammer/metrics/Timer.java | 8 ----- .../com/yammer/metrics/tests/CounterTest.java | 9 ------ 4 files changed, 10 insertions(+), 43 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/Counter.java b/metrics-core/src/main/java/com/yammer/metrics/Counter.java index a087a5fe8e..bb64dab710 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Counter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Counter.java @@ -52,11 +52,4 @@ public void dec(long n) { public long getCount() { return count.get(); } - - /** - * Resets the counter to 0. - */ - public void clear() { - count.set(0); - } } diff --git a/metrics-core/src/main/java/com/yammer/metrics/Histogram.java b/metrics-core/src/main/java/com/yammer/metrics/Histogram.java index 1b72084b81..28c248dc31 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Histogram.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Histogram.java @@ -13,14 +13,13 @@ */ public class Histogram implements Metric, Sampling, Summarizable { private final Sample sample; - private final AtomicLong min = new AtomicLong(); - private final AtomicLong max = new AtomicLong(); - private final AtomicLong sum = new AtomicLong(); + private final AtomicLong min; + private final AtomicLong max; + private final AtomicLong sum; // These are for the Welford algorithm for calculating running variance // without floating-point doom. - private final AtomicReference variance = - new AtomicReference(new double[]{-1, 0}); // M, S - private final AtomicLong count = new AtomicLong(); + private final AtomicReference variance; // M, S + private final AtomicLong count; /** * Creates a new {@link Histogram} with the given sample type. @@ -38,19 +37,11 @@ public Histogram(SampleType type) { */ public Histogram(Sample sample) { this.sample = sample; - clear(); - } - - /** - * Clears all recorded values. - */ - public void clear() { - sample.clear(); - count.set(0); - max.set(Long.MIN_VALUE); - min.set(Long.MAX_VALUE); - sum.set(0); - variance.set(new double[]{ -1, 0 }); + this.min = new AtomicLong(Long.MAX_VALUE); + this.max = new AtomicLong(Long.MIN_VALUE); + this.sum = new AtomicLong(0); + this.variance = new AtomicReference(new double[]{-1, 0}); + this.count = new AtomicLong(0); } /** diff --git a/metrics-core/src/main/java/com/yammer/metrics/Timer.java b/metrics-core/src/main/java/com/yammer/metrics/Timer.java index 848e1fd43b..1c46393a43 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Timer.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Timer.java @@ -27,14 +27,6 @@ public Timer() { Timer(Clock clock) { this.meter = new Meter(clock); this.clock = clock; - clear(); - } - - /** - * Clears all recorded durations. - */ - public void clear() { - histogram.clear(); } /** diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/CounterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/CounterTest.java index e75daaeae1..73a1180a61 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/CounterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/CounterTest.java @@ -45,13 +45,4 @@ public void decrementsByAnArbitraryDelta() throws Exception { assertThat(counter.getCount()) .isEqualTo(-12); } - - @Test - public void isZeroAfterBeingCleared() throws Exception { - counter.inc(3); - counter.clear(); - - assertThat(counter.getCount()) - .isZero(); - } } From 853967bf942b78a158cf7a22aff3f81ae03a9d5f Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 11 Mar 2013 14:21:00 -0700 Subject: [PATCH 0019/2558] Revamp GangliaReporter. --- metrics-ganglia/pom.xml | 16 +- .../metrics/ganglia/GangliaMessage.java | 80 --- .../ganglia/GangliaMessageBuilder.java | 37 - .../metrics/ganglia/GangliaReporter.java | 630 +++++------------- .../ganglia/GangliaMessageBuilderTest.java | 21 - .../metrics/ganglia/GangliaMessageTest.java | 51 -- .../metrics/ganglia/GangliaReporterTest.java | 151 ----- .../ganglia/tests/GangliaReporterTest.java | 252 +++++++ metrics-ganglia/src/test/resources/counter.io | 21 - metrics-ganglia/src/test/resources/gauge.io | 21 - .../src/test/resources/histogram.io | 210 ------ metrics-ganglia/src/test/resources/metered.io | 105 --- metrics-ganglia/src/test/resources/timed.io | 315 --------- 13 files changed, 412 insertions(+), 1498 deletions(-) delete mode 100644 metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaMessage.java delete mode 100644 metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaMessageBuilder.java delete mode 100644 metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaMessageBuilderTest.java delete mode 100644 metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaMessageTest.java delete mode 100644 metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaReporterTest.java create mode 100644 metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java delete mode 100644 metrics-ganglia/src/test/resources/counter.io delete mode 100644 metrics-ganglia/src/test/resources/gauge.io delete mode 100644 metrics-ganglia/src/test/resources/histogram.io delete mode 100644 metrics-ganglia/src/test/resources/metered.io delete mode 100644 metrics-ganglia/src/test/resources/timed.io diff --git a/metrics-ganglia/pom.xml b/metrics-ganglia/pom.xml index 8d3f8d993e..53171a3cb9 100644 --- a/metrics-ganglia/pom.xml +++ b/metrics-ganglia/pom.xml @@ -18,20 +18,12 @@ com.yammer.metrics metrics-core - 2.2.0 + ${project.version} - com.yammer.metrics - metrics-core - 2.2.0 - test-jar - test - - - commons-io - commons-io - 2.4 - test + info.ganglia.gmetric4j + gmetric4j + 1.0.2 diff --git a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaMessage.java b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaMessage.java deleted file mode 100644 index 5e8c6dce86..0000000000 --- a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaMessage.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.yammer.metrics.ganglia; - -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetSocketAddress; - -/** - * Encapsulates logic for creating and sending a Ganglia message - */ -class GangliaMessage { - - private final byte[] buffer; - private int offset = 0; - private final DatagramSocket datagramSocket; - private final InetSocketAddress inetSocketAddress; - - GangliaMessage(InetSocketAddress inetSocketAddress, byte[] buffer, DatagramSocket datagramSocket) { - this.inetSocketAddress = inetSocketAddress; - this.buffer = buffer; - this.datagramSocket = datagramSocket; - } - - /** - * Creates and sends a new {@link DatagramPacket} - * - * @throws IOException if there is an error sending the packet - */ - public void send() throws IOException { - this.datagramSocket - .send(new DatagramPacket(this.buffer, this.offset, this.inetSocketAddress)); - } - - /** - * Puts an integer into the buffer as 4 bytes, big-endian. - * - * @param value the integer to write to the buffer - * @return {@code this} - */ - public GangliaMessage addInt(int value) { - this.buffer[this.offset++] = (byte) ((value >> 24) & 0xff); - this.buffer[this.offset++] = (byte) ((value >> 16) & 0xff); - this.buffer[this.offset++] = (byte) ((value >> 8) & 0xff); - this.buffer[this.offset++] = (byte) (value & 0xff); - - return this; - } - - /** - * Puts a string into the buffer by first writing the size of the string as an int, followed by - * the bytes of the string, padded if necessary to a multiple of 4. - * - * @param value the message to write to the buffer - * @return {@code this} - */ - public GangliaMessage addString(String value) { - final byte[] bytes = value.getBytes(); - final int len = bytes.length; - addInt(len); - System.arraycopy(bytes, 0, this.buffer, this.offset, len); - this.offset += len; - pad(); - - return this; - } - - /** - * Pads the buffer with zero bytes up to the nearest multiple of 4. - */ - private void pad() { - final int newOffset = ((this.offset + 3) / 4) * 4; - while (this.offset < newOffset) { - this.buffer[this.offset++] = 0; - } - } - - int getOffset() { - return this.offset; - } -} diff --git a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaMessageBuilder.java b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaMessageBuilder.java deleted file mode 100644 index 31ff6c92dc..0000000000 --- a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaMessageBuilder.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.yammer.metrics.ganglia; - -import java.net.DatagramSocket; -import java.net.InetSocketAddress; -import java.net.SocketException; - -/** - * Builder for creating Ganglia messages. Note, this builder is not thread safe (the message buffer - * is reused between messages) - */ -class GangliaMessageBuilder { - private final InetSocketAddress inetSocketAddress; - private final byte[] buffer = new byte[1500]; - private final DatagramSocket datagramSocket; - - GangliaMessageBuilder(String hostName, int port) throws SocketException { - this.inetSocketAddress = new InetSocketAddress(hostName, port); - this.datagramSocket = new DatagramSocket(); - } - - /** - * Create a new Ganglia message - * - * @return a new Ganglia message - */ - public GangliaMessage newMessage() { - return new GangliaMessage(this.inetSocketAddress, this.buffer, this.datagramSocket); - } - - public String getHostName() { - return this.inetSocketAddress.getHostName(); - } - - public int getPort() { - return this.inetSocketAddress.getPort(); - } -} diff --git a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java index 2e2cfb4c8f..d6b2fb6091 100644 --- a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java +++ b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java @@ -1,524 +1,206 @@ package com.yammer.metrics.ganglia; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.*; -import com.yammer.metrics.reporting.AbstractPollingReporter; -import com.yammer.metrics.stats.Snapshot; -import com.yammer.metrics.core.MetricPredicate; +import com.yammer.metrics.*; +import info.ganglia.gmetric4j.gmetric.GMetric; +import info.ganglia.gmetric4j.gmetric.GMetricSlope; +import info.ganglia.gmetric4j.gmetric.GMetricType; +import info.ganglia.gmetric4j.gmetric.GangliaException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; import java.util.Locale; import java.util.Map; import java.util.SortedMap; import java.util.concurrent.TimeUnit; -/** - * A simple reporter which sends out application metrics to a Ganglia - * server periodically. - *

- * NOTE: this reporter only works with Ganglia 3.1 and greater. The message protocol for earlier - * versions of Ganglia is different. - *

- * This code heavily borrows from GangliaWriter in JMXTrans - * which is based on GangliaContext31 - * from Hadoop. - */ -public class GangliaReporter extends AbstractPollingReporter implements MetricProcessor { - /* for use as metricType parameter to sendMetricData() */ - public static final String GANGLIA_INT_TYPE = "int32"; - public static final String GANGLIA_DOUBLE_TYPE = "double"; - public static final String GANGLIA_STRING_TYPE = "string"; +import static com.yammer.metrics.MetricRegistry.name; - private static final Logger LOG = LoggerFactory.getLogger(GangliaReporter.class); - private static final int GANGLIA_TMAX = 60; - private static final int GANGLIA_DMAX = 0; - private final MetricPredicate predicate; - private final VirtualMachineMetrics vm; - private final Locale locale = Locale.US; - private String hostLabel; - private String groupPrefix = ""; - private boolean compressPackageNames; - private final GangliaMessageBuilder gangliaMessageBuilder; - public boolean printVMMetrics = true; - - /** - * Enables the ganglia reporter to send data for the default metrics registry to ganglia server - * with the specified period. - * - * @param period the period between successive outputs - * @param unit the time unit of {@code period} - * @param gangliaHost the gangliaHost name of ganglia server (carbon-cache agent) - * @param port the port number on which the ganglia server is listening - */ - public static void enable(long period, TimeUnit unit, String gangliaHost, int port) { - enable(Metrics.defaultRegistry(), period, unit, gangliaHost, port, ""); - } - - /** - * Enables the ganglia reporter to send data for the default metrics registry to ganglia server - * with the specified period. - * - * @param period the period between successive outputs - * @param unit the time unit of {@code period} - * @param gangliaHost the gangliaHost name of ganglia server (carbon-cache agent) - * @param port the port number on which the ganglia server is listening - * @param groupPrefix prefix to the ganglia group name (such as myapp_counter) - */ - public static void enable(long period, TimeUnit unit, String gangliaHost, int port, String groupPrefix) { - enable(Metrics.defaultRegistry(), period, unit, gangliaHost, port, groupPrefix); - } - - /** - * Enables the ganglia reporter to send data for the default metrics registry to ganglia server - * with the specified period. - * - * @param period the period between successive outputs - * @param unit the time unit of {@code period} - * @param gangliaHost the gangliaHost name of ganglia server (carbon-cache agent) - * @param port the port number on which the ganglia server is listening - * @param compressPackageNames if true reporter will compress package names e.g. - * com.foo.MetricName becomes c.f.MetricName - */ - public static void enable(long period, TimeUnit unit, String gangliaHost, int port, boolean compressPackageNames) { - enable(Metrics.defaultRegistry(), - period, - unit, - gangliaHost, - port, - "", - MetricPredicate.ALL, - compressPackageNames); - } +public class GangliaReporter extends AbstractPollingReporter { + private static final Logger LOGGER = LoggerFactory.getLogger(GangliaReporter.class); + private final GMetric ganglia; + private final double durationFactor; + private final String durationUnit; + private final double rateFactor; + private final String rateUnit; + private final int tMax; + private final int dMax; - /** - * Enables the ganglia reporter to send data for the given metrics registry to ganglia server - * with the specified period. - * - * @param metricsRegistry the metrics registry - * @param period the period between successive outputs - * @param unit the time unit of {@code period} - * @param gangliaHost the gangliaHost name of ganglia server (carbon-cache agent) - * @param port the port number on which the ganglia server is listening - * @param groupPrefix prefix to the ganglia group name (such as myapp_counter) - */ - public static void enable(MetricsRegistry metricsRegistry, long period, TimeUnit unit, String gangliaHost, int port, String groupPrefix) { - enable(metricsRegistry, period, unit, gangliaHost, port, groupPrefix, MetricPredicate.ALL); + public GangliaReporter(MetricRegistry registry, + GMetric ganglia, + int tMax, + int dMax, + TimeUnit rateUnit, + TimeUnit durationUnit) { + super(registry, "ganglia-reporter"); + this.ganglia = ganglia; + this.tMax = tMax; + this.dMax = dMax; + this.rateFactor = rateUnit.toSeconds(1); + this.rateUnit = calculateRateUnit(rateUnit); + this.durationFactor = 1.0 / durationUnit.toNanos(1); + this.durationUnit = durationUnit.toString().toLowerCase(Locale.US); } - /** - * Enables the ganglia reporter to send data to ganglia server with the specified period. - * - * @param metricsRegistry the metrics registry - * @param period the period between successive outputs - * @param unit the time unit of {@code period} - * @param gangliaHost the gangliaHost name of ganglia server (carbon-cache agent) - * @param port the port number on which the ganglia server is listening - * @param groupPrefix prefix to the ganglia group name (such as myapp_counter) - * @param predicate filters metrics to be reported - */ - public static void enable(MetricsRegistry metricsRegistry, long period, TimeUnit unit, String gangliaHost, int port, String groupPrefix, MetricPredicate predicate) { - enable(metricsRegistry, period, unit, gangliaHost, port, groupPrefix, predicate, false); + private String calculateRateUnit(TimeUnit unit) { + final String s = unit.toString().toLowerCase(Locale.US); + return s.substring(0, s.length() - 1); } - /** - * Enables the ganglia reporter to send data to ganglia server with the specified period. - * - * @param metricsRegistry the metrics registry - * @param period the period between successive outputs - * @param unit the time unit of {@code period} - * @param gangliaHost the gangliaHost name of ganglia server (carbon-cache agent) - * @param port the port number on which the ganglia server is listening - * @param groupPrefix prefix to the ganglia group name (such as myapp_counter) - * @param predicate filters metrics to be reported - * @param compressPackageNames if true reporter will compress package names e.g. - * com.foo.MetricName becomes c.f.MetricName - */ - public static void enable(MetricsRegistry metricsRegistry, long period, TimeUnit unit, String gangliaHost, - int port, String groupPrefix, MetricPredicate predicate, boolean compressPackageNames) { - try { - final GangliaReporter reporter = new GangliaReporter(metricsRegistry, - gangliaHost, - port, - groupPrefix, - predicate, - compressPackageNames); - reporter.start(period, unit); - } catch (Exception e) { - LOG.error("Error creating/starting ganglia reporter:", e); + @Override + public void report(SortedMap gauges, + SortedMap counters, + SortedMap histograms, + SortedMap meters, + SortedMap timers) { + for (Map.Entry entry : gauges.entrySet()) { + reportGauge(entry.getKey(), entry.getValue()); } - } - - /** - * Creates a new {@link GangliaReporter}. - * - * @param gangliaHost is ganglia server - * @param port is port on which ganglia server is running - * @throws java.io.IOException if there is an error connecting to the ganglia server - */ - public GangliaReporter(String gangliaHost, int port) throws IOException { - this(Metrics.defaultRegistry(), gangliaHost, port, ""); - } - - /** - * Creates a new {@link GangliaReporter}. - * - * @param gangliaHost is ganglia server - * @param port is port on which ganglia server is running - * @param compressPackageNames whether or not Metrics' package names will be shortened - * @throws java.io.IOException if there is an error connecting to the ganglia server - */ - public GangliaReporter(String gangliaHost, int port, boolean compressPackageNames) throws IOException { - this(Metrics.defaultRegistry(), - gangliaHost, - port, - "", - MetricPredicate.ALL, - compressPackageNames); - } - - /** - * Creates a new {@link GangliaReporter}. - * - * @param metricsRegistry the metrics registry - * @param gangliaHost is ganglia server - * @param port is port on which ganglia server is running - * @param groupPrefix prefix to the ganglia group name (such as myapp_counter) - * @throws java.io.IOException if there is an error connecting to the ganglia server - */ - public GangliaReporter(MetricsRegistry metricsRegistry, String gangliaHost, int port, String groupPrefix) throws IOException { - this(metricsRegistry, gangliaHost, port, groupPrefix, MetricPredicate.ALL); - } - - /** - * Creates a new {@link GangliaReporter}. - * - * @param metricsRegistry the metrics registry - * @param gangliaHost is ganglia server - * @param port is port on which ganglia server is running - * @param groupPrefix prefix to the ganglia group name (such as myapp_counter) - * @param predicate filters metrics to be reported - * @throws java.io.IOException if there is an error connecting to the ganglia server - */ - public GangliaReporter(MetricsRegistry metricsRegistry, String gangliaHost, int port, String groupPrefix, MetricPredicate predicate) throws IOException { - this(metricsRegistry, gangliaHost, port, groupPrefix, predicate, false); - } - /** - * Creates a new {@link GangliaReporter}. - * - * @param metricsRegistry the metrics registry - * @param gangliaHost is ganglia server - * @param port is port on which ganglia server is running - * @param groupPrefix prefix to the ganglia group name (such as myapp_counter) - * @param predicate filters metrics to be reported - * @param compressPackageNames if true reporter will compress package names e.g. - * com.foo.MetricName becomes c.f.MetricName - * @throws java.io.IOException if there is an error connecting to the ganglia server - */ - public GangliaReporter(MetricsRegistry metricsRegistry, String gangliaHost, int port, String groupPrefix, - MetricPredicate predicate, boolean compressPackageNames) throws IOException { - this(metricsRegistry, - groupPrefix, - predicate, - compressPackageNames, - new GangliaMessageBuilder(gangliaHost, port), VirtualMachineMetrics.getInstance()); - } + for (Map.Entry entry : counters.entrySet()) { + reportCounter(entry.getKey(), entry.getValue()); + } - /** - * Creates a new {@link GangliaReporter}. - * - * @param metricsRegistry the metrics registry - * @param groupPrefix prefix to the ganglia group name (such as myapp_counter) - * @param predicate filters metrics to be reported - * @param compressPackageNames if true reporter will compress package names e.g. - * com.foo.MetricName becomes c.f.MetricName - * @param gangliaMessageBuilder a {@link GangliaMessageBuilder} instance - * @param vm a {@link VirtualMachineMetrics} isntance - * @throws java.io.IOException if there is an error connecting to the ganglia server - */ - public GangliaReporter(MetricsRegistry metricsRegistry, String groupPrefix, - MetricPredicate predicate, boolean compressPackageNames, - GangliaMessageBuilder gangliaMessageBuilder, VirtualMachineMetrics vm) throws IOException { - super(metricsRegistry, "ganglia-reporter"); - this.gangliaMessageBuilder = gangliaMessageBuilder; - this.groupPrefix = groupPrefix + "_"; - this.hostLabel = getDefaultHostLabel(); - this.predicate = predicate; - this.compressPackageNames = compressPackageNames; - this.vm = vm; - } + for (Map.Entry entry : histograms.entrySet()) { + reportHistogram(entry.getKey(), entry.getValue()); + } - @Override - public void run() { - if (this.printVMMetrics) { - printVmMetrics(); + for (Map.Entry entry : meters.entrySet()) { + reportMeter(entry.getKey(), entry.getValue()); } - printRegularMetrics(); - } - private void printRegularMetrics() { - 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(), null); - } catch (Exception ignored) { - LOG.error("Error printing regular metrics:", ignored); - } - } - } + for (Map.Entry entry : timers.entrySet()) { + reportTimer(entry.getKey(), entry.getValue()); } } - private void sendToGanglia(String metricName, String metricType, String metricValue, String groupName, String units) { + private void reportTimer(String name, Timer timer) { + final String group = group(name); try { - sendMetricData(metricType, metricName, metricValue, groupPrefix + groupName, units); - if (LOG.isTraceEnabled()) { - LOG.trace("Emitting metric " + metricName + ", type " + metricType + ", value " + metricValue + " for gangliaHost: " + this - .gangliaMessageBuilder - .getHostName() + ":" + this.gangliaMessageBuilder.getPort()); - } - } catch (IOException e) { - LOG.error("Error sending to ganglia:", e); + announce(name(name, "max"), group, timer.getMax() * durationFactor, durationUnit); + announce(name(name, "mean"), group, timer.getMean() * durationFactor, durationUnit); + announce(name(name, "min"), group, timer.getMin() * durationFactor, durationUnit); + announce(name(name, "stddev"), group, timer.getStdDev() * durationFactor, durationUnit); + + final Snapshot snapshot = timer.getSnapshot(); + announce(name(name, "p50"), group, snapshot.getMedian() * durationFactor, durationUnit); + announce(name(name, "p75"), group, snapshot.get75thPercentile() * durationFactor, durationUnit); + announce(name(name, "p95"), group, snapshot.get95thPercentile() * durationFactor, durationUnit); + announce(name(name, "p98"), group, snapshot.get98thPercentile() * durationFactor, durationUnit); + announce(name(name, "p99"), group, snapshot.get99thPercentile() * durationFactor, durationUnit); + announce(name(name, "p999"), group, snapshot.get999thPercentile() * durationFactor, durationUnit); + + reportMetered(name, timer, group, "calls"); + } catch (GangliaException e) { + LOGGER.warn("Unable to report timer {}", name, e); } } - private void sendToGanglia(String metricName, String metricType, String metricValue, String groupName) { - sendToGanglia(metricName, metricType, metricValue, groupName, ""); - } - - private void sendMetricData(String metricType, String metricName, String metricValue, String groupName, String units) throws IOException { - sendMetricData(getHostLabel(), metricType, metricName, metricValue, groupName, units); - } - - /** - * allow subclasses to send UDP metrics directly, unchecked. - * note: hostName must be in the format IP:HOST - * (ex: 127.0.0.0:my.host.name) or ganglia will drop the packet. - * no parameters are permitted to be null. - * - * @param hostName IP:HOST formatted string - * @param metricType "int32", "double", "float", etc - * @param metricName name of metric - * @param groupName correlates with ganglia cluster names. - * @param units unit of measure. empty string is OK. - */ - protected void sendMetricData(String hostName, String metricType, String metricName, String metricValue, String groupName, String units) throws IOException { - this.gangliaMessageBuilder.newMessage() - .addInt(128)// metric_id = metadata_msg - .addString(hostName)// hostname - .addString(metricName)// metric name - .addInt(hostName.equals(getHostLabel()) ? 0 : 1)// spoof = True/1 - .addString(metricType)// metric type - .addString(metricName)// metric name - .addString(units)// units - .addInt(3)// slope see gmetric.c - .addInt(GANGLIA_TMAX)// tmax, the maximum time between metrics - .addInt(GANGLIA_DMAX)// dmax, the maximum data value - .addInt(1) - .addString("GROUP")// Group attribute - .addString(groupName)// Group value - .send(); - - this.gangliaMessageBuilder.newMessage() - .addInt(133)// we are sending a string value - .addString(hostName)// hostLabel - .addString(metricName)// metric name - .addInt(hostName.equals(getHostLabel()) ? 0 : 1)// spoof = True/1 - .addString("%s")// format field - .addString(metricValue) // metric value - .send(); - } - - @Override - public void processGauge(MetricName name, Gauge gauge, String x) throws IOException { - final Object value = gauge.value(); - final Class klass = value.getClass(); - - final String type; - if (klass == Integer.class || klass == Long.class) { - type = GANGLIA_INT_TYPE; - } else if (klass == Float.class || klass == Double.class) { - type = GANGLIA_DOUBLE_TYPE; - } else { - type = GANGLIA_STRING_TYPE; + private void reportMeter(String name, Meter meter) { + final String group = group(name); + try { + reportMetered(name, meter, group, "events"); + } catch (GangliaException e) { + LOGGER.warn("Unable to report meter {}", name, e); } - - sendToGanglia(sanitizeName(name), - type, - String.format(locale, "%s", gauge.value()), - "gauge"); - } - - @Override - public void processCounter(MetricName name, Counter counter, String x) throws IOException { - sendToGanglia(sanitizeName(name), - GANGLIA_INT_TYPE, - String.format(locale, "%d", counter.count()), - "counter"); - } - - @Override - public void processMeter(MetricName name, Metered meter, String x) throws IOException { - final String sanitizedName = sanitizeName(name); - final String rateUnits = meter.rateUnit().name(); - final String rateUnit = rateUnits.substring(0, rateUnits.length() - 1).toLowerCase(Locale.US); - final String unit = meter.eventType() + '/' + rateUnit; - printLongField(sanitizedName + ".count", meter.count(), "metered", meter.eventType()); - printDoubleField(sanitizedName + ".meanRate", meter.meanRate(), "metered", unit); - printDoubleField(sanitizedName + ".1MinuteRate", meter.oneMinuteRate(), "metered", unit); - printDoubleField(sanitizedName + ".5MinuteRate", meter.fiveMinuteRate(), "metered", unit); - printDoubleField(sanitizedName + ".15MinuteRate", meter.fifteenMinuteRate(), "metered", unit); - } - - @Override - public void processHistogram(MetricName name, Histogram histogram, String x) throws IOException { - final String sanitizedName = sanitizeName(name); - final Snapshot snapshot = histogram.getSnapshot(); - // TODO: what units make sense for histograms? should we add event type to the Histogram metric? - printDoubleField(sanitizedName + ".min", histogram.min(), "histo"); - printDoubleField(sanitizedName + ".max", histogram.max(), "histo"); - printDoubleField(sanitizedName + ".mean", histogram.mean(), "histo"); - printDoubleField(sanitizedName + ".stddev", histogram.stdDev(), "histo"); - printDoubleField(sanitizedName + ".median", snapshot.getMedian(), "histo"); - printDoubleField(sanitizedName + ".75percentile", snapshot.get75thPercentile(), "histo"); - printDoubleField(sanitizedName + ".95percentile", snapshot.get95thPercentile(), "histo"); - printDoubleField(sanitizedName + ".98percentile", snapshot.get98thPercentile(), "histo"); - printDoubleField(sanitizedName + ".99percentile", snapshot.get99thPercentile(), "histo"); - printDoubleField(sanitizedName + ".999percentile", snapshot.get999thPercentile(), "histo"); - } - - @Override - public void processTimer(MetricName name, Timer timer, String x) throws IOException { - processMeter(name, timer, x); - final String sanitizedName = sanitizeName(name); - final Snapshot snapshot = timer.getSnapshot(); - final String durationUnit = timer.durationUnit().name(); - printDoubleField(sanitizedName + ".min", timer.min(), "timer", durationUnit); - printDoubleField(sanitizedName + ".max", timer.max(), "timer", durationUnit); - printDoubleField(sanitizedName + ".mean", timer.mean(), "timer", durationUnit); - printDoubleField(sanitizedName + ".stddev", timer.stdDev(), "timer", durationUnit); - printDoubleField(sanitizedName + ".median", snapshot.getMedian(), "timer", durationUnit); - printDoubleField(sanitizedName + ".75percentile", snapshot.get75thPercentile(), "timer", durationUnit); - printDoubleField(sanitizedName + ".95percentile", snapshot.get95thPercentile(), "timer", durationUnit); - printDoubleField(sanitizedName + ".98percentile", snapshot.get98thPercentile(), "timer", durationUnit); - printDoubleField(sanitizedName + ".99percentile", snapshot.get99thPercentile(), "timer", durationUnit); - printDoubleField(sanitizedName + ".999percentile", snapshot.get999thPercentile(), "timer", durationUnit); - } - - private void printDoubleField(String name, double value, String groupName, String units) { - sendToGanglia(name, - GANGLIA_DOUBLE_TYPE, - String.format(locale, "%2.2f", value), - groupName, - units); } - private void printDoubleField(String name, double value, String groupName) { - printDoubleField(name, value, groupName, ""); + private void reportMetered(String name, Metered meter, String group, String eventName) throws GangliaException { + final String unit = eventName + '/' + rateUnit; + announce(name(name, "count"), group, meter.getCount(), eventName); + announce(name(name, "m1_rate"), group, meter.getOneMinuteRate() * rateFactor, unit); + announce(name(name, "m5_rate"), group, meter.getFiveMinuteRate() * rateFactor, unit); + announce(name(name, "m15_rate"), group, meter.getFifteenMinuteRate() * rateFactor, unit); + announce(name(name, "mean_rate"), group, meter.getMeanRate() * rateFactor, unit); } - private void printLongField(String name, long value, String groupName) { - printLongField(name, value, groupName, ""); - } - - private void printLongField(String name, long value, String groupName, String units) { - // TODO: ganglia does not support int64, what should we do here? - sendToGanglia(name, GANGLIA_INT_TYPE, String.format(locale, "%d", value), groupName, units); - } - - private void printVmMetrics() { - printDoubleField("jvm.memory.heap_usage", vm.heapUsage(), "jvm"); - printDoubleField("jvm.memory.non_heap_usage", vm.nonHeapUsage(), "jvm"); - for (Map.Entry pool : vm.memoryPoolUsage().entrySet()) { - printDoubleField("jvm.memory.memory_pool_usages." + pool.getKey(), - pool.getValue(), - "jvm"); - } - - printDoubleField("jvm.daemon_thread_count", vm.daemonThreadCount(), "jvm"); - printDoubleField("jvm.thread_count", vm.threadCount(), "jvm"); - printDoubleField("jvm.uptime", vm.uptime(), "jvm"); - printDoubleField("jvm.fd_usage", vm.fileDescriptorUsage(), "jvm"); - - for (Map.Entry entry : vm.threadStatePercentages().entrySet()) { - printDoubleField("jvm.thread-states." + entry.getKey().toString().toLowerCase(), - entry.getValue(), - "jvm"); - } - - for (Map.Entry entry : vm.garbageCollectors().entrySet()) { - printLongField("jvm.gc." + entry.getKey() + ".time", - entry.getValue().getTime(TimeUnit.MILLISECONDS), - "jvm"); - printLongField("jvm.gc." + entry.getKey() + ".runs", entry.getValue().getRuns(), "jvm"); + private void reportHistogram(String name, Histogram histogram) { + final String group = group(name); + try { + final Snapshot snapshot = histogram.getSnapshot(); + + announce(name(name, "count"), group, histogram.getCount(), ""); + announce(name(name, "max"), group, histogram.getMax(), ""); + announce(name(name, "mean"), group, histogram.getMean(), ""); + announce(name(name, "min"), group, histogram.getMin(), ""); + announce(name(name, "stddev"), group, histogram.getStdDev(), ""); + + announce(name(name, "p50"), group, snapshot.getMedian(), ""); + announce(name(name, "p75"), group, snapshot.get75thPercentile(), ""); + announce(name(name, "p95"), group, snapshot.get95thPercentile(), ""); + announce(name(name, "p98"), group, snapshot.get98thPercentile(), ""); + announce(name(name, "p99"), group, snapshot.get99thPercentile(), ""); + announce(name(name, "p999"), group, snapshot.get999thPercentile(), ""); + } catch (GangliaException e) { + LOGGER.warn("Unable to report histogram {}", name, e); } } - String getDefaultHostLabel() { + private void reportCounter(String name, Counter counter) { + final String group = group(name); try { - final InetAddress addr = InetAddress.getLocalHost(); - return addr.getHostAddress() + ":" + addr.getHostName(); - } catch (UnknownHostException e) { - LOG.error("Unable to get local gangliaHost name: ", e); - return "unknown"; + announce(name, group, counter.getCount(), ""); + } catch (GangliaException e) { + LOGGER.warn("Unable to report counter {}", name, e); } } - /* subclass to override in metric packets */ - protected String getHostLabel() { - return hostLabel; + private void reportGauge(String name, Gauge gauge) { + final String group = group(name); + final Object obj = gauge.getValue(); + try { + ganglia.announce(name, String.valueOf(obj), detectType(obj), "", + GMetricSlope.BOTH, tMax, dMax, group); + } catch (GangliaException e) { + LOGGER.warn("Unable to report gauge {}", name, e); + } } - protected String sanitizeName(MetricName name) { - if (name == null) { - return ""; - } - final String qualifiedTypeName = name.getGroup() + "." + name.getType() + "." + name.getName(); - final String metricName = name.hasScope() ? qualifiedTypeName + '.' + name.getScope() : qualifiedTypeName; - final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < metricName.length(); i++) { - final char p = metricName.charAt(i); - if (!(p >= 'A' && p <= 'Z') - && !(p >= 'a' && p <= 'z') - && !(p >= '0' && p <= '9') - && (p != '_') - && (p != '-') - && (p != '.') - && (p != '\0')) { - sb.append('_'); - } else { - sb.append(p); - } + private void announce(String name, String group, double value, String units) throws GangliaException { + ganglia.announce(name, + Double.toString(value), + GMetricType.DOUBLE, + units, + GMetricSlope.BOTH, + tMax, + dMax, + group); + } + + private void announce(String name, String group, long value, String units) throws GangliaException { + final String v = Long.toString(value); + ganglia.announce(name, + v, + GMetricType.DOUBLE, + units, + GMetricSlope.BOTH, + tMax, + dMax, + group); + } + + private GMetricType detectType(Object o) { + if (o instanceof Float) { + return GMetricType.FLOAT; + } else if (o instanceof Double) { + return GMetricType.DOUBLE; + } else if (o instanceof Byte) { + return GMetricType.INT8; + } else if (o instanceof Short) { + return GMetricType.INT16; + } else if (o instanceof Integer) { + return GMetricType.INT32; + } else if (o instanceof Long) { + return GMetricType.DOUBLE; } - return compressPackageName(sb.toString()); + return GMetricType.STRING; } - private String compressPackageName(String name) { - if (compressPackageNames && name.indexOf(".") > 0) { - final String[] nameParts = name.split("\\."); - final StringBuilder sb = new StringBuilder(); - final int numParts = nameParts.length; - int count = 0; - for (String namePart : nameParts) { - if (++count < numParts - 1) { - sb.append(namePart.charAt(0)); - sb.append("."); - } else { - sb.append(namePart); - if (count == numParts - 1) { - sb.append("."); - } - } - } - name = sb.toString(); + private String group(String name) { + final int i = name.lastIndexOf('.'); + if (i < 0) { + return ""; } - return name; + return name.substring(0, i); } } diff --git a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaMessageBuilderTest.java b/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaMessageBuilderTest.java deleted file mode 100644 index d3dc207ec9..0000000000 --- a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaMessageBuilderTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.yammer.metrics.ganglia; - -import com.yammer.metrics.ganglia.GangliaMessageBuilder; -import org.junit.Test; - -import java.net.SocketException; - -import static org.junit.Assert.assertEquals; - -public class GangliaMessageBuilderTest { - @Test - public void providesCorrectHostAndPort() throws SocketException { - final String hostName = "hostName"; - final int port = 12345; - - final GangliaMessageBuilder builder = new GangliaMessageBuilder(hostName, port); - - assertEquals(hostName, builder.getHostName()); - assertEquals(port, builder.getPort()); - } -} diff --git a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaMessageTest.java b/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaMessageTest.java deleted file mode 100644 index d6774b32a7..0000000000 --- a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaMessageTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.yammer.metrics.ganglia; - -import com.yammer.metrics.ganglia.GangliaMessage; -import org.junit.Test; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; - -public class GangliaMessageTest { - @Test - public void canAddInt() { - final int bytesToWrite = 4; //integer - final byte[] buffer = new byte[bytesToWrite]; - final byte[] expecteds = new byte[]{0, 0, 2, (byte) 166}; - - final GangliaMessage message = new GangliaMessage(null, buffer, null); - - message.addInt(678); - - assertArrayEquals(expecteds, buffer); - assertEquals(bytesToWrite, message.getOffset()); - } - - @Test - public void canAddString() { - final int bytesToWrite = 4 + 4; //integer + message - final byte[] buffer = new byte[bytesToWrite]; - final byte[] expecteds = new byte[]{0, 0, 0, 4, 't', 'e', 's', 't'}; - - final GangliaMessage message = new GangliaMessage(null, buffer, null); - - message.addString("test"); - - assertArrayEquals(expecteds, buffer); - assertEquals(bytesToWrite, message.getOffset()); - } - - @Test - public void canAddPaddedString() { - final int bytesToWrite = 4 + 5 + 3; //integer + message + padding - final byte[] buffer = new byte[bytesToWrite]; - final byte[] expecteds = new byte[]{0, 0, 0, 5, 't', 'e', 's', 't', 's', 0, 0, 0}; - - final GangliaMessage message = new GangliaMessage(null, buffer, null); - - message.addString("tests"); - - assertArrayEquals(expecteds, buffer); - assertEquals(bytesToWrite, message.getOffset()); - } -} diff --git a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaReporterTest.java b/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaReporterTest.java deleted file mode 100644 index e84d1927dd..0000000000 --- a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaReporterTest.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.yammer.metrics.ganglia; - -import com.yammer.metrics.core.*; -import com.yammer.metrics.ganglia.GangliaMessage; -import com.yammer.metrics.ganglia.GangliaMessageBuilder; -import com.yammer.metrics.ganglia.GangliaReporter; -import com.yammer.metrics.reporting.AbstractPollingReporter; -import com.yammer.metrics.reporting.tests.AbstractPollingReporterTest; -import com.yammer.metrics.core.MetricPredicate; -import org.apache.commons.io.IOUtils; -import org.junit.Test; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.net.SocketException; - -import static junit.framework.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class GangliaReporterTest extends AbstractPollingReporterTest { - private GangliaMessage testMessage; - - @Override - protected AbstractPollingReporter createReporter(MetricsRegistry registry, OutputStream out, Clock clock) throws Exception { - final OutputStreamWriter output = new OutputStreamWriter(out); - this.testMessage = new GangliaMessage(null, null, null) { - - @Override - public GangliaMessage addInt(int value) { - try { - output.append("addInt(" + value + ")\n").flush(); - } catch (IOException e) { - throw new RuntimeException(e.getMessage(), e); - } - return this; - } - - @Override - public GangliaMessage addString(String value) { - try { - output.append("addString(" + value + ")\n").flush(); - } catch (IOException e) { - throw new RuntimeException(e.getMessage(), e); - } - return this; - } - - @Override - public void send() throws SocketException, IOException { - output.append("send()\n").flush(); - } - - @Override - public String toString() { - return output.toString(); - } - }; - - final GangliaMessageBuilder messageBuilder = mock(GangliaMessageBuilder.class); - when(messageBuilder.newMessage()).thenReturn(this.testMessage); - - final GangliaReporter reporter = new GangliaReporter(registry, - "group-prefix", - MetricPredicate.ALL, - false, - messageBuilder, - VirtualMachineMetrics.getInstance()) { - @Override - String getDefaultHostLabel() { - return "localhost"; - } - - @Override - public void run() { - super.run(); - } - }; - reporter.printVMMetrics = false; - return reporter; - } - - @Test - public void testSanitizeName_noBadCharacters() throws IOException { - final MetricName metricName = new MetricName("thisIs", "AClean", "MetricName"); - final GangliaReporter gangliaReporter = new GangliaReporter("localhost", 5555); - final String cleanMetricName = gangliaReporter.sanitizeName(metricName); - assertEquals("clean metric name was changed unexpectedly", - "thisIs.AClean.MetricName", - cleanMetricName); - } - - @Test - public void testSanitizeName_badCharacters() throws IOException { - final MetricName metricName = new MetricName("thisIs", "AC>&!>lean", "Metric Name"); - final String expectedMetricName = "thisIs.AC____lean.Metric_Name"; - final GangliaReporter gangliaReporter = new GangliaReporter("localhost", 5555); - final String cleanMetricName = gangliaReporter.sanitizeName(metricName); - assertEquals("clean metric name did not match expected value", - expectedMetricName, - cleanMetricName); - } - - @Test - public void testCompressPackageName() throws IOException { - final MetricName metricName = new MetricName("some.long.package.name.thisIs", "AC>&!>lean", "Metric Name"); - final String expectedMetricName = "s.l.p.n.t.AC____lean.Metric_Name"; - final GangliaReporter gangliaReporter = new GangliaReporter("localhost", 5555, true); - final String cleanMetricName = gangliaReporter.sanitizeName(metricName); - assertEquals("clean metric name did not match expected value", - expectedMetricName, - cleanMetricName); - } - - protected String getFromFile(String fileName) { - try { - return IOUtils.toString(new FileInputStream(getClass().getClassLoader() - .getResource(fileName) - .getFile())); - } catch (Exception e) { - throw new RuntimeException(e.getMessage(), e); - } - } - - @Override - public String[] expectedGaugeResult(String value) { - return String.format(getFromFile("gauge.io"), value).split("\\n"); - } - - @Override - public String[] expectedTimerResult() { - return getFromFile("timed.io").split("\\n"); - } - - @Override - public String[] expectedMeterResult() { - return getFromFile("metered.io").split("\\n"); - } - - @Override - public String[] expectedHistogramResult() { - return getFromFile("histogram.io").split("\\n"); - } - - @Override - public String[] expectedCounterResult(long count) { - return String.format(getFromFile("counter.io"), count).split("\\n"); - } -} diff --git a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java b/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java new file mode 100644 index 0000000000..6e241d1f6c --- /dev/null +++ b/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java @@ -0,0 +1,252 @@ +package com.yammer.metrics.ganglia.tests; + +import com.yammer.metrics.*; +import com.yammer.metrics.ganglia.GangliaReporter; +import info.ganglia.gmetric4j.gmetric.GMetric; +import info.ganglia.gmetric4j.gmetric.GMetricSlope; +import info.ganglia.gmetric4j.gmetric.GMetricType; +import org.junit.Test; + +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.concurrent.TimeUnit; + +import static org.mockito.Mockito.*; + +public class GangliaReporterTest { + private final GMetric ganglia = mock(GMetric.class); + private final MetricRegistry registry = mock(MetricRegistry.class); + private final GangliaReporter reporter = new GangliaReporter(registry, + ganglia, + 60, + 0, + TimeUnit.SECONDS, + TimeUnit.MILLISECONDS); + + @Test + public void reportsStringGaugeValues() throws Exception { + reporter.report(map("gauge", gauge("value")), + this.map(), + this.map(), + this.map(), + this.map()); + + verify(ganglia).announce("gauge", "value", GMetricType.STRING, "", GMetricSlope.BOTH, 60, 0, ""); + verifyNoMoreInteractions(ganglia); + } + + @Test + public void reportsByteGaugeValues() throws Exception { + reporter.report(map("gauge", gauge((byte) 1)), + this.map(), + this.map(), + this.map(), + this.map()); + + verify(ganglia).announce("gauge", "1", GMetricType.INT8, "", GMetricSlope.BOTH, 60, 0, ""); + verifyNoMoreInteractions(ganglia); + } + + @Test + public void reportsShortGaugeValues() throws Exception { + reporter.report(map("gauge", gauge((short) 1)), + this.map(), + this.map(), + this.map(), + this.map()); + + verify(ganglia).announce("gauge", "1", GMetricType.INT16, "", GMetricSlope.BOTH, 60, 0, ""); + verifyNoMoreInteractions(ganglia); + } + + @Test + public void reportsIntegerGaugeValues() throws Exception { + reporter.report(map("gauge", gauge(1)), + this.map(), + this.map(), + this.map(), + this.map()); + + verify(ganglia).announce("gauge", "1", GMetricType.INT32, "", GMetricSlope.BOTH, 60, 0, ""); + verifyNoMoreInteractions(ganglia); + } + + @Test + public void reportsLongGaugeValues() throws Exception { + reporter.report(map("gauge", gauge(1L)), + this.map(), + this.map(), + this.map(), + this.map()); + + verify(ganglia).announce("gauge", "1", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, ""); + verifyNoMoreInteractions(ganglia); + } + + @Test + public void reportsFloatGaugeValues() throws Exception { + reporter.report(map("gauge", gauge(1.0f)), + this.map(), + this.map(), + this.map(), + this.map()); + + verify(ganglia).announce("gauge", "1.0", GMetricType.FLOAT, "", GMetricSlope.BOTH, 60, 0, ""); + verifyNoMoreInteractions(ganglia); + } + + @Test + public void reportsDoubleGaugeValues() throws Exception { + reporter.report(map("gauge", gauge(1.0)), + this.map(), + this.map(), + this.map(), + this.map()); + + verify(ganglia).announce("gauge", "1.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, ""); + verifyNoMoreInteractions(ganglia); + } + + @Test + public void reportsCounterValues() throws Exception { + final Counter counter = mock(Counter.class); + when(counter.getCount()).thenReturn(100L); + + reporter.report(this.map(), + map("test.counter", counter), + this.map(), + this.map(), + this.map()); + + verify(ganglia).announce("test.counter", "100", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); + verifyNoMoreInteractions(ganglia); + } + + @Test + public void reportsHistogramValues() throws Exception { + final Histogram histogram = mock(Histogram.class); + when(histogram.getCount()).thenReturn(1L); + when(histogram.getMax()).thenReturn(2L); + when(histogram.getMean()).thenReturn(3.0); + when(histogram.getMin()).thenReturn(4L); + when(histogram.getStdDev()).thenReturn(5.0); + + final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMedian()).thenReturn(6.0); + when(snapshot.get75thPercentile()).thenReturn(7.0); + when(snapshot.get95thPercentile()).thenReturn(8.0); + when(snapshot.get98thPercentile()).thenReturn(9.0); + when(snapshot.get99thPercentile()).thenReturn(10.0); + when(snapshot.get999thPercentile()).thenReturn(11.0); + + when(histogram.getSnapshot()).thenReturn(snapshot); + + reporter.report(this.map(), + this.map(), + map("test.histogram", histogram), + this.map(), + this.map()); + + verify(ganglia).announce("test.histogram.count", "1", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); + verify(ganglia).announce("test.histogram.max", "2", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); + verify(ganglia).announce("test.histogram.mean", "3.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); + verify(ganglia).announce("test.histogram.min", "4", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); + verify(ganglia).announce("test.histogram.stddev", "5.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); + verify(ganglia).announce("test.histogram.p50", "6.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); + verify(ganglia).announce("test.histogram.p75", "7.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); + verify(ganglia).announce("test.histogram.p95", "8.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); + verify(ganglia).announce("test.histogram.p98", "9.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); + verify(ganglia).announce("test.histogram.p99", "10.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); + verify(ganglia).announce("test.histogram.p999", "11.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); + verifyNoMoreInteractions(ganglia); + } + + @Test + public void reportsMeterValues() throws Exception { + final Meter meter = mock(Meter.class); + when(meter.getCount()).thenReturn(1L); + when(meter.getMeanRate()).thenReturn(2.0); + when(meter.getOneMinuteRate()).thenReturn(3.0); + when(meter.getFiveMinuteRate()).thenReturn(4.0); + when(meter.getFifteenMinuteRate()).thenReturn(5.0); + + reporter.report(this.map(), + this.map(), + this.map(), + map("test.meter", meter), + this.map()); + + verify(ganglia).announce("test.meter.count", "1", GMetricType.DOUBLE, "events", GMetricSlope.BOTH, 60, 0, "test"); + verify(ganglia).announce("test.meter.mean_rate", "2.0", GMetricType.DOUBLE, "events/second", GMetricSlope.BOTH, 60, 0, "test"); + verify(ganglia).announce("test.meter.m1_rate", "3.0", GMetricType.DOUBLE, "events/second", GMetricSlope.BOTH, 60, 0, "test"); + verify(ganglia).announce("test.meter.m5_rate", "4.0", GMetricType.DOUBLE, "events/second", GMetricSlope.BOTH, 60, 0, "test"); + verify(ganglia).announce("test.meter.m15_rate", "5.0", GMetricType.DOUBLE, "events/second", GMetricSlope.BOTH, 60, 0, "test"); + verifyNoMoreInteractions(ganglia); + } + + @Test + public void reportsTimerValues() throws Exception { + final Timer timer = mock(Timer.class); + when(timer.getCount()).thenReturn(1L); + when(timer.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); + when(timer.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); + when(timer.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); + when(timer.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); + + when(timer.getMeanRate()).thenReturn(2.0); + when(timer.getOneMinuteRate()).thenReturn(3.0); + when(timer.getFiveMinuteRate()).thenReturn(4.0); + when(timer.getFifteenMinuteRate()).thenReturn(5.0); + + final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); + when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); + when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); + when(snapshot.get98thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(800)); + when(snapshot.get99thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(900)); + when(snapshot.get999thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(1000)); + + when(timer.getSnapshot()).thenReturn(snapshot); + + reporter.report(this.map(), + this.map(), + this.map(), + this.map(), + map("test.another.timer", timer)); + + verify(ganglia).announce("test.another.timer.max", "100.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); + verify(ganglia).announce("test.another.timer.mean", "200.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); + verify(ganglia).announce("test.another.timer.min", "300.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); + verify(ganglia).announce("test.another.timer.stddev", "400.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); + verify(ganglia).announce("test.another.timer.p50", "500.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); + verify(ganglia).announce("test.another.timer.p75", "600.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); + verify(ganglia).announce("test.another.timer.p95", "700.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); + verify(ganglia).announce("test.another.timer.p98", "800.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); + verify(ganglia).announce("test.another.timer.p99", "900.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); + verify(ganglia).announce("test.another.timer.p999", "1000.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); + + verify(ganglia).announce("test.another.timer.count", "1", GMetricType.DOUBLE, "calls", GMetricSlope.BOTH, 60, 0, "test.another"); + verify(ganglia).announce("test.another.timer.mean_rate", "2.0", GMetricType.DOUBLE, "calls/second", GMetricSlope.BOTH, 60, 0, "test.another"); + verify(ganglia).announce("test.another.timer.m1_rate", "3.0", GMetricType.DOUBLE, "calls/second", GMetricSlope.BOTH, 60, 0, "test.another"); + verify(ganglia).announce("test.another.timer.m5_rate", "4.0", GMetricType.DOUBLE, "calls/second", GMetricSlope.BOTH, 60, 0, "test.another"); + verify(ganglia).announce("test.another.timer.m15_rate", "5.0", GMetricType.DOUBLE, "calls/second", GMetricSlope.BOTH, 60, 0, "test.another"); + + verifyNoMoreInteractions(ganglia); + } + + private SortedMap map() { + return new TreeMap(); + } + + private SortedMap map(String name, T metric) { + final TreeMap map = new TreeMap(); + map.put(name, metric); + return map; + } + + private Gauge gauge(T value) { + final Gauge gauge = mock(Gauge.class); + when(gauge.getValue()).thenReturn(value); + return gauge; + } +} diff --git a/metrics-ganglia/src/test/resources/counter.io b/metrics-ganglia/src/test/resources/counter.io deleted file mode 100644 index fd27c4d2e0..0000000000 --- a/metrics-ganglia/src/test/resources/counter.io +++ /dev/null @@ -1,21 +0,0 @@ -addInt(128) -addString(localhost) -addString(java.lang.Object.metric) -addInt(0) -addString(int32) -addString(java.lang.Object.metric) -addString() -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_counter) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric) -addInt(0) -addString(%%s) -addString(%s) -send() diff --git a/metrics-ganglia/src/test/resources/gauge.io b/metrics-ganglia/src/test/resources/gauge.io deleted file mode 100644 index bf6e12404c..0000000000 --- a/metrics-ganglia/src/test/resources/gauge.io +++ /dev/null @@ -1,21 +0,0 @@ -addInt(128) -addString(localhost) -addString(java.lang.Object.metric) -addInt(0) -addString(string) -addString(java.lang.Object.metric) -addString() -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_gauge) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric) -addInt(0) -addString(%%s) -addString(%s) -send() diff --git a/metrics-ganglia/src/test/resources/histogram.io b/metrics-ganglia/src/test/resources/histogram.io deleted file mode 100644 index fe3b0bc223..0000000000 --- a/metrics-ganglia/src/test/resources/histogram.io +++ /dev/null @@ -1,210 +0,0 @@ -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.min) -addInt(0) -addString(double) -addString(java.lang.Object.metric.min) -addString() -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_histo) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.min) -addInt(0) -addString(%s) -addString(1.00) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.max) -addInt(0) -addString(double) -addString(java.lang.Object.metric.max) -addString() -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_histo) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.max) -addInt(0) -addString(%s) -addString(3.00) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.mean) -addInt(0) -addString(double) -addString(java.lang.Object.metric.mean) -addString() -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_histo) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.mean) -addInt(0) -addString(%s) -addString(2.00) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.stddev) -addInt(0) -addString(double) -addString(java.lang.Object.metric.stddev) -addString() -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_histo) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.stddev) -addInt(0) -addString(%s) -addString(1.50) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.median) -addInt(0) -addString(double) -addString(java.lang.Object.metric.median) -addString() -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_histo) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.median) -addInt(0) -addString(%s) -addString(0.50) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.75percentile) -addInt(0) -addString(double) -addString(java.lang.Object.metric.75percentile) -addString() -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_histo) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.75percentile) -addInt(0) -addString(%s) -addString(0.75) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.95percentile) -addInt(0) -addString(double) -addString(java.lang.Object.metric.95percentile) -addString() -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_histo) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.95percentile) -addInt(0) -addString(%s) -addString(0.95) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.98percentile) -addInt(0) -addString(double) -addString(java.lang.Object.metric.98percentile) -addString() -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_histo) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.98percentile) -addInt(0) -addString(%s) -addString(0.98) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.99percentile) -addInt(0) -addString(double) -addString(java.lang.Object.metric.99percentile) -addString() -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_histo) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.99percentile) -addInt(0) -addString(%s) -addString(0.99) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.999percentile) -addInt(0) -addString(double) -addString(java.lang.Object.metric.999percentile) -addString() -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_histo) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.999percentile) -addInt(0) -addString(%s) -addString(1.00) -send() diff --git a/metrics-ganglia/src/test/resources/metered.io b/metrics-ganglia/src/test/resources/metered.io deleted file mode 100644 index ef5910f291..0000000000 --- a/metrics-ganglia/src/test/resources/metered.io +++ /dev/null @@ -1,105 +0,0 @@ -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.count) -addInt(0) -addString(int32) -addString(java.lang.Object.metric.count) -addString(eventType) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_metered) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.count) -addInt(0) -addString(%s) -addString(1) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.meanRate) -addInt(0) -addString(double) -addString(java.lang.Object.metric.meanRate) -addString(eventType/second) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_metered) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.meanRate) -addInt(0) -addString(%s) -addString(2.00) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.1MinuteRate) -addInt(0) -addString(double) -addString(java.lang.Object.metric.1MinuteRate) -addString(eventType/second) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_metered) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.1MinuteRate) -addInt(0) -addString(%s) -addString(1.00) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.5MinuteRate) -addInt(0) -addString(double) -addString(java.lang.Object.metric.5MinuteRate) -addString(eventType/second) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_metered) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.5MinuteRate) -addInt(0) -addString(%s) -addString(5.00) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.15MinuteRate) -addInt(0) -addString(double) -addString(java.lang.Object.metric.15MinuteRate) -addString(eventType/second) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_metered) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.15MinuteRate) -addInt(0) -addString(%s) -addString(15.00) -send() diff --git a/metrics-ganglia/src/test/resources/timed.io b/metrics-ganglia/src/test/resources/timed.io deleted file mode 100644 index 51dc845cb8..0000000000 --- a/metrics-ganglia/src/test/resources/timed.io +++ /dev/null @@ -1,315 +0,0 @@ -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.count) -addInt(0) -addString(int32) -addString(java.lang.Object.metric.count) -addString(eventType) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_metered) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.count) -addInt(0) -addString(%s) -addString(1) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.meanRate) -addInt(0) -addString(double) -addString(java.lang.Object.metric.meanRate) -addString(eventType/second) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_metered) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.meanRate) -addInt(0) -addString(%s) -addString(2.00) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.1MinuteRate) -addInt(0) -addString(double) -addString(java.lang.Object.metric.1MinuteRate) -addString(eventType/second) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_metered) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.1MinuteRate) -addInt(0) -addString(%s) -addString(1.00) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.5MinuteRate) -addInt(0) -addString(double) -addString(java.lang.Object.metric.5MinuteRate) -addString(eventType/second) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_metered) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.5MinuteRate) -addInt(0) -addString(%s) -addString(5.00) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.15MinuteRate) -addInt(0) -addString(double) -addString(java.lang.Object.metric.15MinuteRate) -addString(eventType/second) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_metered) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.15MinuteRate) -addInt(0) -addString(%s) -addString(15.00) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.min) -addInt(0) -addString(double) -addString(java.lang.Object.metric.min) -addString(MILLISECONDS) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_timer) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.min) -addInt(0) -addString(%s) -addString(1.00) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.max) -addInt(0) -addString(double) -addString(java.lang.Object.metric.max) -addString(MILLISECONDS) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_timer) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.max) -addInt(0) -addString(%s) -addString(3.00) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.mean) -addInt(0) -addString(double) -addString(java.lang.Object.metric.mean) -addString(MILLISECONDS) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_timer) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.mean) -addInt(0) -addString(%s) -addString(2.00) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.stddev) -addInt(0) -addString(double) -addString(java.lang.Object.metric.stddev) -addString(MILLISECONDS) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_timer) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.stddev) -addInt(0) -addString(%s) -addString(1.50) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.median) -addInt(0) -addString(double) -addString(java.lang.Object.metric.median) -addString(MILLISECONDS) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_timer) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.median) -addInt(0) -addString(%s) -addString(0.50) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.75percentile) -addInt(0) -addString(double) -addString(java.lang.Object.metric.75percentile) -addString(MILLISECONDS) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_timer) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.75percentile) -addInt(0) -addString(%s) -addString(0.75) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.95percentile) -addInt(0) -addString(double) -addString(java.lang.Object.metric.95percentile) -addString(MILLISECONDS) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_timer) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.95percentile) -addInt(0) -addString(%s) -addString(0.95) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.98percentile) -addInt(0) -addString(double) -addString(java.lang.Object.metric.98percentile) -addString(MILLISECONDS) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_timer) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.98percentile) -addInt(0) -addString(%s) -addString(0.98) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.99percentile) -addInt(0) -addString(double) -addString(java.lang.Object.metric.99percentile) -addString(MILLISECONDS) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_timer) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.99percentile) -addInt(0) -addString(%s) -addString(0.99) -send() -addInt(128) -addString(localhost) -addString(java.lang.Object.metric.999percentile) -addInt(0) -addString(double) -addString(java.lang.Object.metric.999percentile) -addString(MILLISECONDS) -addInt(3) -addInt(60) -addInt(0) -addInt(1) -addString(GROUP) -addString(group-prefix_timer) -send() -addInt(133) -addString(localhost) -addString(java.lang.Object.metric.999percentile) -addInt(0) -addString(%s) -addString(1.00) -send() From 25f901d7c72c4aa6af3b42b58d86062d1e2854d6 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 11 Mar 2013 15:45:56 -0700 Subject: [PATCH 0020/2558] Fix counter names in GangliaReporter. --- .../main/java/com/yammer/metrics/ganglia/GangliaReporter.java | 2 +- .../com/yammer/metrics/ganglia/tests/GangliaReporterTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java index d6b2fb6091..8761188e84 100644 --- a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java +++ b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java @@ -139,7 +139,7 @@ private void reportHistogram(String name, Histogram histogram) { private void reportCounter(String name, Counter counter) { final String group = group(name); try { - announce(name, group, counter.getCount(), ""); + announce(name(name, "count"), group, counter.getCount(), ""); } catch (GangliaException e) { LOGGER.warn("Unable to report counter {}", name, e); } diff --git a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java b/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java index 6e241d1f6c..6b8f502f5a 100644 --- a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java +++ b/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java @@ -118,7 +118,7 @@ public void reportsCounterValues() throws Exception { this.map(), this.map()); - verify(ganglia).announce("test.counter", "100", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); + verify(ganglia).announce("test.counter.count", "100", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); verifyNoMoreInteractions(ganglia); } From 2d9186419d2c3b8d7b981ac559e886eebb4872c0 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 11 Mar 2013 15:54:50 -0700 Subject: [PATCH 0021/2558] Revamp of GraphiteReporter. --- metrics-graphite/pom.xml | 11 +- .../com/yammer/metrics/graphite/Graphite.java | 57 +++ .../metrics/graphite/GraphiteReporter.java | 474 +++++------------- .../metrics/graphite/SocketProvider.java | 7 - .../graphite/GraphiteReporterTest.java | 94 ---- .../graphite/tests/GraphiteReporterTest.java | 302 +++++++++++ .../metrics/graphite/tests/GraphiteTest.java | 89 ++++ 7 files changed, 568 insertions(+), 466 deletions(-) create mode 100644 metrics-graphite/src/main/java/com/yammer/metrics/graphite/Graphite.java delete mode 100644 metrics-graphite/src/main/java/com/yammer/metrics/graphite/SocketProvider.java delete mode 100644 metrics-graphite/src/test/java/com/yammer/metrics/graphite/GraphiteReporterTest.java create mode 100644 metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java create mode 100644 metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteTest.java diff --git a/metrics-graphite/pom.xml b/metrics-graphite/pom.xml index 29a05d7d2b..4a57f8d64f 100644 --- a/metrics-graphite/pom.xml +++ b/metrics-graphite/pom.xml @@ -18,16 +18,7 @@ com.yammer.metrics metrics-core - 2.2.0 - jar - compile - - - com.yammer.metrics - metrics-core - 2.2.0 - test-jar - test + ${project.version} diff --git a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/Graphite.java b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/Graphite.java new file mode 100644 index 0000000000..13327d0d8e --- /dev/null +++ b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/Graphite.java @@ -0,0 +1,57 @@ +package com.yammer.metrics.graphite; + +import javax.net.SocketFactory; +import java.io.*; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.charset.Charset; +import java.util.regex.Pattern; + +public class Graphite implements Closeable { + private static final Pattern WHITESPACE = Pattern.compile("[\\s]+"); + // this may be optimistic about Carbon/Graphite + private static final Charset UTF_8 = Charset.forName("UTF-8"); + + private final InetSocketAddress address; + private final SocketFactory socketFactory; + + private Socket socket; + private Writer writer; + + public Graphite(InetSocketAddress address, SocketFactory socketFactory) { + this.address = address; + this.socketFactory = socketFactory; + } + + public void connect() throws IOException { + if (socket != null) { + throw new IllegalStateException("Already connected"); + } + + this.socket = socketFactory.createSocket(address.getAddress(), address.getPort()); + this.writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), UTF_8)); + } + + public void write(String name, String value, long timestamp) throws IOException { + writer.write(sanitize(name)); + writer.write(' '); + writer.write(sanitize(value)); + writer.write(' '); + writer.write(Long.toString(timestamp)); + writer.write('\n'); + writer.flush(); + } + + @Override + public void close() throws IOException { + if (socket != null) { + socket.close(); + } + this.socket = null; + this.writer = null; + } + + protected String sanitize(String s) { + return WHITESPACE.matcher(s).replaceAll("-"); + } +} diff --git a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java index 96ba962498..1523a73537 100644 --- a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java +++ b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java @@ -1,394 +1,158 @@ package com.yammer.metrics.graphite; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.*; -import com.yammer.metrics.reporting.AbstractPollingReporter; -import com.yammer.metrics.stats.Snapshot; -import com.yammer.metrics.core.MetricPredicate; +import com.yammer.metrics.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.BufferedWriter; import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.lang.Thread.State; -import java.net.Socket; import java.util.Locale; -import java.util.Map.Entry; +import java.util.Map; import java.util.SortedMap; import java.util.concurrent.TimeUnit; +public class GraphiteReporter extends AbstractPollingReporter { + private static final Logger LOGGER = LoggerFactory.getLogger(GraphiteReporter.class); -/** - * A simple reporter which sends out application metrics to a Graphite - * server periodically. - */ -public class GraphiteReporter extends AbstractPollingReporter implements MetricProcessor { - private static final Logger LOG = LoggerFactory.getLogger(GraphiteReporter.class); - protected final String prefix; - protected final MetricPredicate predicate; - protected final Locale locale = Locale.US; - protected final Clock clock; - protected final SocketProvider socketProvider; - protected final VirtualMachineMetrics vm; - protected Writer writer; - public boolean printVMMetrics = true; + private final Graphite graphite; + private final Clock clock; + private final String prefix; + private final double rateFactor; + private final double durationFactor; - /** - * Enables the graphite reporter to send data for the default metrics registry to graphite - * server with the specified period. - * - * @param period the period between successive outputs - * @param unit the time unit of {@code period} - * @param host the host name of graphite server (carbon-cache agent) - * @param port the port number on which the graphite server is listening - */ - public static void enable(long period, TimeUnit unit, String host, int port) { - enable(Metrics.defaultRegistry(), period, unit, host, port); - } - - /** - * Enables the graphite reporter to send data for the given metrics registry to graphite server - * with the specified period. - * - * @param metricsRegistry the metrics registry - * @param period the period between successive outputs - * @param unit the time unit of {@code period} - * @param host the host name of graphite server (carbon-cache agent) - * @param port the port number on which the graphite server is listening - */ - public static void enable(MetricsRegistry metricsRegistry, long period, TimeUnit unit, String host, int port) { - enable(metricsRegistry, period, unit, host, port, null); - } - - /** - * Enables the graphite reporter to send data to graphite server with the specified period. - * - * @param period the period between successive outputs - * @param unit the time unit of {@code period} - * @param host the host name of graphite server (carbon-cache agent) - * @param port the port number on which the graphite server is listening - * @param prefix the string which is prepended to all metric names - */ - public static void enable(long period, TimeUnit unit, String host, int port, String prefix) { - enable(Metrics.defaultRegistry(), period, unit, host, port, prefix); - } - - /** - * Enables the graphite reporter to send data to graphite server with the specified period. - * - * @param metricsRegistry the metrics registry - * @param period the period between successive outputs - * @param unit the time unit of {@code period} - * @param host the host name of graphite server (carbon-cache agent) - * @param port the port number on which the graphite server is listening - * @param prefix the string which is prepended to all metric names - */ - public static void enable(MetricsRegistry metricsRegistry, long period, TimeUnit unit, String host, int port, String prefix) { - enable(metricsRegistry, period, unit, host, port, prefix, MetricPredicate.ALL); - } - - /** - * Enables the graphite reporter to send data to graphite server with the specified period. - * - * @param metricsRegistry the metrics registry - * @param period the period between successive outputs - * @param unit the time unit of {@code period} - * @param host the host name of graphite server (carbon-cache agent) - * @param port the port number on which the graphite server is listening - * @param prefix the string which is prepended to all metric names - * @param predicate filters metrics to be reported - */ - public static void enable(MetricsRegistry metricsRegistry, long period, TimeUnit unit, String host, int port, String prefix, MetricPredicate predicate) { - try { - final GraphiteReporter reporter = new GraphiteReporter(metricsRegistry, - prefix, - predicate, - new DefaultSocketProvider(host, - port), - Clock.defaultClock()); - reporter.start(period, unit); - } catch (Exception e) { - LOG.error("Error creating/starting Graphite reporter:", e); - } - } - - /** - * Creates a new {@link GraphiteReporter}. - * - * @param host is graphite server - * @param port is port on which graphite server is running - * @param prefix is prepended to all names reported to graphite - * @throws IOException if there is an error connecting to the Graphite server - */ - public GraphiteReporter(String host, int port, String prefix) throws IOException { - this(Metrics.defaultRegistry(), host, port, prefix); - } - - /** - * Creates a new {@link GraphiteReporter}. - * - * @param metricsRegistry the metrics registry - * @param host is graphite server - * @param port is port on which graphite server is running - * @param prefix is prepended to all names reported to graphite - * @throws IOException if there is an error connecting to the Graphite server - */ - public GraphiteReporter(MetricsRegistry metricsRegistry, String host, int port, String prefix) throws IOException { - this(metricsRegistry, - prefix, - MetricPredicate.ALL, - new DefaultSocketProvider(host, port), - Clock.defaultClock()); - } - - /** - * Creates a new {@link GraphiteReporter}. - * - * @param metricsRegistry the metrics registry - * @param prefix is prepended to all names reported to graphite - * @param predicate filters metrics to be reported - * @param socketProvider a {@link SocketProvider} instance - * @param clock a {@link Clock} instance - * @throws IOException if there is an error connecting to the Graphite server - */ - public GraphiteReporter(MetricsRegistry metricsRegistry, String prefix, MetricPredicate predicate, SocketProvider socketProvider, Clock clock) throws IOException { - this(metricsRegistry, prefix, predicate, socketProvider, clock, - VirtualMachineMetrics.getInstance()); - } - - /** - * Creates a new {@link GraphiteReporter}. - * - * @param metricsRegistry the metrics registry - * @param prefix is prepended to all names reported to graphite - * @param predicate filters metrics to be reported - * @param socketProvider a {@link SocketProvider} instance - * @param clock a {@link Clock} instance - * @param vm a {@link VirtualMachineMetrics} instance - * @throws IOException if there is an error connecting to the Graphite server - */ - public GraphiteReporter(MetricsRegistry metricsRegistry, String prefix, MetricPredicate predicate, SocketProvider socketProvider, Clock clock, VirtualMachineMetrics vm) throws IOException { - this(metricsRegistry, prefix, predicate, socketProvider, clock, vm, "graphite-reporter"); - } - - /** - * Creates a new {@link GraphiteReporter}. - * - * @param metricsRegistry the metrics registry - * @param prefix is prepended to all names reported to graphite - * @param predicate filters metrics to be reported - * @param socketProvider a {@link SocketProvider} instance - * @param clock a {@link Clock} instance - * @param vm a {@link VirtualMachineMetrics} instance - * @throws IOException if there is an error connecting to the Graphite server - */ - public GraphiteReporter(MetricsRegistry metricsRegistry, String prefix, MetricPredicate predicate, SocketProvider socketProvider, Clock clock, VirtualMachineMetrics vm, String name) throws IOException { - super(metricsRegistry, name); - this.socketProvider = socketProvider; - this.vm = vm; + public GraphiteReporter(MetricRegistry registry, + Graphite graphite, + Clock clock, + String prefix, + TimeUnit rateUnit, + TimeUnit durationUnit) { + super(registry, "graphite-reporter"); + this.graphite = graphite; this.clock = clock; - - if (prefix != null) { - // Pre-append the "." so that we don't need to make anything conditional later. - this.prefix = prefix + "."; - } else { - this.prefix = ""; - } - this.predicate = predicate; + this.prefix = prefix; + this.rateFactor = rateUnit.toSeconds(1); + this.durationFactor = 1.0 / durationUnit.toNanos(1); } @Override - public void run() { - Socket socket = null; + 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 { - socket = this.socketProvider.get(); - writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); + graphite.connect(); - final long epoch = clock.time() / 1000; - if (this.printVMMetrics) { - printVmMetrics(epoch); - } - printRegularMetrics(epoch); - writer.flush(); - } catch (Exception e) { - if (LOG.isDebugEnabled()) { - LOG.debug("Error writing to Graphite", e); - } else { - LOG.warn("Error writing to Graphite: {}", e.getMessage()); - } - if (writer != null) { - try { - writer.flush(); - } catch (IOException e1) { - LOG.error("Error while flushing writer:", e1); - } + for (Map.Entry entry : gauges.entrySet()) { + reportGauge(entry.getKey(), entry.getValue(), timestamp); } - } finally { - if (socket != null) { - try { - socket.close(); - } catch (IOException e) { - LOG.error("Error while closing socket:", e); - } - } - writer = null; - } - } - protected void printRegularMetrics(final Long epoch) { - for (Entry> entry : getMetricsRegistry().groupedMetrics(predicate).entrySet()) { - for (Entry subEntry : entry.getValue().entrySet()) { - final Metric metric = subEntry.getValue(); - if (metric != null) { - try { - metric.processWith(this, subEntry.getKey(), epoch); - } catch (Exception ignored) { - LOG.error("Error printing regular metrics:", ignored); - } - } + for (Map.Entry entry : counters.entrySet()) { + reportCounter(entry.getKey(), entry.getValue(), timestamp); } - } - } - - protected void sendInt(long timestamp, String name, String valueName, long value) { - sendToGraphite(timestamp, name, valueName + " " + String.format(locale, "%d", value)); - } - protected void sendFloat(long timestamp, String name, String valueName, double value) { - sendToGraphite(timestamp, name, valueName + " " + String.format(locale, "%2.2f", value)); - } + for (Map.Entry entry : histograms.entrySet()) { + reportHistogram(entry.getKey(), entry.getValue(), timestamp); + } - protected void sendObjToGraphite(long timestamp, String name, String valueName, Object value) { - sendToGraphite(timestamp, name, valueName + " " + String.format(locale, "%s", value)); - } + for (Map.Entry entry : meters.entrySet()) { + reportMetered(entry.getKey(), entry.getValue(), timestamp); + } - protected void sendToGraphite(long timestamp, String name, String value) { - try { - if (!prefix.isEmpty()) { - writer.write(prefix); + for (Map.Entry entry : timers.entrySet()) { + reportTimer(entry.getKey(), entry.getValue(), timestamp); } - writer.write(sanitizeString(name)); - writer.write('.'); - writer.write(value); - writer.write(' '); - writer.write(Long.toString(timestamp)); - writer.write('\n'); - writer.flush(); } catch (IOException e) { - LOG.error("Error sending to Graphite:", e); + LOGGER.warn("Unable to report to Graphite", graphite, e); + } finally { + try { + graphite.close(); + } catch (IOException e) { + LOGGER.debug("Error disconnecting from Graphite", graphite, e); + } } } - protected String sanitizeName(MetricName name) { - final StringBuilder sb = new StringBuilder() - .append(name.getGroup()) - .append('.') - .append(name.getType()) - .append('.'); - if (name.hasScope()) { - sb.append(name.getScope()) - .append('.'); + private void reportTimer(String name, Timer timer, long timestamp) throws IOException { + graphite.write(prefix(name, "max"), format(timer.getMax() * durationFactor), timestamp); + graphite.write(prefix(name, "mean"), format(timer.getMean() * durationFactor), timestamp); + graphite.write(prefix(name, "min"), format(timer.getMin() * durationFactor), timestamp); + graphite.write(prefix(name, "stddev"), format(timer.getStdDev() * durationFactor), timestamp); + + final Snapshot snapshot = timer.getSnapshot(); + graphite.write(prefix(name, "p50"), format(snapshot.getMedian() * durationFactor), timestamp); + graphite.write(prefix(name, "p75"), format(snapshot.get75thPercentile() * durationFactor), timestamp); + graphite.write(prefix(name, "p95"), format(snapshot.get95thPercentile() * durationFactor), timestamp); + graphite.write(prefix(name, "p98"), format(snapshot.get98thPercentile() * durationFactor), timestamp); + graphite.write(prefix(name, "p99"), format(snapshot.get99thPercentile() * durationFactor), timestamp); + graphite.write(prefix(name, "p999"), format(snapshot.get999thPercentile() * durationFactor), timestamp); + + reportMetered(name, timer, timestamp); + } + + private void reportMetered(String name, Metered meter, long timestamp) throws IOException { + graphite.write(prefix(name, "count"), format(meter.getCount()), timestamp); + graphite.write(prefix(name, "m1_rate"), format(meter.getOneMinuteRate() * rateFactor), timestamp); + graphite.write(prefix(name, "m5_rate"), format(meter.getFiveMinuteRate() * rateFactor), timestamp); + graphite.write(prefix(name, "m15_rate"), format(meter.getFifteenMinuteRate() * rateFactor), timestamp); + graphite.write(prefix(name, "mean_rate"), format(meter.getMeanRate() * rateFactor), timestamp); + } + + private void reportHistogram(String name, Histogram histogram, long timestamp) throws IOException { + graphite.write(prefix(name, "count"), format(histogram.getCount()), timestamp); + graphite.write(prefix(name, "max"), format(histogram.getMax()), timestamp); + graphite.write(prefix(name, "mean"), format(histogram.getMean()), timestamp); + graphite.write(prefix(name, "min"), format(histogram.getMin()), timestamp); + graphite.write(prefix(name, "stddev"), format(histogram.getStdDev()), timestamp); + + final Snapshot snapshot = histogram.getSnapshot(); + graphite.write(prefix(name, "p50"), format(snapshot.getMedian()), timestamp); + graphite.write(prefix(name, "p75"), format(snapshot.get75thPercentile()), timestamp); + graphite.write(prefix(name, "p95"), format(snapshot.get95thPercentile()), timestamp); + graphite.write(prefix(name, "p98"), format(snapshot.get98thPercentile()), timestamp); + graphite.write(prefix(name, "p99"), format(snapshot.get99thPercentile()), timestamp); + graphite.write(prefix(name, "p999"), format(snapshot.get999thPercentile()), timestamp); + } + + private void reportCounter(String name, Counter counter, long timestamp) throws IOException { + graphite.write(prefix(name, "count"), format(counter.getCount()), timestamp); + } + + private void reportGauge(String name, Gauge gauge, long timestamp) throws IOException { + graphite.write(prefix(name), format(gauge.getValue()), timestamp); + } + + private String format(Object o) { + if (o instanceof Float) { + return format(((Float) o).doubleValue()); + } else if (o instanceof Double) { + return format(((Double) o).doubleValue()); + } else if (o instanceof Byte) { + return format(((Byte) o).longValue()); + } else if (o instanceof Short) { + return format(((Short) o).longValue()); + } else if (o instanceof Integer) { + return format(((Integer) o).longValue()); + } else if (o instanceof Long) { + return format(((Long) o).longValue()); } - return sb.append(name.getName()).toString(); - } - - protected String sanitizeString(String s) { - return s.replace(' ', '-'); - } - - @Override - public void processGauge(MetricName name, Gauge gauge, Long epoch) throws IOException { - sendObjToGraphite(epoch, sanitizeName(name), "value", gauge.value()); - } - - @Override - public void processCounter(MetricName name, Counter counter, Long epoch) throws IOException { - sendInt(epoch, sanitizeName(name), "count", counter.count()); - } - - @Override - public void processMeter(MetricName name, Metered meter, Long epoch) throws IOException { - final String sanitizedName = sanitizeName(name); - sendInt(epoch, sanitizedName, "count", meter.count()); - sendFloat(epoch, sanitizedName, "meanRate", meter.meanRate()); - sendFloat(epoch, sanitizedName, "1MinuteRate", meter.oneMinuteRate()); - sendFloat(epoch, sanitizedName, "5MinuteRate", meter.fiveMinuteRate()); - sendFloat(epoch, sanitizedName, "15MinuteRate", meter.fifteenMinuteRate()); - } - - @Override - public void processHistogram(MetricName name, Histogram histogram, Long epoch) throws IOException { - final String sanitizedName = sanitizeName(name); - sendSummarizable(epoch, sanitizedName, histogram); - sendSampling(epoch, sanitizedName, histogram); - } - - @Override - public void processTimer(MetricName name, Timer timer, Long epoch) throws IOException { - processMeter(name, timer, epoch); - final String sanitizedName = sanitizeName(name); - sendSummarizable(epoch, sanitizedName, timer); - sendSampling(epoch, sanitizedName, timer); + return String.valueOf(o); } - protected void sendSummarizable(long epoch, String sanitizedName, Summarizable metric) throws IOException { - sendFloat(epoch, sanitizedName, "min", metric.min()); - sendFloat(epoch, sanitizedName, "max", metric.max()); - sendFloat(epoch, sanitizedName, "mean", metric.mean()); - sendFloat(epoch, sanitizedName, "stddev", metric.stdDev()); + private String prefix(String... components) { + return MetricRegistry.name(prefix, components); } - protected void sendSampling(long epoch, String sanitizedName, Sampling metric) throws IOException { - final Snapshot snapshot = metric.getSnapshot(); - sendFloat(epoch, sanitizedName, "median", snapshot.getMedian()); - sendFloat(epoch, sanitizedName, "75percentile", snapshot.get75thPercentile()); - sendFloat(epoch, sanitizedName, "95percentile", snapshot.get95thPercentile()); - sendFloat(epoch, sanitizedName, "98percentile", snapshot.get98thPercentile()); - sendFloat(epoch, sanitizedName, "99percentile", snapshot.get99thPercentile()); - sendFloat(epoch, sanitizedName, "999percentile", snapshot.get999thPercentile()); + private String format(long n) { + return Long.toString(n); } - protected void printVmMetrics(long epoch) { - sendFloat(epoch, "jvm.memory", "heap_usage", vm.heapUsage()); - sendFloat(epoch, "jvm.memory", "non_heap_usage", vm.nonHeapUsage()); - for (Entry pool : vm.memoryPoolUsage().entrySet()) { - sendFloat(epoch, "jvm.memory.memory_pool_usages", sanitizeString(pool.getKey()), pool.getValue()); - } - - sendInt(epoch, "jvm", "daemon_thread_count", vm.daemonThreadCount()); - sendInt(epoch, "jvm", "thread_count", vm.threadCount()); - sendInt(epoch, "jvm", "uptime", vm.uptime()); - sendFloat(epoch, "jvm", "fd_usage", vm.fileDescriptorUsage()); - - for (Entry entry : vm.threadStatePercentages().entrySet()) { - sendFloat(epoch, "jvm.thread-states", entry.getKey().toString().toLowerCase(), entry.getValue()); - } - - for (Entry entry : vm.garbageCollectors().entrySet()) { - final String name = "jvm.gc." + sanitizeString(entry.getKey()); - sendInt(epoch, name, "time", entry.getValue().getTime(TimeUnit.MILLISECONDS)); - sendInt(epoch, name, "runs", entry.getValue().getRuns()); - } - } - - public static class DefaultSocketProvider implements SocketProvider { - - private final String host; - private final int port; - - public DefaultSocketProvider(String host, int port) { - this.host = host; - this.port = port; - - } - - @Override - public Socket get() throws Exception { - return new Socket(this.host, this.port); - } - + private String format(double v) { + // the Carbon plaintext format is pretty underspecified, but it seems like it just wants + // US-formatted digits + return String.format(Locale.US, "%2.2f", v); } } diff --git a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/SocketProvider.java b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/SocketProvider.java deleted file mode 100644 index e3260faac4..0000000000 --- a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/SocketProvider.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.yammer.metrics.graphite; - -import java.net.Socket; - -public interface SocketProvider { - Socket get() throws Exception; -} diff --git a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/GraphiteReporterTest.java b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/GraphiteReporterTest.java deleted file mode 100644 index 2a19a4ebba..0000000000 --- a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/GraphiteReporterTest.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.yammer.metrics.graphite; - -import com.yammer.metrics.core.Clock; -import com.yammer.metrics.core.MetricPredicate; -import com.yammer.metrics.core.MetricsRegistry; -import com.yammer.metrics.graphite.GraphiteReporter; -import com.yammer.metrics.graphite.SocketProvider; -import com.yammer.metrics.reporting.AbstractPollingReporter; -import com.yammer.metrics.reporting.tests.AbstractPollingReporterTest; - -import java.io.OutputStream; -import java.net.Socket; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class GraphiteReporterTest extends AbstractPollingReporterTest { - @Override - protected AbstractPollingReporter createReporter(MetricsRegistry registry, OutputStream out, Clock clock) throws Exception { - final Socket socket = mock(Socket.class); - when(socket.getOutputStream()).thenReturn(out); - - final SocketProvider provider = mock(SocketProvider.class); - when(provider.get()).thenReturn(socket); - - final GraphiteReporter reporter = new GraphiteReporter(registry, - "prefix", - MetricPredicate.ALL, - provider, - clock); - reporter.printVMMetrics = false; - return reporter; - } - - @Override - public String[] expectedGaugeResult(String value) { - return new String[]{String.format("prefix.java.lang.Object.metric.value %s 5", value)}; - } - - @Override - public String[] expectedTimerResult() { - return new String[]{ - "prefix.java.lang.Object.metric.count 1 5", - "prefix.java.lang.Object.metric.meanRate 2.00 5", - "prefix.java.lang.Object.metric.1MinuteRate 1.00 5", - "prefix.java.lang.Object.metric.5MinuteRate 5.00 5", - "prefix.java.lang.Object.metric.15MinuteRate 15.00 5", - "prefix.java.lang.Object.metric.min 1.00 5", - "prefix.java.lang.Object.metric.max 3.00 5", - "prefix.java.lang.Object.metric.mean 2.00 5", - "prefix.java.lang.Object.metric.stddev 1.50 5", - "prefix.java.lang.Object.metric.median 0.50 5", - "prefix.java.lang.Object.metric.75percentile 0.75 5", - "prefix.java.lang.Object.metric.95percentile 0.95 5", - "prefix.java.lang.Object.metric.98percentile 0.98 5", - "prefix.java.lang.Object.metric.99percentile 0.99 5", - "prefix.java.lang.Object.metric.999percentile 1.00 5" - }; - } - - @Override - public String[] expectedMeterResult() { - return new String[]{ - "prefix.java.lang.Object.metric.count 1 5", - "prefix.java.lang.Object.metric.meanRate 2.00 5", - "prefix.java.lang.Object.metric.1MinuteRate 1.00 5", - "prefix.java.lang.Object.metric.5MinuteRate 5.00 5", - "prefix.java.lang.Object.metric.15MinuteRate 15.00 5", - }; - } - - @Override - public String[] expectedHistogramResult() { - return new String[]{ - "prefix.java.lang.Object.metric.min 1.00 5", - "prefix.java.lang.Object.metric.max 3.00 5", - "prefix.java.lang.Object.metric.mean 2.00 5", - "prefix.java.lang.Object.metric.stddev 1.50 5", - "prefix.java.lang.Object.metric.median 0.50 5", - "prefix.java.lang.Object.metric.75percentile 0.75 5", - "prefix.java.lang.Object.metric.95percentile 0.95 5", - "prefix.java.lang.Object.metric.98percentile 0.98 5", - "prefix.java.lang.Object.metric.99percentile 0.99 5", - "prefix.java.lang.Object.metric.999percentile 1.00 5" - }; - } - - @Override - public String[] expectedCounterResult(long count) { - return new String[]{ - String.format("prefix.java.lang.Object.metric.count %d 5", count) - }; - } -} diff --git a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java new file mode 100644 index 0000000000..5f9b45db45 --- /dev/null +++ b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java @@ -0,0 +1,302 @@ +package com.yammer.metrics.graphite.tests; + +import com.yammer.metrics.*; +import com.yammer.metrics.graphite.Graphite; +import com.yammer.metrics.graphite.GraphiteReporter; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; + +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.concurrent.TimeUnit; + +import static org.mockito.Mockito.*; + +public class GraphiteReporterTest { + private final long timestamp = 1000198; + private final Clock clock = mock(Clock.class); + private final Graphite graphite = mock(Graphite.class); + private final MetricRegistry registry = mock(MetricRegistry.class); + private final GraphiteReporter reporter = new GraphiteReporter(registry, + graphite, + clock, + "prefix", + TimeUnit.SECONDS, + TimeUnit.MILLISECONDS); + + @Before + public void setUp() throws Exception { + when(clock.getTime()).thenReturn(timestamp * 1000); + } + + @Test + public void reportsStringGaugeValues() throws Exception { + reporter.report(map("gauge", gauge("value")), + this.map(), + this.map(), + this.map(), + this.map()); + + final InOrder inOrder = inOrder(graphite); + inOrder.verify(graphite).connect(); + inOrder.verify(graphite).write("prefix.gauge", "value", timestamp); + inOrder.verify(graphite).close(); + + verifyNoMoreInteractions(graphite); + } + + @Test + public void reportsByteGaugeValues() throws Exception { + reporter.report(map("gauge", gauge((byte) 1)), + this.map(), + this.map(), + this.map(), + this.map()); + + final InOrder inOrder = inOrder(graphite); + inOrder.verify(graphite).connect(); + inOrder.verify(graphite).write("prefix.gauge", "1", timestamp); + inOrder.verify(graphite).close(); + + verifyNoMoreInteractions(graphite); + } + + @Test + public void reportsShortGaugeValues() throws Exception { + reporter.report(map("gauge", gauge((short) 1)), + this.map(), + this.map(), + this.map(), + this.map()); + + final InOrder inOrder = inOrder(graphite); + inOrder.verify(graphite).connect(); + inOrder.verify(graphite).write("prefix.gauge", "1", timestamp); + inOrder.verify(graphite).close(); + + verifyNoMoreInteractions(graphite); + } + + @Test + public void reportsIntegerGaugeValues() throws Exception { + reporter.report(map("gauge", gauge(1)), + this.map(), + this.map(), + this.map(), + this.map()); + + final InOrder inOrder = inOrder(graphite); + inOrder.verify(graphite).connect(); + inOrder.verify(graphite).write("prefix.gauge", "1", timestamp); + inOrder.verify(graphite).close(); + + verifyNoMoreInteractions(graphite); + } + + @Test + public void reportsLongGaugeValues() throws Exception { + reporter.report(map("gauge", gauge(1L)), + this.map(), + this.map(), + this.map(), + this.map()); + + final InOrder inOrder = inOrder(graphite); + inOrder.verify(graphite).connect(); + inOrder.verify(graphite).write("prefix.gauge", "1", timestamp); + inOrder.verify(graphite).close(); + + verifyNoMoreInteractions(graphite); + } + + @Test + public void reportsFloatGaugeValues() throws Exception { + reporter.report(map("gauge", gauge(1.1f)), + this.map(), + this.map(), + this.map(), + this.map()); + + final InOrder inOrder = inOrder(graphite); + inOrder.verify(graphite).connect(); + inOrder.verify(graphite).write("prefix.gauge", "1.10", timestamp); + inOrder.verify(graphite).close(); + + verifyNoMoreInteractions(graphite); + } + + @Test + public void reportsDoubleGaugeValues() throws Exception { + reporter.report(map("gauge", gauge(1.1)), + this.map(), + this.map(), + this.map(), + this.map()); + + final InOrder inOrder = inOrder(graphite); + inOrder.verify(graphite).connect(); + inOrder.verify(graphite).write("prefix.gauge", "1.10", timestamp); + inOrder.verify(graphite).close(); + + verifyNoMoreInteractions(graphite); + } + + @Test + public void reportsCounters() throws Exception { + final Counter counter = mock(Counter.class); + when(counter.getCount()).thenReturn(100L); + + reporter.report(this.map(), + this.map("counter", counter), + this.map(), + this.map(), + this.map()); + + final InOrder inOrder = inOrder(graphite); + inOrder.verify(graphite).connect(); + inOrder.verify(graphite).write("prefix.counter.count", "100", timestamp); + inOrder.verify(graphite).close(); + + verifyNoMoreInteractions(graphite); + } + + @Test + public void reportsHistograms() throws Exception { + final Histogram histogram = mock(Histogram.class); + when(histogram.getCount()).thenReturn(1L); + when(histogram.getMax()).thenReturn(2L); + when(histogram.getMean()).thenReturn(3.0); + when(histogram.getMin()).thenReturn(4L); + when(histogram.getStdDev()).thenReturn(5.0); + + final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMedian()).thenReturn(6.0); + when(snapshot.get75thPercentile()).thenReturn(7.0); + when(snapshot.get95thPercentile()).thenReturn(8.0); + when(snapshot.get98thPercentile()).thenReturn(9.0); + when(snapshot.get99thPercentile()).thenReturn(10.0); + when(snapshot.get999thPercentile()).thenReturn(11.0); + + when(histogram.getSnapshot()).thenReturn(snapshot); + + reporter.report(this.map(), + this.map(), + this.map("histogram", histogram), + this.map(), + this.map()); + + final InOrder inOrder = inOrder(graphite); + inOrder.verify(graphite).connect(); + inOrder.verify(graphite).write("prefix.histogram.count", "1", timestamp); + inOrder.verify(graphite).write("prefix.histogram.max", "2", timestamp); + inOrder.verify(graphite).write("prefix.histogram.mean", "3.00", timestamp); + inOrder.verify(graphite).write("prefix.histogram.min", "4", timestamp); + inOrder.verify(graphite).write("prefix.histogram.stddev", "5.00", timestamp); + inOrder.verify(graphite).write("prefix.histogram.p50", "6.00", timestamp); + inOrder.verify(graphite).write("prefix.histogram.p75", "7.00", timestamp); + inOrder.verify(graphite).write("prefix.histogram.p95", "8.00", timestamp); + inOrder.verify(graphite).write("prefix.histogram.p98", "9.00", timestamp); + inOrder.verify(graphite).write("prefix.histogram.p99", "10.00", timestamp); + inOrder.verify(graphite).write("prefix.histogram.p999", "11.00", timestamp); + inOrder.verify(graphite).close(); + + verifyNoMoreInteractions(graphite); + } + + @Test + public void reportsMeters() throws Exception { + final Meter meter = mock(Meter.class); + when(meter.getCount()).thenReturn(1L); + when(meter.getOneMinuteRate()).thenReturn(2.0); + when(meter.getFiveMinuteRate()).thenReturn(3.0); + when(meter.getFifteenMinuteRate()).thenReturn(4.0); + when(meter.getMeanRate()).thenReturn(5.0); + + reporter.report(this.map(), + this.map(), + this.map(), + this.map("meter", meter), + this.map()); + + final InOrder inOrder = inOrder(graphite); + inOrder.verify(graphite).connect(); + inOrder.verify(graphite).write("prefix.meter.count", "1", timestamp); + inOrder.verify(graphite).write("prefix.meter.m1_rate", "2.00", timestamp); + inOrder.verify(graphite).write("prefix.meter.m5_rate", "3.00", timestamp); + inOrder.verify(graphite).write("prefix.meter.m15_rate", "4.00", timestamp); + inOrder.verify(graphite).write("prefix.meter.mean_rate", "5.00", timestamp); + inOrder.verify(graphite).close(); + + verifyNoMoreInteractions(graphite); + } + + @Test + public void reportsTimers() throws Exception { + final Timer timer = mock(Timer.class); + when(timer.getCount()).thenReturn(1L); + when(timer.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); + when(timer.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); + when(timer.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); + when(timer.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); + + when(timer.getMeanRate()).thenReturn(2.0); + when(timer.getOneMinuteRate()).thenReturn(3.0); + when(timer.getFiveMinuteRate()).thenReturn(4.0); + when(timer.getFifteenMinuteRate()).thenReturn(5.0); + + final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); + when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); + when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); + when(snapshot.get98thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(800)); + when(snapshot.get99thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(900)); + when(snapshot.get999thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS + .toNanos(1000)); + + when(timer.getSnapshot()).thenReturn(snapshot); + + reporter.report(this.map(), + this.map(), + this.map(), + this.map(), + map("timer", timer)); + + final InOrder inOrder = inOrder(graphite); + inOrder.verify(graphite).connect(); + inOrder.verify(graphite).write("prefix.timer.max", "100.00", timestamp); + inOrder.verify(graphite).write("prefix.timer.mean", "200.00", timestamp); + inOrder.verify(graphite).write("prefix.timer.min", "300.00", timestamp); + inOrder.verify(graphite).write("prefix.timer.stddev", "400.00", timestamp); + inOrder.verify(graphite).write("prefix.timer.p50", "500.00", timestamp); + inOrder.verify(graphite).write("prefix.timer.p75", "600.00", timestamp); + inOrder.verify(graphite).write("prefix.timer.p95", "700.00", timestamp); + inOrder.verify(graphite).write("prefix.timer.p98", "800.00", timestamp); + inOrder.verify(graphite).write("prefix.timer.p99", "900.00", timestamp); + inOrder.verify(graphite).write("prefix.timer.p999", "1000.00", timestamp); + inOrder.verify(graphite).write("prefix.timer.count", "1", timestamp); + inOrder.verify(graphite).write("prefix.timer.m1_rate", "3.00", timestamp); + inOrder.verify(graphite).write("prefix.timer.m5_rate", "4.00", timestamp); + inOrder.verify(graphite).write("prefix.timer.m15_rate", "5.00", timestamp); + inOrder.verify(graphite).write("prefix.timer.mean_rate", "2.00", timestamp); + inOrder.verify(graphite).close(); + + verifyNoMoreInteractions(graphite); + } + + private SortedMap map() { + return new TreeMap(); + } + + private SortedMap map(String name, T metric) { + final TreeMap map = new TreeMap(); + map.put(name, metric); + return map; + } + + private Gauge gauge(T value) { + final Gauge gauge = mock(Gauge.class); + when(gauge.getValue()).thenReturn(value); + return gauge; + } +} diff --git a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteTest.java b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteTest.java new file mode 100644 index 0000000000..221dd5b8eb --- /dev/null +++ b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteTest.java @@ -0,0 +1,89 @@ +package com.yammer.metrics.graphite.tests; + +import com.yammer.metrics.graphite.Graphite; +import org.junit.Before; +import org.junit.Test; + +import javax.net.SocketFactory; +import java.io.ByteArrayOutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.fest.assertions.api.Fail.failBecauseExceptionWasNotThrown; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.*; + +public class GraphiteTest { + private final SocketFactory socketFactory = mock(SocketFactory.class); + private final InetSocketAddress address = new InetSocketAddress("example.com", 1234); + private final Graphite graphite = new Graphite(address, socketFactory); + + private final Socket socket = mock(Socket.class); + private final ByteArrayOutputStream output = new ByteArrayOutputStream(); + + @Before + public void setUp() throws Exception { + when(socket.getOutputStream()).thenReturn(output); + + when(socketFactory.createSocket(any(InetAddress.class), + anyInt())).thenReturn(socket); + + } + + @Test + public void connectsToGraphite() throws Exception { + graphite.connect(); + + verify(socketFactory).createSocket(address.getAddress(), address.getPort()); + } + + @Test + public void disconnectsFromGraphite() throws Exception { + graphite.connect(); + graphite.close(); + + verify(socket).close(); + } + + @Test + public void doesNotAllowDoubleConnections() throws Exception { + graphite.connect(); + try { + graphite.connect(); + failBecauseExceptionWasNotThrown(IllegalStateException.class); + } catch (IllegalStateException e) { + assertThat(e.getMessage()) + .isEqualTo("Already connected"); + } + } + + @Test + public void writesValuesToGraphite() throws Exception { + graphite.connect(); + graphite.write("name", "value", 100); + + assertThat(output.toString()) + .isEqualTo("name value 100\n"); + } + + @Test + public void sanitizesNames() throws Exception { + graphite.connect(); + graphite.write("name woo", "value", 100); + + assertThat(output.toString()) + .isEqualTo("name-woo value 100\n"); + } + + @Test + public void sanitizesValues() throws Exception { + graphite.connect(); + graphite.write("name", "value woo", 100); + + assertThat(output.toString()) + .isEqualTo("name value-woo 100\n"); + } +} From f74d52a938211f64b7afedfcdd5a070c9c1e1e39 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 11 Mar 2013 16:22:57 -0700 Subject: [PATCH 0022/2558] Don't allow GraphiteReporter to report non-numeric gauge values. Closes #278. --- .../java/com/yammer/metrics/graphite/GraphiteReporter.java | 7 +++++-- .../metrics/graphite/tests/GraphiteReporterTest.java | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java index 1523a73537..26c7d7f7a3 100644 --- a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java +++ b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java @@ -122,7 +122,10 @@ private void reportCounter(String name, Counter counter, long timestamp) throws } private void reportGauge(String name, Gauge gauge, long timestamp) throws IOException { - graphite.write(prefix(name), format(gauge.getValue()), timestamp); + final String value = format(gauge.getValue()); + if (value != null) { + graphite.write(prefix(name), value, timestamp); + } } private String format(Object o) { @@ -139,7 +142,7 @@ private String format(Object o) { } else if (o instanceof Long) { return format(((Long) o).longValue()); } - return String.valueOf(o); + return null; } private String prefix(String... components) { diff --git a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java index 5f9b45db45..6c6334fdbe 100644 --- a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java +++ b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java @@ -31,7 +31,7 @@ public void setUp() throws Exception { } @Test - public void reportsStringGaugeValues() throws Exception { + public void doesNotReportStringGaugeValues() throws Exception { reporter.report(map("gauge", gauge("value")), this.map(), this.map(), @@ -40,7 +40,7 @@ public void reportsStringGaugeValues() throws Exception { final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); - inOrder.verify(graphite).write("prefix.gauge", "value", timestamp); + inOrder.verify(graphite, never()).write("prefix.gauge", "value", timestamp); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); From e83d0d94053f5c2330efb043e2b502789f907b85 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 11 Mar 2013 16:58:41 -0700 Subject: [PATCH 0023/2558] Revamp metrics-jdbi. --- metrics-jdbi/pom.xml | 2 +- .../jdbi/InstrumentedTimingCollector.java | 32 +-- .../DelegatingStatementNameStrategy.java | 13 +- .../jdbi/strategies/NameStrategies.java | 39 ++-- .../jdbi/strategies/ShortNameStrategy.java | 15 +- .../jdbi/strategies/StatementName.java | 34 --- .../strategies/StatementNameStrategy.java | 4 +- .../InstrumentedTimingCollectorTest.java | 200 ++++++++++-------- 8 files changed, 151 insertions(+), 188 deletions(-) delete mode 100644 metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/StatementName.java diff --git a/metrics-jdbi/pom.xml b/metrics-jdbi/pom.xml index f27103399e..c324e192bf 100644 --- a/metrics-jdbi/pom.xml +++ b/metrics-jdbi/pom.xml @@ -16,7 +16,7 @@ com.yammer.metrics metrics-core - 2.2.0 + ${project.version} org.jdbi diff --git a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/InstrumentedTimingCollector.java b/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/InstrumentedTimingCollector.java index b84938bbab..230f10d5ea 100644 --- a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/InstrumentedTimingCollector.java +++ b/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/InstrumentedTimingCollector.java @@ -1,8 +1,7 @@ package com.yammer.metrics.jdbi; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.MetricsRegistry; -import com.yammer.metrics.core.Timer; +import com.yammer.metrics.MetricRegistry; +import com.yammer.metrics.Timer; import com.yammer.metrics.jdbi.strategies.SmartNameStrategy; import com.yammer.metrics.jdbi.strategies.StatementNameStrategy; import org.skife.jdbi.v2.StatementContext; @@ -15,32 +14,17 @@ * method names for millisecond-precision timers. */ public class InstrumentedTimingCollector implements TimingCollector { - private final MetricsRegistry registry; + private final MetricRegistry registry; private final StatementNameStrategy statementNameStrategy; - private final TimeUnit durationUnit; - private final TimeUnit rateUnit; - public InstrumentedTimingCollector() { - this(Metrics.defaultRegistry()); + public InstrumentedTimingCollector(MetricRegistry registry) { + this(registry, new SmartNameStrategy()); } - public InstrumentedTimingCollector(MetricsRegistry registry) { - this(registry, new SmartNameStrategy(), TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - } - - public InstrumentedTimingCollector(MetricsRegistry registry, + public InstrumentedTimingCollector(MetricRegistry registry, StatementNameStrategy statementNameStrategy) { - this(registry, statementNameStrategy, TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - } - - public InstrumentedTimingCollector(MetricsRegistry registry, - StatementNameStrategy statementNameStrategy, - TimeUnit durationUnit, - TimeUnit rateUnit) { this.registry = registry; this.statementNameStrategy = statementNameStrategy; - this.durationUnit = durationUnit; - this.rateUnit = rateUnit; } @Override @@ -50,8 +34,6 @@ public void collect(long elapsedTime, StatementContext ctx) { } private Timer getTimer(StatementContext ctx) { - return registry.newTimer(statementNameStrategy.getStatementName(ctx), - durationUnit, - rateUnit); + return registry.timer(statementNameStrategy.getStatementName(ctx)); } } diff --git a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/DelegatingStatementNameStrategy.java b/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/DelegatingStatementNameStrategy.java index 822cd6155e..c6b3985e35 100644 --- a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/DelegatingStatementNameStrategy.java +++ b/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/DelegatingStatementNameStrategy.java @@ -1,6 +1,5 @@ package com.yammer.metrics.jdbi.strategies; -import com.yammer.metrics.core.MetricName; import org.skife.jdbi.v2.StatementContext; import java.util.ArrayList; @@ -19,13 +18,11 @@ protected void registerStrategies(StatementNameStrategy... strategies) { } @Override - public MetricName getStatementName(StatementContext statementContext) { - if (strategies != null) { - for (StatementNameStrategy strategy : strategies) { - final MetricName statementName = strategy.getStatementName(statementContext); - if (statementName != null) { - return statementName; - } + public String getStatementName(StatementContext statementContext) { + for (StatementNameStrategy strategy : strategies) { + final String statementName = strategy.getStatementName(statementContext); + if (statementName != null) { + return statementName; } } diff --git a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/NameStrategies.java b/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/NameStrategies.java index 47c0e5bf94..4ce9ac6030 100644 --- a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/NameStrategies.java +++ b/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/NameStrategies.java @@ -1,6 +1,5 @@ package com.yammer.metrics.jdbi.strategies; -import com.yammer.metrics.core.MetricName; import org.skife.jdbi.v2.ClasspathStatementLocator; import org.skife.jdbi.v2.StatementContext; @@ -8,6 +7,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import static com.yammer.metrics.MetricRegistry.name; + public final class NameStrategies { public static final StatementNameStrategy CHECK_EMPTY = new CheckEmptyStrategy(); public static final StatementNameStrategy CHECK_RAW = new CheckRawStrategy(); @@ -19,12 +20,12 @@ public final class NameStrategies { /** * An empty SQL statement. */ - private static final MetricName EMPTY_SQL = new MetricName("sql", "empty", ""); + private static final String EMPTY_SQL = "sql.empty"; /** * Unknown SQL. */ - static final MetricName UNKNOWN_SQL = new MetricName("sql", "unknown", ""); + static final String UNKNOWN_SQL = "sql.unknown"; /** * Context attribute name for the metric class. @@ -46,8 +47,8 @@ public final class NameStrategies { */ public static final String STATEMENT_NAME = "_metric_name"; - private static MetricName forRawSql(String rawSql) { - return StatementName.getJmxSafeName("sql", "raw", rawSql); + private static String forRawSql(String rawSql) { + return name("sql", "raw", rawSql); } static final class CheckEmptyStrategy implements StatementNameStrategy { @@ -55,7 +56,7 @@ private CheckEmptyStrategy() { } @Override - public MetricName getStatementName(StatementContext statementContext) { + public String getStatementName(StatementContext statementContext) { final String rawSql = statementContext.getRawSql(); if (rawSql == null || rawSql.length() == 0) { @@ -70,7 +71,7 @@ private CheckRawStrategy() { } @Override - public MetricName getStatementName(StatementContext statementContext) { + public String getStatementName(StatementContext statementContext) { final String rawSql = statementContext.getRawSql(); if (ClasspathStatementLocator.looksLikeSql(rawSql)) { @@ -85,7 +86,7 @@ private NaiveNameStrategy() { } @Override - public MetricName getStatementName(StatementContext statementContext) { + public String getStatementName(StatementContext statementContext) { final String rawSql = statementContext.getRawSql(); // Is it using the template loader? @@ -98,7 +99,7 @@ public MetricName getStatementName(StatementContext statementContext) { final String group = rawSql.substring(0, colon); final String name = rawSql.substring(colon + 1); - return StatementName.getJmxSafeName(group, name, ""); + return name(group, name); } } @@ -107,7 +108,7 @@ private SqlObjectStrategy() { } @Override - public MetricName getStatementName(StatementContext statementContext) { + public String getStatementName(StatementContext statementContext) { final Class clazz = statementContext.getSqlObjectType(); final Method method = statementContext.getSqlObjectMethod(); if (clazz != null) { @@ -116,7 +117,7 @@ public MetricName getStatementName(StatementContext statementContext) { final String group = clazz.getPackage().getName(); final String name = clazz.getSimpleName(); final String type = method == null ? rawSql : method.getName(); - return StatementName.getJmxSafeName(group, name, type); + return name(group, name, type); } return null; } @@ -127,7 +128,7 @@ private ContextClassStrategy() { } @Override - public MetricName getStatementName(StatementContext statementContext) { + public String getStatementName(StatementContext statementContext) { final Object classObj = statementContext.getAttribute(STATEMENT_CLASS); final Object nameObj = statementContext.getAttribute(STATEMENT_NAME); @@ -143,9 +144,9 @@ public MetricName getStatementName(StatementContext statementContext) { return null; } - return StatementName.getJmxSafeName(className.substring(0, dotPos), - className.substring(dotPos + 1), - statementName); + return name(className.substring(0, dotPos), + className.substring(dotPos + 1), + statementName); } } @@ -159,7 +160,7 @@ private ContextNameStrategy() { } @Override - public MetricName getStatementName(StatementContext statementContext) { + public String getStatementName(StatementContext statementContext) { final Object groupObj = statementContext.getAttribute(STATEMENT_GROUP); final Object typeObj = statementContext.getAttribute(STATEMENT_TYPE); final Object nameObj = statementContext.getAttribute(STATEMENT_NAME); @@ -176,14 +177,14 @@ public MetricName getStatementName(StatementContext statementContext) { if (matcher.matches()) { final String groupName = matcher.group(1); final String typeName = matcher.group(2); - return StatementName.getJmxSafeName(groupName, typeName, statementName); + return name(groupName, typeName, statementName); } - return StatementName.getJmxSafeName(group, statementName, ""); + return name(group, statementName, ""); } else { final String type = (String) typeObj; - return StatementName.getJmxSafeName(group, type, statementName); + return name(group, type, statementName); } } } diff --git a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/ShortNameStrategy.java b/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/ShortNameStrategy.java index bf05502212..db0f74034e 100644 --- a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/ShortNameStrategy.java +++ b/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/ShortNameStrategy.java @@ -1,12 +1,13 @@ package com.yammer.metrics.jdbi.strategies; -import com.yammer.metrics.core.MetricName; import org.skife.jdbi.v2.StatementContext; import java.lang.reflect.Method; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import static com.yammer.metrics.MetricRegistry.name; + /** * Assembles all JDBI stats under a common prefix (passed in at constructor time). Stats are grouped * by class name and method; a shortening strategy is applied to make the JMX output nicer. @@ -30,7 +31,7 @@ public ShortNameStrategy(String baseJmxName) { private final class ShortContextClassStrategy implements StatementNameStrategy { @Override - public MetricName getStatementName(StatementContext statementContext) { + public String getStatementName(StatementContext statementContext) { final Object classObj = statementContext.getAttribute(NameStrategies.STATEMENT_CLASS); final Object nameObj = statementContext.getAttribute(NameStrategies.STATEMENT_NAME); @@ -50,16 +51,16 @@ public MetricName getStatementName(StatementContext statementContext) { final String oldClassName = shortClassNames.putIfAbsent(shortName, className); if (oldClassName == null || oldClassName.equals(className)) { - return StatementName.getJmxSafeName(baseJmxName, shortName, statementName); + return name(baseJmxName, shortName, statementName); } else { - return StatementName.getJmxSafeName(baseJmxName, className, statementName); + return name(baseJmxName, className, statementName); } } } private final class ShortSqlObjectStrategy implements StatementNameStrategy { @Override - public MetricName getStatementName(StatementContext statementContext) { + public String getStatementName(StatementContext statementContext) { final Class clazz = statementContext.getSqlObjectType(); final Method method = statementContext.getSqlObjectMethod(); if (clazz != null && method != null) { @@ -75,9 +76,9 @@ public MetricName getStatementName(StatementContext statementContext) { final String oldClassName = shortClassNames.putIfAbsent(shortName, className); if (oldClassName == null || oldClassName.equals(className)) { - return StatementName.getJmxSafeName(baseJmxName, shortName, statementName); + return name(baseJmxName, shortName, statementName); } else { - return StatementName.getJmxSafeName(baseJmxName, className, statementName); + return name(baseJmxName, className, statementName); } } return null; diff --git a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/StatementName.java b/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/StatementName.java deleted file mode 100644 index 4a25839906..0000000000 --- a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/StatementName.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.yammer.metrics.jdbi.strategies; - -import com.yammer.metrics.core.MetricName; - -import java.util.regex.Pattern; - -public class StatementName { - /** - * Characters safe to be used in JMX names. - */ - private static final Pattern JMX_SAFE_CHARS = Pattern.compile("[^a-zA-Z0-9_\\.-]"); - - public static MetricName getJmxSafeName(String groupName, String typeName, String statementName) { - return new MetricName(getJmxSafeName(groupName), - getJmxSafeName(typeName), - getJmxSafeName(statementName)); - } - - /** - * Turns an arbitrary string into a JMX safe name. - * - * @param name an arbitrary string - * @return a JMX-safe name - */ - private static String getJmxSafeName(String name) { - final String result = JMX_SAFE_CHARS.matcher(name).replaceAll("_"); - - if (result == null || result.length() == 0) { - return ""; - } - - return (Character.isDigit(result.charAt(0))) ? "_" + result : result; - } -} diff --git a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/StatementNameStrategy.java b/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/StatementNameStrategy.java index c10c36f5bd..4a83168b74 100644 --- a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/StatementNameStrategy.java +++ b/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/StatementNameStrategy.java @@ -2,11 +2,9 @@ import org.skife.jdbi.v2.StatementContext; -import com.yammer.metrics.core.MetricName; - /** * Interface for strategies to statement contexts to metric names. */ public interface StatementNameStrategy { - MetricName getStatementName(StatementContext statementContext); + String getStatementName(StatementContext statementContext); } diff --git a/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java b/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java index dbee94d75b..e8620431e4 100644 --- a/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java +++ b/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java @@ -1,9 +1,7 @@ package com.yammer.metrics.jdbi.tests; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.MetricName; -import com.yammer.metrics.core.MetricsRegistry; -import com.yammer.metrics.core.Timer; +import com.yammer.metrics.MetricRegistry; +import com.yammer.metrics.Timer; import com.yammer.metrics.jdbi.InstrumentedTimingCollector; import com.yammer.metrics.jdbi.strategies.NameStrategies; import com.yammer.metrics.jdbi.strategies.ShortNameStrategy; @@ -14,19 +12,19 @@ import java.util.concurrent.TimeUnit; -import static org.hamcrest.Matchers.closeTo; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; +import static com.yammer.metrics.MetricRegistry.name; +import static org.fest.assertions.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; public class InstrumentedTimingCollectorTest { - private final MetricsRegistry registry = Metrics.defaultRegistry(); + private final MetricRegistry registry = new MetricRegistry("test"); @Test public void updatesTimerForSqlObjects() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); - final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(Metrics.defaultRegistry(), strategy); + final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, + strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); doReturn(getClass()).when(ctx).getSqlObjectType(); @@ -34,205 +32,225 @@ public void updatesTimerForSqlObjects() throws Exception { collector.collect(TimeUnit.SECONDS.toNanos(1), ctx); - final MetricName name = strategy.getStatementName(ctx); - final Timer timer = registry.newTimer(name, TimeUnit.MILLISECONDS, TimeUnit.SECONDS); + final String name = strategy.getStatementName(ctx); + final Timer timer = registry.timer(name); - assertThat(name, - is(new MetricName(getClass(), "updatesTimerForSqlObjects"))); - assertThat(timer.max(), - is(closeTo(1000.0, 1))); + assertThat(name) + .isEqualTo(name(getClass(), "updatesTimerForSqlObjects")); + assertThat(timer.getMax()) + .isEqualTo(1000000000); } @Test public void updatesTimerForSqlObjectsWithoutMethod() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); - final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(Metrics.defaultRegistry(), strategy); + final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, + strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); doReturn(getClass()).when(ctx).getSqlObjectType(); collector.collect(TimeUnit.SECONDS.toNanos(1), ctx); - final MetricName name = strategy.getStatementName(ctx); - final Timer timer = registry.newTimer(name, TimeUnit.MILLISECONDS, TimeUnit.SECONDS); + final String name = strategy.getStatementName(ctx); + final Timer timer = registry.timer(name); - assertThat(name, - is(new MetricName(getClass(), "SELECT_1"))); - assertThat(timer.max(), - is(closeTo(1000.0, 1))); + assertThat(name) + .isEqualTo(name(getClass(), "SELECT 1")); + assertThat(timer.getMax()) + .isEqualTo(1000000000); } @Test public void updatesTimerForRawSql() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); - final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(Metrics.defaultRegistry(), strategy); + final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, + strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); collector.collect(TimeUnit.SECONDS.toNanos(2), ctx); - final MetricName name = strategy.getStatementName(ctx); - final Timer timer = registry.newTimer(name, TimeUnit.MILLISECONDS, TimeUnit.SECONDS); + final String name = strategy.getStatementName(ctx); + final Timer timer = registry.timer(name); - assertThat(name, - is(new MetricName("sql", "raw", "SELECT_1"))); - assertThat(timer.max(), - is(closeTo(2000.0, 1))); + assertThat(name) + .isEqualTo(name("sql", "raw", "SELECT 1")); + assertThat(timer.getMax()) + .isEqualTo(2000000000); } @Test public void updatesTimerForNoRawSql() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); - final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(Metrics.defaultRegistry(), strategy); + final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, + strategy); final StatementContext ctx = mock(StatementContext.class); collector.collect(TimeUnit.SECONDS.toNanos(2), ctx); - final MetricName name = strategy.getStatementName(ctx); - final Timer timer = registry.newTimer(name, TimeUnit.MILLISECONDS, TimeUnit.SECONDS); + final String name = strategy.getStatementName(ctx); + final Timer timer = registry.timer(name); - assertThat(name, - is(new MetricName("sql", "empty", ""))); - assertThat(timer.max(), - is(closeTo(2000.0, 1))); + assertThat(name) + .isEqualTo(name("sql", "empty")); + assertThat(timer.getMax()) + .isEqualTo(2000000000); } @Test public void updatesTimerForNonSqlishRawSql() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); - final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(Metrics.defaultRegistry(), strategy); + final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, + strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("don't know what it is but it's not SQL").when(ctx).getRawSql(); collector.collect(TimeUnit.SECONDS.toNanos(3), ctx); - final MetricName name = strategy.getStatementName(ctx); - final Timer timer = registry.newTimer(name, TimeUnit.MILLISECONDS, TimeUnit.SECONDS); + final String name = strategy.getStatementName(ctx); + final Timer timer = registry.timer(name); - assertThat(name, - is(new MetricName("sql", "raw", "don_t_know_what_it_is_but_it_s_not_SQL"))); - assertThat(timer.max(), - is(closeTo(3000.0, 1))); + assertThat(name) + .isEqualTo(name("sql", "raw", "don't know what it is but it's not SQL")); + assertThat(timer.getMax()) + .isEqualTo(3000000000L); } @Test public void updatesTimerForContextClass() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); - final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(Metrics.defaultRegistry(), strategy); + final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, + strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); doReturn(getClass().getName()).when(ctx).getAttribute(NameStrategies.STATEMENT_CLASS); - doReturn("updatesTimerForContextClass").when(ctx).getAttribute(NameStrategies.STATEMENT_NAME); + doReturn("updatesTimerForContextClass").when(ctx) + .getAttribute(NameStrategies.STATEMENT_NAME); collector.collect(TimeUnit.SECONDS.toNanos(3), ctx); - final MetricName name = strategy.getStatementName(ctx); - final Timer timer = registry.newTimer(name, TimeUnit.MILLISECONDS, TimeUnit.SECONDS); + final String name = strategy.getStatementName(ctx); + final Timer timer = registry.timer(name); - assertThat(name, - is(new MetricName(getClass(), "updatesTimerForContextClass"))); - assertThat(timer.max(), - is(closeTo(3000.0, 1))); + assertThat(name) + .isEqualTo(name(getClass(), "updatesTimerForContextClass")); + assertThat(timer.getMax()) + .isEqualTo(3000000000L); } @Test public void updatesTimerForTemplateFile() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); - final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(Metrics.defaultRegistry(), strategy); + final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, + strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); doReturn("foo/bar.stg").when(ctx).getAttribute(NameStrategies.STATEMENT_GROUP); - doReturn("updatesTimerForTemplateFile").when(ctx).getAttribute(NameStrategies.STATEMENT_NAME); + doReturn("updatesTimerForTemplateFile").when(ctx) + .getAttribute(NameStrategies.STATEMENT_NAME); collector.collect(TimeUnit.SECONDS.toNanos(4), ctx); - final MetricName name = strategy.getStatementName(ctx); - final Timer timer = registry.newTimer(name, TimeUnit.MILLISECONDS, TimeUnit.SECONDS); + final String name = strategy.getStatementName(ctx); + final Timer timer = registry.timer(name); - assertThat(name, - is(new MetricName("foo", "bar", "updatesTimerForTemplateFile"))); - assertThat(timer.max(), - is(closeTo(4000.0, 1))); + assertThat(name) + .isEqualTo(name("foo", "bar", "updatesTimerForTemplateFile")); + assertThat(timer.getMax()) + .isEqualTo(4000000000L); } @Test public void updatesTimerForContextGroupAndName() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); - final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(Metrics.defaultRegistry(), strategy); + final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, + strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); doReturn("my-group").when(ctx).getAttribute(NameStrategies.STATEMENT_GROUP); - doReturn("updatesTimerForContextGroupAndName").when(ctx).getAttribute(NameStrategies.STATEMENT_NAME); + doReturn("updatesTimerForContextGroupAndName").when(ctx) + .getAttribute(NameStrategies.STATEMENT_NAME); collector.collect(TimeUnit.SECONDS.toNanos(4), ctx); - final MetricName name = strategy.getStatementName(ctx); - final Timer timer = registry.newTimer(name, TimeUnit.MILLISECONDS, TimeUnit.SECONDS); + final String name = strategy.getStatementName(ctx); + final Timer timer = registry.timer(name); - assertThat(name, - is(new MetricName("my-group", "updatesTimerForContextGroupAndName", ""))); - assertThat(timer.max(), - is(closeTo(4000.0, 1))); + assertThat(name) + .isEqualTo(name("my-group", "updatesTimerForContextGroupAndName", "")); + assertThat(timer.getMax()) + .isEqualTo(4000000000L); } @Test public void updatesTimerForContextGroupTypeAndName() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); - final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(Metrics.defaultRegistry(), strategy); + final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, + strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); doReturn("my-group").when(ctx).getAttribute(NameStrategies.STATEMENT_GROUP); doReturn("my-type").when(ctx).getAttribute(NameStrategies.STATEMENT_TYPE); - doReturn("updatesTimerForContextGroupTypeAndName").when(ctx).getAttribute(NameStrategies.STATEMENT_NAME); + doReturn("updatesTimerForContextGroupTypeAndName").when(ctx) + .getAttribute(NameStrategies.STATEMENT_NAME); collector.collect(TimeUnit.SECONDS.toNanos(5), ctx); - final MetricName name = strategy.getStatementName(ctx); - final Timer timer = registry.newTimer(name, TimeUnit.MILLISECONDS, TimeUnit.SECONDS); + final String name = strategy.getStatementName(ctx); + final Timer timer = registry.timer(name); - assertThat(name, - is(new MetricName("my-group", "my-type", "updatesTimerForContextGroupTypeAndName"))); - assertThat(timer.max(), - is(closeTo(5000.0, 1))); + assertThat(name) + .isEqualTo(name("my-group", "my-type", "updatesTimerForContextGroupTypeAndName")); + assertThat(timer.getMax()) + .isEqualTo(5000000000L); } @Test public void updatesTimerForShortSqlObjectStrategy() throws Exception { final StatementNameStrategy strategy = new ShortNameStrategy("jdbi"); - final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(Metrics.defaultRegistry(), strategy); + final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, + strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); doReturn(getClass()).when(ctx).getSqlObjectType(); - doReturn(getClass().getMethod("updatesTimerForShortSqlObjectStrategy")).when(ctx).getSqlObjectMethod(); + doReturn(getClass().getMethod("updatesTimerForShortSqlObjectStrategy")).when(ctx) + .getSqlObjectMethod(); collector.collect(TimeUnit.SECONDS.toNanos(1), ctx); - final MetricName name = strategy.getStatementName(ctx); - final Timer timer = registry.newTimer(name, TimeUnit.MILLISECONDS, TimeUnit.SECONDS); + final String name = strategy.getStatementName(ctx); + final Timer timer = registry.timer(name); - assertThat(name, - is(new MetricName("jdbi", getClass().getSimpleName(), "updatesTimerForShortSqlObjectStrategy"))); - assertThat(timer.max(), - is(closeTo(1000.0, 1))); + assertThat(name) + .isEqualTo(name("jdbi", + getClass().getSimpleName(), + "updatesTimerForShortSqlObjectStrategy")); + assertThat(timer.getMax()) + .isEqualTo(1000000000); } @Test public void updatesTimerForShortContextClassStrategy() throws Exception { final StatementNameStrategy strategy = new ShortNameStrategy("jdbi"); - final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, strategy); + final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, + strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); doReturn(getClass().getName()).when(ctx).getAttribute(NameStrategies.STATEMENT_CLASS); - doReturn("updatesTimerForShortContextClassStrategy").when(ctx).getAttribute(NameStrategies.STATEMENT_NAME); + doReturn("updatesTimerForShortContextClassStrategy").when(ctx) + .getAttribute(NameStrategies.STATEMENT_NAME); collector.collect(TimeUnit.SECONDS.toNanos(3), ctx); - final MetricName name = strategy.getStatementName(ctx); - final Timer timer = registry.newTimer(name, TimeUnit.MILLISECONDS, TimeUnit.SECONDS); + final String name = strategy.getStatementName(ctx); + final Timer timer = registry.timer(name); - assertThat(name, - is(new MetricName("jdbi", getClass().getSimpleName(), "updatesTimerForShortContextClassStrategy"))); - assertThat(timer.max(), - is(closeTo(3000.0, 1))); + assertThat(name) + .isEqualTo(name("jdbi", + getClass().getSimpleName(), + "updatesTimerForShortContextClassStrategy")); + assertThat(timer.getMax()) + .isEqualTo(3000000000L); } } From 508c2953b5606a19ed82ac677a060920143fb810 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 09:48:38 -0700 Subject: [PATCH 0024/2558] Added RatioGauge. --- .../java/com/yammer/metrics/RatioGauge.java | 45 ++++++++++++++ .../yammer/metrics/tests/RatioGaugeTest.java | 61 +++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 metrics-core/src/main/java/com/yammer/metrics/RatioGauge.java create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/RatioGaugeTest.java diff --git a/metrics-core/src/main/java/com/yammer/metrics/RatioGauge.java b/metrics-core/src/main/java/com/yammer/metrics/RatioGauge.java new file mode 100644 index 0000000000..e7468f3388 --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/RatioGauge.java @@ -0,0 +1,45 @@ +package com.yammer.metrics; + +import static java.lang.Double.isInfinite; +import static java.lang.Double.isNaN; + +/** + * A gauge which measures the ratio of one value to another. + *

+ * If the denominator is zero, not a number, or infinite, the resulting ratio is not a number. + */ +public abstract class RatioGauge implements Gauge { + public static class Ratio { + public static Ratio of(double numerator, double denominator) { + return new Ratio(numerator, denominator); + } + + private final double numerator; + private final double denominator; + + private Ratio(double numerator, double denominator) { + this.numerator = numerator; + this.denominator = denominator; + } + + public double getValue() { + final double d = denominator; + if (isNaN(d) || isInfinite(d) || d == 0) { + return Double.NaN; + } + return numerator / d; + } + } + + /** + * Returns the {@link Ratio} which is the gauge's current value. + * + * @return the {@link Ratio} which is the gauge's current value + */ + protected abstract Ratio getRatio(); + + @Override + public Double getValue() { + return getRatio().getValue(); + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/RatioGaugeTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/RatioGaugeTest.java new file mode 100644 index 0000000000..09a334d32b --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/RatioGaugeTest.java @@ -0,0 +1,61 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.RatioGauge; +import org.junit.Test; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +public class RatioGaugeTest { + @Test + public void calculatesTheRatioOfTheNumeratorToTheDenominator() throws Exception { + final RatioGauge regular = new RatioGauge() { + @Override + protected Ratio getRatio() { + return RatioGauge.Ratio.of(2, 4); + } + }; + + assertThat(regular.getValue(), + is(0.5)); + } + + @Test + public void handlesDivideByZeroIssues() throws Exception { + final RatioGauge divByZero = new RatioGauge() { + @Override + protected Ratio getRatio() { + return Ratio.of(100, 0); + } + }; + + assertThat(divByZero.getValue(), + is(Double.NaN)); + } + + @Test + public void handlesInfiniteDenominators() throws Exception { + final RatioGauge infinite = new RatioGauge() { + @Override + protected Ratio getRatio() { + return Ratio.of(10, Double.POSITIVE_INFINITY); + } + }; + + assertThat(infinite.getValue(), + is(Double.NaN)); + } + + @Test + public void handlesNaNDenominators() throws Exception { + final RatioGauge nan = new RatioGauge() { + @Override + protected Ratio getRatio() { + return Ratio.of(10, Double.NaN); + } + }; + + assertThat(nan.getValue(), + is(Double.NaN)); + } +} From 38a6c8d37b00edf562b20234500cf29600de7782 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 09:48:54 -0700 Subject: [PATCH 0025/2558] Revamped metrics-jetty. --- metrics-jetty/pom.xml | 2 +- .../InstrumentedBlockingChannelConnector.java | 59 +++----- .../metrics/jetty/InstrumentedHandler.java | 140 +++++++----------- .../jetty/InstrumentedQueuedThreadPool.java | 34 ++--- .../InstrumentedSelectChannelConnector.java | 60 ++++---- .../jetty/InstrumentedSocketConnector.java | 58 +++----- ...InstrumentedSslSelectChannelConnector.java | 95 ++++-------- .../jetty/InstrumentedSslSocketConnector.java | 62 ++++---- 8 files changed, 192 insertions(+), 318 deletions(-) diff --git a/metrics-jetty/pom.xml b/metrics-jetty/pom.xml index f0e21d7798..80e56fef88 100644 --- a/metrics-jetty/pom.xml +++ b/metrics-jetty/pom.xml @@ -16,7 +16,7 @@ com.yammer.metrics metrics-core - 2.2.0 + ${project.version} org.eclipse.jetty diff --git a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedBlockingChannelConnector.java b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedBlockingChannelConnector.java index 00f20ac1ef..684bac015c 100644 --- a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedBlockingChannelConnector.java +++ b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedBlockingChannelConnector.java @@ -1,52 +1,41 @@ package com.yammer.metrics.jetty; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Counter; -import com.yammer.metrics.core.Meter; -import com.yammer.metrics.core.MetricsRegistry; -import com.yammer.metrics.core.Timer; +import com.yammer.metrics.*; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.nio.BlockingChannelConnector; import java.io.IOException; import java.util.concurrent.TimeUnit; +import static com.yammer.metrics.MetricRegistry.name; + public class InstrumentedBlockingChannelConnector extends BlockingChannelConnector { private final Timer duration; private final Meter accepts, connects, disconnects; private final Counter connections; + private final Clock clock; - public InstrumentedBlockingChannelConnector(int port) { - this(Metrics.defaultRegistry(), port); - } - - public InstrumentedBlockingChannelConnector(MetricsRegistry registry, - int port) { + public InstrumentedBlockingChannelConnector(MetricRegistry registry, + int port, + Clock clock) { super(); + this.clock = clock; setPort(port); - this.duration = registry.newTimer(BlockingChannelConnector.class, - "connection-duration", - Integer.toString(port), - TimeUnit.MILLISECONDS, - TimeUnit.SECONDS); - this.accepts = registry.newMeter(BlockingChannelConnector.class, - "accepts", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.connects = registry.newMeter(BlockingChannelConnector.class, - "connects", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.disconnects = registry.newMeter(BlockingChannelConnector.class, - "disconnects", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.connections = registry.newCounter(BlockingChannelConnector.class, - "active-connections", - Integer.toString(port)); + this.duration = registry.timer(name(BlockingChannelConnector.class, + Integer.toString(port), + "connection-duration")); + this.accepts = registry.meter(name(BlockingChannelConnector.class, + Integer.toString(port), + "accepts")); + this.connects = registry.meter(name(BlockingChannelConnector.class, + Integer.toString(port), + "connects")); + this.disconnects = registry.meter(name(BlockingChannelConnector.class, + Integer.toString(port), + "disconnects")); + this.connections = registry.counter(name(BlockingChannelConnector.class, + Integer.toString(port), + "active-connections")); } @Override @@ -66,7 +55,7 @@ protected void connectionOpened(Connection connection) { protected void connectionClosed(Connection connection) { super.connectionClosed(connection); disconnects.mark(); - final long duration = System.currentTimeMillis() - connection.getTimeStamp(); + final long duration = clock.getTime() - connection.getTimeStamp(); this.duration.update(duration, TimeUnit.MILLISECONDS); connections.dec(); } diff --git a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedHandler.java b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedHandler.java index 7e9aec6e44..969fd28bb0 100644 --- a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedHandler.java +++ b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedHandler.java @@ -1,11 +1,6 @@ package com.yammer.metrics.jetty; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Counter; -import com.yammer.metrics.core.Meter; -import com.yammer.metrics.core.MetricsRegistry; -import com.yammer.metrics.core.Timer; -import com.yammer.metrics.util.RatioGauge; +import com.yammer.metrics.*; import org.eclipse.jetty.continuation.Continuation; import org.eclipse.jetty.continuation.ContinuationListener; import org.eclipse.jetty.server.AsyncContinuation; @@ -19,6 +14,7 @@ import java.io.IOException; import java.util.concurrent.TimeUnit; +import static com.yammer.metrics.MetricRegistry.name; import static org.eclipse.jetty.http.HttpMethods.*; /** @@ -46,110 +42,78 @@ public class InstrumentedHandler extends HandlerWrapper { private final ContinuationListener listener; - /** - * Create a new instrumented handler. - * - * @param underlying the handler about which metrics will be collected - */ - public InstrumentedHandler(Handler underlying) { - this(underlying, Metrics.defaultRegistry()); - } - /** * Create a new instrumented handler using a given metrics registry. * - * @param underlying the handler about which metrics will be collected * @param registry the registry for the metrics + * @param underlying the handler about which metrics will be collected */ - public InstrumentedHandler(Handler underlying, MetricsRegistry registry) { + public InstrumentedHandler(MetricRegistry registry, Handler underlying) { super(); - this.dispatches = registry.newTimer(underlying.getClass(), "dispatches", TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - this.requests = registry.newMeter(underlying.getClass(), "requests", "requests", TimeUnit.SECONDS); - this.resumes = registry.newMeter(underlying.getClass(), "resumes", "requests", TimeUnit.SECONDS); - this.suspends = registry.newMeter(underlying.getClass(), "suspends", "requests", TimeUnit.SECONDS); - this.expires = registry.newMeter(underlying.getClass(), "expires", "requests", TimeUnit.SECONDS); + this.dispatches = registry.timer(name(underlying.getClass(), "dispatches")); + this.requests = registry.meter(name(underlying.getClass(), "requests")); + this.resumes = registry.meter(name(underlying.getClass(), "resumes")); + this.suspends = registry.meter(name(underlying.getClass(), "suspends")); + this.expires = registry.meter(name(underlying.getClass(), "expires")); - this.activeRequests = registry.newCounter(underlying.getClass(), "active-requests"); - this.activeSuspendedRequests = registry.newCounter(underlying.getClass(), "active-suspended-requests"); - this.activeDispatches = registry.newCounter(underlying.getClass(), "active-dispatches"); + this.activeRequests = registry.counter(name(underlying.getClass(), "active-requests")); + this.activeSuspendedRequests = registry.counter(name(underlying.getClass(), + "active-suspended-requests")); + this.activeDispatches = registry.counter(name(underlying.getClass(), "active-dispatches")); this.responses = new Meter[]{ - registry.newMeter(underlying.getClass(), "1xx-responses", "responses", TimeUnit.SECONDS), // 1xx - registry.newMeter(underlying.getClass(), "2xx-responses", "responses", TimeUnit.SECONDS), // 2xx - registry.newMeter(underlying.getClass(), "3xx-responses", "responses", TimeUnit.SECONDS), // 3xx - registry.newMeter(underlying.getClass(), "4xx-responses", "responses", TimeUnit.SECONDS), // 4xx - registry.newMeter(underlying.getClass(), "5xx-responses", "responses", TimeUnit.SECONDS) // 5xx + registry.meter(name(underlying.getClass(), "1xx-responses")), // 1xx + registry.meter(name(underlying.getClass(), "2xx-responses")), // 2xx + registry.meter(name(underlying.getClass(), "3xx-responses")), // 3xx + registry.meter(name(underlying.getClass(), "4xx-responses")), // 4xx + registry.meter(name(underlying.getClass(), "5xx-responses")) // 5xx }; - registry.newGauge(underlying.getClass(), "percent-4xx-1m", new RatioGauge() { - @Override - protected double getNumerator() { - return responses[3].oneMinuteRate(); - } - + registry.register(name(underlying.getClass(), "percent-4xx-1m"), new RatioGauge() { @Override - protected double getDenominator() { - return requests.oneMinuteRate(); + protected Ratio getRatio() { + return Ratio.of(responses[3].getOneMinuteRate(), + requests.getOneMinuteRate()); } }); - registry.newGauge(underlying.getClass(), "percent-4xx-5m", new RatioGauge() { - @Override - protected double getNumerator() { - return responses[3].fiveMinuteRate(); - } - + registry.register(name(underlying.getClass(), "percent-4xx-5m"), new RatioGauge() { @Override - protected double getDenominator() { - return requests.fiveMinuteRate(); + protected Ratio getRatio() { + return Ratio.of(responses[3].getFiveMinuteRate(), + requests.getFiveMinuteRate()); } }); - registry.newGauge(underlying.getClass(), "percent-4xx-15m", new RatioGauge() { - @Override - protected double getNumerator() { - return responses[3].fifteenMinuteRate(); - } - + registry.register(name(underlying.getClass(), "percent-4xx-15m"), new RatioGauge() { @Override - protected double getDenominator() { - return requests.fifteenMinuteRate(); + protected Ratio getRatio() { + return Ratio.of(responses[3].getFifteenMinuteRate(), + requests.getFifteenMinuteRate()); } }); - registry.newGauge(underlying.getClass(), "percent-5xx-1m", new RatioGauge() { - @Override - protected double getNumerator() { - return responses[4].oneMinuteRate(); - } - + registry.register(name(underlying.getClass(), "percent-5xx-1m"), new RatioGauge() { @Override - protected double getDenominator() { - return requests.oneMinuteRate(); + protected Ratio getRatio() { + return Ratio.of(responses[4].getOneMinuteRate(), + requests.getOneMinuteRate()); } }); - registry.newGauge(underlying.getClass(), "percent-5xx-5m", new RatioGauge() { + registry.register(name(underlying.getClass(), "percent-5xx-5m"), new RatioGauge() { @Override - protected double getNumerator() { - return responses[4].fiveMinuteRate(); - } - - @Override - protected double getDenominator() { - return requests.fiveMinuteRate(); + protected Ratio getRatio() { + return Ratio.of(responses[4].getFiveMinuteRate(), + requests.getFiveMinuteRate()); } }); - registry.newGauge(underlying.getClass(), "percent-5xx-15m", new RatioGauge() { - @Override - protected double getNumerator() { - return responses[4].fifteenMinuteRate(); - } - + registry.register(name(underlying.getClass(), "percent-5xx-15m"), new RatioGauge() { @Override - protected double getDenominator() { - return requests.fifteenMinuteRate(); + protected Ratio getRatio() { + return Ratio.of(responses[4].getFifteenMinuteRate(), + requests.getFifteenMinuteRate()); } }); @@ -169,16 +133,16 @@ public void onTimeout(Continuation continuation) { } }; - this.getRequests = registry.newTimer(underlying.getClass(), "get-requests", TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - this.postRequests = registry.newTimer(underlying.getClass(), "post-requests", TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - this.headRequests = registry.newTimer(underlying.getClass(), "head-requests", TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - this.putRequests = registry.newTimer(underlying.getClass(), "put-requests", TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - this.deleteRequests = registry.newTimer(underlying.getClass(), "delete-requests", TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - this.optionsRequests = registry.newTimer(underlying.getClass(), "options-requests", TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - this.traceRequests = registry.newTimer(underlying.getClass(), "trace-requests", TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - this.connectRequests = registry.newTimer(underlying.getClass(), "connect-requests", TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - this.patchRequests = registry.newTimer(underlying.getClass(), "patch-requests", TimeUnit.MILLISECONDS, TimeUnit.SECONDS); - this.otherRequests = registry.newTimer(underlying.getClass(), "other-requests", TimeUnit.MILLISECONDS, TimeUnit.SECONDS); + this.getRequests = registry.timer(name(underlying.getClass(), "get-requests")); + this.postRequests = registry.timer(name(underlying.getClass(), "post-requests")); + this.headRequests = registry.timer(name(underlying.getClass(), "head-requests")); + this.putRequests = registry.timer(name(underlying.getClass(), "put-requests")); + this.deleteRequests = registry.timer(name(underlying.getClass(), "delete-requests")); + this.optionsRequests = registry.timer(name(underlying.getClass(), "options-requests")); + this.traceRequests = registry.timer(name(underlying.getClass(), "trace-requests")); + this.connectRequests = registry.timer(name(underlying.getClass(), "connect-requests")); + this.patchRequests = registry.timer(name(underlying.getClass(), "patch-requests")); + this.otherRequests = registry.timer(name(underlying.getClass(), "other-requests")); setHandler(underlying); } diff --git a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedQueuedThreadPool.java b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedQueuedThreadPool.java index deffa2009c..38e5e358c5 100644 --- a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedQueuedThreadPool.java +++ b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedQueuedThreadPool.java @@ -1,39 +1,31 @@ package com.yammer.metrics.jetty; -import com.yammer.metrics.util.RatioGauge; +import com.yammer.metrics.Gauge; +import com.yammer.metrics.MetricRegistry; +import com.yammer.metrics.RatioGauge; import org.eclipse.jetty.util.thread.QueuedThreadPool; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Gauge; -import com.yammer.metrics.core.MetricsRegistry; +import static com.yammer.metrics.MetricRegistry.name; public class InstrumentedQueuedThreadPool extends QueuedThreadPool { - public InstrumentedQueuedThreadPool() { - this(Metrics.defaultRegistry()); - } - - public InstrumentedQueuedThreadPool(MetricsRegistry registry) { + public InstrumentedQueuedThreadPool(MetricRegistry registry) { super(); - registry.newGauge(QueuedThreadPool.class, "percent-idle", new RatioGauge() { + registry.register(name(QueuedThreadPool.class, "percent-idle"), new RatioGauge() { @Override - protected double getNumerator() { - return getIdleThreads(); - } - - @Override - protected double getDenominator() { - return getThreads(); + protected Ratio getRatio() { + return Ratio.of(getIdleThreads(), + getThreads()); } }); - registry.newGauge(QueuedThreadPool.class, "active-threads", new Gauge() { + registry.register(name(QueuedThreadPool.class, "active-threads"), new Gauge() { @Override - public Integer value() { + public Integer getValue() { return getThreads(); } }); - registry.newGauge(QueuedThreadPool.class, "idle-threads", new Gauge() { + registry.register(name(QueuedThreadPool.class, "idle-threads"), new Gauge() { @Override - public Integer value() { + public Integer getValue() { return getIdleThreads(); } }); diff --git a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSelectChannelConnector.java b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSelectChannelConnector.java index 43e3f6f7c5..6723e99c04 100644 --- a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSelectChannelConnector.java +++ b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSelectChannelConnector.java @@ -1,52 +1,42 @@ package com.yammer.metrics.jetty; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Counter; -import com.yammer.metrics.core.Meter; -import com.yammer.metrics.core.MetricsRegistry; -import com.yammer.metrics.core.Timer; +import com.yammer.metrics.*; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.nio.SelectChannelConnector; import java.io.IOException; import java.util.concurrent.TimeUnit; +import static com.yammer.metrics.MetricRegistry.name; + public class InstrumentedSelectChannelConnector extends SelectChannelConnector { private final Timer duration; private final Meter accepts, connects, disconnects; private final Counter connections; + private final Clock clock; - public InstrumentedSelectChannelConnector(int port) { - this(Metrics.defaultRegistry(), port); - } - - public InstrumentedSelectChannelConnector(MetricsRegistry registry, - int port) { + public InstrumentedSelectChannelConnector(MetricRegistry registry, + int port, + Clock clock) { super(); + this.clock = clock; setPort(port); - this.duration = registry.newTimer(SelectChannelConnector.class, - "connection-duration", - Integer.toString(port), - TimeUnit.MILLISECONDS, - TimeUnit.SECONDS); - this.accepts = registry.newMeter(SelectChannelConnector.class, - "accepts", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.connects = registry.newMeter(SelectChannelConnector.class, - "connects", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.disconnects = registry.newMeter(SelectChannelConnector.class, - "disconnects", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.connections = registry.newCounter(SelectChannelConnector.class, - "active-connections", - Integer.toString(port)); + + this.duration = registry.timer(name(SelectChannelConnector.class, + Integer.toString(port), + "connection-duration")); + this.accepts = registry.meter(name(SelectChannelConnector.class, + Integer.toString(port), + "accepts")); + this.connects = registry.meter(name(SelectChannelConnector.class, + Integer.toString(port), + "connects")); + this.disconnects = registry.meter(name(SelectChannelConnector.class, + Integer.toString(port), + "disconnects")); + this.connections = registry.counter(name(SelectChannelConnector.class, + Integer.toString(port), + "active-connections")); } @Override @@ -66,7 +56,7 @@ protected void connectionOpened(Connection connection) { protected void connectionClosed(Connection connection) { super.connectionClosed(connection); disconnects.mark(); - final long duration = System.currentTimeMillis() - connection.getTimeStamp(); + final long duration = clock.getTime() - connection.getTimeStamp(); this.duration.update(duration, TimeUnit.MILLISECONDS); connections.dec(); } diff --git a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSocketConnector.java b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSocketConnector.java index a18036cd3b..58abdb6f6a 100644 --- a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSocketConnector.java +++ b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSocketConnector.java @@ -1,51 +1,41 @@ package com.yammer.metrics.jetty; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Counter; -import com.yammer.metrics.core.Meter; -import com.yammer.metrics.core.MetricsRegistry; -import com.yammer.metrics.core.Timer; +import com.yammer.metrics.*; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.bio.SocketConnector; import java.io.IOException; import java.util.concurrent.TimeUnit; +import static com.yammer.metrics.MetricRegistry.name; + public class InstrumentedSocketConnector extends SocketConnector { private final Timer duration; private final Meter accepts, connects, disconnects; private final Counter connections; + private final Clock clock; - public InstrumentedSocketConnector(int port) { - this(Metrics.defaultRegistry(), port); - } - - public InstrumentedSocketConnector(MetricsRegistry registry, int port) { + public InstrumentedSocketConnector(MetricRegistry registry, + int port, + Clock clock) { super(); + this.clock = clock; setPort(port); - this.duration = registry.newTimer(SocketConnector.class, - "connection-duration", - Integer.toString(port), - TimeUnit.MILLISECONDS, - TimeUnit.SECONDS); - this.accepts = registry.newMeter(SocketConnector.class, - "accepts", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.connects = registry.newMeter(SocketConnector.class, - "connects", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.disconnects = registry.newMeter(SocketConnector.class, - "disconnects", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.connections = registry.newCounter(SocketConnector.class, - "active-connections", - Integer.toString(port)); + this.duration = registry.timer(name(SocketConnector.class, + Integer.toString(port), + "connection-duration")); + this.accepts = registry.meter(name(SocketConnector.class, + Integer.toString(port), + "accepts")); + this.connects = registry.meter(name(SocketConnector.class, + Integer.toString(port), + "connects")); + this.disconnects = registry.meter(name(SocketConnector.class, + Integer.toString(port), + "disconnects")); + this.connections = registry.counter(name(SocketConnector.class, + Integer.toString(port), + "active-connections")); } @Override @@ -65,7 +55,7 @@ protected void connectionOpened(Connection connection) { protected void connectionClosed(Connection connection) { super.connectionClosed(connection); disconnects.mark(); - final long duration = System.currentTimeMillis() - connection.getTimeStamp(); + final long duration = clock.getTime() - connection.getTimeStamp(); this.duration.update(duration, TimeUnit.MILLISECONDS); connections.dec(); } diff --git a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSelectChannelConnector.java b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSelectChannelConnector.java index 221bfea6a2..76ca9ac6f2 100644 --- a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSelectChannelConnector.java +++ b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSelectChannelConnector.java @@ -1,86 +1,43 @@ package com.yammer.metrics.jetty; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Counter; -import com.yammer.metrics.core.Meter; -import com.yammer.metrics.core.MetricsRegistry; -import com.yammer.metrics.core.Timer; -import org.eclipse.jetty.util.ssl.SslContextFactory; +import com.yammer.metrics.*; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; +import org.eclipse.jetty.util.ssl.SslContextFactory; import java.io.IOException; import java.util.concurrent.TimeUnit; +import static com.yammer.metrics.MetricRegistry.name; + public class InstrumentedSslSelectChannelConnector extends SslSelectChannelConnector { private final Timer duration; private final Meter accepts, connects, disconnects; private final Counter connections; + private final Clock clock; - public InstrumentedSslSelectChannelConnector(int port) { - this(Metrics.defaultRegistry(), port); - } - - public InstrumentedSslSelectChannelConnector(SslContextFactory factory, int port) { - this(Metrics.defaultRegistry(), port, factory); - } - - public InstrumentedSslSelectChannelConnector(MetricsRegistry registry, int port) { - super(); - setPort(port); - this.duration = registry.newTimer(SslSelectChannelConnector.class, - "connection-duration", - Integer.toString(port), - TimeUnit.MILLISECONDS, - TimeUnit.SECONDS); - this.accepts = registry.newMeter(SslSelectChannelConnector.class, - "accepts", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.connects = registry.newMeter(SslSelectChannelConnector.class, - "connects", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.disconnects = registry.newMeter(SslSelectChannelConnector.class, - "disconnects", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.connections = registry.newCounter(SslSelectChannelConnector.class, - "active-connections", - Integer.toString(port)); - - } - - public InstrumentedSslSelectChannelConnector(MetricsRegistry registry, - int port, SslContextFactory factory) { + public InstrumentedSslSelectChannelConnector(MetricRegistry registry, + int port, + SslContextFactory factory, + Clock clock) { super(factory); + this.clock = clock; setPort(port); - this.duration = registry.newTimer(SslSelectChannelConnector.class, - "connection-duration", - Integer.toString(port), - TimeUnit.MILLISECONDS, - TimeUnit.SECONDS); - this.accepts = registry.newMeter(SslSelectChannelConnector.class, - "accepts", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.connects = registry.newMeter(SslSelectChannelConnector.class, - "connects", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.disconnects = registry.newMeter(SslSelectChannelConnector.class, - "disconnects", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.connections = registry.newCounter(SslSelectChannelConnector.class, - "active-connections", - Integer.toString(port)); + this.duration = registry.timer(name(SslSelectChannelConnector.class, + Integer.toString(port), + "connection-duration")); + this.accepts = registry.meter(name(SslSelectChannelConnector.class, + Integer.toString(port), + "accepts")); + this.connects = registry.meter(name(SslSelectChannelConnector.class, + Integer.toString(port), + "connects")); + this.disconnects = registry.meter(name(SslSelectChannelConnector.class, + Integer.toString(port), + "disconnects")); + this.connections = registry.counter(name(SslSelectChannelConnector.class, + Integer.toString(port), + "active-connections")); } @@ -101,7 +58,7 @@ protected void connectionOpened(Connection connection) { protected void connectionClosed(Connection connection) { super.connectionClosed(connection); disconnects.mark(); - final long duration = System.currentTimeMillis() - connection.getTimeStamp(); + final long duration = clock.getTime() - connection.getTimeStamp(); this.duration.update(duration, TimeUnit.MILLISECONDS); connections.dec(); } diff --git a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSocketConnector.java b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSocketConnector.java index 1f86a7eb4c..6459acb5dd 100644 --- a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSocketConnector.java +++ b/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSocketConnector.java @@ -1,51 +1,43 @@ package com.yammer.metrics.jetty; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Counter; -import com.yammer.metrics.core.Meter; -import com.yammer.metrics.core.MetricsRegistry; -import com.yammer.metrics.core.Timer; +import com.yammer.metrics.*; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.ssl.SslSocketConnector; +import org.eclipse.jetty.util.ssl.SslContextFactory; import java.io.IOException; import java.util.concurrent.TimeUnit; +import static com.yammer.metrics.MetricRegistry.name; + public class InstrumentedSslSocketConnector extends SslSocketConnector { private final Timer duration; private final Meter accepts, connects, disconnects; private final Counter connections; + private final Clock clock; - public InstrumentedSslSocketConnector(int port) { - this(Metrics.defaultRegistry(), port); - } - - public InstrumentedSslSocketConnector(MetricsRegistry registry, int port) { - super(); + public InstrumentedSslSocketConnector(MetricRegistry registry, + int port, + SslContextFactory factory, + Clock clock) { + super(factory); + this.clock = clock; setPort(port); - this.duration = registry.newTimer(SslSocketConnector.class, - "connection-duration", - Integer.toString(port), - TimeUnit.MILLISECONDS, - TimeUnit.SECONDS); - this.accepts = registry.newMeter(SslSocketConnector.class, - "accepts", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.connects = registry.newMeter(SslSocketConnector.class, - "connects", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.disconnects = registry.newMeter(SslSocketConnector.class, - "disconnects", - Integer.toString(port), - "connections", - TimeUnit.SECONDS); - this.connections = registry.newCounter(SslSocketConnector.class, - "active-connections", - Integer.toString(port)); + this.duration = registry.timer(name(SslSocketConnector.class, + Integer.toString(port), + "connection-duration")); + this.accepts = registry.meter(name(SslSocketConnector.class, + Integer.toString(port), + "accepts")); + this.connects = registry.meter(name(SslSocketConnector.class, + Integer.toString(port), + "connects")); + this.disconnects = registry.meter(name(SslSocketConnector.class, + Integer.toString(port), + "disconnects")); + this.connections = registry.counter(name(SslSocketConnector.class, + Integer.toString(port), + "active-connections")); } @Override @@ -65,7 +57,7 @@ protected void connectionOpened(Connection connection) { protected void connectionClosed(Connection connection) { super.connectionClosed(connection); disconnects.mark(); - final long duration = System.currentTimeMillis() - connection.getTimeStamp(); + final long duration = clock.getTime() - connection.getTimeStamp(); this.duration.update(duration, TimeUnit.MILLISECONDS); connections.dec(); } From b9522c607cc158fdd5ff827fee02752d2bc2efc2 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 09:49:46 -0700 Subject: [PATCH 0026/2558] Rename metrics-jetty to metrics-jetty8. Yes, this does imply that metrics-jetty9 will exist. --- {metrics-jetty => metrics-jetty8}/pom.xml | 4 ++-- .../metrics/jetty/InstrumentedBlockingChannelConnector.java | 0 .../java/com/yammer/metrics/jetty/InstrumentedHandler.java | 0 .../yammer/metrics/jetty/InstrumentedQueuedThreadPool.java | 0 .../metrics/jetty/InstrumentedSelectChannelConnector.java | 0 .../com/yammer/metrics/jetty/InstrumentedSocketConnector.java | 0 .../metrics/jetty/InstrumentedSslSelectChannelConnector.java | 0 .../yammer/metrics/jetty/InstrumentedSslSocketConnector.java | 0 pom.xml | 2 +- 9 files changed, 3 insertions(+), 3 deletions(-) rename {metrics-jetty => metrics-jetty8}/pom.xml (91%) rename {metrics-jetty => metrics-jetty8}/src/main/java/com/yammer/metrics/jetty/InstrumentedBlockingChannelConnector.java (100%) rename {metrics-jetty => metrics-jetty8}/src/main/java/com/yammer/metrics/jetty/InstrumentedHandler.java (100%) rename {metrics-jetty => metrics-jetty8}/src/main/java/com/yammer/metrics/jetty/InstrumentedQueuedThreadPool.java (100%) rename {metrics-jetty => metrics-jetty8}/src/main/java/com/yammer/metrics/jetty/InstrumentedSelectChannelConnector.java (100%) rename {metrics-jetty => metrics-jetty8}/src/main/java/com/yammer/metrics/jetty/InstrumentedSocketConnector.java (100%) rename {metrics-jetty => metrics-jetty8}/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSelectChannelConnector.java (100%) rename {metrics-jetty => metrics-jetty8}/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSocketConnector.java (100%) diff --git a/metrics-jetty/pom.xml b/metrics-jetty8/pom.xml similarity index 91% rename from metrics-jetty/pom.xml rename to metrics-jetty8/pom.xml index 80e56fef88..df6160596d 100644 --- a/metrics-jetty/pom.xml +++ b/metrics-jetty8/pom.xml @@ -8,8 +8,8 @@ 3.0.0-SNAPSHOT - metrics-jetty - Metrics Jetty Support + metrics-jetty8 + Metrics Support for Jetty 8 bundle diff --git a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedBlockingChannelConnector.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedBlockingChannelConnector.java similarity index 100% rename from metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedBlockingChannelConnector.java rename to metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedBlockingChannelConnector.java diff --git a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedHandler.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedHandler.java similarity index 100% rename from metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedHandler.java rename to metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedHandler.java diff --git a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedQueuedThreadPool.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedQueuedThreadPool.java similarity index 100% rename from metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedQueuedThreadPool.java rename to metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedQueuedThreadPool.java diff --git a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSelectChannelConnector.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSelectChannelConnector.java similarity index 100% rename from metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSelectChannelConnector.java rename to metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSelectChannelConnector.java diff --git a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSocketConnector.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSocketConnector.java similarity index 100% rename from metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSocketConnector.java rename to metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSocketConnector.java diff --git a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSelectChannelConnector.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSelectChannelConnector.java similarity index 100% rename from metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSelectChannelConnector.java rename to metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSelectChannelConnector.java diff --git a/metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSocketConnector.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSocketConnector.java similarity index 100% rename from metrics-jetty/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSocketConnector.java rename to metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSocketConnector.java diff --git a/pom.xml b/pom.xml index fa62414bdb..832691cb2a 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ metrics-httpclient metrics-jdbi metrics-jersey - metrics-jetty + metrics-jetty8 metrics-jvm metrics-log4j metrics-logback From 8f5366451c6cd610f78abdada83f33a33e5d5e34 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 09:56:01 -0700 Subject: [PATCH 0027/2558] Revamped metrics-log4j. --- metrics-log4j/pom.xml | 2 +- .../metrics/log4j/InstrumentedAppender.java | 27 +++++++++---------- .../log4j/tests/InstrumentedAppenderTest.java | 23 ++++++++-------- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/metrics-log4j/pom.xml b/metrics-log4j/pom.xml index 4c79b9a932..078abcb5b2 100644 --- a/metrics-log4j/pom.xml +++ b/metrics-log4j/pom.xml @@ -16,7 +16,7 @@ com.yammer.metrics metrics-core - 2.2.0 + ${project.version} log4j diff --git a/metrics-log4j/src/main/java/com/yammer/metrics/log4j/InstrumentedAppender.java b/metrics-log4j/src/main/java/com/yammer/metrics/log4j/InstrumentedAppender.java index f0501079df..ae30f37d0d 100644 --- a/metrics-log4j/src/main/java/com/yammer/metrics/log4j/InstrumentedAppender.java +++ b/metrics-log4j/src/main/java/com/yammer/metrics/log4j/InstrumentedAppender.java @@ -1,14 +1,13 @@ package com.yammer.metrics.log4j; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Meter; -import com.yammer.metrics.core.MetricsRegistry; +import com.yammer.metrics.Meter; +import com.yammer.metrics.MetricRegistry; import org.apache.log4j.Appender; import org.apache.log4j.AppenderSkeleton; import org.apache.log4j.Level; import org.apache.log4j.spi.LoggingEvent; -import java.util.concurrent.TimeUnit; +import static com.yammer.metrics.MetricRegistry.name; /** * A Log4J {@link Appender} delegate which has seven meters, one for each logging level and one for @@ -23,19 +22,19 @@ public class InstrumentedAppender extends AppenderSkeleton { private final Meter error; private final Meter fatal; - public InstrumentedAppender() { - this(Metrics.defaultRegistry()); + public InstrumentedAppender(MetricRegistry registry) { + this(registry, null); } - public InstrumentedAppender(MetricsRegistry registry) { + public InstrumentedAppender(MetricRegistry registry, String name) { super(); - this.all = registry.newMeter(Appender.class, "all", "statements", TimeUnit.SECONDS); - this.trace = registry.newMeter(Appender.class, "trace", "statements", TimeUnit.SECONDS); - this.debug = registry.newMeter(Appender.class, "debug", "statements", TimeUnit.SECONDS); - this.info = registry.newMeter(Appender.class, "info", "statements", TimeUnit.SECONDS); - this.warn = registry.newMeter(Appender.class, "warn", "statements", TimeUnit.SECONDS); - this.error = registry.newMeter(Appender.class, "error", "statements", TimeUnit.SECONDS); - this.fatal = registry.newMeter(Appender.class, "fatal", "statements", TimeUnit.SECONDS); + this.all = registry.meter(name(Appender.class, name, "all")); + this.trace = registry.meter(name(Appender.class, name, "trace")); + this.debug = registry.meter(name(Appender.class, name, "debug")); + this.info = registry.meter(name(Appender.class, name, "info")); + this.warn = registry.meter(name(Appender.class, name, "warn")); + this.error = registry.meter(name(Appender.class, name, "error")); + this.fatal = registry.meter(name(Appender.class, name, "fatal")); } @Override diff --git a/metrics-log4j/src/test/java/com/yammer/metrics/log4j/tests/InstrumentedAppenderTest.java b/metrics-log4j/src/test/java/com/yammer/metrics/log4j/tests/InstrumentedAppenderTest.java index c58395f855..972796aa11 100644 --- a/metrics-log4j/src/test/java/com/yammer/metrics/log4j/tests/InstrumentedAppenderTest.java +++ b/metrics-log4j/src/test/java/com/yammer/metrics/log4j/tests/InstrumentedAppenderTest.java @@ -1,7 +1,7 @@ package com.yammer.metrics.log4j.tests; -import com.yammer.metrics.core.Meter; -import com.yammer.metrics.core.MetricsRegistry; +import com.yammer.metrics.Meter; +import com.yammer.metrics.MetricRegistry; import com.yammer.metrics.log4j.InstrumentedAppender; import org.apache.log4j.Appender; import org.apache.log4j.Level; @@ -9,8 +9,7 @@ import org.junit.Before; import org.junit.Test; -import java.util.concurrent.TimeUnit; - +import static com.yammer.metrics.MetricRegistry.name; import static org.mockito.Mockito.*; public class InstrumentedAppenderTest { @@ -31,14 +30,14 @@ public void setUp() throws Exception { this.event = mock(LoggingEvent.class); when(event.getLevel()).thenReturn(Level.INFO); - final MetricsRegistry registry = mock(MetricsRegistry.class); - when(registry.newMeter(Appender.class, "all", "statements", TimeUnit.SECONDS)).thenReturn(all); - when(registry.newMeter(Appender.class, "trace", "statements", TimeUnit.SECONDS)).thenReturn(trace); - when(registry.newMeter(Appender.class, "debug", "statements", TimeUnit.SECONDS)).thenReturn(debug); - when(registry.newMeter(Appender.class, "info", "statements", TimeUnit.SECONDS)).thenReturn(info); - when(registry.newMeter(Appender.class, "warn", "statements", TimeUnit.SECONDS)).thenReturn(warn); - when(registry.newMeter(Appender.class, "error", "statements", TimeUnit.SECONDS)).thenReturn(error); - when(registry.newMeter(Appender.class, "fatal", "statements", TimeUnit.SECONDS)).thenReturn(fatal); + final MetricRegistry registry = mock(MetricRegistry.class); + when(registry.meter(name(Appender.class, "all"))).thenReturn(all); + when(registry.meter(name(Appender.class, "trace"))).thenReturn(trace); + when(registry.meter(name(Appender.class, "debug"))).thenReturn(debug); + when(registry.meter(name(Appender.class, "info"))).thenReturn(info); + when(registry.meter(name(Appender.class, "warn"))).thenReturn(warn); + when(registry.meter(name(Appender.class, "error"))).thenReturn(error); + when(registry.meter(name(Appender.class, "fatal"))).thenReturn(fatal); this.instrumented = new InstrumentedAppender(registry); } From 2d5f9c6ed2161c0ef0d6e8813af8271c8c739612 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 09:58:57 -0700 Subject: [PATCH 0028/2558] Revamped metrics-logback. --- metrics-logback/pom.xml | 7 +----- .../metrics/logback/InstrumentedAppender.java | 25 +++++++++---------- .../tests/InstrumentedAppenderTest.java | 25 ++++++++----------- 3 files changed, 24 insertions(+), 33 deletions(-) diff --git a/metrics-logback/pom.xml b/metrics-logback/pom.xml index 71743c0ff8..2ab9966659 100644 --- a/metrics-logback/pom.xml +++ b/metrics-logback/pom.xml @@ -20,12 +20,7 @@ com.yammer.metrics metrics-core - 2.2.0 - - - ch.qos.logback - logback-core - ${logback.version} + ${project.version} ch.qos.logback diff --git a/metrics-logback/src/main/java/com/yammer/metrics/logback/InstrumentedAppender.java b/metrics-logback/src/main/java/com/yammer/metrics/logback/InstrumentedAppender.java index 6578e1e39b..faecb6a199 100644 --- a/metrics-logback/src/main/java/com/yammer/metrics/logback/InstrumentedAppender.java +++ b/metrics-logback/src/main/java/com/yammer/metrics/logback/InstrumentedAppender.java @@ -4,11 +4,10 @@ import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; import ch.qos.logback.core.AppenderBase; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Meter; -import com.yammer.metrics.core.MetricsRegistry; +import com.yammer.metrics.Meter; +import com.yammer.metrics.MetricRegistry; -import java.util.concurrent.TimeUnit; +import static com.yammer.metrics.MetricRegistry.name; /** * A Logback {@link AppenderBase} which has six meters, one for each logging level and one for the @@ -22,17 +21,17 @@ public class InstrumentedAppender extends AppenderBase { private final Meter warn; private final Meter error; - public InstrumentedAppender() { - this(Metrics.defaultRegistry()); + public InstrumentedAppender(MetricRegistry registry) { + this(registry, null); } - public InstrumentedAppender(MetricsRegistry registry) { - this.all = registry.newMeter(Appender.class, "all", "statements", TimeUnit.SECONDS); - this.trace = registry.newMeter(Appender.class, "trace", "statements", TimeUnit.SECONDS); - this.debug = registry.newMeter(Appender.class, "debug", "statements", TimeUnit.SECONDS); - this.info = registry.newMeter(Appender.class, "info", "statements", TimeUnit.SECONDS); - this.warn = registry.newMeter(Appender.class, "warn", "statements", TimeUnit.SECONDS); - this.error = registry.newMeter(Appender.class, "error", "statements", TimeUnit.SECONDS); + public InstrumentedAppender(MetricRegistry registry, String name) { + this.all = registry.meter(name(Appender.class, name, "all")); + this.trace = registry.meter(name(Appender.class, name, "trace")); + this.debug = registry.meter(name(Appender.class, name, "debug")); + this.info = registry.meter(name(Appender.class, name, "info")); + this.warn = registry.meter(name(Appender.class, name, "warn")); + this.error = registry.meter(name(Appender.class, name, "error")); } @Override diff --git a/metrics-logback/src/test/java/com/yammer/metrics/logback/tests/InstrumentedAppenderTest.java b/metrics-logback/src/test/java/com/yammer/metrics/logback/tests/InstrumentedAppenderTest.java index 83eedf463a..fcb230d87f 100644 --- a/metrics-logback/src/test/java/com/yammer/metrics/logback/tests/InstrumentedAppenderTest.java +++ b/metrics-logback/src/test/java/com/yammer/metrics/logback/tests/InstrumentedAppenderTest.java @@ -3,18 +3,15 @@ import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; -import com.yammer.metrics.core.Meter; -import com.yammer.metrics.core.MetricsRegistry; +import com.yammer.metrics.Meter; +import com.yammer.metrics.MetricRegistry; import com.yammer.metrics.logback.InstrumentedAppender; import org.junit.After; import org.junit.Before; import org.junit.Test; -import java.util.concurrent.TimeUnit; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static com.yammer.metrics.MetricRegistry.name; +import static org.mockito.Mockito.*; public class InstrumentedAppenderTest { private Meter all, trace, debug, info, warn, error; @@ -33,13 +30,13 @@ public void setUp() throws Exception { this.event = mock(ILoggingEvent.class); when(event.getLevel()).thenReturn(Level.INFO); - final MetricsRegistry registry = mock(MetricsRegistry.class); - when(registry.newMeter(Appender.class, "all", "statements", TimeUnit.SECONDS)).thenReturn(all); - when(registry.newMeter(Appender.class, "trace", "statements", TimeUnit.SECONDS)).thenReturn(trace); - when(registry.newMeter(Appender.class, "debug", "statements", TimeUnit.SECONDS)).thenReturn(debug); - when(registry.newMeter(Appender.class, "info", "statements", TimeUnit.SECONDS)).thenReturn(info); - when(registry.newMeter(Appender.class, "warn", "statements", TimeUnit.SECONDS)).thenReturn(warn); - when(registry.newMeter(Appender.class, "error", "statements", TimeUnit.SECONDS)).thenReturn(error); + final MetricRegistry registry = mock(MetricRegistry.class); + when(registry.meter(name(Appender.class, "all"))).thenReturn(all); + when(registry.meter(name(Appender.class, "trace"))).thenReturn(trace); + when(registry.meter(name(Appender.class, "debug"))).thenReturn(debug); + when(registry.meter(name(Appender.class, "info"))).thenReturn(info); + when(registry.meter(name(Appender.class, "warn"))).thenReturn(warn); + when(registry.meter(name(Appender.class, "error"))).thenReturn(error); this.instrumented = new InstrumentedAppender(registry); instrumented.start(); From 31bd94b79462a3094b1abc09e383a030451065c7 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 09:59:37 -0700 Subject: [PATCH 0029/2558] Clean up InstrumentedAppender a little. --- .../main/java/com/yammer/metrics/log4j/InstrumentedAppender.java | 1 - 1 file changed, 1 deletion(-) diff --git a/metrics-log4j/src/main/java/com/yammer/metrics/log4j/InstrumentedAppender.java b/metrics-log4j/src/main/java/com/yammer/metrics/log4j/InstrumentedAppender.java index ae30f37d0d..3bf8bf5ab7 100644 --- a/metrics-log4j/src/main/java/com/yammer/metrics/log4j/InstrumentedAppender.java +++ b/metrics-log4j/src/main/java/com/yammer/metrics/log4j/InstrumentedAppender.java @@ -27,7 +27,6 @@ public InstrumentedAppender(MetricRegistry registry) { } public InstrumentedAppender(MetricRegistry registry, String name) { - super(); this.all = registry.meter(name(Appender.class, name, "all")); this.trace = registry.meter(name(Appender.class, name, "trace")); this.debug = registry.meter(name(Appender.class, name, "debug")); From 7693e0c1f039c3eb15ef0cb3e55cd78a49a421f0 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 11:40:37 -0700 Subject: [PATCH 0030/2558] Added metrics-json. --- metrics-json/pom.xml | 29 +++ .../yammer/metrics/json/MetricsModule.java | 224 ++++++++++++++++++ .../metrics/json/tests/MetricsModuleTest.java | 201 ++++++++++++++++ pom.xml | 1 + 4 files changed, 455 insertions(+) create mode 100644 metrics-json/pom.xml create mode 100644 metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java create mode 100644 metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java diff --git a/metrics-json/pom.xml b/metrics-json/pom.xml new file mode 100644 index 0000000000..17b05dfa86 --- /dev/null +++ b/metrics-json/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + + com.yammer.metrics + metrics-parent + 3.0.0-SNAPSHOT + + + metrics-json + JSON Support for Metrics + bundle + + + + com.yammer.metrics + metrics-core + ${project.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + diff --git a/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java b/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java new file mode 100644 index 0000000000..4a8c0d808c --- /dev/null +++ b/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java @@ -0,0 +1,224 @@ +package com.yammer.metrics.json; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.module.SimpleSerializers; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import com.yammer.metrics.*; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +public class MetricsModule extends Module { + private static final Version VERSION = new Version(3, 0, 0, "", "com.yammer.metrics", "metrics-json"); + + private static class GaugeSerializer extends StdSerializer { + private GaugeSerializer() { + super(Gauge.class); + } + + @Override + public void serialize(Gauge gauge, + JsonGenerator json, + SerializerProvider provider) throws IOException { + final Object value = gauge.getValue(); + json.writeStartObject(); + json.writeObjectField("value", value); + json.writeEndObject(); + } + } + + private static class CounterSerializer extends StdSerializer { + private CounterSerializer() { + super(Counter.class); + } + + @Override + public void serialize(Counter counter, + JsonGenerator json, + SerializerProvider provider) throws IOException { + json.writeStartObject(); + json.writeNumberField("count", counter.getCount()); + json.writeEndObject(); + } + } + + private static class HistogramSerializer extends StdSerializer { + private final boolean showSamples; + + private HistogramSerializer(boolean showSamples) { + super(Histogram.class); + this.showSamples = showSamples; + } + + @Override + public void serialize(Histogram histogram, + JsonGenerator json, + SerializerProvider provider) throws IOException { + json.writeStartObject(); + json.writeNumberField("count", histogram.getCount()); + json.writeNumberField("max", histogram.getMax()); + json.writeNumberField("mean", histogram.getMean()); + json.writeNumberField("min", histogram.getMin()); + + final Snapshot snapshot = histogram.getSnapshot(); + json.writeNumberField("p50", snapshot.getMedian()); + json.writeNumberField("p75", snapshot.get75thPercentile()); + json.writeNumberField("p95", snapshot.get95thPercentile()); + json.writeNumberField("p98", snapshot.get98thPercentile()); + json.writeNumberField("p99", snapshot.get99thPercentile()); + json.writeNumberField("p999", snapshot.get999thPercentile()); + + if (showSamples) { + json.writeObjectField("values", snapshot.getValues()); + } + + json.writeNumberField("stddev", histogram.getStdDev()); + json.writeEndObject(); + } + } + + private static class MeterSerializer extends StdSerializer { + private final String rateUnit; + private final double rateFactor; + + public MeterSerializer(TimeUnit rateUnit) { + super(Meter.class); + this.rateFactor = rateUnit.toSeconds(1); + this.rateUnit = calculateRateUnit(rateUnit, "events"); + } + + @Override + public void serialize(Meter meter, + JsonGenerator json, + SerializerProvider provider) throws IOException { + json.writeStartObject(); + json.writeNumberField("count", meter.getCount()); + json.writeNumberField("m15_rate", meter.getOneMinuteRate() * rateFactor); + json.writeNumberField("m1_rate", meter.getFifteenMinuteRate() * rateFactor); + json.writeNumberField("m5_rate", meter.getFiveMinuteRate() * rateFactor); + json.writeNumberField("mean_rate", meter.getMeanRate() * rateFactor); + json.writeStringField("units", rateUnit); + json.writeEndObject(); + } + } + + private static class TimerSerializer extends StdSerializer { + private final String rateUnit; + private final double rateFactor; + private final String durationUnit; + private final double durationFactor; + private final boolean showSamples; + + private TimerSerializer(TimeUnit rateUnit, + TimeUnit durationUnit, + boolean showSamples) { + super(Timer.class); + this.rateUnit = calculateRateUnit(rateUnit, "calls"); + this.rateFactor = rateUnit.toSeconds(1); + this.durationUnit = durationUnit.toString().toLowerCase(Locale.US); + this.durationFactor = 1.0 / durationUnit.toNanos(1); + this.showSamples = showSamples; + } + + @Override + public void serialize(Timer timer, + JsonGenerator json, + SerializerProvider provider) throws IOException { + json.writeStartObject(); + json.writeNumberField("count", timer.getCount()); + json.writeNumberField("max", timer.getMax() * durationFactor); + json.writeNumberField("mean", timer.getMean() * durationFactor); + json.writeNumberField("min", timer.getMin() * durationFactor); + + final Snapshot snapshot = timer.getSnapshot(); + json.writeNumberField("p50", snapshot.getMedian() * durationFactor); + json.writeNumberField("p75", snapshot.get75thPercentile() * durationFactor); + json.writeNumberField("p95", snapshot.get95thPercentile() * durationFactor); + json.writeNumberField("p98", snapshot.get98thPercentile() * durationFactor); + json.writeNumberField("p99", snapshot.get99thPercentile() * durationFactor); + json.writeNumberField("p999", snapshot.get999thPercentile() * durationFactor); + + if (showSamples) { + final long[] values = snapshot.getValues(); + final double[] scaledValues = new double[values.length]; + for (int i = 0; i < values.length; i++) { + scaledValues[i] = values[i] * durationFactor; + } + json.writeObjectField("values", scaledValues); + } + + json.writeNumberField("stddev", timer.getStdDev() * durationFactor); + json.writeNumberField("m15_rate", timer.getOneMinuteRate() * rateFactor); + json.writeNumberField("m1_rate", timer.getFifteenMinuteRate() * rateFactor); + json.writeNumberField("m5_rate", timer.getFiveMinuteRate() * rateFactor); + json.writeNumberField("mean_rate", timer.getMeanRate() * rateFactor); + json.writeStringField("duration_units", durationUnit); + json.writeStringField("rate_units", rateUnit); + json.writeEndObject(); + } + } + + private static class MetricRegistrySerializer extends StdSerializer { + private MetricRegistrySerializer() { + super(MetricRegistry.class); + } + + @Override + public void serialize(MetricRegistry registry, + JsonGenerator json, + SerializerProvider provider) throws IOException { + json.writeStartObject(); + json.writeStringField("name", registry.getName()); + json.writeStringField("version", VERSION.toString()); + json.writeObjectField("gauges", registry.getGauges()); + json.writeObjectField("counters", registry.getCounters()); + json.writeObjectField("histograms", registry.getHistograms()); + json.writeObjectField("meters", registry.getMeters()); + json.writeObjectField("timers", registry.getTimers()); + json.writeEndObject(); + } + } + + private final TimeUnit rateUnit; + private final TimeUnit durationUnit; + private final boolean showSamples; + + public MetricsModule(TimeUnit rateUnit, TimeUnit durationUnit, boolean showSamples) { + this.rateUnit = rateUnit; + this.durationUnit = durationUnit; + this.showSamples = showSamples; + } + + @Override + public String getModuleName() { + return "metrics"; + } + + @Override + public Version version() { + return VERSION; + } + + @Override + public void setupModule(SetupContext context) { + context.addSerializers(new SimpleSerializers(Arrays.>asList( + new GaugeSerializer(), + new CounterSerializer(), + new HistogramSerializer(showSamples), + new MeterSerializer(rateUnit), + new TimerSerializer(rateUnit, durationUnit, showSamples), + new MetricRegistrySerializer() + ))); + } + + private static String calculateRateUnit(TimeUnit unit, String name) { + final String s = unit.toString().toLowerCase(Locale.US); + return name + '/' + s.substring(0, s.length() - 1); + } +} diff --git a/metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java b/metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java new file mode 100644 index 0000000000..04d3e3fb3d --- /dev/null +++ b/metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java @@ -0,0 +1,201 @@ +package com.yammer.metrics.json.tests; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.yammer.metrics.*; +import com.yammer.metrics.json.MetricsModule; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MetricsModuleTest { + private final ObjectMapper mapper = new ObjectMapper().registerModule( + new MetricsModule(TimeUnit.SECONDS, TimeUnit.MILLISECONDS, false)); + + @Test + public void serializesGauges() throws Exception { + final Gauge gauge = new Gauge() { + @Override + public Integer getValue() { + return 100; + } + }; + + assertThat(mapper.writeValueAsString(gauge)) + .isEqualTo("{\"value\":100}"); + } + + @Test + public void serializesCounters() throws Exception { + final Counter counter = mock(Counter.class); + when(counter.getCount()).thenReturn(100L); + + assertThat(mapper.writeValueAsString(counter)) + .isEqualTo("{\"count\":100}"); + } + + @Test + public void serializesHistograms() throws Exception { + final Histogram histogram = mock(Histogram.class); + when(histogram.getCount()).thenReturn(1L); + when(histogram.getMax()).thenReturn(2L); + when(histogram.getMean()).thenReturn(3.0); + when(histogram.getMin()).thenReturn(4L); + when(histogram.getStdDev()).thenReturn(5.0); + + final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMedian()).thenReturn(6.0); + when(snapshot.get75thPercentile()).thenReturn(7.0); + when(snapshot.get95thPercentile()).thenReturn(8.0); + when(snapshot.get98thPercentile()).thenReturn(9.0); + when(snapshot.get99thPercentile()).thenReturn(10.0); + when(snapshot.get999thPercentile()).thenReturn(11.0); + when(snapshot.getValues()).thenReturn(new long[]{ 1, 2, 3 }); + + when(histogram.getSnapshot()).thenReturn(snapshot); + + assertThat(mapper.writeValueAsString(histogram)) + .isEqualTo("{" + + "\"count\":1," + + "\"max\":2," + + "\"mean\":3.0," + + "\"min\":4," + + "\"p50\":6.0," + + "\"p75\":7.0," + + "\"p95\":8.0," + + "\"p98\":9.0," + + "\"p99\":10.0," + + "\"p999\":11.0," + + "\"stddev\":5.0}"); + + final ObjectMapper fullMapper = new ObjectMapper().registerModule( + new MetricsModule(TimeUnit.SECONDS, TimeUnit.MILLISECONDS, true)); + + assertThat(fullMapper.writeValueAsString(histogram)) + .isEqualTo("{" + + "\"count\":1," + + "\"max\":2," + + "\"mean\":3.0," + + "\"min\":4," + + "\"p50\":6.0," + + "\"p75\":7.0," + + "\"p95\":8.0," + + "\"p98\":9.0," + + "\"p99\":10.0," + + "\"p999\":11.0," + + "\"values\":[1,2,3]," + + "\"stddev\":5.0}"); + } + + @Test + public void serializesMeters() throws Exception { + final Meter meter = mock(Meter.class); + when(meter.getCount()).thenReturn(1L); + when(meter.getMeanRate()).thenReturn(2.0); + when(meter.getOneMinuteRate()).thenReturn(3.0); + when(meter.getFiveMinuteRate()).thenReturn(4.0); + when(meter.getFifteenMinuteRate()).thenReturn(5.0); + + assertThat(mapper.writeValueAsString(meter)) + .isEqualTo("{" + + "\"count\":1," + + "\"m15_rate\":3.0," + + "\"m1_rate\":5.0," + + "\"m5_rate\":4.0," + + "\"mean_rate\":2.0," + + "\"units\":\"events/second\"}"); + } + + @Test + public void serializesTimers() throws Exception { + final Timer timer = mock(Timer.class); + when(timer.getCount()).thenReturn(1L); + when(timer.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); + when(timer.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); + when(timer.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); + when(timer.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); + + when(timer.getMeanRate()).thenReturn(2.0); + when(timer.getOneMinuteRate()).thenReturn(3.0); + when(timer.getFiveMinuteRate()).thenReturn(4.0); + when(timer.getFifteenMinuteRate()).thenReturn(5.0); + + final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); + when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); + when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); + when(snapshot.get98thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(800)); + when(snapshot.get99thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(900)); + when(snapshot.get999thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(1000)); + + when(snapshot.getValues()).thenReturn(new long[]{ + TimeUnit.MILLISECONDS.toNanos(1), + TimeUnit.MILLISECONDS.toNanos(2), + TimeUnit.MILLISECONDS.toNanos(3) + }); + + when(timer.getSnapshot()).thenReturn(snapshot); + + assertThat(mapper.writeValueAsString(timer)) + .isEqualTo("{" + + "\"count\":1," + + "\"max\":100.0," + + "\"mean\":200.0," + + "\"min\":300.0," + + "\"p50\":500.0," + + "\"p75\":600.0," + + "\"p95\":700.0," + + "\"p98\":800.0," + + "\"p99\":900.0," + + "\"p999\":1000.0," + + "\"stddev\":400.0," + + "\"m15_rate\":3.0," + + "\"m1_rate\":5.0," + + "\"m5_rate\":4.0," + + "\"mean_rate\":2.0," + + "\"duration_units\":\"milliseconds\"," + + "\"rate_units\":\"calls/second\"}"); + + final ObjectMapper fullMapper = new ObjectMapper().registerModule( + new MetricsModule(TimeUnit.SECONDS, TimeUnit.MILLISECONDS, true)); + + assertThat(fullMapper.writeValueAsString(timer)) + .isEqualTo("{" + + "\"count\":1," + + "\"max\":100.0," + + "\"mean\":200.0," + + "\"min\":300.0," + + "\"p50\":500.0," + + "\"p75\":600.0," + + "\"p95\":700.0," + + "\"p98\":800.0," + + "\"p99\":900.0," + + "\"p999\":1000.0," + + "\"values\":[1.0,2.0,3.0]," + + "\"stddev\":400.0," + + "\"m15_rate\":3.0," + + "\"m1_rate\":5.0," + + "\"m5_rate\":4.0," + + "\"mean_rate\":2.0," + + "\"duration_units\":\"milliseconds\"," + + "\"rate_units\":\"calls/second\"}"); + } + + @Test + public void serializesMetricRegistries() throws Exception { + final MetricRegistry registry = new MetricRegistry("metrics"); + + assertThat(mapper.writeValueAsString(registry)) + .isEqualTo("{" + + "\"name\":\"metrics\"," + + "\"version\":\"3.0.0\"," + + "\"gauges\":{}," + + "\"counters\":{}," + + "\"histograms\":{}," + + "\"meters\":{}," + + "\"timers\":{}}"); + } +} diff --git a/pom.xml b/pom.xml index 832691cb2a..399429f925 100644 --- a/pom.xml +++ b/pom.xml @@ -26,6 +26,7 @@ metrics-jdbi metrics-jersey metrics-jetty8 + metrics-json metrics-jvm metrics-log4j metrics-logback From cc6c0022cae953a947f2eea8126f843fc32ac838 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 12:40:26 -0700 Subject: [PATCH 0031/2558] Test snapshot dumping. --- .../java/com/yammer/metrics/Snapshot.java | 19 ++++++++----------- .../yammer/metrics/tests/SnapshotTest.java | 11 +++++++++++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java b/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java index 3ed8713880..ec09ead4c5 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java @@ -1,8 +1,6 @@ package com.yammer.metrics; -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; +import java.io.*; import java.util.*; import static java.lang.Math.floor; @@ -147,19 +145,18 @@ public long[] getValues() { } /** - * Writes the values of the sample to the given file. + * Writes the values of the sample to the given stream. * - * @param output the file to which the values will be written - * @throws IOException if there is an error writing the values + * @param output an output stream */ - public void dump(File output) throws IOException { - final PrintWriter writer = new PrintWriter(output); + public void dump(OutputStream output) { + final PrintWriter out = new PrintWriter(output); try { - for (double value : values) { - writer.printf("%f\n", value); + for (long value : values) { + out.printf("%d%n", value); } } finally { - writer.close(); + out.close(); } } } diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java index 29a8163ca4..be834ed79f 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java @@ -3,6 +3,7 @@ import com.yammer.metrics.Snapshot; import org.junit.Test; +import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.List; @@ -108,4 +109,14 @@ public void worksWithOverestimatedCollections() throws Exception { assertThat(other.getValues()) .containsOnly(1, 2, 3, 4, 5); } + + @Test + public void dumpsToAStream() throws Exception { + final ByteArrayOutputStream output = new ByteArrayOutputStream(); + + snapshot.dump(output); + + assertThat(output.toString()) + .isEqualTo(String.format("1%n2%n3%n4%n5%n")); + } } From 8e9df092c39eb696bb84ae0e743d0fae2c7ed35e Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 13:39:45 -0700 Subject: [PATCH 0032/2558] Flesh out CsvReporter. --- .../java/com/yammer/metrics/CsvReporter.java | 155 +++++++++++++++++- 1 file changed, 153 insertions(+), 2 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java index 3458214cb9..37783090b8 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java @@ -1,17 +1,51 @@ package com.yammer.metrics; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Locale; +import java.util.Map; import java.util.SortedMap; +import java.util.concurrent.TimeUnit; -// TODO: 3/10/13 -- implement CsvReporter +// TODO: 3/12/13 -- test and document CsvReporter public class CsvReporter extends AbstractPollingReporter { + private static final Logger LOGGER = LoggerFactory.getLogger(CsvReporter.class); + + private final File directory; + private final Locale locale; + private final Clock clock; + private final double durationFactor; + private final String durationUnit; + private final double rateFactor; + private final String rateUnit; + /** * Creates a new {@link CsvReporter} instance. * * @param registry the {@link MetricRegistry} containing the metrics this reporter will report + * @param directory the directory in which CSV files will be created + * @param locale the locale to use for formatting */ - protected CsvReporter(MetricRegistry registry) { + public CsvReporter(MetricRegistry registry, + File directory, + Locale locale, + TimeUnit rateUnit, + TimeUnit durationUnit, + Clock clock) { super(registry, "csv-reporter"); + this.directory = directory; + this.locale = locale; + this.clock = clock; + this.rateFactor = rateUnit.toSeconds(1); + this.rateUnit = calculateRateUnit(rateUnit); + this.durationFactor = 1.0 / durationUnit.toNanos(1); + this.durationUnit = durationUnit.toString().toLowerCase(Locale.US); } @Override @@ -20,5 +54,122 @@ public void report(SortedMap gauges, SortedMap histograms, SortedMap meters, SortedMap timers) { + final long timestamp = TimeUnit.MILLISECONDS.toSeconds(clock.getTime()); + + for (Map.Entry entry : gauges.entrySet()) { + reportGauge(timestamp, entry.getKey(), entry.getValue()); + } + + for (Map.Entry entry : counters.entrySet()) { + reportCounter(timestamp, entry.getKey(), entry.getValue()); + } + + for (Map.Entry entry : histograms.entrySet()) { + reportHistogram(timestamp, entry.getKey(), entry.getValue()); + } + + for (Map.Entry entry : meters.entrySet()) { + reportMeter(timestamp, entry.getKey(), entry.getValue()); + } + + for (Map.Entry entry : timers.entrySet()) { + reportTimer(timestamp, entry.getKey(), entry.getValue()); + } + } + + private void reportTimer(long timestamp, String name, Timer timer) { + final Snapshot snapshot = timer.getSnapshot(); + + report(timestamp, + name, + "count,max,mean,min,stddev,p50,p75,p95,p98,p99,p999,mean_rate,m1_rate,m5_rate,m15_rate,rate_unit,duration_unit", + "%d,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,calls/%s,%s", + timer.getCount(), + timer.getMax() * durationFactor, + timer.getMean() * durationFactor, + timer.getMin() * durationFactor, + timer.getStdDev() * durationFactor, + snapshot.getMedian() * durationFactor, + snapshot.get75thPercentile() * durationFactor, + snapshot.get95thPercentile() * durationFactor, + snapshot.get98thPercentile() * durationFactor, + snapshot.get99thPercentile() * durationFactor, + snapshot.get999thPercentile() * durationFactor, + timer.getMeanRate() * rateFactor, + timer.getOneMinuteRate() * rateFactor, + timer.getFiveMinuteRate() * rateFactor, + timer.getFifteenMinuteRate() * rateFactor, + rateUnit, + durationUnit); + } + + private void reportMeter(long timestamp, String name, Meter meter) { + report(timestamp, + name, + "count,mean_rate,m1_rate,m5_rate,m15_rate,rate_unit", + "%d,%f,%f,%f,%f,events/%s", + meter.getCount(), + meter.getMeanRate() * rateFactor, + meter.getOneMinuteRate() * rateFactor, + meter.getFiveMinuteRate() * rateFactor, + meter.getFifteenMinuteRate() * rateFactor, + rateUnit); + } + + private void reportHistogram(long timestamp, String name, Histogram histogram) { + final Snapshot snapshot = histogram.getSnapshot(); + + report(timestamp, + name, + "count,max,mean,min,stddev,p50,p75,p95,p98,p99,p999", + "%d,%d,%f,%d,%f,%f,%f,%f,%f,%f,%f", + histogram.getCount(), + histogram.getMax(), + histogram.getMean(), + histogram.getMin(), + histogram.getStdDev(), + snapshot.getMedian(), + snapshot.get75thPercentile(), + snapshot.get95thPercentile(), + snapshot.get98thPercentile(), + snapshot.get99thPercentile(), + snapshot.get999thPercentile()); + } + + private void reportCounter(long timestamp, String name, Counter counter) { + report(timestamp, name, "count", "%d", counter.getCount()); + } + + private void reportGauge(long timestamp, String name, Gauge gauge) { + report(timestamp, name, "value", "%s", gauge.getValue()); + } + + private void report(long timestamp, String name, String header, String line, Object... values) { + try { + final File file = new File(directory, sanitize(name) + ".csv"); + final boolean fileAlreadyExists = file.exists(); + if (fileAlreadyExists || file.createNewFile()) { + final PrintWriter out = new PrintWriter(new FileOutputStream(file, true)); + try { + if (!fileAlreadyExists) { + out.println("t," + header); + } + out.printf(locale, String.format(locale, "%d,%s%n", timestamp, line), values); + } finally { + out.close(); + } + } + } catch (IOException e) { + LOGGER.warn("Error writing to {}", name, e); + } + } + + private String calculateRateUnit(TimeUnit unit) { + final String s = unit.toString().toLowerCase(Locale.US); + return s.substring(0, s.length() - 1); + } + + protected String sanitize(String name) { + return name; } } From 3143b1942ee71c80f8b9558509723cc27df30de6 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 15:06:08 -0700 Subject: [PATCH 0033/2558] Rename metric-servlet to metric-servlets. --- {metrics-servlet => metrics-servlets}/pom.xml | 2 +- .../src/main/java/com/yammer/metrics/servlet/AdminServlet.java | 0 .../java/com/yammer/metrics/servlet/HealthCheckServlet.java | 0 .../main/java/com/yammer/metrics/servlet/MetricsServlet.java | 0 .../src/main/java/com/yammer/metrics/servlet/PingServlet.java | 0 .../main/java/com/yammer/metrics/servlet/ThreadDumpServlet.java | 0 .../com/yammer/metrics/servlet/experiments/ExampleServer.java | 0 .../java/com/yammer/metrics/servlet/tests/AdminServletTest.java | 0 .../yammer/metrics/servlet/tests/HealthCheckServletTest.java | 0 .../com/yammer/metrics/servlet/tests/MetricsServletTest.java | 0 .../java/com/yammer/metrics/servlet/tests/PingServletTest.java | 0 .../com/yammer/metrics/servlet/tests/ThreadDumpServletTest.java | 0 pom.xml | 2 +- 13 files changed, 2 insertions(+), 2 deletions(-) rename {metrics-servlet => metrics-servlets}/pom.xml (97%) rename {metrics-servlet => metrics-servlets}/src/main/java/com/yammer/metrics/servlet/AdminServlet.java (100%) rename {metrics-servlet => metrics-servlets}/src/main/java/com/yammer/metrics/servlet/HealthCheckServlet.java (100%) rename {metrics-servlet => metrics-servlets}/src/main/java/com/yammer/metrics/servlet/MetricsServlet.java (100%) rename {metrics-servlet => metrics-servlets}/src/main/java/com/yammer/metrics/servlet/PingServlet.java (100%) rename {metrics-servlet => metrics-servlets}/src/main/java/com/yammer/metrics/servlet/ThreadDumpServlet.java (100%) rename {metrics-servlet => metrics-servlets}/src/test/java/com/yammer/metrics/servlet/experiments/ExampleServer.java (100%) rename {metrics-servlet => metrics-servlets}/src/test/java/com/yammer/metrics/servlet/tests/AdminServletTest.java (100%) rename {metrics-servlet => metrics-servlets}/src/test/java/com/yammer/metrics/servlet/tests/HealthCheckServletTest.java (100%) rename {metrics-servlet => metrics-servlets}/src/test/java/com/yammer/metrics/servlet/tests/MetricsServletTest.java (100%) rename {metrics-servlet => metrics-servlets}/src/test/java/com/yammer/metrics/servlet/tests/PingServletTest.java (100%) rename {metrics-servlet => metrics-servlets}/src/test/java/com/yammer/metrics/servlet/tests/ThreadDumpServletTest.java (100%) diff --git a/metrics-servlet/pom.xml b/metrics-servlets/pom.xml similarity index 97% rename from metrics-servlet/pom.xml rename to metrics-servlets/pom.xml index db5d61eada..32878aae21 100644 --- a/metrics-servlet/pom.xml +++ b/metrics-servlets/pom.xml @@ -8,7 +8,7 @@ 3.0.0-SNAPSHOT - metrics-servlet + metrics-servlets Metrics Servlet bundle diff --git a/metrics-servlet/src/main/java/com/yammer/metrics/servlet/AdminServlet.java b/metrics-servlets/src/main/java/com/yammer/metrics/servlet/AdminServlet.java similarity index 100% rename from metrics-servlet/src/main/java/com/yammer/metrics/servlet/AdminServlet.java rename to metrics-servlets/src/main/java/com/yammer/metrics/servlet/AdminServlet.java diff --git a/metrics-servlet/src/main/java/com/yammer/metrics/servlet/HealthCheckServlet.java b/metrics-servlets/src/main/java/com/yammer/metrics/servlet/HealthCheckServlet.java similarity index 100% rename from metrics-servlet/src/main/java/com/yammer/metrics/servlet/HealthCheckServlet.java rename to metrics-servlets/src/main/java/com/yammer/metrics/servlet/HealthCheckServlet.java diff --git a/metrics-servlet/src/main/java/com/yammer/metrics/servlet/MetricsServlet.java b/metrics-servlets/src/main/java/com/yammer/metrics/servlet/MetricsServlet.java similarity index 100% rename from metrics-servlet/src/main/java/com/yammer/metrics/servlet/MetricsServlet.java rename to metrics-servlets/src/main/java/com/yammer/metrics/servlet/MetricsServlet.java diff --git a/metrics-servlet/src/main/java/com/yammer/metrics/servlet/PingServlet.java b/metrics-servlets/src/main/java/com/yammer/metrics/servlet/PingServlet.java similarity index 100% rename from metrics-servlet/src/main/java/com/yammer/metrics/servlet/PingServlet.java rename to metrics-servlets/src/main/java/com/yammer/metrics/servlet/PingServlet.java diff --git a/metrics-servlet/src/main/java/com/yammer/metrics/servlet/ThreadDumpServlet.java b/metrics-servlets/src/main/java/com/yammer/metrics/servlet/ThreadDumpServlet.java similarity index 100% rename from metrics-servlet/src/main/java/com/yammer/metrics/servlet/ThreadDumpServlet.java rename to metrics-servlets/src/main/java/com/yammer/metrics/servlet/ThreadDumpServlet.java diff --git a/metrics-servlet/src/test/java/com/yammer/metrics/servlet/experiments/ExampleServer.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlet/experiments/ExampleServer.java similarity index 100% rename from metrics-servlet/src/test/java/com/yammer/metrics/servlet/experiments/ExampleServer.java rename to metrics-servlets/src/test/java/com/yammer/metrics/servlet/experiments/ExampleServer.java diff --git a/metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/AdminServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/AdminServletTest.java similarity index 100% rename from metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/AdminServletTest.java rename to metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/AdminServletTest.java diff --git a/metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/HealthCheckServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/HealthCheckServletTest.java similarity index 100% rename from metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/HealthCheckServletTest.java rename to metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/HealthCheckServletTest.java diff --git a/metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/MetricsServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/MetricsServletTest.java similarity index 100% rename from metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/MetricsServletTest.java rename to metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/MetricsServletTest.java diff --git a/metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/PingServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/PingServletTest.java similarity index 100% rename from metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/PingServletTest.java rename to metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/PingServletTest.java diff --git a/metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/ThreadDumpServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/ThreadDumpServletTest.java similarity index 100% rename from metrics-servlet/src/test/java/com/yammer/metrics/servlet/tests/ThreadDumpServletTest.java rename to metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/ThreadDumpServletTest.java diff --git a/pom.xml b/pom.xml index 399429f925..0870074b96 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ metrics-jvm metrics-log4j metrics-logback - metrics-servlet + metrics-servlets metrics-web From 8a0045f75690fe67c3f7f75ac2d46e62326735c2 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 15:08:47 -0700 Subject: [PATCH 0034/2558] Rename metrics-web to metrics-servlet. --- {metrics-web => metrics-servlet}/pom.xml | 2 +- .../java/com/yammer/metrics/web/DefaultWebappMetricsFilter.java | 0 .../main/java/com/yammer/metrics/web/WebappMetricsFilter.java | 0 pom.xml | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) rename {metrics-web => metrics-servlet}/pom.xml (95%) rename {metrics-web => metrics-servlet}/src/main/java/com/yammer/metrics/web/DefaultWebappMetricsFilter.java (100%) rename {metrics-web => metrics-servlet}/src/main/java/com/yammer/metrics/web/WebappMetricsFilter.java (100%) diff --git a/metrics-web/pom.xml b/metrics-servlet/pom.xml similarity index 95% rename from metrics-web/pom.xml rename to metrics-servlet/pom.xml index 476ca261e6..b5a2a88b76 100644 --- a/metrics-web/pom.xml +++ b/metrics-servlet/pom.xml @@ -10,7 +10,7 @@ 3.0.0-SNAPSHOT - metrics-web + metrics-servlet Metrics Web Application Support bundle diff --git a/metrics-web/src/main/java/com/yammer/metrics/web/DefaultWebappMetricsFilter.java b/metrics-servlet/src/main/java/com/yammer/metrics/web/DefaultWebappMetricsFilter.java similarity index 100% rename from metrics-web/src/main/java/com/yammer/metrics/web/DefaultWebappMetricsFilter.java rename to metrics-servlet/src/main/java/com/yammer/metrics/web/DefaultWebappMetricsFilter.java diff --git a/metrics-web/src/main/java/com/yammer/metrics/web/WebappMetricsFilter.java b/metrics-servlet/src/main/java/com/yammer/metrics/web/WebappMetricsFilter.java similarity index 100% rename from metrics-web/src/main/java/com/yammer/metrics/web/WebappMetricsFilter.java rename to metrics-servlet/src/main/java/com/yammer/metrics/web/WebappMetricsFilter.java diff --git a/pom.xml b/pom.xml index 0870074b96..12455e3cd4 100644 --- a/pom.xml +++ b/pom.xml @@ -30,8 +30,8 @@ metrics-jvm metrics-log4j metrics-logback + metrics-servlet metrics-servlets - metrics-web From a0e8da8f6922987e5d04a458834f1fc45d102718 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 15:11:20 -0700 Subject: [PATCH 0035/2558] Upgrade to Hamcrest 1.3. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 12455e3cd4..86623a3a16 100644 --- a/pom.xml +++ b/pom.xml @@ -107,7 +107,7 @@ org.hamcrest hamcrest-all - 1.1 + 1.3 test From 25e9074dd3dc43992dc5d85aac52c6a5538079a8 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 15:14:13 -0700 Subject: [PATCH 0036/2558] Replace old usages of Hamcrest. --- .../yammer/metrics/tests/RatioGaugeTest.java | 23 +++++++++---------- .../tests/InstrumentedHttpClientTest.java | 8 +++---- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/RatioGaugeTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/RatioGaugeTest.java index 09a334d32b..091deb736a 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/RatioGaugeTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/RatioGaugeTest.java @@ -3,8 +3,7 @@ import com.yammer.metrics.RatioGauge; import org.junit.Test; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; +import static org.fest.assertions.api.Assertions.assertThat; public class RatioGaugeTest { @Test @@ -16,8 +15,8 @@ protected Ratio getRatio() { } }; - assertThat(regular.getValue(), - is(0.5)); + assertThat(regular.getValue()) + .isEqualTo(0.5); } @Test @@ -29,8 +28,8 @@ protected Ratio getRatio() { } }; - assertThat(divByZero.getValue(), - is(Double.NaN)); + assertThat(divByZero.getValue()) + .isNaN(); } @Test @@ -41,9 +40,9 @@ protected Ratio getRatio() { return Ratio.of(10, Double.POSITIVE_INFINITY); } }; - - assertThat(infinite.getValue(), - is(Double.NaN)); + + assertThat(infinite.getValue()) + .isNaN(); } @Test @@ -54,8 +53,8 @@ protected Ratio getRatio() { return Ratio.of(10, Double.NaN); } }; - - assertThat(nan.getValue(), - is(Double.NaN)); + + assertThat(nan.getValue()) + .isNaN(); } } diff --git a/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/tests/InstrumentedHttpClientTest.java b/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/tests/InstrumentedHttpClientTest.java index 645e77b288..6a7b8564eb 100644 --- a/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/tests/InstrumentedHttpClientTest.java +++ b/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/tests/InstrumentedHttpClientTest.java @@ -6,9 +6,7 @@ import org.apache.http.client.HttpClient; import org.junit.Test; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; +import static org.fest.assertions.api.Assertions.assertThat; public class InstrumentedHttpClientTest { private final MetricRegistry registry = new MetricRegistry("test"); @@ -17,7 +15,7 @@ public class InstrumentedHttpClientTest { @Test public void hasAnInstrumentedConnectionManager() throws Exception { - assertThat(client.getConnectionManager(), - is(instanceOf(InstrumentedClientConnManager.class))); + assertThat(client.getConnectionManager()) + .isInstanceOf(InstrumentedClientConnManager.class); } } From 9caa4dd11b6d51b67ff02567939dc6a8110d2472 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 15:14:51 -0700 Subject: [PATCH 0037/2558] Rename com.yammer.metrics.web to com.yammer.metrics.servlet. --- docs/source/manual/webapps.rst | 2 +- .../{web => servlet}/DefaultWebappMetricsFilter.java | 6 +++--- .../metrics/{web => servlet}/WebappMetricsFilter.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename metrics-servlet/src/main/java/com/yammer/metrics/{web => servlet}/DefaultWebappMetricsFilter.java (89%) rename metrics-servlet/src/main/java/com/yammer/metrics/{web => servlet}/WebappMetricsFilter.java (98%) diff --git a/docs/source/manual/webapps.rst b/docs/source/manual/webapps.rst index bb72f7788b..cf677a2419 100644 --- a/docs/source/manual/webapps.rst +++ b/docs/source/manual/webapps.rst @@ -12,7 +12,7 @@ for the number of active requests, and a timer for request duration. You can use webappMetricsFilter - com.yammer.metrics.web.DefaultWebappMetricsFilter + com.yammer.metrics.servlet.DefaultWebappMetricsFilter webappMetricsFilter diff --git a/metrics-servlet/src/main/java/com/yammer/metrics/web/DefaultWebappMetricsFilter.java b/metrics-servlet/src/main/java/com/yammer/metrics/servlet/DefaultWebappMetricsFilter.java similarity index 89% rename from metrics-servlet/src/main/java/com/yammer/metrics/web/DefaultWebappMetricsFilter.java rename to metrics-servlet/src/main/java/com/yammer/metrics/servlet/DefaultWebappMetricsFilter.java index edb801cd44..d598361990 100644 --- a/metrics-servlet/src/main/java/com/yammer/metrics/web/DefaultWebappMetricsFilter.java +++ b/metrics-servlet/src/main/java/com/yammer/metrics/servlet/DefaultWebappMetricsFilter.java @@ -1,15 +1,15 @@ -package com.yammer.metrics.web; +package com.yammer.metrics.servlet; import java.util.HashMap; import java.util.Map; /** * Implementation of the {@link WebappMetricsFilter} which provides a default set of response codes - * to capture information about.

Use it in your web.xml like this:

+ * to capture information about.

Use it in your servlet.xml like this:

*
{@code
  * 
  *     webappMetricsFilter
- *     com.yammer.metrics.web.DefaultWebappMetricsFilter
+ *     com.yammer.metrics.servlet.DefaultWebappMetricsFilter
  * 
  * 
  *     webappMetricsFilter
diff --git a/metrics-servlet/src/main/java/com/yammer/metrics/web/WebappMetricsFilter.java b/metrics-servlet/src/main/java/com/yammer/metrics/servlet/WebappMetricsFilter.java
similarity index 98%
rename from metrics-servlet/src/main/java/com/yammer/metrics/web/WebappMetricsFilter.java
rename to metrics-servlet/src/main/java/com/yammer/metrics/servlet/WebappMetricsFilter.java
index f252cf6763..45756d2aee 100644
--- a/metrics-servlet/src/main/java/com/yammer/metrics/web/WebappMetricsFilter.java
+++ b/metrics-servlet/src/main/java/com/yammer/metrics/servlet/WebappMetricsFilter.java
@@ -1,4 +1,4 @@
-package com.yammer.metrics.web;
+package com.yammer.metrics.servlet;
 
 import com.yammer.metrics.Counter;
 import com.yammer.metrics.Meter;
@@ -76,7 +76,7 @@ private MetricRegistry getMetricsFactory(FilterConfig filterConfig) {
             metricsRegistry = (MetricRegistry) o;
         } else {
             // TODO: 3/10/13  -- figure out how to coordinate on registry names
-            metricsRegistry = new MetricRegistry("web");
+            metricsRegistry = new MetricRegistry("servlet");
         }
         return metricsRegistry;
     }

From b7e7461b6742f7502b087f7de3718c93d2ed4f26 Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Tue, 12 Mar 2013 15:15:12 -0700
Subject: [PATCH 0038/2558] Don't need to exclude Scala any more.

---
 findbugs-exclude.xml | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/findbugs-exclude.xml b/findbugs-exclude.xml
index 20644f907e..4eb91a9923 100644
--- a/findbugs-exclude.xml
+++ b/findbugs-exclude.xml
@@ -1,8 +1,5 @@
 
 
-    
-    
-
     
     
         

From 261c25873e71983afece65ffff439f6256250781 Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Tue, 12 Mar 2013 15:16:30 -0700
Subject: [PATCH 0039/2558] Rename com.yammer.metrics.jetty to jetty8.

---
 .../{jetty => jetty8}/InstrumentedBlockingChannelConnector.java | 2 +-
 .../yammer/metrics/{jetty => jetty8}/InstrumentedHandler.java   | 2 +-
 .../metrics/{jetty => jetty8}/InstrumentedQueuedThreadPool.java | 2 +-
 .../{jetty => jetty8}/InstrumentedSelectChannelConnector.java   | 2 +-
 .../metrics/{jetty => jetty8}/InstrumentedSocketConnector.java  | 2 +-
 .../InstrumentedSslSelectChannelConnector.java                  | 2 +-
 .../{jetty => jetty8}/InstrumentedSslSocketConnector.java       | 2 +-
 7 files changed, 7 insertions(+), 7 deletions(-)
 rename metrics-jetty8/src/main/java/com/yammer/metrics/{jetty => jetty8}/InstrumentedBlockingChannelConnector.java (98%)
 rename metrics-jetty8/src/main/java/com/yammer/metrics/{jetty => jetty8}/InstrumentedHandler.java (99%)
 rename metrics-jetty8/src/main/java/com/yammer/metrics/{jetty => jetty8}/InstrumentedQueuedThreadPool.java (96%)
 rename metrics-jetty8/src/main/java/com/yammer/metrics/{jetty => jetty8}/InstrumentedSelectChannelConnector.java (98%)
 rename metrics-jetty8/src/main/java/com/yammer/metrics/{jetty => jetty8}/InstrumentedSocketConnector.java (98%)
 rename metrics-jetty8/src/main/java/com/yammer/metrics/{jetty => jetty8}/InstrumentedSslSelectChannelConnector.java (98%)
 rename metrics-jetty8/src/main/java/com/yammer/metrics/{jetty => jetty8}/InstrumentedSslSocketConnector.java (98%)

diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedBlockingChannelConnector.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedBlockingChannelConnector.java
similarity index 98%
rename from metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedBlockingChannelConnector.java
rename to metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedBlockingChannelConnector.java
index 684bac015c..45ed085f9a 100644
--- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedBlockingChannelConnector.java
+++ b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedBlockingChannelConnector.java
@@ -1,4 +1,4 @@
-package com.yammer.metrics.jetty;
+package com.yammer.metrics.jetty8;
 
 import com.yammer.metrics.*;
 import org.eclipse.jetty.io.Connection;
diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedHandler.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java
similarity index 99%
rename from metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedHandler.java
rename to metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java
index 969fd28bb0..489eea53b3 100644
--- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedHandler.java
+++ b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java
@@ -1,4 +1,4 @@
-package com.yammer.metrics.jetty;
+package com.yammer.metrics.jetty8;
 
 import com.yammer.metrics.*;
 import org.eclipse.jetty.continuation.Continuation;
diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedQueuedThreadPool.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedQueuedThreadPool.java
similarity index 96%
rename from metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedQueuedThreadPool.java
rename to metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedQueuedThreadPool.java
index 38e5e358c5..698cd005c4 100644
--- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedQueuedThreadPool.java
+++ b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedQueuedThreadPool.java
@@ -1,4 +1,4 @@
-package com.yammer.metrics.jetty;
+package com.yammer.metrics.jetty8;
 
 import com.yammer.metrics.Gauge;
 import com.yammer.metrics.MetricRegistry;
diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSelectChannelConnector.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSelectChannelConnector.java
similarity index 98%
rename from metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSelectChannelConnector.java
rename to metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSelectChannelConnector.java
index 6723e99c04..5af1028860 100644
--- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSelectChannelConnector.java
+++ b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSelectChannelConnector.java
@@ -1,4 +1,4 @@
-package com.yammer.metrics.jetty;
+package com.yammer.metrics.jetty8;
 
 import com.yammer.metrics.*;
 import org.eclipse.jetty.io.Connection;
diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSocketConnector.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSocketConnector.java
similarity index 98%
rename from metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSocketConnector.java
rename to metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSocketConnector.java
index 58abdb6f6a..63df4d791a 100644
--- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSocketConnector.java
+++ b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSocketConnector.java
@@ -1,4 +1,4 @@
-package com.yammer.metrics.jetty;
+package com.yammer.metrics.jetty8;
 
 import com.yammer.metrics.*;
 import org.eclipse.jetty.io.Connection;
diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSelectChannelConnector.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSslSelectChannelConnector.java
similarity index 98%
rename from metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSelectChannelConnector.java
rename to metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSslSelectChannelConnector.java
index 76ca9ac6f2..0055efd795 100644
--- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSelectChannelConnector.java
+++ b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSslSelectChannelConnector.java
@@ -1,4 +1,4 @@
-package com.yammer.metrics.jetty;
+package com.yammer.metrics.jetty8;
 
 import com.yammer.metrics.*;
 import org.eclipse.jetty.io.Connection;
diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSocketConnector.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSslSocketConnector.java
similarity index 98%
rename from metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSocketConnector.java
rename to metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSslSocketConnector.java
index 6459acb5dd..8473ba1fc5 100644
--- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty/InstrumentedSslSocketConnector.java
+++ b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSslSocketConnector.java
@@ -1,4 +1,4 @@
-package com.yammer.metrics.jetty;
+package com.yammer.metrics.jetty8;
 
 import com.yammer.metrics.*;
 import org.eclipse.jetty.io.Connection;

From ef35f3a1580062cb5631434813b0ca1820540393 Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Tue, 12 Mar 2013 15:39:29 -0700
Subject: [PATCH 0040/2558] Add ThreadDump.

---
 .../com/yammer/metrics/jvm/ThreadDump.java    | 91 +++++++++++++++++++
 .../metrics/jvm/tests/ThreadDumpTest.java     | 52 +++++++++++
 2 files changed, 143 insertions(+)
 create mode 100644 metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDump.java
 create mode 100644 metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadDumpTest.java

diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDump.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDump.java
new file mode 100644
index 0000000000..bb78fc28a4
--- /dev/null
+++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDump.java
@@ -0,0 +1,91 @@
+package com.yammer.metrics.jvm;
+
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.lang.management.LockInfo;
+import java.lang.management.MonitorInfo;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+
+/**
+ * A convenience class for getting a thread dump.
+ */
+public class ThreadDump {
+    private final ThreadMXBean threadMXBean;
+
+    public ThreadDump(ThreadMXBean threadMXBean) {
+        this.threadMXBean = threadMXBean;
+    }
+
+    /**
+     * Dumps all of the threads' current information to an output stream.
+     *
+     * @param out an output stream
+     */
+    public void dump(OutputStream out) {
+        final ThreadInfo[] threads = this.threadMXBean.dumpAllThreads(true, true);
+        final PrintWriter writer = new PrintWriter(out, true);
+
+        for (int ti = threads.length - 1; ti >= 0; ti--) {
+            final ThreadInfo t = threads[ti];
+            writer.printf("%s id=%d state=%s",
+                          t.getThreadName(),
+                          t.getThreadId(),
+                          t.getThreadState());
+            final LockInfo lock = t.getLockInfo();
+            if (lock != null && t.getThreadState() != Thread.State.BLOCKED) {
+                writer.printf("%n    - waiting on <0x%08x> (a %s)",
+                              lock.getIdentityHashCode(),
+                              lock.getClassName());
+                writer.printf("%n    - locked <0x%08x> (a %s)",
+                              lock.getIdentityHashCode(),
+                              lock.getClassName());
+            } else if (lock != null && t.getThreadState() == Thread.State.BLOCKED) {
+                writer.printf("%n    - waiting to lock <0x%08x> (a %s)",
+                              lock.getIdentityHashCode(),
+                              lock.getClassName());
+            }
+
+            if (t.isSuspended()) {
+                writer.print(" (suspended)");
+            }
+
+            if (t.isInNative()) {
+                writer.print(" (running in native)");
+            }
+
+            writer.println();
+            if (t.getLockOwnerName() != null) {
+                writer.printf("     owned by %s id=%d%n", t.getLockOwnerName(), t.getLockOwnerId());
+            }
+
+            final StackTraceElement[] elements = t.getStackTrace();
+            final MonitorInfo[] monitors = t.getLockedMonitors();
+
+            for (int i = 0; i < elements.length; i++) {
+                final StackTraceElement element = elements[i];
+                writer.printf("    at %s%n", element);
+                for (int j = 1; j < monitors.length; j++) {
+                    final MonitorInfo monitor = monitors[j];
+                    if (monitor.getLockedStackDepth() == i) {
+                        writer.printf("      - locked %s%n", monitor);
+                    }
+                }
+            }
+            writer.println();
+
+            final LockInfo[] locks = t.getLockedSynchronizers();
+            if (locks.length > 0) {
+                writer.printf("    Locked synchronizers: count = %d%n", locks.length);
+                for (LockInfo l : locks) {
+                    writer.printf("      - %s%n", l);
+                }
+                writer.println();
+            }
+        }
+
+        writer.println();
+        writer.flush();
+    }
+
+}
diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadDumpTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadDumpTest.java
new file mode 100644
index 0000000000..57fdfb430d
--- /dev/null
+++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadDumpTest.java
@@ -0,0 +1,52 @@
+package com.yammer.metrics.jvm.tests;
+
+import com.yammer.metrics.jvm.ThreadDump;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.lang.management.LockInfo;
+import java.lang.management.MonitorInfo;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+
+import static org.fest.assertions.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+// TODO: 3/12/13  -- improve test coverage for ThreadDump
+
+public class ThreadDumpTest {
+    private final ThreadMXBean threadMXBean = mock(ThreadMXBean.class);
+    private final ThreadDump threadDump = new ThreadDump(threadMXBean);
+
+    private final ThreadInfo runnable = mock(ThreadInfo.class);
+
+    @Before
+    public void setUp() throws Exception {
+        final StackTraceElement rLine1 = new StackTraceElement("Blah", "blee", "Blah.java", 100);
+
+        when(runnable.getThreadName()).thenReturn("runnable");
+        when(runnable.getThreadId()).thenReturn(100L);
+        when(runnable.getThreadState()).thenReturn(Thread.State.RUNNABLE);
+        when(runnable.getStackTrace()).thenReturn(new StackTraceElement[]{ rLine1 });
+        when(runnable.getLockedMonitors()).thenReturn(new MonitorInfo[]{ });
+        when(runnable.getLockedSynchronizers()).thenReturn(new LockInfo[]{ });
+
+        when(threadMXBean.dumpAllThreads(true, true)).thenReturn(new ThreadInfo[]{
+                runnable
+        });
+    }
+
+    @Test
+    public void dumpsAllThreads() throws Exception {
+        final ByteArrayOutputStream output = new ByteArrayOutputStream();
+        threadDump.dump(output);
+
+        assertThat(output.toString())
+                .isEqualTo(String.format("runnable id=100 state=RUNNABLE%n" +
+                                                 "    at Blah.blee(Blah.java:100)%n" +
+                                                 "%n" +
+                                                 "%n"));
+    }
+}

From 9319236e80fe03fa36b139279b8355b7707e0a8e Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Tue, 12 Mar 2013 16:09:20 -0700
Subject: [PATCH 0041/2558] Added JSON support for health checks.

---
 metrics-json/pom.xml                          |  5 ++
 .../metrics/json/HealthCheckModule.java       | 73 ++++++++++++++++
 .../yammer/metrics/json/MetricsModule.java    |  2 +-
 .../json/tests/HealthCheckModuleTest.java     | 85 +++++++++++++++++++
 4 files changed, 164 insertions(+), 1 deletion(-)
 create mode 100644 metrics-json/src/main/java/com/yammer/metrics/json/HealthCheckModule.java
 create mode 100644 metrics-json/src/test/java/com/yammer/metrics/json/tests/HealthCheckModuleTest.java

diff --git a/metrics-json/pom.xml b/metrics-json/pom.xml
index 17b05dfa86..e250095f64 100644
--- a/metrics-json/pom.xml
+++ b/metrics-json/pom.xml
@@ -20,6 +20,11 @@
             metrics-core
             ${project.version}
         
+        
+            com.yammer.metrics
+            metrics-healthchecks
+            ${project.version}
+        
         
             com.fasterxml.jackson.core
             jackson-databind
diff --git a/metrics-json/src/main/java/com/yammer/metrics/json/HealthCheckModule.java b/metrics-json/src/main/java/com/yammer/metrics/json/HealthCheckModule.java
new file mode 100644
index 0000000000..53d061ca61
--- /dev/null
+++ b/metrics-json/src/main/java/com/yammer/metrics/json/HealthCheckModule.java
@@ -0,0 +1,73 @@
+package com.yammer.metrics.json;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.Module;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.module.SimpleSerializers;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import com.yammer.metrics.health.HealthCheck;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+public class HealthCheckModule extends Module {
+    private static class HealthCheckResultSerializer extends StdSerializer {
+        private HealthCheckResultSerializer() {
+            super(HealthCheck.Result.class);
+        }
+
+        @Override
+        public void serialize(HealthCheck.Result result,
+                              JsonGenerator json,
+                              SerializerProvider provider) throws IOException {
+            json.writeStartObject();
+            json.writeBooleanField("healthy", result.isHealthy());
+
+            final String message = result.getMessage();
+            if (message != null) {
+                json.writeStringField("message", message);
+            }
+
+            serializeThrowable(json, result.getError(), "error");
+
+            json.writeEndObject();
+        }
+
+        private void serializeThrowable(JsonGenerator json, Throwable error, String name) throws IOException {
+            if (error != null) {
+                json.writeObjectFieldStart(name);
+                json.writeStringField("message", error.getMessage());
+                json.writeArrayFieldStart("stack");
+                for (StackTraceElement element : error.getStackTrace()) {
+                    json.writeString(element.toString());
+                }
+                json.writeEndArray();
+
+                if (error.getCause() != null) {
+                    serializeThrowable(json, error.getCause(), "cause");
+                }
+
+                json.writeEndObject();
+            }
+        }
+    }
+
+    @Override
+    public String getModuleName() {
+        return "healthchecks";
+    }
+
+    @Override
+    public Version version() {
+        return MetricsModule.VERSION;
+    }
+
+    @Override
+    public void setupModule(SetupContext context) {
+        context.addSerializers(new SimpleSerializers(Arrays.>asList(
+                new HealthCheckResultSerializer()
+        )));
+    }
+}
diff --git a/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java b/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java
index 4a8c0d808c..223d1b6af7 100644
--- a/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java
+++ b/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java
@@ -15,7 +15,7 @@
 import java.util.concurrent.TimeUnit;
 
 public class MetricsModule extends Module {
-    private static final Version VERSION = new Version(3, 0, 0, "", "com.yammer.metrics", "metrics-json");
+    static final Version VERSION = new Version(3, 0, 0, "", "com.yammer.metrics", "metrics-json");
 
     private static class GaugeSerializer extends StdSerializer {
         private GaugeSerializer() {
diff --git a/metrics-json/src/test/java/com/yammer/metrics/json/tests/HealthCheckModuleTest.java b/metrics-json/src/test/java/com/yammer/metrics/json/tests/HealthCheckModuleTest.java
new file mode 100644
index 0000000000..4490a67170
--- /dev/null
+++ b/metrics-json/src/test/java/com/yammer/metrics/json/tests/HealthCheckModuleTest.java
@@ -0,0 +1,85 @@
+package com.yammer.metrics.json.tests;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yammer.metrics.health.HealthCheck;
+import com.yammer.metrics.json.HealthCheckModule;
+import org.junit.Test;
+
+import static org.fest.assertions.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class HealthCheckModuleTest {
+    private final ObjectMapper mapper = new ObjectMapper().registerModule(new HealthCheckModule());
+
+    @Test
+    public void serializesAHealthyResult() throws Exception {
+        assertThat(mapper.writeValueAsString(HealthCheck.Result.healthy()))
+                .isEqualTo("{\"healthy\":true}");
+    }
+
+    @Test
+    public void serializesAHealthyResultWithAMessage() throws Exception {
+        assertThat(mapper.writeValueAsString(HealthCheck.Result.healthy("yay for %s", "me")))
+                .isEqualTo("{" +
+                                   "\"healthy\":true," +
+                                   "\"message\":\"yay for me\"}");
+    }
+
+    @Test
+    public void serializesAnUnhealthyResult() throws Exception {
+        assertThat(mapper.writeValueAsString(HealthCheck.Result.unhealthy("boo")))
+                .isEqualTo("{" +
+                                   "\"healthy\":false," +
+                                   "\"message\":\"boo\"}");
+    }
+
+    @Test
+    public void serializesAnUnhealthyResultWithAnException() throws Exception {
+        final Throwable e = mock(Throwable.class);
+        when(e.getMessage()).thenReturn("oh no");
+        when(e.getStackTrace()).thenReturn(new StackTraceElement[]{
+                new StackTraceElement("Blah", "bloo", "Blah.java", 100)
+        });
+
+        assertThat(mapper.writeValueAsString(HealthCheck.Result.unhealthy(e)))
+                .isEqualTo("{" +
+                                   "\"healthy\":false," +
+                                   "\"message\":\"oh no\"," +
+                                   "\"error\":{" +
+                                       "\"message\":\"oh no\"," +
+                                       "\"stack\":[\"Blah.bloo(Blah.java:100)\"]" +
+                                   "}" +
+                                   "}");
+    }
+
+    @Test
+    public void serializesAnUnhealthyResultWithNestedExceptions() throws Exception {
+        final Throwable a = mock(Throwable.class);
+        when(a.getMessage()).thenReturn("oh no");
+        when(a.getStackTrace()).thenReturn(new StackTraceElement[]{
+                new StackTraceElement("Blah", "bloo", "Blah.java", 100)
+        });
+
+        final Throwable b = mock(Throwable.class);
+        when(b.getMessage()).thenReturn("oh well");
+        when(b.getStackTrace()).thenReturn(new StackTraceElement[]{
+                new StackTraceElement("Blah", "blee", "Blah.java", 150)
+        });
+        when(b.getCause()).thenReturn(a);
+
+        assertThat(mapper.writeValueAsString(HealthCheck.Result.unhealthy(b)))
+                .isEqualTo("{" +
+                                   "\"healthy\":false," +
+                                   "\"message\":\"oh well\"," +
+                                   "\"error\":{" +
+                                       "\"message\":\"oh well\"," +
+                                       "\"stack\":[\"Blah.blee(Blah.java:150)\"]," +
+                                       "\"cause\":{" +
+                                           "\"message\":\"oh no\"," +
+                                           "\"stack\":[\"Blah.bloo(Blah.java:100)\"]" +
+                                       "}" +
+                                   "}" +
+                                   "}");
+    }
+}

From f49a80e294517084b918fc61d5a3a1e48b4fcb87 Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Tue, 12 Mar 2013 17:59:28 -0700
Subject: [PATCH 0042/2558] Fix JSON serialization of gauges which throw
 exceptions.

---
 .../java/com/yammer/metrics/json/MetricsModule.java |  9 +++++++--
 .../metrics/json/tests/MetricsModuleTest.java       | 13 +++++++++++++
 2 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java b/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java
index 223d1b6af7..7b9c4d7784 100644
--- a/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java
+++ b/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java
@@ -26,9 +26,14 @@ private GaugeSerializer() {
         public void serialize(Gauge gauge,
                               JsonGenerator json,
                               SerializerProvider provider) throws IOException {
-            final Object value = gauge.getValue();
             json.writeStartObject();
-            json.writeObjectField("value", value);
+            final Object value;
+            try {
+                value = gauge.getValue();
+                json.writeObjectField("value", value);
+            } catch (RuntimeException e) {
+                json.writeObjectField("error", e.toString());
+            }
             json.writeEndObject();
         }
     }
diff --git a/metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java b/metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java
index 04d3e3fb3d..8d629bb2a0 100644
--- a/metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java
+++ b/metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java
@@ -28,6 +28,19 @@ public Integer getValue() {
                 .isEqualTo("{\"value\":100}");
     }
 
+    @Test
+    public void serializesGaugesThatThrowExceptions() throws Exception {
+        final Gauge gauge = new Gauge() {
+            @Override
+            public Integer getValue() {
+                throw new IllegalArgumentException("poops");
+            }
+        };
+
+        assertThat(mapper.writeValueAsString(gauge))
+                .isEqualTo("{\"error\":\"java.lang.IllegalArgumentException: poops\"}");
+    }
+
     @Test
     public void serializesCounters() throws Exception {
         final Counter counter = mock(Counter.class);

From 07490b14ad746336bb1320b8f52761830a74b970 Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Tue, 12 Mar 2013 18:00:18 -0700
Subject: [PATCH 0043/2558] Standardize on a Jetty 8 version.

---
 metrics-jetty8/pom.xml | 2 +-
 pom.xml                | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/metrics-jetty8/pom.xml b/metrics-jetty8/pom.xml
index df6160596d..67e8f7289b 100644
--- a/metrics-jetty8/pom.xml
+++ b/metrics-jetty8/pom.xml
@@ -21,7 +21,7 @@
         
             org.eclipse.jetty
             jetty-server
-            8.1.9.v20130131
+            ${jetty8.version}
         
     
 
diff --git a/pom.xml b/pom.xml
index 86623a3a16..b6a582ff5d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -40,6 +40,7 @@
         2.5
         1.7.2
         2.1.4
+        8.1.9.v20130131
     
 
     

From 2b2e09aa0587eb48227095d123a1e232ddc9a02a Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Tue, 12 Mar 2013 18:27:53 -0700
Subject: [PATCH 0044/2558] Revamp metrics-servlets.

Now, with real tests!
---
 metrics-servlets/pom.xml                      |  33 +-
 .../yammer/metrics/servlet/AdminServlet.java  | 126 ------
 .../metrics/servlet/HealthCheckServlet.java   | 108 -----
 .../metrics/servlet/MetricsServlet.java       | 420 ------------------
 .../yammer/metrics/servlets/AdminServlet.java | 209 +++++++++
 .../metrics/servlets/HealthCheckServlet.java  |  95 ++++
 .../metrics/servlets/MetricsServlet.java      |  85 ++++
 .../{servlet => servlets}/PingServlet.java    |   9 +-
 .../ThreadDumpServlet.java                    |  35 +-
 .../servlet/experiments/ExampleServer.java    |  54 ---
 .../servlet/tests/AdminServletTest.java       | 172 -------
 .../servlet/tests/HealthCheckServletTest.java | 140 ------
 .../servlet/tests/MetricsServletTest.java     | 207 ---------
 .../servlet/tests/PingServletTest.java        |  41 --
 .../servlet/tests/ThreadDumpServletTest.java  |  41 --
 .../servlets/experiments/ExampleServer.java   |  62 +++
 .../servlets/tests/AbstractServletTest.java   |  29 ++
 .../servlets/tests/AdminServletTest.java      |  58 +++
 .../tests/HealthCheckServletTest.java         | 121 +++++
 .../servlets/tests/MetricsServletTest.java    | 146 ++++++
 .../servlets/tests/PingServletTest.java       |  49 ++
 .../servlets/tests/ThreadDumpServletTest.java |  49 ++
 22 files changed, 949 insertions(+), 1340 deletions(-)
 delete mode 100755 metrics-servlets/src/main/java/com/yammer/metrics/servlet/AdminServlet.java
 delete mode 100644 metrics-servlets/src/main/java/com/yammer/metrics/servlet/HealthCheckServlet.java
 delete mode 100644 metrics-servlets/src/main/java/com/yammer/metrics/servlet/MetricsServlet.java
 create mode 100755 metrics-servlets/src/main/java/com/yammer/metrics/servlets/AdminServlet.java
 create mode 100644 metrics-servlets/src/main/java/com/yammer/metrics/servlets/HealthCheckServlet.java
 create mode 100644 metrics-servlets/src/main/java/com/yammer/metrics/servlets/MetricsServlet.java
 rename metrics-servlets/src/main/java/com/yammer/metrics/{servlet => servlets}/PingServlet.java (68%)
 rename metrics-servlets/src/main/java/com/yammer/metrics/{servlet => servlets}/ThreadDumpServlet.java (55%)
 delete mode 100644 metrics-servlets/src/test/java/com/yammer/metrics/servlet/experiments/ExampleServer.java
 delete mode 100755 metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/AdminServletTest.java
 delete mode 100644 metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/HealthCheckServletTest.java
 delete mode 100644 metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/MetricsServletTest.java
 delete mode 100644 metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/PingServletTest.java
 delete mode 100644 metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/ThreadDumpServletTest.java
 create mode 100644 metrics-servlets/src/test/java/com/yammer/metrics/servlets/experiments/ExampleServer.java
 create mode 100644 metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AbstractServletTest.java
 create mode 100755 metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AdminServletTest.java
 create mode 100644 metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/HealthCheckServletTest.java
 create mode 100644 metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java
 create mode 100644 metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/PingServletTest.java
 create mode 100644 metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/ThreadDumpServletTest.java

diff --git a/metrics-servlets/pom.xml b/metrics-servlets/pom.xml
index 32878aae21..da1f04a27f 100644
--- a/metrics-servlets/pom.xml
+++ b/metrics-servlets/pom.xml
@@ -16,12 +16,27 @@
         
             com.yammer.metrics
             metrics-core
-            2.2.0
+            ${project.version}
+        
+        
+            com.yammer.metrics
+            metrics-healthchecks
+            ${project.version}
+        
+        
+            com.yammer.metrics
+            metrics-json
+            ${project.version}
+        
+        
+            com.yammer.metrics
+            metrics-jvm
+            ${project.version}
         
         
             javax.servlet
-            servlet-api
-            ${servlet.version}
+            javax.servlet-api
+            3.0.1
             provided
         
         
@@ -29,16 +44,22 @@
             jackson-databind
             ${jackson.version}
         
+        
+            org.eclipse.jetty
+            test-jetty-servlet
+            ${jetty8.version}
+            test
+        
         
             org.eclipse.jetty
             jetty-servlet
-            8.1.9.v20130131
+            ${jetty8.version}
             test
         
         
             com.yammer.metrics
-            metrics-jetty
-            2.2.0
+            metrics-jetty8
+            ${project.version}
             test
         
     
diff --git a/metrics-servlets/src/main/java/com/yammer/metrics/servlet/AdminServlet.java b/metrics-servlets/src/main/java/com/yammer/metrics/servlet/AdminServlet.java
deleted file mode 100755
index 98298ef6d9..0000000000
--- a/metrics-servlets/src/main/java/com/yammer/metrics/servlet/AdminServlet.java
+++ /dev/null
@@ -1,126 +0,0 @@
-package com.yammer.metrics.servlet;
-
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.text.MessageFormat;
-
-public class AdminServlet extends HttpServlet {
-    private static final long serialVersionUID = 1363903248255082791L;
-
-    private static final String TEMPLATE = "\n" +
-                                           "\n" +
-                                           "\n" +
-                                           "  Codestin Search App\n" +
-                                           "\n" +
-                                           "\n" +
-                                           "  

Operational Menu{8}

\n" + - " \n" + - "\n" + - ""; - - public static final String DEFAULT_HEALTHCHECK_URI = "/healthcheck"; - public static final String DEFAULT_METRICS_URI = "/metrics"; - public static final String DEFAULT_PING_URI = "/ping"; - public static final String DEFAULT_THREADS_URI = "/threads"; - private static final String CONTENT_TYPE = "text/html"; - - private final HealthCheckServlet healthCheckServlet; - private final MetricsServlet metricsServlet; - private final PingServlet pingServlet; - private final ThreadDumpServlet threadDumpServlet; - - private String metricsUri; - private String pingUri; - private String threadsUri; - private String healthcheckUri; - private String serviceName; - - public AdminServlet() { - this(new HealthCheckServlet(), new MetricsServlet(), new PingServlet(), - new ThreadDumpServlet(), DEFAULT_HEALTHCHECK_URI, DEFAULT_METRICS_URI, - DEFAULT_PING_URI, DEFAULT_THREADS_URI); - } - - public AdminServlet(HealthCheckServlet healthCheckServlet, - MetricsServlet metricsServlet, - PingServlet pingServlet, - ThreadDumpServlet threadDumpServlet, - String healthcheckUri, - String metricsUri, - String pingUri, - String threadsUri) { - this.healthCheckServlet = healthCheckServlet; - this.metricsServlet = metricsServlet; - this.pingServlet = pingServlet; - this.threadDumpServlet = threadDumpServlet; - - this.metricsUri = metricsUri; - this.pingUri = pingUri; - this.threadsUri = threadsUri; - this.healthcheckUri = healthcheckUri; - } - - @Override - public void init(ServletConfig config) throws ServletException { - super.init(config); - healthCheckServlet.init(config); - metricsServlet.init(config); - pingServlet.init(config); - threadDumpServlet.init(config); - - //final ServletContext context = config.getServletContext(); - this.metricsUri = getParam(config.getInitParameter("metrics-uri"), this.metricsUri); - this.pingUri = getParam(config.getInitParameter("ping-uri"), this.pingUri); - this.threadsUri = getParam(config.getInitParameter("threads-uri"), this.threadsUri); - this.healthcheckUri = getParam(config.getInitParameter("healthcheck-uri"), this.healthcheckUri); - this.serviceName = getParam(config.getInitParameter("service-name"), this.serviceName); - } - - public void setServiceName(String serviceName) { - this.serviceName = serviceName; - } - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); - final String uri = req.getPathInfo(); - final String path = req.getContextPath() + req.getServletPath(); - if (uri == null || uri.equals("/")) { - resp.setStatus(HttpServletResponse.SC_OK); - resp.setContentType(CONTENT_TYPE); - final PrintWriter writer = resp.getWriter(); - try { - writer.println(MessageFormat.format(TEMPLATE, path, metricsUri, path, pingUri, path, - threadsUri, path, healthcheckUri, - serviceName == null ? "" : " (" + serviceName + ")")); - } finally { - writer.close(); - } - } else if (uri.equals(healthcheckUri)) { - healthCheckServlet.service(req, resp); - } else if (uri.startsWith(metricsUri)) { - metricsServlet.service(req, resp); - } else if (uri.equals(pingUri)) { - pingServlet.service(req, resp); - } else if (uri.equals(threadsUri)) { - threadDumpServlet.service(req, resp); - } else { - resp.sendError(HttpServletResponse.SC_NOT_FOUND); - } - } - - private static String getParam(String initParam, String defaultValue) { - return initParam == null ? defaultValue : initParam; - } -} diff --git a/metrics-servlets/src/main/java/com/yammer/metrics/servlet/HealthCheckServlet.java b/metrics-servlets/src/main/java/com/yammer/metrics/servlet/HealthCheckServlet.java deleted file mode 100644 index 155c63ed01..0000000000 --- a/metrics-servlets/src/main/java/com/yammer/metrics/servlet/HealthCheckServlet.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.yammer.metrics.servlet; - -import com.yammer.metrics.HealthChecks; -import com.yammer.metrics.core.HealthCheck; -import com.yammer.metrics.core.HealthCheckRegistry; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.Map; - -/** - * An HTTP servlet which runs the health checks registered with a given {@link HealthCheckRegistry} - * and prints the results as a {@code text/plain} entity. Only responds to {@code GET} requests. - *

- * If the servlet context has an attribute named - * {@code com.yammer.metrics.servlet.HealthCheckServlet.registry} which is a - * {@link HealthCheckRegistry} instance, {@link HealthCheckServlet} will use it instead of - * {@link HealthChecks}. - */ -public class HealthCheckServlet extends HttpServlet { - /** - * The attribute name of the {@link HealthCheckRegistry} instance in the servlet context. - */ - public static final String REGISTRY_ATTRIBUTE = HealthCheckServlet.class.getName() + ".registry"; - private static final String CONTENT_TYPE = "text/plain"; - - private HealthCheckRegistry registry; - - /** - * Creates a new {@link HealthCheckServlet} with the given {@link HealthCheckRegistry}. - * - * @param registry a {@link HealthCheckRegistry} - */ - public HealthCheckServlet(HealthCheckRegistry registry) { - this.registry = registry; - } - - /** - * Creates a new {@link HealthCheckServlet} with the default {@link HealthCheckRegistry}. - */ - public HealthCheckServlet() { - this(HealthChecks.defaultRegistry()); - } - - @Override - public void init(ServletConfig config) throws ServletException { - final Object o = config.getServletContext().getAttribute(REGISTRY_ATTRIBUTE); - if (o instanceof HealthCheckRegistry) { - this.registry = (HealthCheckRegistry) o; - } - } - - @Override - protected void doGet(HttpServletRequest req, - HttpServletResponse resp) throws ServletException, IOException { - final Map results = registry.runHealthChecks(); - resp.setContentType(CONTENT_TYPE); - resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); - final PrintWriter writer = resp.getWriter(); - if (results.isEmpty()) { - resp.setStatus(HttpServletResponse.SC_NOT_IMPLEMENTED); - writer.println("! No health checks registered."); - } else { - if (isAllHealthy(results)) { - resp.setStatus(HttpServletResponse.SC_OK); - } else { - resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - } - for (Map.Entry entry : results.entrySet()) { - final HealthCheck.Result result = entry.getValue(); - if (result.isHealthy()) { - if (result.getMessage() != null) { - writer.format("* %s: OK\n %s\n", entry.getKey(), result.getMessage()); - } else { - writer.format("* %s: OK\n", entry.getKey()); - } - } else { - if (result.getMessage() != null) { - writer.format("! %s: ERROR\n! %s\n", entry.getKey(), result.getMessage()); - } - - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") - final Throwable error = result.getError(); - if (error != null) { - writer.println(); - error.printStackTrace(writer); - writer.println(); - } - } - } - } - writer.close(); - } - - private static boolean isAllHealthy(Map results) { - for (HealthCheck.Result result : results.values()) { - if (!result.isHealthy()) { - return false; - } - } - return true; - } -} diff --git a/metrics-servlets/src/main/java/com/yammer/metrics/servlet/MetricsServlet.java b/metrics-servlets/src/main/java/com/yammer/metrics/servlet/MetricsServlet.java deleted file mode 100644 index 7ad8494dca..0000000000 --- a/metrics-servlets/src/main/java/com/yammer/metrics/servlet/MetricsServlet.java +++ /dev/null @@ -1,420 +0,0 @@ -package com.yammer.metrics.servlet; - -import com.fasterxml.jackson.core.JsonEncoding; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.*; -import com.yammer.metrics.stats.Snapshot; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.OutputStream; -import java.util.Map; -import java.util.SortedMap; -import java.util.concurrent.TimeUnit; - -/** - * An HTTP servlet which outputs the metrics in a {@link MetricsRegistry} (and optionally the data - * provided by {@link VirtualMachineMetrics}) in a JSON object. Only responds to {@code GET} - * requests. - *

- * If the servlet context has an attribute named - * {@code com.yammer.metrics.servlet.MetricsServlet.registry} which is a - * {@link MetricsRegistry} instance, {@link MetricsServlet} will use it instead of {@link Metrics}. - *

- * {@link MetricsServlet} also takes an initialization parameter, {@code show-jvm-metrics}, which - * should be a boolean value (e.g., {@code "true"} or {@code "false"}). It determines whether or not - * JVM-level metrics will be included in the JSON output. - *

- * {@code GET} requests to {@link MetricsServlet} can make use of the following query-string - * parameters: - *

- *
/metrics?class=com.example.service
- *
- * class is a string used to filter the metrics in the JSON by metric name. In - * the given example, only metrics for classes whose canonical name starts with - * com.example.service would be shown. You can also use jvm for - * just the JVM-level metrics. - *
- * - *
/metrics?pretty=true
- *
- * pretty determines whether or not the JSON which is returned is printed with - * indented whitespace or not. If you're looking at the JSON in the browser, use this. - *
- * - *
/metrics?full-samples=true
- *
- * full-samples determines whether or not the JSON which is returned will - * include the full content of histograms' and timers' reservoir samples. If you're - * aggregating across hosts, you may want to do this to allow for more accurate quantile - * calculations. - *
- *
- */ -public class MetricsServlet extends HttpServlet implements MetricProcessor { - - /** - * The attribute name of the {@link MetricsRegistry} instance in the servlet context. - */ - public static final String REGISTRY_ATTRIBUTE = MetricsServlet.class.getName() + ".registry"; - - /** - * The attribute name of the {@link JsonFactory} instance in the servlet context. - */ - public static final String JSON_FACTORY_ATTRIBUTE = JsonFactory.class.getCanonicalName(); - - /** - * The initialization parameter name which determines whether or not JVM_level metrics will be - * included in the JSON output. - */ - public static final String SHOW_JVM_METRICS = "show-jvm-metrics"; - - static final class Context { - final boolean showFullSamples; - final JsonGenerator json; - - Context(JsonGenerator json, boolean showFullSamples) { - this.json = json; - this.showFullSamples = showFullSamples; - } - } - - private static final JsonFactory DEFAULT_JSON_FACTORY = new JsonFactory(new ObjectMapper()); - private static final Logger LOGGER = LoggerFactory.getLogger(MetricsServlet.class); - private static final String CONTENT_TYPE = "application/json"; - - private final Clock clock; - private final VirtualMachineMetrics vm; - private MetricsRegistry registry; - private JsonFactory factory; - private boolean showJvmMetrics; - - /** - * Creates a new {@link MetricsServlet}. - */ - public MetricsServlet() { - this(Clock.defaultClock(), VirtualMachineMetrics.getInstance(), - Metrics.defaultRegistry(), DEFAULT_JSON_FACTORY, true); - } - - /** - * Creates a new {@link MetricsServlet}. - * - * @param showJvmMetrics whether or not JVM-level metrics will be included in the output - */ - public MetricsServlet(boolean showJvmMetrics) { - this(Clock.defaultClock(), VirtualMachineMetrics.getInstance(), - Metrics.defaultRegistry(), DEFAULT_JSON_FACTORY, showJvmMetrics); - } - - /** - * Creates a new {@link MetricsServlet}. - * - * @param clock the clock used for the current time - * @param vm a {@link VirtualMachineMetrics} instance - * @param registry a {@link MetricsRegistry} - * @param factory a {@link JsonFactory} - * @param showJvmMetrics whether or not JVM-level metrics will be included in the output - */ - public MetricsServlet(Clock clock, - VirtualMachineMetrics vm, - MetricsRegistry registry, - JsonFactory factory, - boolean showJvmMetrics) { - this.clock = clock; - this.vm = vm; - this.registry = registry; - this.factory = factory; - this.showJvmMetrics = showJvmMetrics; - } - - @Override - public void init(ServletConfig config) throws ServletException { - final Object factory = config.getServletContext() - .getAttribute(JSON_FACTORY_ATTRIBUTE); - if (factory instanceof JsonFactory) { - this.factory = (JsonFactory) factory; - } - - final Object o = config.getServletContext().getAttribute(REGISTRY_ATTRIBUTE); - if (o instanceof MetricsRegistry) { - this.registry = (MetricsRegistry) o; - } - - final String showJvmMetricsParam = config.getInitParameter(SHOW_JVM_METRICS); - if (showJvmMetricsParam != null) { - this.showJvmMetrics = Boolean.parseBoolean(showJvmMetricsParam); - } - } - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - final String classPrefix = req.getParameter("class"); - final boolean pretty = Boolean.parseBoolean(req.getParameter("pretty")); - final boolean showFullSamples = Boolean.parseBoolean(req.getParameter("full-samples")); - - resp.setStatus(HttpServletResponse.SC_OK); - resp.setContentType(CONTENT_TYPE); - final OutputStream output = resp.getOutputStream(); - final JsonGenerator json = factory.createJsonGenerator(output, JsonEncoding.UTF8); - if (pretty) { - json.useDefaultPrettyPrinter(); - } - json.writeStartObject(); - { - if (showJvmMetrics && ("jvm".equals(classPrefix) || classPrefix == null)) { - writeVmMetrics(json); - } - - writeRegularMetrics(json, classPrefix, showFullSamples); - } - json.writeEndObject(); - json.close(); - } - - private void writeVmMetrics(JsonGenerator json) throws IOException { - json.writeFieldName("jvm"); - json.writeStartObject(); - { - json.writeFieldName("vm"); - json.writeStartObject(); - { - json.writeStringField("name", vm.name()); - json.writeStringField("version", vm.version()); - } - json.writeEndObject(); - - json.writeFieldName("memory"); - json.writeStartObject(); - { - json.writeNumberField("totalInit", vm.totalInit()); - json.writeNumberField("totalUsed", vm.totalUsed()); - json.writeNumberField("totalMax", vm.totalMax()); - json.writeNumberField("totalCommitted", vm.totalCommitted()); - - json.writeNumberField("heapInit", vm.heapInit()); - json.writeNumberField("heapUsed", vm.heapUsed()); - json.writeNumberField("heapMax", vm.heapMax()); - json.writeNumberField("heapCommitted", vm.heapCommitted()); - - json.writeNumberField("heap_usage", vm.heapUsage()); - json.writeNumberField("non_heap_usage", vm.nonHeapUsage()); - json.writeFieldName("memory_pool_usages"); - json.writeStartObject(); - { - for (Map.Entry pool : vm.memoryPoolUsage().entrySet()) { - json.writeNumberField(pool.getKey(), pool.getValue()); - } - } - json.writeEndObject(); - } - json.writeEndObject(); - - final Map bufferPoolStats = vm.getBufferPoolStats(); - if (!bufferPoolStats.isEmpty()) { - json.writeFieldName("buffers"); - json.writeStartObject(); - { - json.writeFieldName("direct"); - json.writeStartObject(); - { - json.writeNumberField("count", bufferPoolStats.get("direct").getCount()); - json.writeNumberField("memoryUsed", bufferPoolStats.get("direct").getMemoryUsed()); - json.writeNumberField("totalCapacity", bufferPoolStats.get("direct").getTotalCapacity()); - } - json.writeEndObject(); - - json.writeFieldName("mapped"); - json.writeStartObject(); - { - json.writeNumberField("count", bufferPoolStats.get("mapped").getCount()); - json.writeNumberField("memoryUsed", bufferPoolStats.get("mapped").getMemoryUsed()); - json.writeNumberField("totalCapacity", bufferPoolStats.get("mapped").getTotalCapacity()); - } - json.writeEndObject(); - } - json.writeEndObject(); - } - - - json.writeNumberField("daemon_thread_count", vm.daemonThreadCount()); - json.writeNumberField("thread_count", vm.threadCount()); - json.writeNumberField("current_time", clock.time()); - json.writeNumberField("uptime", vm.uptime()); - json.writeNumberField("fd_usage", vm.fileDescriptorUsage()); - - json.writeFieldName("thread-states"); - json.writeStartObject(); - { - for (Map.Entry entry : vm.threadStatePercentages() - .entrySet()) { - json.writeNumberField(entry.getKey().toString().toLowerCase(), - entry.getValue()); - } - } - json.writeEndObject(); - - json.writeFieldName("garbage-collectors"); - json.writeStartObject(); - { - for (Map.Entry entry : vm.garbageCollectors() - .entrySet()) { - json.writeFieldName(entry.getKey()); - json.writeStartObject(); - { - final VirtualMachineMetrics.GarbageCollectorStats gc = entry.getValue(); - json.writeNumberField("runs", gc.getRuns()); - json.writeNumberField("time", gc.getTime(TimeUnit.MILLISECONDS)); - } - json.writeEndObject(); - } - } - json.writeEndObject(); - } - json.writeEndObject(); - } - - public void writeRegularMetrics(JsonGenerator json, String classPrefix, boolean showFullSamples) throws IOException { - final Context context = new Context(json, showFullSamples); - for (Map.Entry> entry : registry.groupedMetrics().entrySet()) { - if (classPrefix == null || entry.getKey().startsWith(classPrefix)) { - json.writeFieldName(entry.getKey()); - json.writeStartObject(); - { - for (Map.Entry subEntry : entry.getValue().entrySet()) { - json.writeFieldName(subEntry.getKey().getName()); - try { - subEntry.getValue().processWith(this, subEntry.getKey(), context); - } catch (Exception e) { - LOGGER.warn("Error writing out " + subEntry.getKey(), e); - } - } - } - json.writeEndObject(); - } - } - } - - @Override - public void processHistogram(MetricName name, Histogram histogram, Context context) throws Exception { - final JsonGenerator json = context.json; - json.writeStartObject(); - { - json.writeStringField("type", "histogram"); - json.writeNumberField("count", histogram.count()); - writeSummarizable(histogram, json); - writeSampling(histogram, json); - - if (context.showFullSamples) { - json.writeObjectField("values", histogram.getSnapshot().getValues()); - } - } - json.writeEndObject(); - } - - @Override - public void processCounter(MetricName name, Counter counter, Context context) throws Exception { - final JsonGenerator json = context.json; - json.writeStartObject(); - { - json.writeStringField("type", "counter"); - json.writeNumberField("count", counter.count()); - } - json.writeEndObject(); - } - - @Override - public void processGauge(MetricName name, Gauge gauge, Context context) throws Exception { - final JsonGenerator json = context.json; - json.writeStartObject(); - { - json.writeStringField("type", "gauge"); - json.writeObjectField("value", evaluateGauge(gauge)); - } - json.writeEndObject(); - } - - @Override - public void processMeter(MetricName name, Metered meter, Context context) throws Exception { - final JsonGenerator json = context.json; - json.writeStartObject(); - { - json.writeStringField("type", "meter"); - json.writeStringField("event_type", meter.eventType()); - writeMeteredFields(meter, json); - } - json.writeEndObject(); - } - - @Override - public void processTimer(MetricName name, Timer timer, Context context) throws Exception { - final JsonGenerator json = context.json; - json.writeStartObject(); - { - json.writeStringField("type", "timer"); - json.writeFieldName("duration"); - json.writeStartObject(); - { - json.writeStringField("unit", timer.durationUnit().toString().toLowerCase()); - writeSummarizable(timer, json); - writeSampling(timer, json); - if (context.showFullSamples) { - json.writeObjectField("values", timer.getSnapshot().getValues()); - } - } - json.writeEndObject(); - - json.writeFieldName("rate"); - json.writeStartObject(); - { - writeMeteredFields(timer, json); - } - json.writeEndObject(); - } - json.writeEndObject(); - } - - private static Object evaluateGauge(Gauge gauge) { - try { - return gauge.value(); - } catch (RuntimeException e) { - LOGGER.warn("Error evaluating gauge", e); - return "error reading gauge: " + e.getMessage(); - } - } - - private static void writeSummarizable(Summarizable metric, JsonGenerator json) throws IOException { - json.writeNumberField("min", metric.min()); - json.writeNumberField("max", metric.max()); - json.writeNumberField("mean", metric.mean()); - json.writeNumberField("std_dev", metric.stdDev()); - } - - private static void writeSampling(Sampling metric, JsonGenerator json) throws IOException { - final Snapshot snapshot = metric.getSnapshot(); - json.writeNumberField("median", snapshot.getMedian()); - json.writeNumberField("p75", snapshot.get75thPercentile()); - json.writeNumberField("p95", snapshot.get95thPercentile()); - json.writeNumberField("p98", snapshot.get98thPercentile()); - json.writeNumberField("p99", snapshot.get99thPercentile()); - json.writeNumberField("p999", snapshot.get999thPercentile()); - } - - private static void writeMeteredFields(Metered metered, JsonGenerator json) throws IOException { - json.writeStringField("unit", metered.rateUnit().toString().toLowerCase()); - json.writeNumberField("count", metered.count()); - json.writeNumberField("mean", metered.meanRate()); - json.writeNumberField("m1", metered.oneMinuteRate()); - json.writeNumberField("m5", metered.fiveMinuteRate()); - json.writeNumberField("m15", metered.fifteenMinuteRate()); - } -} diff --git a/metrics-servlets/src/main/java/com/yammer/metrics/servlets/AdminServlet.java b/metrics-servlets/src/main/java/com/yammer/metrics/servlets/AdminServlet.java new file mode 100755 index 0000000000..eb682d3e46 --- /dev/null +++ b/metrics-servlets/src/main/java/com/yammer/metrics/servlets/AdminServlet.java @@ -0,0 +1,209 @@ +package com.yammer.metrics.servlets; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.text.MessageFormat; + +public class AdminServlet extends HttpServlet { + public static final String DEFAULT_HEALTHCHECK_URI = "/healthcheck"; + public static final String DEFAULT_METRICS_URI = "/metrics"; + public static final String DEFAULT_PING_URI = "/ping"; + public static final String DEFAULT_THREADS_URI = "/threads"; + private static final String TEMPLATE = "\n" + + "\n" + + "\n" + + " Codestin Search App\n" + + "\n" + + "\n" + + "

Operational Menu{8}

\n" + + " \n" + + "\n" + + ""; + private static final String CONTENT_TYPE = "text/html"; + private static final long serialVersionUID = -2850794040708785318L; + + private transient HealthCheckServlet healthCheckServlet; + private transient MetricsServlet metricsServlet; + private transient PingServlet pingServlet; + private transient ThreadDumpServlet threadDumpServlet; + private transient String metricsUri; + private transient String pingUri; + private transient String threadsUri; + private transient String healthcheckUri; + private transient String serviceName; + + @Override + public void init(ServletConfig config) throws ServletException { + this.healthCheckServlet = new HealthCheckServlet(); + healthCheckServlet.init(config); + + this.metricsServlet = new MetricsServlet(); + metricsServlet.init(config); + + this.pingServlet = new PingServlet(); + pingServlet.init(config); + + this.threadDumpServlet = new ThreadDumpServlet(); + threadDumpServlet.init(config); + + this.metricsUri = getParam(config.getInitParameter("metrics-uri"), DEFAULT_METRICS_URI); + this.pingUri = getParam(config.getInitParameter("ping-uri"), DEFAULT_PING_URI); + this.threadsUri = getParam(config.getInitParameter("threads-uri"), DEFAULT_THREADS_URI); + this.healthcheckUri = getParam(config.getInitParameter("healthcheck-uri"), DEFAULT_HEALTHCHECK_URI); + this.serviceName = getParam(config.getInitParameter("service-name"), null); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + final String path = req.getContextPath() + req.getServletPath(); + + resp.setStatus(HttpServletResponse.SC_OK); + resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); + resp.setContentType(CONTENT_TYPE); + final PrintWriter writer = resp.getWriter(); + try { + writer.println(MessageFormat.format(TEMPLATE, path, metricsUri, path, pingUri, path, + threadsUri, path, healthcheckUri, + serviceName == null ? "" : " (" + serviceName + ")")); + } finally { + writer.close(); + } + } + + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + final String uri = req.getPathInfo(); + if (uri == null || uri.equals("/")) { + super.service(req, resp); + } else if (uri.equals(healthcheckUri)) { + healthCheckServlet.service(req, resp); + } else if (uri.startsWith(metricsUri)) { + metricsServlet.service(req, resp); + } else if (uri.equals(pingUri)) { + pingServlet.service(req, resp); + } else if (uri.equals(threadsUri)) { + threadDumpServlet.service(req, resp); + } else { + resp.sendError(HttpServletResponse.SC_NOT_FOUND); + } + } + + private static String getParam(String initParam, String defaultValue) { + return initParam == null ? defaultValue : initParam; + } +} + +//import javax.servlet.ServletConfig; +//import javax.servlet.ServletException; +//import javax.servlet.http.HttpServlet; +//import javax.servlet.http.HttpServletRequest; +//import javax.servlet.http.HttpServletResponse; +//import java.io.IOException; +//import java.io.PrintWriter; +//import java.text.MessageFormat; +// +//public class AdminServlet extends HttpServlet { +// private static final long serialVersionUID = 1363903248255082791L; +// + +// +// private final HealthCheckServlet healthCheckServlet; +// private final MetricsServlet metricsServlet; +// private final PingServlet pingServlet; +// private final ThreadDumpServlet threadDumpServlet; +// +// private String metricsUri; +// private String pingUri; +// private String threadsUri; +// private String healthcheckUri; +// private String serviceName; +// +// public AdminServlet() { +// this(new HealthCheckServlet(), new MetricsServlet(), new PingServlet(), +// new ThreadDumpServlet(), DEFAULT_HEALTHCHECK_URI, DEFAULT_METRICS_URI, +// DEFAULT_PING_URI, DEFAULT_THREADS_URI); +// } +// +// public AdminServlet(HealthCheckServlet healthCheckServlet, +// MetricsServlet metricsServlet, +// PingServlet pingServlet, +// ThreadDumpServlet threadDumpServlet, +// String healthcheckUri, +// String metricsUri, +// String pingUri, +// String threadsUri) { +// this.healthCheckServlet = healthCheckServlet; +// this.metricsServlet = metricsServlet; +// this.pingServlet = pingServlet; +// this.threadDumpServlet = threadDumpServlet; +// +// this.metricsUri = metricsUri; +// this.pingUri = pingUri; +// this.threadsUri = threadsUri; +// this.healthcheckUri = healthcheckUri; +// } +// +// @Override +// public void init(ServletConfig config) throws ServletException { +// super.init(config); +// healthCheckServlet.init(config); +// metricsServlet.init(config); +// pingServlet.init(config); +// threadDumpServlet.init(config); +// +// //final ServletContext context = config.getServletContext(); +// this.metricsUri = getParam(config.getInitParameter("metrics-uri"), this.metricsUri); +// this.pingUri = getParam(config.getInitParameter("ping-uri"), this.pingUri); +// this.threadsUri = getParam(config.getInitParameter("threads-uri"), this.threadsUri); +// this.healthcheckUri = getParam(config.getInitParameter("healthcheck-uri"), this.healthcheckUri); +// this.serviceName = getParam(config.getInitParameter("service-name"), this.serviceName); +// } +// +// public void setServiceName(String serviceName) { +// this.serviceName = serviceName; +// } +// +// @Override +// protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { +// resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); +// final String uri = req.getPathInfo(); +// final String path = req.getContextPath() + req.getServletPath(); +// if (uri == null || uri.equals("/")) { +// resp.setStatus(HttpServletResponse.SC_OK); +// resp.setContentType(CONTENT_TYPE); +// final PrintWriter writer = resp.getWriter(); +// try { +// writer.println(MessageFormat.format(TEMPLATE, path, metricsUri, path, pingUri, path, +// threadsUri, path, healthcheckUri, +// serviceName == null ? "" : " (" + serviceName + ")")); +// } finally { +// writer.close(); +// } +// } else if (uri.equals(healthcheckUri)) { +// healthCheckServlet.service(req, resp); +// } else if (uri.startsWith(metricsUri)) { +// metricsServlet.service(req, resp); +// } else if (uri.equals(pingUri)) { +// pingServlet.service(req, resp); +// } else if (uri.equals(threadsUri)) { +// threadDumpServlet.service(req, resp); +// } else { +// resp.sendError(HttpServletResponse.SC_NOT_FOUND); +// } +// } +// +// private static String getParam(String initParam, String defaultValue) { +// return initParam == null ? defaultValue : initParam; +// } +//} diff --git a/metrics-servlets/src/main/java/com/yammer/metrics/servlets/HealthCheckServlet.java b/metrics-servlets/src/main/java/com/yammer/metrics/servlets/HealthCheckServlet.java new file mode 100644 index 0000000000..32b151ac61 --- /dev/null +++ b/metrics-servlets/src/main/java/com/yammer/metrics/servlets/HealthCheckServlet.java @@ -0,0 +1,95 @@ +package com.yammer.metrics.servlets; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.yammer.metrics.health.HealthCheck; +import com.yammer.metrics.health.HealthCheckRegistry; +import com.yammer.metrics.json.HealthCheckModule; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; +import java.util.SortedMap; +import java.util.concurrent.ExecutorService; + +public class HealthCheckServlet extends HttpServlet { + public static final String HEALTH_CHECK_REGISTRY = HealthCheckServlet.class.getCanonicalName() + ".registry"; + public static final String HEALTH_CHECK_EXECUTOR = HealthCheckServlet.class.getCanonicalName() + ".executor"; + + private static final long serialVersionUID = -8432996484889177321L; + private static final String CONTENT_TYPE = "application/json"; + + private transient HealthCheckRegistry registry; + private transient ExecutorService executorService; + private transient ObjectMapper mapper; + + @Override + public void init(ServletConfig config) throws ServletException { + final Object registryAttr = config.getServletContext().getAttribute(HEALTH_CHECK_REGISTRY); + if (registryAttr instanceof HealthCheckRegistry) { + this.registry = (HealthCheckRegistry) registryAttr; + } else { + throw new ServletException("Couldn't find a HealthCheckRegistry instance."); + } + + final Object executorAttr = config.getServletContext().getAttribute(HEALTH_CHECK_EXECUTOR); + if (executorAttr instanceof ExecutorService) { + this.executorService = (ExecutorService) executorAttr; + } + + this.mapper = new ObjectMapper().registerModule(new HealthCheckModule()); + } + + @Override + protected void doGet(HttpServletRequest req, + HttpServletResponse resp) throws ServletException, IOException { + final SortedMap results = runHealthChecks(); + resp.setContentType(CONTENT_TYPE); + resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); + if (results.isEmpty()) { + resp.setStatus(HttpServletResponse.SC_NOT_IMPLEMENTED); + } else { + if (isAllHealthy(results)) { + resp.setStatus(HttpServletResponse.SC_OK); + } else { + resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + + final OutputStream output = resp.getOutputStream(); + try { + getWriter(req).writeValue(output, results); + } finally { + output.close(); + } + } + + private ObjectWriter getWriter(HttpServletRequest request) { + final boolean prettyPrint = Boolean.parseBoolean(request.getParameter("pretty")); + if (prettyPrint) { + return mapper.writerWithDefaultPrettyPrinter(); + } + return mapper.writer(); + } + + private SortedMap runHealthChecks() { + if (executorService == null) { + return registry.runHealthChecks(); + } + return registry.runHealthChecks(executorService); + } + + private static boolean isAllHealthy(Map results) { + for (HealthCheck.Result result : results.values()) { + if (!result.isHealthy()) { + return false; + } + } + return true; + } +} diff --git a/metrics-servlets/src/main/java/com/yammer/metrics/servlets/MetricsServlet.java b/metrics-servlets/src/main/java/com/yammer/metrics/servlets/MetricsServlet.java new file mode 100644 index 0000000000..38fa9ffc35 --- /dev/null +++ b/metrics-servlets/src/main/java/com/yammer/metrics/servlets/MetricsServlet.java @@ -0,0 +1,85 @@ +package com.yammer.metrics.servlets; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.yammer.metrics.MetricRegistry; +import com.yammer.metrics.json.MetricsModule; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +public class MetricsServlet extends HttpServlet { + public static final String RATE_UNIT = MetricsServlet.class.getCanonicalName() + ".rateUnit"; + public static final String DURATION_UNIT = MetricsServlet.class.getCanonicalName() + ".durationUnit"; + public static final String SHOW_SAMPLES = MetricsServlet.class.getCanonicalName() + ".showSamples"; + public static final String METRICS_REGISTRY = MetricsServlet.class.getCanonicalName() + ".registry"; + + private static final long serialVersionUID = 1049773947734939602L; + private static final String CONTENT_TYPE = "application/json"; + + private transient MetricRegistry registry; + private transient ObjectMapper mapper; + + @Override + public void init(ServletConfig config) throws ServletException { + final Object registryAttr = config.getServletContext().getAttribute(METRICS_REGISTRY); + if (registryAttr instanceof MetricRegistry) { + this.registry = (MetricRegistry) registryAttr; + } else { + throw new ServletException("Couldn't find a MetricRegistry instance."); + } + + final TimeUnit rateUnit = parseTimeUnit(config.getServletContext() + .getInitParameter(RATE_UNIT), + TimeUnit.SECONDS); + + final TimeUnit durationUnit = parseTimeUnit(config.getServletContext() + .getInitParameter(DURATION_UNIT), + TimeUnit.SECONDS); + + final boolean showSamples = Boolean.parseBoolean(config.getServletContext() + .getInitParameter(SHOW_SAMPLES)); + + this.mapper = new ObjectMapper().registerModule(new MetricsModule(rateUnit, + durationUnit, + showSamples)); + } + + @Override + protected void doGet(HttpServletRequest req, + HttpServletResponse resp) throws ServletException, IOException { + resp.setContentType(CONTENT_TYPE); + resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); + resp.setStatus(HttpServletResponse.SC_OK); + + final OutputStream output = resp.getOutputStream(); + try { + getWriter(req).writeValue(output, registry); + } finally { + output.close(); + } + } + + private ObjectWriter getWriter(HttpServletRequest request) { + final boolean prettyPrint = Boolean.parseBoolean(request.getParameter("pretty")); + if (prettyPrint) { + return mapper.writerWithDefaultPrettyPrinter(); + } + return mapper.writer(); + } + + private TimeUnit parseTimeUnit(String value, TimeUnit defaultValue) { + try { + return TimeUnit.valueOf(String.valueOf(value).toUpperCase(Locale.US)); + } catch (IllegalArgumentException e) { + return defaultValue; + } + } +} diff --git a/metrics-servlets/src/main/java/com/yammer/metrics/servlet/PingServlet.java b/metrics-servlets/src/main/java/com/yammer/metrics/servlets/PingServlet.java similarity index 68% rename from metrics-servlets/src/main/java/com/yammer/metrics/servlet/PingServlet.java rename to metrics-servlets/src/main/java/com/yammer/metrics/servlets/PingServlet.java index 225b0d0da5..70251a6203 100644 --- a/metrics-servlets/src/main/java/com/yammer/metrics/servlet/PingServlet.java +++ b/metrics-servlets/src/main/java/com/yammer/metrics/servlets/PingServlet.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.servlet; +package com.yammer.metrics.servlets; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -8,17 +8,20 @@ import java.io.PrintWriter; /** - * An HTTP servlet which outputs a {@code text/plain} {@code "pong"} response. + * An HTTP servlets which outputs a {@code text/plain} {@code "pong"} response. */ public class PingServlet extends HttpServlet { + private static final long serialVersionUID = 3772654177231086757L; private static final String CONTENT_TYPE = "text/plain"; private static final String CONTENT = "pong"; + private static final String CACHE_CONTROL = "Cache-Control"; + private static final String NO_CACHE = "must-revalidate,no-cache,no-store"; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setStatus(HttpServletResponse.SC_OK); - resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); + resp.setHeader(CACHE_CONTROL, NO_CACHE); resp.setContentType(CONTENT_TYPE); final PrintWriter writer = resp.getWriter(); try { diff --git a/metrics-servlets/src/main/java/com/yammer/metrics/servlet/ThreadDumpServlet.java b/metrics-servlets/src/main/java/com/yammer/metrics/servlets/ThreadDumpServlet.java similarity index 55% rename from metrics-servlets/src/main/java/com/yammer/metrics/servlet/ThreadDumpServlet.java rename to metrics-servlets/src/main/java/com/yammer/metrics/servlets/ThreadDumpServlet.java index 556a16c370..045ae2142f 100644 --- a/metrics-servlets/src/main/java/com/yammer/metrics/servlet/ThreadDumpServlet.java +++ b/metrics-servlets/src/main/java/com/yammer/metrics/servlets/ThreadDumpServlet.java @@ -1,38 +1,29 @@ -package com.yammer.metrics.servlet; +package com.yammer.metrics.servlets; -import com.yammer.metrics.core.VirtualMachineMetrics; +import com.yammer.metrics.jvm.ThreadDump; +import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.OutputStream; +import java.lang.management.ManagementFactory; /** - * An HTTP servlet which outputs a {@code text/plain} dump of all threads in the VM. Only responds - * to {@code GET} requests. - */ +* An HTTP servlets which outputs a {@code text/plain} dump of all threads in the VM. Only responds +* to {@code GET} requests. +*/ public class ThreadDumpServlet extends HttpServlet { + private static final long serialVersionUID = -2690343532336103046L; private static final String CONTENT_TYPE = "text/plain"; - private final VirtualMachineMetrics vm; + private transient ThreadDump threadDump; - /** - * Creates a new {@link ThreadDumpServlet}. - */ - public ThreadDumpServlet() { - this(VirtualMachineMetrics.getInstance()); - } - - /** - * Creates a new {@link ThreadDumpServlet} with the given {@link VirtualMachineMetrics} - * instance. - * - * @param vm a {@link VirtualMachineMetrics} instance - */ - public ThreadDumpServlet(VirtualMachineMetrics vm) { - this.vm = vm; + @Override + public void init(ServletConfig config) throws ServletException { + this.threadDump = new ThreadDump(ManagementFactory.getThreadMXBean()); } @Override @@ -43,7 +34,7 @@ protected void doGet(HttpServletRequest req, resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); final OutputStream output = resp.getOutputStream(); try { - vm.threadDump(output); + threadDump.dump(output); } finally { output.close(); } diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlet/experiments/ExampleServer.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlet/experiments/ExampleServer.java deleted file mode 100644 index c1fa50dc2c..0000000000 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlet/experiments/ExampleServer.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.yammer.metrics.servlet.experiments; - -import com.yammer.metrics.core.Gauge; -import com.yammer.metrics.core.MetricsRegistry; -import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.util.thread.ThreadPool; - -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Counter; -import com.yammer.metrics.jetty.InstrumentedHandler; -import com.yammer.metrics.jetty.InstrumentedQueuedThreadPool; -import com.yammer.metrics.jetty.InstrumentedSelectChannelConnector; -import com.yammer.metrics.servlet.AdminServlet; - -public class ExampleServer { - private static final MetricsRegistry REGISTRY = Metrics.defaultRegistry(); - private static final Counter COUNTER_1 = REGISTRY.newCounter(ExampleServer.class, "wah", "doody"); - private static final Counter COUNTER_2 = REGISTRY.newCounter(ExampleServer.class, "woo"); - static { - Metrics.defaultRegistry().newGauge(ExampleServer.class, "boo", new Gauge() { - @Override - public Integer value() { - throw new RuntimeException("asplode!"); - } - }); - } - - public static void main(String[] args) throws Exception { - COUNTER_1.inc(); - COUNTER_2.inc(); - - final Server server = new Server(); - - final Connector connector = new InstrumentedSelectChannelConnector(8080); - server.addConnector(connector); - - final ThreadPool threadPool = new InstrumentedQueuedThreadPool(); - server.setThreadPool(threadPool); - - final ServletContextHandler context = new ServletContextHandler(); - context.setContextPath("/initial"); - - final ServletHolder holder = new ServletHolder(new AdminServlet()); - context.addServlet(holder, "/dingo/*"); - - server.setHandler(new InstrumentedHandler(context)); - - server.start(); - server.join(); - } -} diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/AdminServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/AdminServletTest.java deleted file mode 100755 index 10f7601759..0000000000 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/AdminServletTest.java +++ /dev/null @@ -1,172 +0,0 @@ -package com.yammer.metrics.servlet.tests; - -import com.yammer.metrics.servlet.*; -import org.junit.Before; -import org.junit.Test; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import java.io.ByteArrayOutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; - -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class AdminServletTest { - private final MetricsServlet metricsServlet = mock(MetricsServlet.class); - private final HealthCheckServlet healthCheckServlet = mock(HealthCheckServlet.class); - private final ThreadDumpServlet threadDumpServlet = mock(ThreadDumpServlet.class); - private final PingServlet pingServlet = mock(PingServlet.class); - - private final ServletConfig config = mock(ServletConfig.class); - private final ServletContext context = mock(ServletContext.class); - - private final HttpServletRequest request = mock(HttpServletRequest.class); - private final HttpServletResponse response = mock(HttpServletResponse.class); - - private final ByteArrayOutputStream output = new ByteArrayOutputStream(); - - private final AdminServlet servlet = new AdminServlet(healthCheckServlet,metricsServlet, - pingServlet, threadDumpServlet, - "/healthcheck", - "/metrics", - "/ping", - "/threads"); - - @Before - public void setUp() throws Exception { - when(config.getServletContext()).thenReturn(context); - - when(request.getContextPath()).thenReturn("/context"); - when(request.getMethod()).thenReturn("GET"); - when(request.getServletPath()).thenReturn("/admin"); - when(response.getWriter()).thenReturn(new PrintWriter(new OutputStreamWriter(output))); - - servlet.init(config); - } - - @Test - public void initializesUnderlyingServlets() throws Exception { - verify(healthCheckServlet).init(config); - verify(metricsServlet).init(config); - verify(pingServlet).init(config); - verify(threadDumpServlet).init(config); - } - - @Test - public void rendersAnHTMLPageOnRoot() throws Exception { - when(request.getPathInfo()).thenReturn("/"); - - servlet.service(request, response); - - verify(response).setStatus(200); - verify(response).setContentType("text/html"); - - assertThat(output.toString().replaceAll("\r\n", "\n"), - is("\n\n\n " + - "Codestin Search App\n\n\n " + - "

Operational Menu

\n \n\n\n")); - } - - @Test - public void rendersAnHTMLPageOnMissingURI() throws Exception { - when(request.getPathInfo()).thenReturn(null); - - servlet.service(request, response); - - verify(response).setStatus(200); - verify(response).setContentType("text/html"); - - assertThat(output.toString().replaceAll("\r\n", "\n"), - is("\n\n\n " + - "Codestin Search App\n\n\n " + - "

Operational Menu

\n \n\n\n")); - } - - @Test - public void displaysServiceNameIfSet() throws Exception { - when(request.getPathInfo()).thenReturn("/"); - - servlet.setServiceName("my service"); - - servlet.service(request, response); - - verify(response).setStatus(200); - verify(response).setContentType("text/html"); - - assertThat(output.toString().replaceAll("\r\n", "\n"), - is("\n\n\n " + - "Codestin Search App\n\n\n " + - "

Operational Menu (my service)

\n \n\n\n")); - } - - @Test - public void forwardsToMetrics() throws Exception { - when(request.getPathInfo()).thenReturn("/metrics"); - - servlet.service(request, response); - - verify(metricsServlet).service(request, response); - } - - @Test - public void forwardsToHealthCheck() throws Exception { - when(request.getPathInfo()).thenReturn("/healthcheck"); - - servlet.service(request, response); - - verify(healthCheckServlet).service(request, response); - } - - @Test - public void forwardsToPing() throws Exception { - when(request.getPathInfo()).thenReturn("/ping"); - - servlet.service(request, response); - - verify(pingServlet).service(request, response); - } - - @Test - public void forwardsToThreadDump() throws Exception { - when(request.getPathInfo()).thenReturn("/threads"); - - servlet.service(request, response); - - verify(threadDumpServlet).service(request, response); - } - - @Test - public void everythingElseIsNotFound() throws Exception { - when(request.getPathInfo()).thenReturn("/wobble"); - - servlet.service(request, response); - - verify(response).sendError(404); - } -} diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/HealthCheckServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/HealthCheckServletTest.java deleted file mode 100644 index b3698b78c5..0000000000 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/HealthCheckServletTest.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.yammer.metrics.servlet.tests; - -import com.yammer.metrics.core.HealthCheck; -import com.yammer.metrics.core.HealthCheckRegistry; -import com.yammer.metrics.servlet.HealthCheckServlet; -import org.junit.Before; -import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.util.SortedMap; -import java.util.TreeMap; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -public class HealthCheckServletTest { - private final HealthCheckRegistry registry = mock(HealthCheckRegistry.class); - private final HealthCheckServlet servlet = new HealthCheckServlet(registry); - private final SortedMap results = new TreeMap(); - - private final HttpServletRequest request = mock(HttpServletRequest.class); - private final HttpServletResponse response = mock(HttpServletResponse.class); - private final ByteArrayOutputStream output = new ByteArrayOutputStream(); - - @Before - public void setUp() throws Exception { - when(request.getMethod()).thenReturn("GET"); - - when(registry.runHealthChecks()).thenReturn(results); - - when(response.getWriter()).thenReturn(new PrintWriter(new OutputStreamWriter(output))); - } - - @Test - public void returnsNotImplementedIfNoHealthChecksAreRegistered() throws Exception { - results.clear(); - - servlet.service(request, response); - - assertThat(output.toString().replaceAll("\r\n", "\n"), - is("! No health checks registered.\n")); - - verify(response).setStatus(501); - verify(response).setContentType("text/plain"); - } - - @Test - public void returnsOkIfAllHealthChecksAreHealthy() throws Exception { - results.put("one", HealthCheck.Result.healthy()); - results.put("two", HealthCheck.Result.healthy("msg")); - - servlet.service(request, response); - - assertThat(output.toString(), - is( - "* one: OK\n" + - "* two: OK\n" + - " msg\n")); - - verify(response).setStatus(200); - verify(response).setContentType("text/plain"); - } - - @Test - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") - public void returnsServerErrorIfHealthChecksAreUnhealthy() throws Exception { - final IOException ex = mock(IOException.class); - when(ex.getMessage()).thenReturn("ex msg"); - doAnswer(new Answer() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - ((PrintWriter) invocation.getArguments()[0]).println("stack trace"); - return null; - } - }).when(ex).printStackTrace(any(PrintWriter.class)); - - results.put("one", HealthCheck.Result.unhealthy("msg")); - results.put("two", HealthCheck.Result.unhealthy(ex)); - - servlet.service(request, response); - - assertThat(output.toString().replaceAll("\r\n", "\n"), - is( - "! one: ERROR\n" + - "! msg\n" + - "! two: ERROR\n" + - "! ex msg\n" + - "\n" + - "stack trace\n\n")); - - verify(response).setStatus(500); - verify(response).setContentType("text/plain"); - } - - @Test - public void picksUpTheHealthCheckRegistryFromTheConfig() throws Exception { - final SortedMap otherResults = new TreeMap(); - otherResults.put("one", HealthCheck.Result.healthy()); - - final HealthCheckRegistry reg = mock(HealthCheckRegistry.class); - when(reg.runHealthChecks()).thenReturn(otherResults); - - final ServletContext context = mock(ServletContext.class); - when(context.getAttribute(HealthCheckServlet.REGISTRY_ATTRIBUTE)).thenReturn(reg); - - final ServletConfig config = mock(ServletConfig.class); - when(config.getServletContext()).thenReturn(context); - - servlet.init(config); - servlet.service(request, response); - - assertThat(output.toString(), - is("* one: OK\n")); - - verify(response).setStatus(200); - verify(response).setContentType("text/plain"); - } - - @Test - public void doesNotThrowAnErrorIfTheConfigIsWeird() throws Exception { - final ServletContext context = mock(ServletContext.class); - when(context.getAttribute(HealthCheckServlet.REGISTRY_ATTRIBUTE)).thenReturn("yes wait what"); - - final ServletConfig config = mock(ServletConfig.class); - when(config.getServletContext()).thenReturn(context); - - servlet.init(config); - servlet.service(request, response); - } -} diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/MetricsServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/MetricsServletTest.java deleted file mode 100644 index a38c6d4566..0000000000 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/MetricsServletTest.java +++ /dev/null @@ -1,207 +0,0 @@ -package com.yammer.metrics.servlet.tests; - -import com.fasterxml.jackson.core.JsonEncoding; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.yammer.metrics.core.Clock; -import com.yammer.metrics.core.Gauge; -import com.yammer.metrics.core.MetricsRegistry; -import com.yammer.metrics.core.VirtualMachineMetrics; -import com.yammer.metrics.servlet.MetricsServlet; -import org.junit.Before; -import org.junit.Test; - -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.ByteArrayOutputStream; -import java.util.Map; -import java.util.TreeMap; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class MetricsServletTest { - private final Clock clock = mock(Clock.class); - private final VirtualMachineMetrics vm = mock(VirtualMachineMetrics.class); - private final MetricsRegistry registry = new MetricsRegistry(clock); - private final JsonFactory factory = mock(JsonFactory.class); - - private final HttpServletRequest request = mock(HttpServletRequest.class); - private final HttpServletResponse response = mock(HttpServletResponse.class); - private final ServletOutputStream output = mock(ServletOutputStream.class); - private final MetricsServlet servlet = new MetricsServlet(clock, vm, registry, factory, false); - - private final ByteArrayOutputStream json = new ByteArrayOutputStream(); - - @Before - public void setUp() throws Exception { - when(clock.time()).thenReturn(12345678L); - - when(request.getMethod()).thenReturn("GET"); - - when(response.getOutputStream()).thenReturn(output); - - final JsonGenerator generator = new JsonFactory(new ObjectMapper()).createJsonGenerator(json, - JsonEncoding.UTF8); - when(factory.createJsonGenerator(output, JsonEncoding.UTF8)).thenReturn(generator); - } - - @Test - public void generatesVirtualMachineMetrics() throws Exception { - when(vm.name()).thenReturn("vm"); - when(vm.version()).thenReturn("version"); - when(vm.totalInit()).thenReturn(1.0); - when(vm.totalUsed()).thenReturn(2.0); - when(vm.totalMax()).thenReturn(3.0); - when(vm.totalCommitted()).thenReturn(4.0); - when(vm.heapInit()).thenReturn(5.0); - when(vm.heapUsed()).thenReturn(6.0); - when(vm.heapMax()).thenReturn(7.0); - when(vm.heapCommitted()).thenReturn(8.0); - - final Map pools = new TreeMap(); - pools.put("one", 100.0); - pools.put("two", 200.0); - when(vm.memoryPoolUsage()).thenReturn(pools); - - when(vm.daemonThreadCount()).thenReturn(300); - when(vm.threadCount()).thenReturn(400); - - when(vm.heapUsage()).thenReturn(34.0); - when(vm.nonHeapUsage()).thenReturn(37.0); - when(vm.uptime()).thenReturn(9991L); - when(vm.fileDescriptorUsage()).thenReturn(0.222); - - final Map threads = new TreeMap(); - threads.put(Thread.State.BLOCKED, 0.33); - when(vm.threadStatePercentages()).thenReturn(threads); - - final Map gcs = - new TreeMap(); - - final VirtualMachineMetrics.GarbageCollectorStats gc = mock(VirtualMachineMetrics.GarbageCollectorStats.class); - when(gc.getTime(TimeUnit.MILLISECONDS)).thenReturn(40L); - when(gc.getRuns()).thenReturn(20L); - gcs.put("one", gc); - when(vm.garbageCollectors()).thenReturn(gcs); - - final VirtualMachineMetrics.BufferPoolStats direct = mock(VirtualMachineMetrics.BufferPoolStats.class); - when(direct.getCount()).thenReturn(1L); - when(direct.getMemoryUsed()).thenReturn(2L); - when(direct.getTotalCapacity()).thenReturn(3L); - - final VirtualMachineMetrics.BufferPoolStats mapped = mock(VirtualMachineMetrics.BufferPoolStats.class); - when(mapped.getCount()).thenReturn(10L); - when(mapped.getMemoryUsed()).thenReturn(20L); - when(mapped.getTotalCapacity()).thenReturn(30L); - - final Map bufferPoolStats = - new TreeMap(); - - bufferPoolStats.put("direct", direct); - bufferPoolStats.put("mapped", mapped); - - when(vm.getBufferPoolStats()).thenReturn(bufferPoolStats); - - final MetricsServlet servlet = new MetricsServlet(clock, vm, registry, factory, true); - - servlet.service(request, response); - - assertThat(json.toString(), - is("{\"jvm\":{\"vm\":{\"name\":\"vm\",\"version\":\"version\"},\"memory\":{" + - "\"totalInit\":1.0,\"totalUsed\":2.0,\"totalMax\":3.0," + - "\"totalCommitted\":4.0,\"heapInit\":5.0,\"heapUsed\":6.0,\"" + - "heapMax\":7.0,\"heapCommitted\":8.0,\"heap_usage\":34.0," + - "\"non_heap_usage\":37.0,\"memory_pool_usages\":{\"one\":100.0," + - "\"two\":200.0}},\"buffers\":{\"direct\":{\"count\":1," + - "\"memoryUsed\":2,\"totalCapacity\":3},\"mapped\":{\"count\":10," + - "\"memoryUsed\":20,\"totalCapacity\":30}},\"daemon_thread_count\":300," + - "\"thread_count\":400,\"current_time\":12345678,\"uptime\":9991," + - "\"fd_usage\":0.222,\"thread-states\":{\"blocked\":0.33}," + - "\"garbage-collectors\":{\"one\":{\"runs\":20,\"time\":40}}}}")); - } - - @Test - public void generatesGauges() throws Exception { - registry.newGauge(MetricsServletTest.class, "gauge", new Gauge() { - @Override - public Double value() { - return 22.2; - } - }); - - servlet.service(request, response); - - assertThat(json.toString(), - is("{\"com.yammer.metrics.servlet.tests.MetricsServletTest\":" + - "{\"gauge\":{\"type\":\"gauge\",\"value\":22.2}}}")); - } - - @Test - public void generatesCounters() throws Exception { - registry.newCounter(MetricsServletTest.class, "counter").inc(12); - - servlet.service(request, response); - - assertThat(json.toString(), - is("{\"com.yammer.metrics.servlet.tests.MetricsServletTest\":" + - "{\"counter\":{\"type\":\"counter\",\"count\":12}}}")); - } - - @Test - public void generatesHistograms() throws Exception { - registry.newHistogram(MetricsServletTest.class, "histogram").update(12); - - servlet.service(request, response); - - assertThat(json.toString(), - is("{\"com.yammer.metrics.servlet.tests.MetricsServletTest\":" + - "{\"histogram\":{\"type\":\"histogram\",\"count\":1,\"min\":12.0," + - "\"max\":12.0,\"mean\":12.0,\"std_dev\":0.0,\"median\":12.0," + - "\"p75\":12.0,\"p95\":12.0,\"p98\":12.0,\"p99\":12.0,\"p999\":12.0}}}")); - } - - @Test - public void generatesMeters() throws Exception { - when(clock.tick()).thenReturn(100000L, 110000L); - - registry.newMeter(MetricsServletTest.class, "meter", "things", TimeUnit.SECONDS) - .mark(12); - - servlet.service(request, response); - - assertThat(json.toString(), - is("{\"com.yammer.metrics.servlet.tests.MetricsServletTest\":" + - "{\"meter\":{\"type\":\"meter\",\"event_type\":\"things\"," + - "\"unit\":\"seconds\",\"count\":12,\"mean\":1200000.0," + - "\"m1\":0.0,\"m5\":0.0,\"m15\":0.0}}}")); - } - - @Test - public void generatesTimers() throws Exception { - when(clock.tick()).thenReturn(100000L, 110000L); - - registry.newTimer(MetricsServletTest.class, "timer").update(100, TimeUnit.MILLISECONDS); - - servlet.service(request, response); - - assertThat(json.toString(), - is("{\"com.yammer.metrics.servlet.tests.MetricsServletTest\":{\"timer\":" + - "{\"type\":\"timer\",\"duration\":{\"unit\":\"milliseconds\"," + - "\"min\":100.0,\"max\":100.0,\"mean\":100.0,\"std_dev\":0.0," + - "\"median\":100.0,\"p75\":100.0,\"p95\":100.0,\"p98\":100.0," + - "\"p99\":100.0,\"p999\":100.0},\"rate\":{\"unit\":\"seconds\"," + - "\"count\":1,\"mean\":100000.0,\"m1\":0.0,\"m5\":0.0," + - "\"m15\":0.0}}}}")); - } - - // TODO: 1/19/12 -- test class prefix - // TODO: 1/19/12 -- test pretty printing - // TODO: 1/19/12 -- test full sample dumping - // TODO: 1/19/12 -- test servlet configuring -} diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/PingServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/PingServletTest.java deleted file mode 100644 index 5aa7ab255e..0000000000 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/PingServletTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.yammer.metrics.servlet.tests; - -import com.yammer.metrics.servlet.PingServlet; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InOrder; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.PrintWriter; - -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class PingServletTest { - private final PingServlet servlet = new PingServlet(); - - private final HttpServletRequest request = mock(HttpServletRequest.class); - private final HttpServletResponse response = mock(HttpServletResponse.class); - - private final PrintWriter output = mock(PrintWriter.class); - - @Before - public void setUp() throws Exception { - when(request.getMethod()).thenReturn("GET"); - - when(response.getWriter()).thenReturn(output); - } - - @Test - public void printsPongOnGET() throws Exception { - servlet.service(request, response); - - final InOrder inOrder = inOrder(response, output); - inOrder.verify(response).setStatus(200); - inOrder.verify(response).setContentType("text/plain"); - inOrder.verify(output).println("pong"); - inOrder.verify(output).close(); - } -} diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/ThreadDumpServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/ThreadDumpServletTest.java deleted file mode 100644 index e95bf9e18e..0000000000 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlet/tests/ThreadDumpServletTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.yammer.metrics.servlet.tests; - -import com.yammer.metrics.core.VirtualMachineMetrics; -import com.yammer.metrics.servlet.ThreadDumpServlet; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InOrder; - -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import static org.mockito.Mockito.*; - -public class ThreadDumpServletTest { - private final VirtualMachineMetrics vm = mock(VirtualMachineMetrics.class); - private final ThreadDumpServlet servlet = new ThreadDumpServlet(vm); - - private final HttpServletRequest request = mock(HttpServletRequest.class); - private final HttpServletResponse response = mock(HttpServletResponse.class); - - private final ServletOutputStream output = mock(ServletOutputStream.class); - - @Before - public void setUp() throws Exception { - when(request.getMethod()).thenReturn("GET"); - - when(response.getOutputStream()).thenReturn(output); - } - - @Test - public void printsAThreadDumpOnGET() throws Exception { - servlet.service(request, response); - - final InOrder inOrder = inOrder(response, output, vm); - inOrder.verify(response).setStatus(200); - inOrder.verify(response).setContentType("text/plain"); - inOrder.verify(vm).threadDump(output); - inOrder.verify(output).close(); - } -} diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/experiments/ExampleServer.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/experiments/ExampleServer.java new file mode 100644 index 0000000000..2a61cbb189 --- /dev/null +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/experiments/ExampleServer.java @@ -0,0 +1,62 @@ +package com.yammer.metrics.servlets.experiments; + +import com.yammer.metrics.Clock; +import com.yammer.metrics.Counter; +import com.yammer.metrics.Gauge; +import com.yammer.metrics.MetricRegistry; +import com.yammer.metrics.health.HealthCheckRegistry; +import com.yammer.metrics.jetty8.InstrumentedHandler; +import com.yammer.metrics.jetty8.InstrumentedQueuedThreadPool; +import com.yammer.metrics.jetty8.InstrumentedSelectChannelConnector; +import com.yammer.metrics.servlets.AdminServlet; +import com.yammer.metrics.servlets.HealthCheckServlet; +import com.yammer.metrics.servlets.MetricsServlet; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.thread.ThreadPool; + +import static com.yammer.metrics.MetricRegistry.name; + +public class ExampleServer { + private static final MetricRegistry REGISTRY = new MetricRegistry("example"); + private static final Counter COUNTER_1 = REGISTRY.counter(name(ExampleServer.class, + "wah", + "doody")); + private static final Counter COUNTER_2 = REGISTRY.counter(name(ExampleServer.class, "woo")); + static { + REGISTRY.register(name(ExampleServer.class, "boo"), new Gauge() { + @Override + public Integer getValue() { + throw new RuntimeException("asplode!"); + } + }); + } + + public static void main(String[] args) throws Exception { + COUNTER_1.inc(); + COUNTER_2.inc(); + + final Server server = new Server(); + + final Connector connector = new InstrumentedSelectChannelConnector(REGISTRY, 8080, Clock.defaultClock()); + server.addConnector(connector); + + final ThreadPool threadPool = new InstrumentedQueuedThreadPool(REGISTRY); + server.setThreadPool(threadPool); + + final ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/initial"); + context.setAttribute(MetricsServlet.METRICS_REGISTRY, REGISTRY); + context.setAttribute(HealthCheckServlet.HEALTH_CHECK_REGISTRY, new HealthCheckRegistry()); + + final ServletHolder holder = new ServletHolder(new AdminServlet()); + context.addServlet(holder, "/dingo/*"); + + server.setHandler(new InstrumentedHandler(REGISTRY, context)); + + server.start(); + server.join(); + } +} diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AbstractServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AbstractServletTest.java new file mode 100644 index 0000000000..63bd6c8c40 --- /dev/null +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AbstractServletTest.java @@ -0,0 +1,29 @@ +package com.yammer.metrics.servlets.tests; + +import org.eclipse.jetty.testing.HttpTester; +import org.eclipse.jetty.testing.ServletTester; +import org.junit.After; +import org.junit.Before; + +public abstract class AbstractServletTest { + private final ServletTester tester = new ServletTester(); + protected final HttpTester request = new HttpTester(); + protected final HttpTester response = new HttpTester(); + + @Before + public void setUpTester() throws Exception { + setUp(tester); + tester.start(); + } + + protected abstract void setUp(ServletTester tester); + + @After + public void tearDownTester() throws Exception { + tester.stop(); + } + + protected void processRequest() throws Exception { + response.parse(tester.getResponses(request.generate())); + } +} diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AdminServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AdminServletTest.java new file mode 100755 index 0000000000..5771324329 --- /dev/null +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AdminServletTest.java @@ -0,0 +1,58 @@ +package com.yammer.metrics.servlets.tests; + +import com.yammer.metrics.MetricRegistry; +import com.yammer.metrics.health.HealthCheckRegistry; +import com.yammer.metrics.servlets.AdminServlet; +import org.eclipse.jetty.testing.ServletTester; +import org.junit.Before; +import org.junit.Test; + +import static org.fest.assertions.api.Assertions.assertThat; + +public class AdminServletTest extends AbstractServletTest { + private final MetricRegistry registry = new MetricRegistry("test"); + private final HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry(); + + @Override + protected void setUp(ServletTester tester) { + tester.setContextPath("/context"); + + tester.setAttribute("com.yammer.metrics.servlets.MetricsServlet.registry", registry); + tester.setAttribute("com.yammer.metrics.servlets.HealthCheckServlet.registry", healthCheckRegistry); + tester.addServlet(AdminServlet.class, "/admin"); + } + + @Before + public void setUp() throws Exception { + request.setMethod("GET"); + request.setURI("/context/admin"); + request.setVersion("HTTP/1.0"); + } + + @Test + public void returnsA200() throws Exception { + processRequest(); + + assertThat(response.getStatus()) + .isEqualTo(200); + assertThat(response.getContent()) + .isEqualTo("\n" + + "\n" + + "\n" + + " Codestin Search App\n" + + "\n" + + "\n" + + "

Operational Menu

\n" + + " \n" + + "\n" + + "\n"); + assertThat(response.getContentType()) + .isEqualTo("text/html;charset=ISO-8859-1"); + } +} diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/HealthCheckServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/HealthCheckServletTest.java new file mode 100644 index 0000000000..e02dcbbed4 --- /dev/null +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/HealthCheckServletTest.java @@ -0,0 +1,121 @@ +package com.yammer.metrics.servlets.tests; + +import com.yammer.metrics.health.HealthCheck; +import com.yammer.metrics.health.HealthCheckRegistry; +import com.yammer.metrics.servlets.HealthCheckServlet; +import org.eclipse.jetty.testing.ServletTester; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static org.fest.assertions.api.Assertions.assertThat; + +public class HealthCheckServletTest extends AbstractServletTest { + private final HealthCheckRegistry registry = new HealthCheckRegistry(); + private final ExecutorService threadPool = Executors.newCachedThreadPool(); + + @Override + protected void setUp(ServletTester tester) { + tester.addServlet(HealthCheckServlet.class, "/healthchecks"); + tester.setAttribute("com.yammer.metrics.servlets.HealthCheckServlet.registry", registry); + tester.setAttribute("com.yammer.metrics.servlets.HealthCheckServlet.executor", threadPool); + } + + @Before + public void setUp() throws Exception { + request.setMethod("GET"); + request.setURI("/healthchecks"); + request.setVersion("HTTP/1.0"); + } + + @After + public void tearDown() throws Exception { + threadPool.shutdown(); + } + + @Test + public void returns501IfNoHealthChecksAreRegistered() throws Exception { + processRequest(); + + assertThat(response.getStatus()) + .isEqualTo(501); + assertThat(response.getContent()) + .isEqualTo("{}"); + assertThat(response.getContentType()) + .isEqualTo("application/json"); + } + + @Test + public void returnsA200IfAllHealthChecksAreHealthy() throws Exception { + registry.register("fun", new HealthCheck() { + @Override + protected Result check() throws Exception { + return Result.healthy("whee"); + } + }); + + processRequest(); + + assertThat(response.getStatus()) + .isEqualTo(200); + assertThat(response.getContent()) + .isEqualTo("{\"fun\":{\"healthy\":true,\"message\":\"whee\"}}"); + assertThat(response.getContentType()) + .isEqualTo("application/json"); + } + + @Test + public void returnsA500IfAnyHealthChecksAreUnhealthy() throws Exception { + registry.register("fun", new HealthCheck() { + @Override + protected Result check() throws Exception { + return Result.healthy("whee"); + } + }); + + registry.register("notFun", new HealthCheck() { + @Override + protected Result check() throws Exception { + return Result.unhealthy("whee"); + } + }); + + processRequest(); + + assertThat(response.getStatus()) + .isEqualTo(500); + assertThat(response.getContent()) + .isEqualTo("{\"fun\":{\"healthy\":true,\"message\":\"whee\"},\"notFun\":{\"healthy\":false,\"message\":\"whee\"}}"); + assertThat(response.getContentType()) + .isEqualTo("application/json"); + } + + @Test + public void optionallyPrettyPrintsTheJson() throws Exception { + registry.register("fun", new HealthCheck() { + @Override + protected Result check() throws Exception { + return Result.healthy("whee"); + } + }); + + request.setURI("/healthchecks?pretty=true"); + + processRequest(); + + assertThat(response.getStatus()) + .isEqualTo(200); + assertThat(response.getContent()) + .isEqualTo(String.format("{%n" + + " \"fun\" : {%n" + + " \"healthy\" : true,%n" + + " \"message\" : \"whee\"%n" + + " }%n" + + "}")); + assertThat(response.getContentType()) + .isEqualTo("application/json"); + } +} diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java new file mode 100644 index 0000000000..6c909b1e65 --- /dev/null +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java @@ -0,0 +1,146 @@ +package com.yammer.metrics.servlets.tests; + +import com.yammer.metrics.Clock; +import com.yammer.metrics.Gauge; +import com.yammer.metrics.MetricRegistry; +import com.yammer.metrics.servlets.MetricsServlet; +import org.eclipse.jetty.testing.ServletTester; +import org.junit.Before; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MetricsServletTest extends AbstractServletTest { + private final Clock clock = mock(Clock.class); + private final MetricRegistry registry = new MetricRegistry("test", clock); + + @Override + protected void setUp(ServletTester tester) { + tester.setAttribute("com.yammer.metrics.servlets.MetricsServlet.registry", registry); + tester.addServlet(MetricsServlet.class, "/metrics"); + } + + @Before + public void setUp() throws Exception { + when(clock.getTick()).thenReturn(100L, 200L, 300L, 400L); + + registry.register("g1", new Gauge() { + @Override + public Long getValue() { + return 100L; + } + }); + registry.counter("c").inc(); + registry.histogram("h").update(1); + registry.meter("m").mark(); + registry.timer("t").update(1, TimeUnit.SECONDS); + + request.setMethod("GET"); + request.setURI("/metrics"); + request.setVersion("HTTP/1.0"); + } + + @Test + public void returnsA200() throws Exception { + processRequest(); + + assertThat(response.getStatus()) + .isEqualTo(200); + assertThat(response.getContent()) + .isEqualTo("{" + + "\"name\":\"test\"," + + "\"version\":\"3.0.0\"," + + "\"gauges\":{" + + "\"g1\":{\"value\":100}" + + "}," + + "\"counters\":{" + + "\"c\":{\"count\":1}" + + "}," + + "\"histograms\":{" + + "\"h\":{\"count\":1,\"max\":1,\"mean\":1.0,\"min\":1,\"p50\":1.0,\"p75\":1.0,\"p95\":1.0,\"p98\":1.0,\"p99\":1.0,\"p999\":1.0,\"stddev\":0.0}" + + "}," + + "\"meters\":{" + + "\"m\":{\"count\":1,\"m15_rate\":0.0,\"m1_rate\":0.0,\"m5_rate\":0.0,\"mean_rate\":3333333.3333333335,\"units\":\"events/second\"}},\"timers\":{\"t\":{\"count\":1,\"max\":1.0,\"mean\":1.0,\"min\":1.0,\"p50\":1.0,\"p75\":1.0,\"p95\":1.0,\"p98\":1.0,\"p99\":1.0,\"p999\":1.0,\"stddev\":0.0,\"m15_rate\":0.0,\"m1_rate\":0.0,\"m5_rate\":0.0,\"mean_rate\":1.0E7,\"duration_units\":\"seconds\",\"rate_units\":\"calls/second\"}" + + "}" + + "}"); + assertThat(response.getContentType()) + .isEqualTo("application/json"); + } + + @Test + public void optionallyPrettyPrintsTheJson() throws Exception { + request.setURI("/metrics?pretty=true"); + + processRequest(); + + assertThat(response.getStatus()) + .isEqualTo(200); + assertThat(response.getContent()) + .isEqualTo(String.format("{%n" + + " \"name\" : \"test\",%n" + + " \"version\" : \"3.0.0\",%n" + + " \"gauges\" : {%n" + + " \"g1\" : {%n" + + " \"value\" : 100%n" + + " }%n" + + " },%n" + + " \"counters\" : {%n" + + " \"c\" : {%n" + + " \"count\" : 1%n" + + " }%n" + + " },%n" + + " \"histograms\" : {%n" + + " \"h\" : {%n" + + " \"count\" : 1,%n" + + " \"max\" : 1,%n" + + " \"mean\" : 1.0,%n" + + " \"min\" : 1,%n" + + " \"p50\" : 1.0,%n" + + " \"p75\" : 1.0,%n" + + " \"p95\" : 1.0,%n" + + " \"p98\" : 1.0,%n" + + " \"p99\" : 1.0,%n" + + " \"p999\" : 1.0,%n" + + " \"stddev\" : 0.0%n" + + " }%n" + + " },%n" + + " \"meters\" : {%n" + + " \"m\" : {%n" + + " \"count\" : 1,%n" + + " \"m15_rate\" : 0.0,%n" + + " \"m1_rate\" : 0.0,%n" + + " \"m5_rate\" : 0.0,%n" + + " \"mean_rate\" : 3333333.3333333335,%n" + + " \"units\" : \"events/second\"%n" + + " }%n" + + " },%n" + + " \"timers\" : {%n" + + " \"t\" : {%n" + + " \"count\" : 1,%n" + + " \"max\" : 1.0,%n" + + " \"mean\" : 1.0,%n" + + " \"min\" : 1.0,%n" + + " \"p50\" : 1.0,%n" + + " \"p75\" : 1.0,%n" + + " \"p95\" : 1.0,%n" + + " \"p98\" : 1.0,%n" + + " \"p99\" : 1.0,%n" + + " \"p999\" : 1.0,%n" + + " \"stddev\" : 0.0,%n" + + " \"m15_rate\" : 0.0,%n" + + " \"m1_rate\" : 0.0,%n" + + " \"m5_rate\" : 0.0,%n" + + " \"mean_rate\" : 1.0E7,%n" + + " \"duration_units\" : \"seconds\",%n" + + " \"rate_units\" : \"calls/second\"%n" + + " }%n" + + " }%n" + + "}")); + assertThat(response.getContentType()) + .isEqualTo("application/json"); + } +} diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/PingServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/PingServletTest.java new file mode 100644 index 0000000000..d45fdcda0a --- /dev/null +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/PingServletTest.java @@ -0,0 +1,49 @@ +package com.yammer.metrics.servlets.tests; + +import com.yammer.metrics.servlets.PingServlet; +import org.eclipse.jetty.testing.ServletTester; +import org.junit.Before; +import org.junit.Test; + +import static org.fest.assertions.api.Assertions.assertThat; + +public class PingServletTest extends AbstractServletTest { + @Override + protected void setUp(ServletTester tester) { + tester.addServlet(PingServlet.class, "/ping"); + } + + @Before + public void setUp() throws Exception { + request.setMethod("GET"); + request.setURI("/ping"); + request.setVersion("HTTP/1.0"); + + processRequest(); + } + + @Test + public void returns200OK() throws Exception { + assertThat(response.getStatus()) + .isEqualTo(200); + } + + @Test + public void returnsPong() throws Exception { + assertThat(response.getContent()) + .isEqualTo(String.format("pong%n")); + } + + @Test + public void returnsTextPlain() throws Exception { + assertThat(response.getContentType()) + .isEqualTo("text/plain;charset=ISO-8859-1"); + } + + @Test + public void returnsUncacheable() throws Exception { + assertThat(response.getHeader("Cache-Control")) + .isEqualTo("must-revalidate,no-cache,no-store"); + + } +} diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/ThreadDumpServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/ThreadDumpServletTest.java new file mode 100644 index 0000000000..680e729005 --- /dev/null +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/ThreadDumpServletTest.java @@ -0,0 +1,49 @@ +package com.yammer.metrics.servlets.tests; + +import com.yammer.metrics.servlets.ThreadDumpServlet; +import org.eclipse.jetty.testing.ServletTester; +import org.junit.Before; +import org.junit.Test; + +import static org.fest.assertions.api.Assertions.assertThat; + +public class ThreadDumpServletTest extends AbstractServletTest { + @Override + protected void setUp(ServletTester tester) { + tester.addServlet(ThreadDumpServlet.class, "/threads"); + } + + @Before + public void setUp() throws Exception { + request.setMethod("GET"); + request.setURI("/threads"); + request.setVersion("HTTP/1.0"); + + processRequest(); + } + + @Test + public void returns200OK() throws Exception { + assertThat(response.getStatus()) + .isEqualTo(200); + } + + @Test + public void returnsAThreadDump() throws Exception { + assertThat(response.getContent()) + .contains("Finalizer"); + } + + @Test + public void returnsTextPlain() throws Exception { + assertThat(response.getContentType()) + .isEqualTo("text/plain"); + } + + @Test + public void returnsUncacheable() throws Exception { + assertThat(response.getHeader("Cache-Control")) + .isEqualTo("must-revalidate,no-cache,no-store"); + + } +} From aa46a85ac5a4c812ca68d83dd921c262ffd2838e Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 18:28:14 -0700 Subject: [PATCH 0045/2558] Fix some format strings! --- .../src/main/java/com/yammer/metrics/ConsoleReporter.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java b/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java index c7b53e962e..f7466dea8c 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java @@ -89,8 +89,8 @@ private void printGauge(Map.Entry entry) { private void printHistogram(Histogram histogram) { output.printf(locale, " count = %d%n", histogram.getCount()); - output.printf(locale, " min = %2.2f%n", histogram.getMin()); - output.printf(locale, " max = %2.2f%n", histogram.getMax()); + output.printf(locale, " min = %d%n", histogram.getMin()); + output.printf(locale, " max = %d%n", histogram.getMax()); output.printf(locale, " mean = %2.2f%n", histogram.getMean()); output.printf(locale, " stddev = %2.2f%n", histogram.getStdDev()); printSnapshot(histogram.getSnapshot()); @@ -99,8 +99,8 @@ private void printHistogram(Histogram histogram) { private void printTimer(Timer timer) { final Snapshot snapshot = timer.getSnapshot(); printMetered(timer); - output.printf(locale, " min = %2.2f%n", timer.getMin()); - output.printf(locale, " max = %2.2f%n", timer.getMax()); + output.printf(locale, " min = %d%n", timer.getMin()); + output.printf(locale, " max = %d%n", timer.getMax()); output.printf(locale, " mean = %2.2f%n", timer.getMean()); output.printf(locale, " stddev = %2.2f%n", timer.getStdDev()); printSnapshot(snapshot); From 07daa7306ca3036e324fe7949a428bd307b5e3f5 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 18:28:49 -0700 Subject: [PATCH 0046/2558] Stop accidentally using the default encoding. --- .../src/main/java/com/yammer/metrics/CsvReporter.java | 9 ++++----- .../src/main/java/com/yammer/metrics/Snapshot.java | 4 +++- .../src/main/java/com/yammer/metrics/jvm/ThreadDump.java | 6 +++++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java index 37783090b8..83afbbb6a0 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java @@ -3,10 +3,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; +import java.io.*; +import java.nio.charset.Charset; import java.util.Locale; import java.util.Map; import java.util.SortedMap; @@ -16,6 +14,7 @@ public class CsvReporter extends AbstractPollingReporter { private static final Logger LOGGER = LoggerFactory.getLogger(CsvReporter.class); + private static final Charset UTF_8 = Charset.forName("UTF-8"); private final File directory; private final Locale locale; @@ -149,7 +148,7 @@ private void report(long timestamp, String name, String header, String line, Obj final File file = new File(directory, sanitize(name) + ".csv"); final boolean fileAlreadyExists = file.exists(); if (fileAlreadyExists || file.createNewFile()) { - final PrintWriter out = new PrintWriter(new FileOutputStream(file, true)); + final PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file, true), UTF_8)); try { if (!fileAlreadyExists) { out.println("t," + header); diff --git a/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java b/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java index ec09ead4c5..80c525a9b6 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java @@ -1,6 +1,7 @@ package com.yammer.metrics; import java.io.*; +import java.nio.charset.Charset; import java.util.*; import static java.lang.Math.floor; @@ -9,6 +10,7 @@ * A statistical snapshot of a {@link Snapshot}. */ public class Snapshot { + private static final Charset UTF_8 = Charset.forName("UTF-8"); private static final double MEDIAN_Q = 0.5; private static final double P75_Q = 0.75; private static final double P95_Q = 0.95; @@ -150,7 +152,7 @@ public long[] getValues() { * @param output an output stream */ public void dump(OutputStream output) { - final PrintWriter out = new PrintWriter(output); + final PrintWriter out = new PrintWriter(new OutputStreamWriter(output, UTF_8)); try { for (long value : values) { out.printf("%d%n", value); diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDump.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDump.java index bb78fc28a4..a608b38eff 100644 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDump.java +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDump.java @@ -1,16 +1,20 @@ package com.yammer.metrics.jvm; import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.lang.management.LockInfo; import java.lang.management.MonitorInfo; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; +import java.nio.charset.Charset; /** * A convenience class for getting a thread dump. */ public class ThreadDump { + private static final Charset UTF_8 = Charset.forName("UTF-8"); + private final ThreadMXBean threadMXBean; public ThreadDump(ThreadMXBean threadMXBean) { @@ -24,7 +28,7 @@ public ThreadDump(ThreadMXBean threadMXBean) { */ public void dump(OutputStream out) { final ThreadInfo[] threads = this.threadMXBean.dumpAllThreads(true, true); - final PrintWriter writer = new PrintWriter(out, true); + final PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, UTF_8)); for (int ti = threads.length - 1; ti >= 0; ti--) { final ThreadInfo t = threads[ti]; From 5299fff678fec7b0a6968431a5a2ffe735530397 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 18:29:26 -0700 Subject: [PATCH 0047/2558] Tidy things up and get Findbugs OK. --- findbugs-exclude.xml | 15 ++++++++++++--- pom.xml | 8 +------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/findbugs-exclude.xml b/findbugs-exclude.xml index 4eb91a9923..e213a07e55 100644 --- a/findbugs-exclude.xml +++ b/findbugs-exclude.xml @@ -1,8 +1,17 @@ - + - - + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index b6a582ff5d..bcd1171a25 100644 --- a/pom.xml +++ b/pom.xml @@ -105,12 +105,6 @@ 2.0M10 test
- - org.hamcrest - hamcrest-all - 1.3 - test - org.mockito mockito-all @@ -220,7 +214,7 @@ org.codehaus.mojo findbugs-maven-plugin - 2.3.3 + 2.4.0 Max Default From 2f909dc4b594f4782ee6a53df7c82d1ccd2b18b5 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 18:33:56 -0700 Subject: [PATCH 0048/2558] Don't need the old thread dump logic. --- .../metrics/jvm/VirtualMachineMetrics.java | 72 ------------------- 1 file changed, 72 deletions(-) diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/VirtualMachineMetrics.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/VirtualMachineMetrics.java index 321a699829..018c49fb85 100644 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/VirtualMachineMetrics.java +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/VirtualMachineMetrics.java @@ -1,8 +1,6 @@ package com.yammer.metrics.jvm; import javax.management.*; -import java.io.OutputStream; -import java.io.PrintWriter; import java.lang.Thread.State; import java.lang.management.*; import java.lang.reflect.InvocationTargetException; @@ -398,76 +396,6 @@ public Map getThreadStatePercentages() { return Collections.unmodifiableMap(conditions); } - /** - * Dumps all of the threads' current information to an output stream. - * - * @param out an output stream - */ - public void getThreadDump(OutputStream out) { - final ThreadInfo[] threads = this.threads.dumpAllThreads(true, true); - final PrintWriter writer = new PrintWriter(out, true); - - for (int ti = threads.length - 1; ti >= 0; ti--) { - final ThreadInfo t = threads[ti]; - writer.printf("%s id=%d state=%s", - t.getThreadName(), - t.getThreadId(), - t.getThreadState()); - final LockInfo lock = t.getLockInfo(); - if (lock != null && t.getThreadState() != Thread.State.BLOCKED) { - writer.printf("\n - waiting on <0x%08x> (a %s)", - lock.getIdentityHashCode(), - lock.getClassName()); - writer.printf("\n - locked <0x%08x> (a %s)", - lock.getIdentityHashCode(), - lock.getClassName()); - } else if (lock != null && t.getThreadState() == Thread.State.BLOCKED) { - writer.printf("\n - waiting to lock <0x%08x> (a %s)", - lock.getIdentityHashCode(), - lock.getClassName()); - } - - if (t.isSuspended()) { - writer.print(" (suspended)"); - } - - if (t.isInNative()) { - writer.print(" (running in native)"); - } - - writer.println(); - if (t.getLockOwnerName() != null) { - writer.printf(" owned by %s id=%d\n", t.getLockOwnerName(), t.getLockOwnerId()); - } - - final StackTraceElement[] elements = t.getStackTrace(); - final MonitorInfo[] monitors = t.getLockedMonitors(); - - for (int i = 0; i < elements.length; i++) { - final StackTraceElement element = elements[i]; - writer.printf(" at %s\n", element); - for (int j = 1; j < monitors.length; j++) { - final MonitorInfo monitor = monitors[j]; - if (monitor.getLockedStackDepth() == i) { - writer.printf(" - locked %s\n", monitor); - } - } - } - writer.println(); - - final LockInfo[] locks = t.getLockedSynchronizers(); - if (locks.length > 0) { - writer.printf(" Locked synchronizers: count = %d\n", locks.length); - for (LockInfo l : locks) { - writer.printf(" - %s\n", l); - } - writer.println(); - } - } - - writer.println(); - writer.flush(); - } public Map getBufferPoolStats() { try { From dd55bb3fe944eb9822cae030f532b8075d911444 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 18:48:30 -0700 Subject: [PATCH 0049/2558] There is a lot of stuff left to do. But hey! Here we are! --- TODO.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000000..66f73293a4 --- /dev/null +++ b/TODO.md @@ -0,0 +1,20 @@ +Things What Need Doing +====================== + +* Batch gauges? Sets of gauges which share a common name prefix and pre-read method (e.g., JVM shiz) +* Get back up to feature parity w/ JVM instrumentation, EOL ``VirtualMachineMetrics`` +* Figure out what configuring Jetty w/ instrumented stuff looks like (JNDI?) +* Figure out what configuring Ehcache w/ instrumented stuff looks like (JNDI?) +* Go through the docs with a fine-toothed comb and make sure things make sense +* Go through ``2.x-maintenance`` and make sure I didn't forget to forward-port something +* Add tests for ``JmxReporter`` +* Add tests for ``JxmAttributeGauge`` +* Add tests for ``LoggerReporter`` +* Add tests and rate/duration units for ``ConsoleReporter`` +* Add tests for ``CsvReporter`` +* Go back and hit ``metrics-servlet`` with a stick +* Add javadocs for pretty much everything +* Do integration testing w/ Ganglia +* Do integration testing w/ Graphite + +Do you see something here which is near and dear to your heart? WELL COME ON DOWN. From 4fd0c4a1a0885bcc769832939b62fc47318fbce8 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 18:53:22 -0700 Subject: [PATCH 0050/2558] Fix some names. --- metrics-jvm/pom.xml | 4 +++- metrics-servlet/pom.xml | 2 +- metrics-servlets/pom.xml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/metrics-jvm/pom.xml b/metrics-jvm/pom.xml index 7b5cd8cd3d..d3c851a02c 100644 --- a/metrics-jvm/pom.xml +++ b/metrics-jvm/pom.xml @@ -11,7 +11,9 @@ metrics-jvm - + Metrics JVM Instrumentation + bundle + com.yammer.metrics diff --git a/metrics-servlet/pom.xml b/metrics-servlet/pom.xml index b5a2a88b76..ba54882c42 100644 --- a/metrics-servlet/pom.xml +++ b/metrics-servlet/pom.xml @@ -11,7 +11,7 @@ metrics-servlet - Metrics Web Application Support + Metrics Servlet Support bundle diff --git a/metrics-servlets/pom.xml b/metrics-servlets/pom.xml index da1f04a27f..adb9cfe3a3 100644 --- a/metrics-servlets/pom.xml +++ b/metrics-servlets/pom.xml @@ -9,7 +9,7 @@ metrics-servlets - Metrics Servlet + Metrics Utility Servlets bundle From 835b4eb2ec2d28bbac2f9fc086a3fbe815386a6d Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 19:13:02 -0700 Subject: [PATCH 0051/2558] More ideas. --- TODO.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO.md b/TODO.md index 66f73293a4..349682001f 100644 --- a/TODO.md +++ b/TODO.md @@ -16,5 +16,7 @@ Things What Need Doing * Add javadocs for pretty much everything * Do integration testing w/ Ganglia * Do integration testing w/ Graphite +* Figure out a consistent builder API for complicated reporter constructor (no more accordion + constructors). Do you see something here which is near and dear to your heart? WELL COME ON DOWN. From 907c7f99ac7e870d85d53abf5a813137e67af41a Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 19:34:21 -0700 Subject: [PATCH 0052/2558] Fix some docs. --- .../com/yammer/metrics/annotation/ExceptionMetered.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/ExceptionMetered.java b/metrics-annotation/src/main/java/com/yammer/metrics/annotation/ExceptionMetered.java index 81cfd17fa9..976010ae2c 100644 --- a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/ExceptionMetered.java +++ b/metrics-annotation/src/main/java/com/yammer/metrics/annotation/ExceptionMetered.java @@ -20,9 +20,6 @@ * {@code #fancyName(String)} throws an exception of type {@code cause} (or a subclass), the meter * will be marked. *

- * By default, the annotation default to capturing all exceptions (subclasses of {@link Exception}) - * and will use the default event-type of "exceptions". - *

* A name for the metric can be specified as an annotation parameter, otherwise, the metric will be * named based on the method name. *

@@ -34,8 +31,8 @@ * } *

*

- * A meter named {@code fancyNameExceptionMetric} will be created with event-type named - * "exceptions". The meter will be marked every time an exception is thrown. + * A meter named {@code fancyName.exceptions} will be created and marked every time an exception is + * thrown. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) From 656771dfac92ca017e4f34baf64c1a71a4c4de81 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 20:14:11 -0700 Subject: [PATCH 0053/2558] Added a test for MetricRegistry#getNames(). --- .../com/yammer/metrics/tests/MetricRegistryTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java index c05e0f0de5..a35e161088 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java @@ -229,4 +229,16 @@ public void hasAMapOfRegisteredTimers() throws Exception { assertThat(registry.getTimers()) .contains(entry("timer", timer)); } + + @Test + public void hasASetOfRegisteredMetricNames() throws Exception { + registry.register("gauge", gauge); + registry.register("counter", counter); + registry.register("histogram", histogram); + registry.register("meter", meter); + registry.register("timer", timer); + + assertThat(registry.getNames()) + .containsOnly("gauge", "counter", "histogram", "meter", "timer"); + } } From 171f561ac68bf13cc11cf0a518240b1f3b13d816 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 20:17:22 -0700 Subject: [PATCH 0054/2558] Added the ability to register batches of metrics. --- .../com/yammer/metrics/MetricRegistry.java | 25 +++++++++++++++++ .../metrics/tests/MetricRegistryTest.java | 27 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java index 5d68d8fef6..e37e115adc 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -81,6 +81,31 @@ public T register(String name, T metric) throws IllegalArgume return metric; } + /** + * Given a map of metric names to metrics, registers them. + * + * @param metrics a map of metric names to metrics + * @param the type of the metrics + * @throws IllegalArgumentException if any of the names are already registered + */ + public void registerAll(Map metrics) throws IllegalArgumentException { + registerAll(null, metrics); + } + + /** + * Given a map of metric names to metrics, registers them using a prefix. + * + * @param prefix the prefix for all the names + * @param metrics a map of metric names to metrics + * @param the type of the metrics + * @throws IllegalArgumentException if any of the names are already registered + */ + public void registerAll(String prefix, Map metrics) throws IllegalArgumentException { + for (Map.Entry entry : metrics.entrySet()) { + register(name(prefix, entry.getKey()), entry.getValue()); + } + } + /** * Creates a new {@link Counter} and registers it under the given name. * diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java index a35e161088..ccf0940e48 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java @@ -4,6 +4,9 @@ import org.junit.Before; import org.junit.Test; +import java.util.HashMap; +import java.util.Map; + import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.data.MapEntry.entry; import static org.mockito.Mockito.*; @@ -241,4 +244,28 @@ public void hasASetOfRegisteredMetricNames() throws Exception { assertThat(registry.getNames()) .containsOnly("gauge", "counter", "histogram", "meter", "timer"); } + + @Test + public void registersMultipleMetrics() throws Exception { + final Map metrics = new HashMap(); + metrics.put("gauge", gauge); + metrics.put("counter", counter); + + registry.registerAll(metrics); + + assertThat(registry.getNames()) + .containsOnly("gauge", "counter"); + } + + @Test + public void registersMultipleMetricsWithAPrefix() throws Exception { + final Map metrics = new HashMap(); + metrics.put("gauge", gauge); + metrics.put("counter", counter); + + registry.registerAll("my", metrics); + + assertThat(registry.getNames()) + .containsOnly("my.gauge", "my.counter"); + } } From 4a3954e97a03e2a990aaa651cae8e5197973c768 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 20:19:51 -0700 Subject: [PATCH 0055/2558] Added some javadocs to MetricRegistry. --- .../com/yammer/metrics/MetricRegistry.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java index e37e115adc..b6465b45c1 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -186,30 +186,65 @@ public void removeListener(MetricRegistryListener listener) { listeners.remove(listener); } + /** + * Returns the registry's name. + * + * @return the registry's name + */ public String getName() { return name; } + /** + * Returns a set of the names of all the metrics in the registry. + * + * @return the names of all the metrics + */ public SortedSet getNames() { return Collections.unmodifiableSortedSet(new TreeSet(metrics.keySet())); } + /** + * Returns a map of all the gauges in the registry and their names. + * + * @return all the gauges in the registry + */ public SortedMap getGauges() { return getMetrics(Gauge.class); } + /** + * Returns a map of all the counters in the registry and their names. + * + * @return all the counters in the registry + */ public SortedMap getCounters() { return getMetrics(Counter.class); } + /** + * Returns a map of all the histograms in the registry and their names. + * + * @return all the histograms in the registry + */ public SortedMap getHistograms() { return getMetrics(Histogram.class); } + /** + * Returns a map of all the meters in the registry and their names. + * + * @return all the meters in the registry + */ public SortedMap getMeters() { return getMetrics(Meter.class); } + /** + * Returns a map of all the timers in the registry and their names. + * + * @return all the timers in the registry + */ public SortedMap getTimers() { return getMetrics(Timer.class); } From af96c5c81a8b23bcedc91880ee23787d0d275d9d Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 20:54:40 -0700 Subject: [PATCH 0056/2558] Generalize to a MetricSet. --- .../com/yammer/metrics/MetricRegistry.java | 16 ++++++------- .../java/com/yammer/metrics/MetricSet.java | 18 ++++++++++++++ .../metrics/tests/MetricRegistryTest.java | 24 ++++++++++++++----- 3 files changed, 43 insertions(+), 15 deletions(-) create mode 100644 metrics-core/src/main/java/com/yammer/metrics/MetricSet.java diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java index b6465b45c1..99ed21d263 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -82,26 +82,24 @@ public T register(String name, T metric) throws IllegalArgume } /** - * Given a map of metric names to metrics, registers them. + * Given a metric set, registers them. * - * @param metrics a map of metric names to metrics - * @param the type of the metrics + * @param metrics a set of metrics * @throws IllegalArgumentException if any of the names are already registered */ - public void registerAll(Map metrics) throws IllegalArgumentException { + public void registerAll(MetricSet metrics) throws IllegalArgumentException { registerAll(null, metrics); } /** - * Given a map of metric names to metrics, registers them using a prefix. + * Given a metric set, registers them using a prefix. * * @param prefix the prefix for all the names - * @param metrics a map of metric names to metrics - * @param the type of the metrics + * @param metrics a set of metrics * @throws IllegalArgumentException if any of the names are already registered */ - public void registerAll(String prefix, Map metrics) throws IllegalArgumentException { - for (Map.Entry entry : metrics.entrySet()) { + public void registerAll(String prefix, MetricSet metrics) throws IllegalArgumentException { + for (Map.Entry entry : metrics.getMetrics().entrySet()) { register(name(prefix, entry.getKey()), entry.getValue()); } } diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricSet.java b/metrics-core/src/main/java/com/yammer/metrics/MetricSet.java new file mode 100644 index 0000000000..a842f4d746 --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricSet.java @@ -0,0 +1,18 @@ +package com.yammer.metrics; + +import java.util.Map; + +/** + * A set of named metrics. + * + * @see MetricRegistry#registerAll(MetricSet) + * @see MetricRegistry#registerAll(String,MetricSet) + */ +public interface MetricSet { + /** + * A map of metric names to metrics. + * + * @return the metrics + */ + Map getMetrics(); +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java index ccf0940e48..51e5966782 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java @@ -247,9 +247,15 @@ public void hasASetOfRegisteredMetricNames() throws Exception { @Test public void registersMultipleMetrics() throws Exception { - final Map metrics = new HashMap(); - metrics.put("gauge", gauge); - metrics.put("counter", counter); + final MetricSet metrics = new MetricSet() { + @Override + public Map getMetrics() { + final Map metrics = new HashMap(); + metrics.put("gauge", gauge); + metrics.put("counter", counter); + return metrics; + } + }; registry.registerAll(metrics); @@ -259,9 +265,15 @@ public void registersMultipleMetrics() throws Exception { @Test public void registersMultipleMetricsWithAPrefix() throws Exception { - final Map metrics = new HashMap(); - metrics.put("gauge", gauge); - metrics.put("counter", counter); + final MetricSet metrics = new MetricSet() { + @Override + public Map getMetrics() { + final Map metrics = new HashMap(); + metrics.put("gauge", gauge); + metrics.put("counter", counter); + return metrics; + } + }; registry.registerAll("my", metrics); From e7cbc662aa7854336384ecbc8e129ee81a629234 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 21:31:27 -0700 Subject: [PATCH 0057/2558] Added tests and docs for JmxAttributeGauge. Closes #337. --- .../metrics/jvm}/JmxAttributeGauge.java | 17 ++++++--- .../jvm/tests/JmxAttributeGaugeTest.java | 35 +++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) rename {metrics-core/src/main/java/com/yammer/metrics => metrics-jvm/src/main/java/com/yammer/metrics/jvm}/JmxAttributeGauge.java (61%) create mode 100644 metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/JmxAttributeGaugeTest.java diff --git a/metrics-core/src/main/java/com/yammer/metrics/JmxAttributeGauge.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/JmxAttributeGauge.java similarity index 61% rename from metrics-core/src/main/java/com/yammer/metrics/JmxAttributeGauge.java rename to metrics-jvm/src/main/java/com/yammer/metrics/jvm/JmxAttributeGauge.java index 8ae198fbd5..9052261425 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/JmxAttributeGauge.java +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/JmxAttributeGauge.java @@ -1,15 +1,24 @@ -package com.yammer.metrics; +package com.yammer.metrics.jvm; -import javax.management.*; +import com.yammer.metrics.Gauge; -// TODO: 3/10/13 -- write tests -// TODO: 3/10/13 -- write docs +import javax.management.*; +/** + * A {@link Gauge} implementation which queries a {@link MBeanServer} for an attribute of an object. + */ public class JmxAttributeGauge implements Gauge { private final MBeanServer mBeanServer; private final ObjectName objectName; private final String attributeName; + /** + * Creates a new JmxAttributeGauge. + * + * @param mBeanServer the {@link MBeanServer} + * @param objectName the name of the object + * @param attributeName the name of the object's attribute + */ public JmxAttributeGauge(MBeanServer mBeanServer, ObjectName objectName, String attributeName) { this.mBeanServer = mBeanServer; this.objectName = objectName; diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/JmxAttributeGaugeTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/JmxAttributeGaugeTest.java new file mode 100644 index 0000000000..e91b56b904 --- /dev/null +++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/JmxAttributeGaugeTest.java @@ -0,0 +1,35 @@ +package com.yammer.metrics.jvm.tests; + +import com.yammer.metrics.jvm.JmxAttributeGauge; +import org.junit.Test; + +import javax.management.AttributeNotFoundException; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class JmxAttributeGaugeTest { + private final MBeanServer mBeanServer = mock(MBeanServer.class); + private final ObjectName objectName = mock(ObjectName.class); + private final JmxAttributeGauge gauge = new JmxAttributeGauge(mBeanServer, objectName, "attr"); + private final Object value = mock(Object.class); + + @Test + public void returnsAJmxAttribute() throws Exception { + when(mBeanServer.getAttribute(objectName, "attr")).thenReturn(value); + + assertThat(gauge.getValue()) + .isEqualTo(value); + } + + @Test + public void returnsNullIfThereIsAnException() throws Exception { + when(mBeanServer.getAttribute(objectName, "attr")).thenThrow(new AttributeNotFoundException()); + + assertThat(gauge.getValue()) + .isNull(); + } +} From fd12a1b89d8a8b8603417a760363d0460f2790b2 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 21:50:44 -0700 Subject: [PATCH 0058/2558] Fleshed out BufferPoolMetricSet. Closes #346. --- ...GaugeMap.java => BufferPoolMetricSet.java} | 30 +++-- .../jvm/tests/BufferPoolMetricSetTest.java | 110 ++++++++++++++++++ 2 files changed, 131 insertions(+), 9 deletions(-) rename metrics-jvm/src/main/java/com/yammer/metrics/jvm/{BufferPoolGaugeMap.java => BufferPoolMetricSet.java} (54%) create mode 100644 metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/BufferPoolMetricSetTest.java diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolGaugeMap.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolMetricSet.java similarity index 54% rename from metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolGaugeMap.java rename to metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolMetricSet.java index 2960f3196e..1c7ac6d76e 100644 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolGaugeMap.java +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolMetricSet.java @@ -1,24 +1,36 @@ package com.yammer.metrics.jvm; -import com.yammer.metrics.Gauge; -import com.yammer.metrics.JmxAttributeGauge; +import com.yammer.metrics.Metric; +import com.yammer.metrics.MetricSet; -import javax.management.*; -import java.util.*; +import javax.management.JMException; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; -public class BufferPoolGaugeMap { +import static com.yammer.metrics.MetricRegistry.name; + +/** + * A set of gauges for the count, usage, and capacity of the JVM's direct and mapped buffer pools. + *

+ * These JMX objects are only available on Java 7 and above. + */ +public class BufferPoolMetricSet implements MetricSet { private static final String[] ATTRIBUTES = { "Count", "MemoryUsed", "TotalCapacity" }; private static final String[] NAMES = { "count", "used", "capacity" }; private static final String[] POOLS = { "direct", "mapped" }; private final MBeanServer mBeanServer; - public BufferPoolGaugeMap(MBeanServer mBeanServer) { + public BufferPoolMetricSet(MBeanServer mBeanServer) { this.mBeanServer = mBeanServer; } - public Map> getGauges() { - final Map> gauges = new HashMap>(); + @Override + public Map getMetrics() { + final Map gauges = new HashMap(); for (String pool : POOLS) { for (int i = 0; i < ATTRIBUTES.length; i++) { final String attribute = ATTRIBUTES[i]; @@ -26,7 +38,7 @@ public Map> getGauges() { try { final ObjectName on = new ObjectName("java.nio:type=BufferPool,name=" + pool); mBeanServer.getMBeanInfo(on); - gauges.put("jvm.buffers." + pool + "." + name, + gauges.put(name(pool, name), new JmxAttributeGauge(mBeanServer, on, attribute)); } catch (JMException ignored) { diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/BufferPoolMetricSetTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/BufferPoolMetricSetTest.java new file mode 100644 index 0000000000..15bd1c0dbd --- /dev/null +++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/BufferPoolMetricSetTest.java @@ -0,0 +1,110 @@ +package com.yammer.metrics.jvm.tests; + +import com.yammer.metrics.Gauge; +import com.yammer.metrics.jvm.BufferPoolMetricSet; +import org.junit.Before; +import org.junit.Test; + +import javax.management.InstanceNotFoundException; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BufferPoolMetricSetTest { + private final MBeanServer mBeanServer = mock(MBeanServer.class); + private final BufferPoolMetricSet buffers = new BufferPoolMetricSet(mBeanServer); + + private ObjectName mapped; + private ObjectName direct; + + @Before + public void setUp() throws Exception { + this.mapped = new ObjectName("java.nio:type=BufferPool,name=mapped"); + this.direct = new ObjectName("java.nio:type=BufferPool,name=direct"); + + } + + @Test + public void includesGaugesForDirectAndMappedPools() throws Exception { + assertThat(buffers.getMetrics().keySet()) + .containsOnly("direct.count", + "mapped.used", + "mapped.capacity", + "direct.capacity", + "mapped.count", + "direct.used"); + } + + @Test + public void ignoresGaugesForObjectsWhichCannotBeFound() throws Exception { + when(mBeanServer.getMBeanInfo(mapped)).thenThrow(new InstanceNotFoundException()); + + assertThat(buffers.getMetrics().keySet()) + .containsOnly("direct.count", + "direct.capacity", + "direct.used"); + } + + @Test + public void includesAGaugeForDirectCount() throws Exception { + final Gauge gauge = (Gauge) buffers.getMetrics().get("direct.count"); + + when(mBeanServer.getAttribute(direct, "Count")).thenReturn(100); + + assertThat(gauge.getValue()) + .isEqualTo(100); + } + + @Test + public void includesAGaugeForDirectMemoryUsed() throws Exception { + final Gauge gauge = (Gauge) buffers.getMetrics().get("direct.used"); + + when(mBeanServer.getAttribute(direct, "MemoryUsed")).thenReturn(100); + + assertThat(gauge.getValue()) + .isEqualTo(100); + } + + @Test + public void includesAGaugeForDirectCapacity() throws Exception { + final Gauge gauge = (Gauge) buffers.getMetrics().get("direct.capacity"); + + when(mBeanServer.getAttribute(direct, "TotalCapacity")).thenReturn(100); + + assertThat(gauge.getValue()) + .isEqualTo(100); + } + + @Test + public void includesAGaugeForMappedCount() throws Exception { + final Gauge gauge = (Gauge) buffers.getMetrics().get("mapped.count"); + + when(mBeanServer.getAttribute(mapped, "Count")).thenReturn(100); + + assertThat(gauge.getValue()) + .isEqualTo(100); + } + + @Test + public void includesAGaugeForMappedMemoryUsed() throws Exception { + final Gauge gauge = (Gauge) buffers.getMetrics().get("mapped.used"); + + when(mBeanServer.getAttribute(mapped, "MemoryUsed")).thenReturn(100); + + assertThat(gauge.getValue()) + .isEqualTo(100); + } + + @Test + public void includesAGaugeForMappedCapacity() throws Exception { + final Gauge gauge = (Gauge) buffers.getMetrics().get("mapped.capacity"); + + when(mBeanServer.getAttribute(mapped, "TotalCapacity")).thenReturn(100); + + assertThat(gauge.getValue()) + .isEqualTo(100); + } +} From 7febcfc3030969329d662948ea5ca021f48a4e5b Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 22:10:14 -0700 Subject: [PATCH 0059/2558] Fleshed out GarbageCollectorMetricSet. Closes #344. --- .../jvm/GarbageCollectorMetricSet.java | 59 +++++++++++++++++++ .../tests/GarbageCollectorMetricSetTest.java | 51 ++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 metrics-jvm/src/main/java/com/yammer/metrics/jvm/GarbageCollectorMetricSet.java create mode 100644 metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/GarbageCollectorMetricSetTest.java diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/GarbageCollectorMetricSet.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/GarbageCollectorMetricSet.java new file mode 100644 index 0000000000..3091ed5830 --- /dev/null +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/GarbageCollectorMetricSet.java @@ -0,0 +1,59 @@ +package com.yammer.metrics.jvm; + +import com.yammer.metrics.Gauge; +import com.yammer.metrics.Metric; +import com.yammer.metrics.MetricSet; + +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.util.*; +import java.util.regex.Pattern; + +import static com.yammer.metrics.MetricRegistry.name; + +/** + * A set of gauges for the counts and elapsed times of garbage collections. + */ +public class GarbageCollectorMetricSet implements MetricSet { + private static final Pattern WHITESPACE = Pattern.compile("[\\s]+"); + + private final List garbageCollectors; + + /** + * Creates a new set of gauges for all discoverable garbage collectors. + */ + public GarbageCollectorMetricSet() { + this(ManagementFactory.getGarbageCollectorMXBeans()); + } + + /** + * Creates a new set of gauges for the given collection of garbage collectors. + * + * @param garbageCollectors the garbage collectors + */ + public GarbageCollectorMetricSet(Collection garbageCollectors) { + this.garbageCollectors = new ArrayList(garbageCollectors); + } + + @Override + public Map getMetrics() { + final Map gauges = new HashMap(); + for (final GarbageCollectorMXBean gc : garbageCollectors) { + final String name = WHITESPACE.matcher(gc.getName()).replaceAll("-"); + gauges.put(name(name, "count"), new Gauge() { + @Override + public Long getValue() { + return gc.getCollectionCount(); + } + }); + + gauges.put(name(name, "time"), new Gauge() { + @Override + public Long getValue() { + return gc.getCollectionTime(); + } + }); + } + return Collections.unmodifiableMap(gauges); + } +} diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/GarbageCollectorMetricSetTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/GarbageCollectorMetricSetTest.java new file mode 100644 index 0000000000..a190219df8 --- /dev/null +++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/GarbageCollectorMetricSetTest.java @@ -0,0 +1,51 @@ +package com.yammer.metrics.jvm.tests; + +import com.yammer.metrics.Gauge; +import com.yammer.metrics.jvm.GarbageCollectorMetricSet; +import org.junit.Before; +import org.junit.Test; + +import java.lang.management.GarbageCollectorMXBean; +import java.util.Arrays; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class GarbageCollectorMetricSetTest { + private final GarbageCollectorMXBean gc = mock(GarbageCollectorMXBean.class); + private final GarbageCollectorMetricSet metrics = new GarbageCollectorMetricSet(Arrays.asList(gc)); + + @Before + public void setUp() throws Exception { + when(gc.getName()).thenReturn("PS OldGen"); + when(gc.getCollectionCount()).thenReturn(1L); + when(gc.getCollectionTime()).thenReturn(2L); + } + + @Test + public void hasGaugesForGcCountsAndElapsedTimes() throws Exception { + assertThat(metrics.getMetrics().keySet()) + .containsOnly("PS-OldGen.time", "PS-OldGen.count"); + } + + @Test + public void hasAGaugeForGcCounts() throws Exception { + final Gauge gauge = (Gauge) metrics.getMetrics().get("PS-OldGen.count"); + assertThat(gauge.getValue()) + .isEqualTo(1L); + } + + @Test + public void hasAGaugeForGcTimes() throws Exception { + final Gauge gauge = (Gauge) metrics.getMetrics().get("PS-OldGen.time"); + assertThat(gauge.getValue()) + .isEqualTo(2L); + } + + @Test + public void autoDiscoversGCs() throws Exception { + assertThat(new GarbageCollectorMetricSet().getMetrics().keySet()) + .isNotEmpty(); + } +} From 35fac83a0201f90cce5faf95dc4db444730d2352 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 12 Mar 2013 22:22:29 -0700 Subject: [PATCH 0060/2558] Added FileDescriptorRatioGauge. Closes #341. --- .../metrics/jvm/FileDescriptorRatioGauge.java | 46 ++++++++++++ .../tests/FileDescriptorRatioGaugeTest.java | 75 +++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 metrics-jvm/src/main/java/com/yammer/metrics/jvm/FileDescriptorRatioGauge.java create mode 100644 metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/FileDescriptorRatioGaugeTest.java diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/FileDescriptorRatioGauge.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/FileDescriptorRatioGauge.java new file mode 100644 index 0000000000..89b6bf5d87 --- /dev/null +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/FileDescriptorRatioGauge.java @@ -0,0 +1,46 @@ +package com.yammer.metrics.jvm; + +import com.yammer.metrics.RatioGauge; + +import java.lang.management.ManagementFactory; +import java.lang.management.OperatingSystemMXBean; +import java.lang.reflect.Method; + +/** + * A gauge for the ratio of used to total file descriptors. + */ +public class FileDescriptorRatioGauge extends RatioGauge { + private final OperatingSystemMXBean os; + + /** + * Creates a new gauge using the platform OS bean. + */ + public FileDescriptorRatioGauge() { + this(ManagementFactory.getOperatingSystemMXBean()); + } + + /** + * Creates a new gauge using the given OS bean. + * + * @param os an {@link OperatingSystemMXBean} + */ + public FileDescriptorRatioGauge(OperatingSystemMXBean os) { + this.os = os; + } + + @Override + protected Ratio getRatio() { + try { + return Ratio.of(invoke("getOpenFileDescriptorCount"), + invoke("getMaxFileDescriptorCount")); + } catch (ReflectiveOperationException e) { + return Ratio.of(Double.NaN, Double.NaN); + } + } + + private long invoke(String name) throws ReflectiveOperationException { + final Method method = os.getClass().getDeclaredMethod(name); + method.setAccessible(true); + return (Long) method.invoke(os); + } +} diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/FileDescriptorRatioGaugeTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/FileDescriptorRatioGaugeTest.java new file mode 100644 index 0000000000..733bc4d5fd --- /dev/null +++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/FileDescriptorRatioGaugeTest.java @@ -0,0 +1,75 @@ +package com.yammer.metrics.jvm.tests; + +import com.yammer.metrics.jvm.FileDescriptorRatioGauge; +import org.junit.Test; + +import javax.management.ObjectName; +import java.lang.management.OperatingSystemMXBean; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +@SuppressWarnings("UnusedDeclaration") +public class FileDescriptorRatioGaugeTest { + private final OperatingSystemMXBean os = new OperatingSystemMXBean() { + @Override + public String getName() { + return null; + } + + @Override + public String getArch() { + return null; + } + + @Override + public String getVersion() { + return null; + } + + @Override + public int getAvailableProcessors() { + return 0; + } + + @Override + public double getSystemLoadAverage() { + return 0; + } + + @Override + public ObjectName getObjectName() { + return null; + } + + // these duplicate methods from UnixOperatingSystem + + private long getOpenFileDescriptorCount() { + return 10; + } + + private long getMaxFileDescriptorCount() { + return 100; + } + }; + private final FileDescriptorRatioGauge gauge = new FileDescriptorRatioGauge(os); + + @Test + public void calculatesTheRatioOfUsedToTotalFileDescriptors() throws Exception { + assertThat(gauge.getValue()) + .isEqualTo(0.1); + } + + @Test + public void autoDetectsTheOperationSystemBean() throws Exception { + assertThat(new FileDescriptorRatioGauge().getValue()) + .isGreaterThanOrEqualTo(0.0) + .isLessThanOrEqualTo(1.0); + } + + @Test + public void returnsNaNWhenTheInformationIsUnavailable() throws Exception { + assertThat(new FileDescriptorRatioGauge(mock(OperatingSystemMXBean.class)).getValue()) + .isNaN(); + } +} From f8af831cf8ff6d8d065f688d5356efc627a3d342 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Wed, 13 Mar 2013 07:17:03 -0700 Subject: [PATCH 0061/2558] Added MemoryUsageGaugeSet. Closes #339. --- .../metrics/jvm/MemoryUsageGaugeSet.java | 164 +++++++++++++ .../jvm/tests/MemoryUsageGaugeSetTest.java | 216 ++++++++++++++++++ 2 files changed, 380 insertions(+) create mode 100644 metrics-jvm/src/main/java/com/yammer/metrics/jvm/MemoryUsageGaugeSet.java create mode 100644 metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/MemoryUsageGaugeSetTest.java diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/MemoryUsageGaugeSet.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/MemoryUsageGaugeSet.java new file mode 100644 index 0000000000..0b9f42e179 --- /dev/null +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/MemoryUsageGaugeSet.java @@ -0,0 +1,164 @@ +package com.yammer.metrics.jvm; + +import com.yammer.metrics.Gauge; +import com.yammer.metrics.Metric; +import com.yammer.metrics.MetricSet; +import com.yammer.metrics.RatioGauge; + +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MemoryUsage; +import java.util.*; +import java.util.regex.Pattern; + +import static com.yammer.metrics.MetricRegistry.name; + +/** + * A set of gauges for JVM memory usage, including stats on heap vs. non-heap memory, plus + * GC-specific memory pools. + */ +public class MemoryUsageGaugeSet implements MetricSet { + private static final Pattern WHITESPACE = Pattern.compile("[\\s]+"); + + private final MemoryMXBean mxBean; + private final List memoryPools; + + public MemoryUsageGaugeSet() { + this(ManagementFactory.getMemoryMXBean(), + ManagementFactory.getMemoryPoolMXBeans()); + } + + public MemoryUsageGaugeSet(MemoryMXBean mxBean, + Collection memoryPools) { + this.mxBean = mxBean; + this.memoryPools = new ArrayList(memoryPools); + } + + @Override + public Map getMetrics() { + final Map gauges = new HashMap(); + + gauges.put("total.init", new Gauge() { + @Override + public Long getValue() { + return mxBean.getHeapMemoryUsage().getInit() + + mxBean.getNonHeapMemoryUsage().getInit(); + } + }); + + gauges.put("total.used", new Gauge() { + @Override + public Long getValue() { + return mxBean.getHeapMemoryUsage().getUsed() + + mxBean.getNonHeapMemoryUsage().getUsed(); + } + }); + + gauges.put("total.max", new Gauge() { + @Override + public Long getValue() { + return mxBean.getHeapMemoryUsage().getMax() + + mxBean.getNonHeapMemoryUsage().getMax(); + } + }); + + gauges.put("total.committed", new Gauge() { + @Override + public Long getValue() { + return mxBean.getHeapMemoryUsage().getCommitted() + + mxBean.getNonHeapMemoryUsage().getCommitted(); + } + }); + + + gauges.put("heap.init", new Gauge() { + @Override + public Long getValue() { + return mxBean.getHeapMemoryUsage().getInit(); + } + }); + + gauges.put("heap.used", new Gauge() { + @Override + public Long getValue() { + return mxBean.getHeapMemoryUsage().getUsed(); + } + }); + + gauges.put("heap.max", new Gauge() { + @Override + public Long getValue() { + return mxBean.getHeapMemoryUsage().getMax(); + } + }); + + gauges.put("heap.committed", new Gauge() { + @Override + public Long getValue() { + return mxBean.getHeapMemoryUsage().getCommitted(); + } + }); + + gauges.put("heap.usage", new RatioGauge() { + @Override + protected Ratio getRatio() { + final MemoryUsage usage = mxBean.getHeapMemoryUsage(); + return Ratio.of(usage.getUsed(), usage.getMax()); + } + }); + + gauges.put("non-heap.init", new Gauge() { + @Override + public Long getValue() { + return mxBean.getNonHeapMemoryUsage().getInit(); + } + }); + + gauges.put("non-heap.used", new Gauge() { + @Override + public Long getValue() { + return mxBean.getNonHeapMemoryUsage().getUsed(); + } + }); + + gauges.put("non-heap.max", new Gauge() { + @Override + public Long getValue() { + return mxBean.getNonHeapMemoryUsage().getMax(); + } + }); + + gauges.put("non-heap.committed", new Gauge() { + @Override + public Long getValue() { + return mxBean.getNonHeapMemoryUsage().getCommitted(); + } + }); + + gauges.put("non-heap.usage", new RatioGauge() { + @Override + protected Ratio getRatio() { + final MemoryUsage usage = mxBean.getNonHeapMemoryUsage(); + return Ratio.of(usage.getUsed(), usage.getMax()); + } + }); + + for (final MemoryPoolMXBean pool : memoryPools) { + gauges.put(name("pools", + WHITESPACE.matcher(pool.getName()).replaceAll("-"), + "usage"), + new RatioGauge() { + @Override + protected Ratio getRatio() { + final long max = pool.getUsage().getMax() == -1 ? + pool.getUsage().getCommitted() : + pool.getUsage().getMax(); + return Ratio.of(pool.getUsage().getUsed(), max); + } + }); + } + + return Collections.unmodifiableMap(gauges); + } +} diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/MemoryUsageGaugeSetTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/MemoryUsageGaugeSetTest.java new file mode 100644 index 0000000000..183bb9de98 --- /dev/null +++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/MemoryUsageGaugeSetTest.java @@ -0,0 +1,216 @@ +package com.yammer.metrics.jvm.tests; + +import com.yammer.metrics.Gauge; +import com.yammer.metrics.jvm.MemoryUsageGaugeSet; +import org.junit.Before; +import org.junit.Test; + +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MemoryUsage; +import java.util.Arrays; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MemoryUsageGaugeSetTest { + private final MemoryUsage heap = mock(MemoryUsage.class); + private final MemoryUsage nonHeap = mock(MemoryUsage.class); + private final MemoryUsage pool = mock(MemoryUsage.class); + private final MemoryUsage weirdPool = mock(MemoryUsage.class); + private final MemoryMXBean mxBean = mock(MemoryMXBean.class); + private final MemoryPoolMXBean memoryPool = mock(MemoryPoolMXBean.class); + private final MemoryPoolMXBean weirdMemoryPool = mock(MemoryPoolMXBean.class); + + private final MemoryUsageGaugeSet gauges = new MemoryUsageGaugeSet(mxBean, + Arrays.asList(memoryPool, + weirdMemoryPool)); + + @Before + public void setUp() throws Exception { + when(heap.getCommitted()).thenReturn(10L); + when(heap.getInit()).thenReturn(20L); + when(heap.getUsed()).thenReturn(30L); + when(heap.getMax()).thenReturn(40L); + + when(nonHeap.getCommitted()).thenReturn(1L); + when(nonHeap.getInit()).thenReturn(2L); + when(nonHeap.getUsed()).thenReturn(3L); + when(nonHeap.getMax()).thenReturn(4L); + + when(pool.getCommitted()).thenReturn(100L); + when(pool.getInit()).thenReturn(200L); + when(pool.getUsed()).thenReturn(300L); + when(pool.getMax()).thenReturn(400L); + + when(weirdPool.getCommitted()).thenReturn(100L); + when(weirdPool.getInit()).thenReturn(200L); + when(weirdPool.getUsed()).thenReturn(300L); + when(weirdPool.getMax()).thenReturn(-1L); + + when(mxBean.getHeapMemoryUsage()).thenReturn(heap); + when(mxBean.getNonHeapMemoryUsage()).thenReturn(nonHeap); + + when(memoryPool.getUsage()).thenReturn(pool); + when(memoryPool.getName()).thenReturn("Big Pool"); + + when(weirdMemoryPool.getUsage()).thenReturn(weirdPool); + when(weirdMemoryPool.getName()).thenReturn("Weird Pool"); + } + + @Test + public void hasASetOfGauges() throws Exception { + assertThat(gauges.getMetrics().keySet()) + .containsOnly("heap.init", + "non-heap.usage", + "total.max", + "total.init", + "heap.committed", + "non-heap.max", + "heap.usage", + "pools.Big-Pool.usage", + "pools.Weird-Pool.usage", + "heap.used", + "non-heap.used", + "non-heap.init", + "total.used", + "total.committed", + "non-heap.committed", + "heap.max"); + } + + @Test + public void hasAGaugeForTotalCommitted() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("total.committed"); + + assertThat(gauge.getValue()) + .isEqualTo(11L); + } + + @Test + public void hasAGaugeForTotalInit() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("total.init"); + + assertThat(gauge.getValue()) + .isEqualTo(22L); + } + + @Test + public void hasAGaugeForTotalUsed() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("total.used"); + + assertThat(gauge.getValue()) + .isEqualTo(33L); + } + + @Test + public void hasAGaugeForTotalMax() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("total.max"); + + assertThat(gauge.getValue()) + .isEqualTo(44L); + } + + @Test + public void hasAGaugeForHeapCommitted() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("heap.committed"); + + assertThat(gauge.getValue()) + .isEqualTo(10L); + } + + @Test + public void hasAGaugeForHeapInit() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("heap.init"); + + assertThat(gauge.getValue()) + .isEqualTo(20L); + } + + @Test + public void hasAGaugeForHeapUsed() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("heap.used"); + + assertThat(gauge.getValue()) + .isEqualTo(30L); + } + + @Test + public void hasAGaugeForHeapMax() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("heap.max"); + + assertThat(gauge.getValue()) + .isEqualTo(40L); + } + + @Test + public void hasAGaugeForHeapUsage() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("heap.usage"); + + assertThat(gauge.getValue()) + .isEqualTo(0.75); + } + + @Test + public void hasAGaugeForNonHeapCommitted() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("non-heap.committed"); + + assertThat(gauge.getValue()) + .isEqualTo(1L); + } + + @Test + public void hasAGaugeForNonHeapInit() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("non-heap.init"); + + assertThat(gauge.getValue()) + .isEqualTo(2L); + } + + @Test + public void hasAGaugeForNonHeapUsed() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("non-heap.used"); + + assertThat(gauge.getValue()) + .isEqualTo(3L); + } + + @Test + public void hasAGaugeForNonHeapMax() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("non-heap.max"); + + assertThat(gauge.getValue()) + .isEqualTo(4L); + } + + @Test + public void hasAGaugeForNonHeapUsage() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("non-heap.usage"); + + assertThat(gauge.getValue()) + .isEqualTo(0.75); + } + + @Test + public void hasAGaugeForMemoryPoolUsage() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("pools.Big-Pool.usage"); + + assertThat(gauge.getValue()) + .isEqualTo(0.75); + } + + @Test + public void hasAGaugeForWeirdMemoryPoolUsage() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("pools.Weird-Pool.usage"); + + assertThat(gauge.getValue()) + .isEqualTo(3.0); + } + + @Test + public void autoDetectsMemoryUsageBeanAndMemoryPools() throws Exception { + assertThat(new MemoryUsageGaugeSet().getMetrics().keySet()) + .isNotEmpty(); + } +} From 1fa758668248be2728b120bbb2d77b41f94f926b Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Wed, 13 Mar 2013 11:07:58 -0700 Subject: [PATCH 0062/2558] Added JvmAttributeGaugeSet. Closes #342. --- .../metrics/jvm/JvmAttributeGaugeSet.java | 66 +++++++++++++++++++ .../jvm/tests/JvmAttributeGaugeSetTest.java | 66 +++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 metrics-jvm/src/main/java/com/yammer/metrics/jvm/JvmAttributeGaugeSet.java create mode 100644 metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/JvmAttributeGaugeSetTest.java diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/JvmAttributeGaugeSet.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/JvmAttributeGaugeSet.java new file mode 100644 index 0000000000..0c120ed71c --- /dev/null +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/JvmAttributeGaugeSet.java @@ -0,0 +1,66 @@ +package com.yammer.metrics.jvm; + +import com.yammer.metrics.Gauge; +import com.yammer.metrics.Metric; +import com.yammer.metrics.MetricSet; + +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +/** + * A set of gauges for the JVM name, vendor, and uptime. + */ +public class JvmAttributeGaugeSet implements MetricSet { + private final RuntimeMXBean runtime; + + /** + * Creates a new set of gauges. + */ + public JvmAttributeGaugeSet() { + this(ManagementFactory.getRuntimeMXBean()); + } + + /** + * Creates a new set of gauges with the given {@link RuntimeMXBean}. + */ + public JvmAttributeGaugeSet(RuntimeMXBean runtime) { + this.runtime = runtime; + } + + @Override + public Map getMetrics() { + final Map gauges = new HashMap(); + + gauges.put("name", new Gauge() { + @Override + public String getValue() { + return runtime.getName(); + } + }); + + gauges.put("vendor", new Gauge() { + @Override + public String getValue() { + return String.format(Locale.US, + "%s %s %s (%s)", + runtime.getVmVendor(), + runtime.getVmName(), + runtime.getVmVersion(), + runtime.getSpecVersion()); + } + }); + + gauges.put("uptime", new Gauge() { + @Override + public Long getValue() { + return runtime.getUptime(); + } + }); + + return Collections.unmodifiableMap(gauges); + } +} diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/JvmAttributeGaugeSetTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/JvmAttributeGaugeSetTest.java new file mode 100644 index 0000000000..45f9d8e92c --- /dev/null +++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/JvmAttributeGaugeSetTest.java @@ -0,0 +1,66 @@ +package com.yammer.metrics.jvm.tests; + +import com.yammer.metrics.Gauge; +import com.yammer.metrics.jvm.JvmAttributeGaugeSet; +import org.junit.Before; +import org.junit.Test; + +import java.lang.management.RuntimeMXBean; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class JvmAttributeGaugeSetTest { + private final RuntimeMXBean runtime = mock(RuntimeMXBean.class); + private final JvmAttributeGaugeSet gauges = new JvmAttributeGaugeSet(runtime); + + @Before + public void setUp() throws Exception { + when(runtime.getName()).thenReturn("9928@example.com"); + + when(runtime.getVmVendor()).thenReturn("Oracle Corporation"); + when(runtime.getVmName()).thenReturn("Java HotSpot(TM) 64-Bit Server VM"); + when(runtime.getVmVersion()).thenReturn("23.7-b01"); + when(runtime.getSpecVersion()).thenReturn("1.7"); + when(runtime.getUptime()).thenReturn(100L); + } + + @Test + public void hasASetOfGauges() throws Exception { + assertThat(gauges.getMetrics().keySet()) + .containsOnly("vendor", "name", "uptime"); + } + + @Test + public void hasAGaugeForTheJVMName() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("name"); + + assertThat(gauge.getValue()) + .isEqualTo("9928@example.com"); + } + + @Test + public void hasAGaugeForTheJVMVendor() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("vendor"); + + assertThat(gauge.getValue()) + .isEqualTo("Oracle Corporation Java HotSpot(TM) 64-Bit Server VM 23.7-b01 (1.7)"); + } + + @Test + public void hasAGaugeForTheJVMUptime() throws Exception { + final Gauge gauge = (Gauge) gauges.getMetrics().get("uptime"); + + assertThat(gauge.getValue()) + .isEqualTo(100L); + } + + @Test + public void autoDiscoversTheRuntimeBean() throws Exception { + final Gauge gauge = (Gauge) new JvmAttributeGaugeSet().getMetrics().get("uptime"); + + assertThat((Long) gauge.getValue()) + .isPositive(); + } +} From 4c4cbbd538a213c8fa93c5b85e0d6ea1c2899cdd Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Wed, 13 Mar 2013 12:03:13 -0700 Subject: [PATCH 0063/2558] Added ThreadDeadlockDetector. With tests, no less. --- .../metrics/jvm/ThreadDeadlockDetector.java | 66 ++++++++++++++++++ .../jvm/tests/ThreadDeadlockDetectorTest.java | 69 +++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDeadlockDetector.java create mode 100644 metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadDeadlockDetectorTest.java diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDeadlockDetector.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDeadlockDetector.java new file mode 100644 index 0000000000..6da92f1c77 --- /dev/null +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDeadlockDetector.java @@ -0,0 +1,66 @@ +package com.yammer.metrics.jvm; + +import + java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * A utility class for detecting deadlocked threads. + */ +public class ThreadDeadlockDetector { + private static final int MAX_STACK_TRACE_DEPTH = 100; + + private final ThreadMXBean threads; + + /** + * Creates a new detector. + */ + public ThreadDeadlockDetector() { + this(ManagementFactory.getThreadMXBean()); + } + + /** + * Creates a new detector using the given {@link ThreadMXBean}. + * + * @param threads a {@link ThreadMXBean} + */ + public ThreadDeadlockDetector(ThreadMXBean threads) { + this.threads = threads; + } + + /** + * Returns a set of diagnostic stack traces for any deadlocked threads. If no threads are + * deadlocked, returns an empty set. + * + * @return stack traces for deadlocked threads or an empty set + */ + public Set getDeadlockedThreads() { + final long[] ids = threads.findDeadlockedThreads(); + if (ids != null) { + final Set deadlocks = new HashSet(); + for (ThreadInfo info : threads.getThreadInfo(ids, MAX_STACK_TRACE_DEPTH)) { + final StringBuilder stackTrace = new StringBuilder(); + for (StackTraceElement element : info.getStackTrace()) { + stackTrace.append("\t at ") + .append(element.toString()) + .append(String.format("%n")); + } + + deadlocks.add( + String.format("%s locked on %s (owned by %s):%n%s", + info.getThreadName(), + info.getLockName(), + info.getLockOwnerName(), + stackTrace.toString() + ) + ); + } + return Collections.unmodifiableSet(deadlocks); + } + return Collections.emptySet(); + } +} diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadDeadlockDetectorTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadDeadlockDetectorTest.java new file mode 100644 index 0000000000..337f62a40c --- /dev/null +++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadDeadlockDetectorTest.java @@ -0,0 +1,69 @@ +package com.yammer.metrics.jvm.tests; + +import com.yammer.metrics.jvm.ThreadDeadlockDetector; +import org.junit.Test; + +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.Locale; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ThreadDeadlockDetectorTest { + private final ThreadMXBean threads = mock(ThreadMXBean.class); + private final ThreadDeadlockDetector detector = new ThreadDeadlockDetector(threads); + + @Test + public void returnsAnEmptySetIfNoThreadsAreDeadlocked() throws Exception { + when(threads.findDeadlockedThreads()).thenReturn(null); + + assertThat(detector.getDeadlockedThreads()) + .isEmpty(); + } + + @Test + public void returnsASetOfThreadsIfAnyAreDeadlocked() throws Exception { + final ThreadInfo thread1 = mock(ThreadInfo.class); + when(thread1.getThreadName()).thenReturn("thread1"); + when(thread1.getLockName()).thenReturn("lock2"); + when(thread1.getLockOwnerName()).thenReturn("thread2"); + when(thread1.getStackTrace()).thenReturn(new StackTraceElement[]{ + new StackTraceElement("Blah", "bloo", "Blah.java", 150), + new StackTraceElement("Blah", "blee", "Blah.java", 100) + }); + + final ThreadInfo thread2 = mock(ThreadInfo.class); + when(thread2.getThreadName()).thenReturn("thread2"); + when(thread2.getLockName()).thenReturn("lock1"); + when(thread2.getLockOwnerName()).thenReturn("thread1"); + when(thread2.getStackTrace()).thenReturn(new StackTraceElement[]{ + new StackTraceElement("Blah", "blee", "Blah.java", 100), + new StackTraceElement("Blah", "bloo", "Blah.java", 150) + }); + + final long[] ids = { 1, 2 }; + when(threads.findDeadlockedThreads()).thenReturn(ids); + when(threads.getThreadInfo(eq(ids), anyInt())) + .thenReturn(new ThreadInfo[]{ thread1, thread2 }); + + assertThat(detector.getDeadlockedThreads()) + .containsOnly(String.format(Locale.US, + "thread1 locked on lock2 (owned by thread2):%n" + + "\t at Blah.bloo(Blah.java:150)%n" + + "\t at Blah.blee(Blah.java:100)%n"), + String.format(Locale.US, + "thread2 locked on lock1 (owned by thread1):%n" + + "\t at Blah.blee(Blah.java:100)%n" + + "\t at Blah.bloo(Blah.java:150)%n")); + } + + @Test + public void autoDiscoversTheThreadMXBean() throws Exception { + assertThat(new ThreadDeadlockDetector().getDeadlockedThreads()) + .isNotNull(); + } +} From 898b9f9458362bafe0cc77fb5ab168f17b4065a2 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Wed, 13 Mar 2013 12:23:42 -0700 Subject: [PATCH 0064/2558] Added ThreadStatesGaugeSet. Closes #345. --- .../metrics/jvm/ThreadStatesGaugeMap.java | 45 ------- .../metrics/jvm/ThreadStatesGaugeSet.java | 93 ++++++++++++++ .../jvm/tests/ThreadStatesGaugeSetTest.java | 117 ++++++++++++++++++ 3 files changed, 210 insertions(+), 45 deletions(-) delete mode 100644 metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeMap.java create mode 100644 metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeSet.java create mode 100644 metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadStatesGaugeSetTest.java diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeMap.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeMap.java deleted file mode 100644 index b236cad113..0000000000 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeMap.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.yammer.metrics.jvm; - -import com.yammer.metrics.Gauge; - -import java.lang.management.ThreadInfo; -import java.lang.management.ThreadMXBean; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -public class ThreadStatesGaugeMap { - private final ThreadMXBean threads; - - public ThreadStatesGaugeMap(ThreadMXBean threads) { - this.threads = threads; - } - - public Map> getGauges() { - final Map> gauges = new HashMap>(); - for (final Thread.State state : Thread.State.values()) { - gauges.put("jvm.thread-states." + state.toString().toLowerCase(), - new Gauge() { - @Override - public Object getValue() { - return getThreadCount(state); - } - }); - } - return Collections.unmodifiableMap(gauges); - } - - private int getThreadCount(Thread.State state) { - final ThreadInfo[] allThreads = threads.getThreadInfo(threads.getAllThreadIds()); - int count = 0; - for (ThreadInfo info : allThreads) { - if (info != null) { - if (info.getThreadState() == state) { - count++; - } - } - } - - return count; - } -} diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeSet.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeSet.java new file mode 100644 index 0000000000..35b0808fe9 --- /dev/null +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeSet.java @@ -0,0 +1,93 @@ +package com.yammer.metrics.jvm; + +import com.yammer.metrics.Gauge; +import com.yammer.metrics.Metric; +import com.yammer.metrics.MetricSet; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import static com.yammer.metrics.MetricRegistry.name; + +/** + * A set of gauges for the number of threads in their various states and deadlock detection. + */ +public class ThreadStatesGaugeSet implements MetricSet { + private final ThreadMXBean threads; + private final ThreadDeadlockDetector deadlockDetector; + + /** + * Creates a new set of gauges using the default MXBeans. + */ + public ThreadStatesGaugeSet() { + this(ManagementFactory.getThreadMXBean(), new ThreadDeadlockDetector()); + } + + /** + * Creates a new set of gauges using the given MXBean and detector. + * + * @param threads a thread MXBean + * @param deadlockDetector a deadlock detector + */ + public ThreadStatesGaugeSet(ThreadMXBean threads, + ThreadDeadlockDetector deadlockDetector) { + this.threads = threads; + this.deadlockDetector = deadlockDetector; + } + + @Override + public Map getMetrics() { + final Map gauges = new HashMap(); + + for (final Thread.State state : Thread.State.values()) { + gauges.put(name(state.toString().toLowerCase(), "count"), + new Gauge() { + @Override + public Object getValue() { + return getThreadCount(state); + } + }); + } + + gauges.put("count", new Gauge() { + @Override + public Integer getValue() { + return threads.getThreadCount(); + } + }); + + gauges.put("daemon.count", new Gauge() { + @Override + public Integer getValue() { + return threads.getDaemonThreadCount(); + } + }); + + gauges.put("deadlocks", new Gauge>() { + @Override + public Set getValue() { + return deadlockDetector.getDeadlockedThreads(); + } + }); + + return Collections.unmodifiableMap(gauges); + } + + private int getThreadCount(Thread.State state) { + final ThreadInfo[] allThreads = threads.getThreadInfo(threads.getAllThreadIds()); + int count = 0; + for (ThreadInfo info : allThreads) { + if (info != null) { + if (info.getThreadState() == state) { + count++; + } + } + } + return count; + } +} diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadStatesGaugeSetTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadStatesGaugeSetTest.java new file mode 100644 index 0000000000..46c3edd191 --- /dev/null +++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadStatesGaugeSetTest.java @@ -0,0 +1,117 @@ +package com.yammer.metrics.jvm.tests; + +import com.yammer.metrics.Gauge; +import com.yammer.metrics.jvm.ThreadDeadlockDetector; +import com.yammer.metrics.jvm.ThreadStatesGaugeSet; +import org.junit.Before; +import org.junit.Test; + +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.HashSet; +import java.util.Set; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ThreadStatesGaugeSetTest { + private final ThreadMXBean threads = mock(ThreadMXBean.class); + private final ThreadDeadlockDetector detector = mock(ThreadDeadlockDetector.class); + private final ThreadStatesGaugeSet gauges = new ThreadStatesGaugeSet(threads, detector); + private final long[] ids = new long[]{ 1, 2, 3 }; + + private final ThreadInfo newThread = mock(ThreadInfo.class); + private final ThreadInfo runnableThread = mock(ThreadInfo.class); + private final ThreadInfo blockedThread = mock(ThreadInfo.class); + private final ThreadInfo waitingThread = mock(ThreadInfo.class); + private final ThreadInfo timedWaitingThread = mock(ThreadInfo.class); + private final ThreadInfo terminatedThread = mock(ThreadInfo.class); + + private final Set deadlocks = new HashSet(); + + @Before + public void setUp() throws Exception { + deadlocks.add("yay"); + + when(newThread.getThreadState()).thenReturn(Thread.State.NEW); + when(runnableThread.getThreadState()).thenReturn(Thread.State.RUNNABLE); + when(blockedThread.getThreadState()).thenReturn(Thread.State.BLOCKED); + when(waitingThread.getThreadState()).thenReturn(Thread.State.WAITING); + when(timedWaitingThread.getThreadState()).thenReturn(Thread.State.TIMED_WAITING); + when(terminatedThread.getThreadState()).thenReturn(Thread.State.TERMINATED); + + when(threads.getAllThreadIds()).thenReturn(ids); + when(threads.getThreadInfo(ids)).thenReturn(new ThreadInfo[]{ + newThread, runnableThread, blockedThread, + waitingThread, timedWaitingThread, terminatedThread + }); + + when(threads.getThreadCount()).thenReturn(12); + when(threads.getDaemonThreadCount()).thenReturn(13); + + when(detector.getDeadlockedThreads()).thenReturn(deadlocks); + } + + @Test + public void hasASetOfGauges() throws Exception { + assertThat(gauges.getMetrics().keySet()) + .containsOnly("terminated.count", + "new.count", + "count", + "timed_waiting.count", + "deadlocks", + "blocked.count", + "waiting.count", + "daemon.count", + "runnable.count"); + } + + @Test + public void hasAGaugeForEachThreadState() throws Exception { + assertThat(((Gauge) gauges.getMetrics().get("new.count")).getValue()) + .isEqualTo(1); + + assertThat(((Gauge) gauges.getMetrics().get("runnable.count")).getValue()) + .isEqualTo(1); + + assertThat(((Gauge) gauges.getMetrics().get("blocked.count")).getValue()) + .isEqualTo(1); + + assertThat(((Gauge) gauges.getMetrics().get("waiting.count")).getValue()) + .isEqualTo(1); + + assertThat(((Gauge) gauges.getMetrics().get("timed_waiting.count")).getValue()) + .isEqualTo(1); + + assertThat(((Gauge) gauges.getMetrics().get("terminated.count")).getValue()) + .isEqualTo(1); + } + + @Test + public void hasAGaugeForTheNumberOfThreads() throws Exception { + assertThat(((Gauge) gauges.getMetrics().get("count")).getValue()) + .isEqualTo(12); + } + + @Test + public void hasAGaugeForTheNumberOfDaemonThreads() throws Exception { + assertThat(((Gauge) gauges.getMetrics().get("daemon.count")).getValue()) + .isEqualTo(13); + } + + @Test + public void hasAGaugeForAnyDeadlocks() throws Exception { + assertThat(((Gauge) gauges.getMetrics().get("deadlocks")).getValue()) + .isEqualTo(deadlocks); + } + + @Test + public void autoDiscoversTheMXBeans() throws Exception { + final ThreadStatesGaugeSet set = new ThreadStatesGaugeSet(); + assertThat(((Gauge) set.getMetrics().get("count")).getValue()) + .isNotNull(); + assertThat(((Gauge) set.getMetrics().get("deadlocks")).getValue()) + .isNotNull(); + } +} From b70505613aec495e784de569819fa61b99e9e1d6 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Wed, 13 Mar 2013 12:23:52 -0700 Subject: [PATCH 0065/2558] Remove old VirtualMachineMetrics. --- findbugs-exclude.xml | 5 - .../metrics/jvm/VirtualMachineMetrics.java | 435 ------------------ 2 files changed, 440 deletions(-) delete mode 100644 metrics-jvm/src/main/java/com/yammer/metrics/jvm/VirtualMachineMetrics.java diff --git a/findbugs-exclude.xml b/findbugs-exclude.xml index e213a07e55..5e5881843a 100644 --- a/findbugs-exclude.xml +++ b/findbugs-exclude.xml @@ -9,9 +9,4 @@ - - - - - diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/VirtualMachineMetrics.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/VirtualMachineMetrics.java deleted file mode 100644 index 018c49fb85..0000000000 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/VirtualMachineMetrics.java +++ /dev/null @@ -1,435 +0,0 @@ -package com.yammer.metrics.jvm; - -import javax.management.*; -import java.lang.Thread.State; -import java.lang.management.*; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.*; -import java.util.concurrent.TimeUnit; - -/** - * A collection of Java Virtual Machine metrics. - */ -public class VirtualMachineMetrics { - private static final int MAX_STACK_TRACE_DEPTH = 100; - - private static final VirtualMachineMetrics INSTANCE = new VirtualMachineMetrics( - ManagementFactory.getMemoryMXBean(), - ManagementFactory.getMemoryPoolMXBeans(), - ManagementFactory.getOperatingSystemMXBean(), - ManagementFactory.getThreadMXBean(), - ManagementFactory.getGarbageCollectorMXBeans(), - ManagementFactory.getRuntimeMXBean(), - ManagementFactory.getPlatformMBeanServer()); - - /** - * The default instance of {@link VirtualMachineMetrics}. - * - * @return the default {@link VirtualMachineMetrics instance} - */ - public static VirtualMachineMetrics getInstance() { - return INSTANCE; - } - - /** - * Per-GC statistics. - */ - public static class GarbageCollectorStats { - private final long runs, timeMS; - - private GarbageCollectorStats(long runs, long timeMS) { - this.runs = runs; - this.timeMS = timeMS; - } - - /** - * Returns the number of times the garbage collector has run. - * - * @return the number of times the garbage collector has run - */ - public long getRuns() { - return runs; - } - - /** - * Returns the amount of time in the given unit the garbage collector has taken in total. - * - * @param unit the time unit for the return value - * @return the amount of time in the given unit the garbage collector - */ - public long getTime(TimeUnit unit) { - return unit.convert(timeMS, TimeUnit.MILLISECONDS); - } - } - - /** - * The management interface for a buffer pool, for example a pool of {@link - * java.nio.ByteBuffer#allocateDirect direct} or {@link java.nio.MappedByteBuffer mapped} - * buffers. - */ - public static class BufferPoolStats { - private final long count, memoryUsed, totalCapacity; - - private BufferPoolStats(long count, long memoryUsed, long totalCapacity) { - this.count = count; - this.memoryUsed = memoryUsed; - this.totalCapacity = totalCapacity; - } - - /** - * Returns an estimate of the number of buffers in the pool. - * - * @return An estimate of the number of buffers in this pool - */ - public long getCount() { - return count; - } - - /** - * Returns an estimate of the memory that the Java virtual machine is using for this buffer - * pool. The value returned by this method may differ from the estimate of the total {@link - * #getTotalCapacity capacity} of the buffers in this pool. This difference is explained by - * alignment, memory allocator, and other implementation specific reasons. - * - * @return An estimate of the memory that the Java virtual machine is using for this buffer - * pool in bytes, or {@code -1L} if an estimate of the memory usage is not - * available - */ - public long getMemoryUsed() { - return memoryUsed; - } - - /** - * Returns an estimate of the total capacity of the buffers in this pool. A buffer's - * capacity is the number of elements it contains and the value returned by this method is - * an estimate of the total capacity of buffers in the pool in bytes. - * - * @return An estimate of the total capacity of the buffers in this pool in bytes - */ - public long getTotalCapacity() { - return totalCapacity; - } - } - - private final MemoryMXBean memory; - private final List memoryPools; - private final OperatingSystemMXBean os; - private final ThreadMXBean threads; - private final List garbageCollectors; - private final RuntimeMXBean runtime; - private final MBeanServer mBeanServer; - - VirtualMachineMetrics(MemoryMXBean memory, - List memoryPools, - OperatingSystemMXBean os, - ThreadMXBean threads, - List garbageCollectors, - RuntimeMXBean runtime, MBeanServer mBeanServer) { - this.memory = memory; - this.memoryPools = memoryPools; - this.os = os; - this.threads = threads; - this.garbageCollectors = garbageCollectors; - this.runtime = runtime; - this.mBeanServer = mBeanServer; - } - - /** - * Returns the total initial memory of the current JVM. - * - * @return total Heap and non-heap initial JVM memory in bytes. - */ - public double getTotalInit() { - return memory.getHeapMemoryUsage().getInit() + - memory.getNonHeapMemoryUsage().getInit(); - } - - /** - * Returns the total memory currently used by the current JVM. - * - * @return total Heap and non-heap memory currently used by JVM in bytes. - */ - public double getTotalUsed() { - return memory.getHeapMemoryUsage().getUsed() + - memory.getNonHeapMemoryUsage().getUsed(); - } - - /** - * Returns the total memory currently used by the current JVM. - * - * @return total Heap and non-heap memory currently used by JVM in bytes. - */ - public double getTotalMax() { - return memory.getHeapMemoryUsage().getMax() + - memory.getNonHeapMemoryUsage().getMax(); - } - - /** - * Returns the total memory committed to the JVM. - * - * @return total Heap and non-heap memory currently committed to the JVM in bytes. - */ - public double getTotalCommitted() { - return memory.getHeapMemoryUsage().getCommitted() + - memory.getNonHeapMemoryUsage().getCommitted(); - } - - /** - * Returns the heap initial memory of the current JVM. - * - * @return Heap initial JVM memory in bytes. - */ - public double getHeapInit() { - return memory.getHeapMemoryUsage().getInit(); - } - - /** - * Returns the heap memory currently used by the current JVM. - * - * @return Heap memory currently used by JVM in bytes. - */ - public double getHeapUsed() { - return memory.getHeapMemoryUsage().getUsed(); - } - - /** - * Returns the heap memory currently used by the current JVM. - * - * @return Heap memory currently used by JVM in bytes. - */ - public double getHeapMax() { - return memory.getHeapMemoryUsage().getMax(); - } - - /** - * Returns the heap memory committed to the JVM. - * - * @return Heap memory currently committed to the JVM in bytes. - */ - public double getHeapCommitted() { - return memory.getHeapMemoryUsage().getCommitted(); - } - - /** - * Returns the percentage of the JVM's heap which is being used. - * - * @return the percentage of the JVM's heap which is being used - */ - public double getHeapUsage() { - final MemoryUsage usage = memory.getHeapMemoryUsage(); - return usage.getUsed() / (double) usage.getMax(); - } - - /** - * Returns the percentage of the JVM's non-heap memory (e.g., direct buffers) which is being - * used. - * - * @return the percentage of the JVM's non-heap memory which is being used - */ - public double getNonHeapUsage() { - final MemoryUsage usage = memory.getNonHeapMemoryUsage(); - return usage.getUsed() / (double) usage.getMax(); - } - - /** - * Returns a map of memory pool names to the percentage of that pool which is being used. - * - * @return a map of memory pool names to the percentage of that pool which is being used - */ - public Map getMemoryPoolUsage() { - final Map pools = new TreeMap(); - for (MemoryPoolMXBean pool : memoryPools) { - final double max = pool.getUsage().getMax() == -1 ? - pool.getUsage().getCommitted() : - pool.getUsage().getMax(); - pools.put(pool.getName(), pool.getUsage().getUsed() / max); - } - return Collections.unmodifiableMap(pools); - } - - /** - * Returns the percentage of available file descriptors which are currently in use. - * - * @return the percentage of available file descriptors which are currently in use, or {@code - * NaN} if the running JVM does not have access to this information - */ - public double getFileDescriptorUsage() { - try { - final Method getOpenFileDescriptorCount = os.getClass() - .getDeclaredMethod( - "getOpenFileDescriptorCount"); - getOpenFileDescriptorCount.setAccessible(true); - final Long openFds = (Long) getOpenFileDescriptorCount.invoke(os); - final Method getMaxFileDescriptorCount = os.getClass() - .getDeclaredMethod( - "getMaxFileDescriptorCount"); - getMaxFileDescriptorCount.setAccessible(true); - final Long maxFds = (Long) getMaxFileDescriptorCount.invoke(os); - return openFds.doubleValue() / maxFds.doubleValue(); - } catch (NoSuchMethodException e) { - return Double.NaN; - } catch (IllegalAccessException e) { - return Double.NaN; - } catch (InvocationTargetException e) { - return Double.NaN; - } - } - - /** - * Returns the version of the currently-running jvm. - * - * @return the version of the currently-running jvm, eg "1.6.0_24" - * @see J2SE SDK/JRE Version String - * Naming Convention - */ - public String getVersion() { - return System.getProperty("java.runtime.version"); - } - - /** - * Returns the name of the currently-running jvm. - * - * @return the name of the currently-running jvm, eg "Java HotSpot(TM) Client VM" - * @see System.getProperties() - */ - public String getName() { - return System.getProperty("java.vm.name"); - } - - /** - * Returns the number of seconds the JVM process has been running. - * - * @return the number of seconds the JVM process has been running - */ - public long getUptime() { - return TimeUnit.MILLISECONDS.toSeconds(runtime.getUptime()); - } - - /** - * Returns the number of live threads (includes {@link #getDaemonThreadCount}. - * - * @return the number of live threads - */ - public int getThreadCount() { - return threads.getThreadCount(); - } - - /** - * Returns the number of live daemon threads. - * - * @return the number of live daemon threads - */ - public int getDaemonThreadCount() { - return threads.getDaemonThreadCount(); - } - - /** - * Returns a map of garbage collector names to garbage collector information. - * - * @return a map of garbage collector names to garbage collector information - */ - public Map getGarbageCollectors() { - final Map stats = new HashMap(); - for (GarbageCollectorMXBean gc : garbageCollectors) { - stats.put(gc.getName(), - new GarbageCollectorStats(gc.getCollectionCount(), - gc.getCollectionTime())); - } - return Collections.unmodifiableMap(stats); - } - - /** - * Returns a set of strings describing deadlocked threads, if any are deadlocked. - * - * @return a set of any deadlocked threads - */ - public Set getDeadlockedThreads() { - final long[] threadIds = threads.findDeadlockedThreads(); - if (threadIds != null) { - final Set threads = new HashSet(); - for (ThreadInfo info : this.threads.getThreadInfo(threadIds, MAX_STACK_TRACE_DEPTH)) { - final StringBuilder stackTrace = new StringBuilder(); - for (StackTraceElement element : info.getStackTrace()) { - stackTrace.append("\t at ").append(element.toString()).append('\n'); - } - - threads.add( - String.format( - "%s locked on %s (owned by %s):\n%s", - info.getThreadName(), info.getLockName(), - info.getLockOwnerName(), - stackTrace.toString() - ) - ); - } - return Collections.unmodifiableSet(threads); - } - return Collections.emptySet(); - } - - /** - * Returns a map of thread states to the percentage of all threads which are in that state. - * - * @return a map of thread states to percentages - */ - public Map getThreadStatePercentages() { - final Map conditions = new HashMap(); - for (State state : State.values()) { - conditions.put(state, 0.0); - } - - final long[] allThreadIds = threads.getAllThreadIds(); - final ThreadInfo[] allThreads = threads.getThreadInfo(allThreadIds); - int liveCount = 0; - for (ThreadInfo info : allThreads) { - if (info != null) { - final State state = info.getThreadState(); - conditions.put(state, conditions.get(state) + 1); - liveCount++; - } - } - for (State state : new ArrayList(conditions.keySet())) { - conditions.put(state, conditions.get(state) / liveCount); - } - - return Collections.unmodifiableMap(conditions); - } - - - public Map getBufferPoolStats() { - try { - final String[] attributes = { "Count", "MemoryUsed", "TotalCapacity" }; - - final ObjectName direct = new ObjectName("java.nio:type=BufferPool,name=direct"); - final ObjectName mapped = new ObjectName("java.nio:type=BufferPool,name=mapped"); - - final AttributeList directAttributes = mBeanServer.getAttributes(direct, attributes); - final AttributeList mappedAttributes = mBeanServer.getAttributes(mapped, attributes); - - final Map stats = new TreeMap(); - - final BufferPoolStats directStats = new BufferPoolStats((Long) ((Attribute) directAttributes - .get(0)).getValue(), - (Long) ((Attribute) directAttributes - .get(1)).getValue(), - (Long) ((Attribute) directAttributes - .get(2)).getValue()); - - stats.put("direct", directStats); - - final BufferPoolStats mappedStats = new BufferPoolStats((Long) ((Attribute) mappedAttributes - .get(0)).getValue(), - (Long) ((Attribute) mappedAttributes - .get(1)).getValue(), - (Long) ((Attribute) mappedAttributes - .get(2)).getValue()); - - stats.put("mapped", mappedStats); - - return Collections.unmodifiableMap(stats); - } catch (JMException e) { - return Collections.emptyMap(); - } - } -} From 4766c837908ee72a305096f9d075f515be519bf3 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Wed, 13 Mar 2013 14:18:49 -0700 Subject: [PATCH 0066/2558] Updated the Getting Started guide for 3.0. Improves on #350 a bit. --- docs/source/getting-started.rst | 104 ++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 32 deletions(-) diff --git a/docs/source/getting-started.rst b/docs/source/getting-started.rst index d980d66c07..b8d45ccfd1 100644 --- a/docs/source/getting-started.rst +++ b/docs/source/getting-started.rst @@ -23,12 +23,27 @@ Just add the ``metrics-core`` library as a dependency: com.yammer.metrics metrics-core - 2.1.3 + 3.0.0 Now it's time to add some metrics to your application! +.. _gs-registry: + +The Registry +============ + +The centerpiece of Metrics is the ``MetricRegistry`` class, which is the container for all your +application's metrics. Go ahead and create a new one and give it a name: + +.. code-block:: java + + final MetricRegistry metrics = new MetricRegistry("example"); + +You'll probably want to integrate this into your application's lifecycle (maybe using your +dependency injection framework), but for now a ``static`` field is fine. + .. _gs-gauges: Gauges @@ -40,18 +55,31 @@ of pending jobs in a queue: .. code-block:: java public class QueueManager { - private Queue queue; - - private final Gauge myGauge = - Metrics.newGauge(QueueManager.class, "pending-jobs", new Gauge() { - @Override - public Integer value() { - return queue.size(); - } - }); + private final Queue queue; + + public QueueManager(MetricRegistry metrics, String name) { + this.queue = new Queue(); + metrics.register(MetricRegistry.name(QueueManager.class, name, "size"), + new Gauge() { + @Override + public Integer getValue() { + return queue.size(); + } + }); + } } -Every time this gauge is measured, it will return the number of jobs in the queue. +When this gauge is measured, it will return the number of jobs in the queue. + +Every metric in a registry has a unique name, which is just a dotted-name string like +``"things.count"`` or ``"com.example.Thing.latency"``. ``MetricRegistry`` has a static helper method +for constructing these names: + +.. code-block:: java + + MetricRegistry.name(QueueManager.class, "jobs", "size") + +This will return a string with something like ``"com.example.QueueManager.jobs.size"``. For most queue and queue-like structures, you won't want to simply return ``queue.size()``. Most of ``java.util`` and ``java.util.concurrent`` have implementations of ``#size()`` which are ``O(n)``, @@ -67,7 +95,7 @@ For example, we may want a more efficient way of measuring the pending job in a .. code-block:: java - private final Counter pendingJobs = Metrics.newCounter(QueueManager.class, "pending-jobs"); + private final Counter pendingJobs = metrics.counter(name(QueueManager.class, "pending-jobs")); public void addJob(Job job) { pendingJobs.inc(); @@ -81,6 +109,14 @@ For example, we may want a more efficient way of measuring the pending job in a Every time this counter is measured, it will return the number of jobs in the queue. +As you can see, the API for counters is slightly different: ``#counter(String)`` instead of +``#register(String, Metric)``. While you can use ``register`` and create your own ``Counter`` +instance, ``#counter(String)`` does all the work for you, and allows you to reuse metrics with the +same name. + +Also, we've statically imported ``MetricRegistry``'s ``name`` method in this scope to reduce +clutter. + .. _gs-meters: Meters @@ -91,7 +127,7 @@ rate, meters also track 1-, 5-, and 15-minute moving averages. .. code-block:: java - private final Meter requests = Metrics.newMeter(RequestHandler.class, "requests", "requests", TimeUnit.SECONDS); + private final Meter requests = metrics.meter(name(RequestHandler.class, "requests")); public void handleRequest(Request request, Response response) { requests.mark(); @@ -111,7 +147,7 @@ percentiles. .. code-block:: java - private final Histogram responseSizes = Metrics.newHistogram(RequestHandler.class, "response-sizes"); + private final Histogram responseSizes = metrics.histogram(name(RequestHandler.class, "response-sizes"); public void handleRequest(Request request, Response response) { // etc @@ -131,10 +167,10 @@ duration. .. code-block:: java - private final Timer responses = Metrics.newTimer(RequestHandler.class, "responses", TimeUnit.MILLISECONDS, TimeUnit.SECONDS); + private final Timer responses = metrics.timer(name(RequestHandler.class, "responses"); public String handleRequest(Request request, Response response) { - final TimerContext context = responses.time(); + final Timer.Context context = responses.time(); try { // etc; return "OK"; @@ -143,7 +179,7 @@ duration. } } -This timer will measure the amount of time it takes to process each request in milliseconds and +This timer will measure the amount of time it takes to process each request in nanoseconds and provide a rate of requests in requests per second. @@ -152,14 +188,18 @@ provide a rate of requests in requests per second. Health Checks ============= -Metrics also has the ability to centralize your service's health checks. First, implement a -``HealthCheck`` instance: - +Metrics also has the ability to centralize your service's health checks with the +``metrics-healthchecks`` module. +First, create a new ``HealthCheckRegistry`` instance: .. code-block:: java - import com.yammer.metrics.core.HealthCheck.Result; + final HealthCheckRegistry healthChecks = new HealthCheckRegistry(); + +Second, implement a ``HealthCheck`` subclass: + +.. code-block:: java public class DatabaseHealthCheck extends HealthCheck { private final Database database; @@ -170,11 +210,11 @@ Metrics also has the ability to centralize your service's health checks. First, } @Override - public Result check() throws Exception { + public HealthCheck.Result check() throws Exception { if (database.isConnected()) { - return Result.healthy(); + return HealthCheck.Result.healthy(); } else { - return Result.unhealthy("Cannot connect to " + database.getUrl()); + return HealthCheck.Result.unhealthy("Cannot connect to " + database.getUrl()); } } } @@ -183,15 +223,14 @@ Then register an instance of it with Metrics: .. code-block:: java - HealthChecks.register(new DatabaseHealthCheck(database)); + healthChecks.register("postgres", new DatabaseHealthCheck(database)); To run all of the registered health checks: .. code-block:: java - - final Map results = HealthChecks.runHealthChecks(); - for (Entry entry : results.entrySet()) { + final Map results = healthChecks.runHealthChecks(); + for (Entry entry : results.entrySet()) { if (entry.getValue().isHealthy()) { System.out.println(entry.getKey() + " is healthy"); } else { @@ -203,7 +242,7 @@ To run all of the registered health checks: } } -Metrics comes with a pre-built health check: ``DeadlockHealthCheck``, which uses Java 1.6's built-in +Metrics comes with a pre-built health check: ``DeadlockHealthCheck``, which uses Java's built-in thread deadlock detection to determine if any threads are deadlocked. .. _gs-jmx: @@ -233,14 +272,14 @@ registered metrics. It will also run health checks, print out a thread dump, and ``HealthCheckServlet``, ``ThreadDumpServlet``, and ``PingServlet``--which do these individual tasks.) -To use this servlet, include the ``metrics-servlet`` module as a dependency: +To use this servlet, include the ``metrics-servlets`` module as a dependency: .. code-block:: xml com.yammer.metrics - metrics-servlet - 2.1.2 + metrics-servlets + 3.0.0 From there on, you can map the servlet to whatever path you see fit. @@ -254,5 +293,6 @@ In addition to JMX and HTTP, Metrics also has reporters for the following output * ``STDOUT``, using :ref:`ConsoleReporter ` from ``metrics-core`` * ``CSV`` files, using :ref:`CsvReporter ` from ``metrics-core`` +* Slf4j loggers, using :ref:`LoggerReporter ` from ``metrics-core`` * Ganglia, using :ref:`GangliaReporter ` from ``metrics-ganglia`` * Graphite, using :ref:`GraphiteReporter ` from ``metrics-graphite`` From c9ebdada81799f9791c3e9cb3af933a670332f3e Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 14 Mar 2013 20:56:00 -0700 Subject: [PATCH 0067/2558] =?UTF-8?q?Added=20tests=20and=20docs=20for=20Sl?= =?UTF-8?q?f4jReporter=20(ne=C3=A9=20LoggerReport).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #347. --- ...LoggerReporter.java => Slf4jReporter.java} | 93 +++++---- .../metrics/tests/Slf4jReporterTest.java | 186 ++++++++++++++++++ 2 files changed, 245 insertions(+), 34 deletions(-) rename metrics-core/src/main/java/com/yammer/metrics/{LoggerReporter.java => Slf4jReporter.java} (50%) create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java diff --git a/metrics-core/src/main/java/com/yammer/metrics/LoggerReporter.java b/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java similarity index 50% rename from metrics-core/src/main/java/com/yammer/metrics/LoggerReporter.java rename to metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java index 342876b166..9cf1f94586 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/LoggerReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java @@ -3,8 +3,10 @@ import org.slf4j.Logger; import org.slf4j.Marker; +import java.util.Locale; import java.util.Map.Entry; import java.util.SortedMap; +import java.util.concurrent.TimeUnit; /** * Metrics reporter class for logging metrics values to a SLF4J {@link Logger} periodically, similar @@ -12,10 +14,13 @@ * instead. It also supports specifying a {@link Marker} instance that can be used by custom * appenders and filters for the bound logging toolkit to further process metrics reports. */ -public class LoggerReporter extends AbstractPollingReporter { - - private Logger logger; - private Marker marker; +public class Slf4jReporter extends AbstractPollingReporter { + private final Logger logger; + private final Marker marker; + private final double durationFactor; + private final String durationUnit; + private final double rateFactor; + private final String rateUnit; /** * Construct a new SLF4J reporter. @@ -23,22 +28,32 @@ public class LoggerReporter extends AbstractPollingReporter { * @param registry Metrics registry to report from. * @param logger SLF4J {@link Logger} instance to send metrics reports to */ - public LoggerReporter(MetricRegistry registry, Logger logger) { - this(registry, logger, null); + public Slf4jReporter(MetricRegistry registry, Logger logger) { + this(registry, logger, null, TimeUnit.SECONDS, TimeUnit.SECONDS); } /** * Construct a new SLF4J reporter. * - * @param registry Metrics registry to report from. - * @param logger SLF4J {@link Logger} instance to send metrics reports to - * @param marker SLF4J {@link Marker} instance to log with metrics class, or null if - * none. + * @param registry Metrics registry to report from. + * @param logger SLF4J {@link Logger} instance to send metrics reports to + * @param marker SLF4J {@link Marker} instance to log with metrics class, or null if + * none. + * @param rateUnit the unit to which rates will be converted + * @param durationUnit the unit to which durations will be converted */ - public LoggerReporter(MetricRegistry registry, Logger logger, Marker marker) { + public Slf4jReporter(MetricRegistry registry, + Logger logger, + Marker marker, + TimeUnit rateUnit, + TimeUnit durationUnit) { super(registry, "logger-reporter"); this.logger = logger; this.marker = marker; + this.rateFactor = rateUnit.toSeconds(1); + this.rateUnit = "events/" + calculateRateUnit(rateUnit); + this.durationFactor = 1.0 / durationUnit.toNanos(1); + this.durationUnit = durationUnit.toString().toLowerCase(Locale.US); } @Override @@ -71,34 +86,39 @@ public void report(SortedMap gauges, private void logTimer(String name, Timer timer) { final Snapshot snapshot = timer.getSnapshot(); logger.info(marker, - "type=TIMER, name={}, count={}, min={}, max={}, mean={}, stddev={}, median={}, p75={}, p95={}, p98={}, p999={}, mean_rate={}, m1={}, m5={}, m15={}", + "type=TIMER, name={}, count={}, min={}, max={}, mean={}, stddev={}, median={}, " + + "p75={}, p95={}, p98={}, p999={}, mean_rate={}, m1={}, m5={}, m15={}, " + + "rate_unit={}, duration_unit={}", name, timer.getCount(), - timer.getMin(), - timer.getMax(), - timer.getMean(), - timer.getStdDev(), - snapshot.getMedian(), - snapshot.get75thPercentile(), - snapshot.get95thPercentile(), - snapshot.get98thPercentile(), - snapshot.get99thPercentile(), - snapshot.get999thPercentile(), - timer.getMeanRate(), - timer.getOneMinuteRate(), - timer.getFiveMinuteRate(), - timer.getFifteenMinuteRate()); + timer.getMin() * durationFactor, + timer.getMax() * durationFactor, + timer.getMean() * durationFactor, + timer.getStdDev() * durationFactor, + snapshot.getMedian() * durationFactor, + snapshot.get75thPercentile() * durationFactor, + snapshot.get95thPercentile() * durationFactor, + snapshot.get98thPercentile() * durationFactor, + snapshot.get99thPercentile() * durationFactor, + snapshot.get999thPercentile() * durationFactor, + timer.getMeanRate() * rateFactor, + timer.getOneMinuteRate() * rateFactor, + timer.getFiveMinuteRate() * rateFactor, + timer.getFifteenMinuteRate() * rateFactor, + rateUnit, + durationUnit); } private void logMeter(String name, Meter meter) { logger.info(marker, - "type=METER, name={}, count={}, mean_rate={}, m1={}, m5={}, m15={}", - name, - meter.getCount(), - meter.getMeanRate(), - meter.getOneMinuteRate(), - meter.getFiveMinuteRate(), - meter.getFifteenMinuteRate()); + "type=METER, name={}, count={}, mean_rate={}, m1={}, m5={}, m15={}, rate_unit={}", + name, + meter.getCount(), + meter.getMeanRate() * rateFactor, + meter.getOneMinuteRate() * rateFactor, + meter.getFiveMinuteRate() * rateFactor, + meter.getFifteenMinuteRate() * rateFactor, + rateUnit); } private void logHistogram(String name, Histogram histogram) { @@ -120,10 +140,15 @@ private void logHistogram(String name, Histogram histogram) { } private void logCounter(String name, Counter counter) { - logger.info(marker, "type=COUNTER, name={}, count={} ", name, counter.getCount()); + logger.info(marker, "type=COUNTER, name={}, count={}", name, counter.getCount()); } private void logGauge(String name, Gauge gauge) { logger.info(marker, "type=GAUGE, name={}, value={}", name, gauge.getValue()); } + + private String calculateRateUnit(TimeUnit unit) { + final String s = unit.toString().toLowerCase(Locale.US); + return s.substring(0, s.length() - 1); + } } diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java new file mode 100644 index 0000000000..359c91aba1 --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java @@ -0,0 +1,186 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.*; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.Marker; + +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.concurrent.TimeUnit; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class Slf4jReporterTest { + private final Logger logger = mock(Logger.class); + private final Marker marker = mock(Marker.class); + private final MetricRegistry registry = mock(MetricRegistry.class); + private final Slf4jReporter reporter = new Slf4jReporter(registry, + logger, + marker, + TimeUnit.SECONDS, + TimeUnit.MILLISECONDS); + + @Test + public void reportsGaugeValues() throws Exception { + reporter.report(map("gauge", gauge("value")), + this.map(), + this.map(), + this.map(), + this.map()); + + verify(logger).info(marker, "type=GAUGE, name={}, value={}", "gauge", "value"); + } + + @Test + public void reportsCounterValues() throws Exception { + final Counter counter = mock(Counter.class); + when(counter.getCount()).thenReturn(100L); + + reporter.report(this.map(), + map("test.counter", counter), + this.map(), + this.map(), + this.map()); + + verify(logger).info(marker, "type=COUNTER, name={}, count={}", "test.counter", 100L); + } + + @Test + public void reportsHistogramValues() throws Exception { + final Histogram histogram = mock(Histogram.class); + when(histogram.getCount()).thenReturn(1L); + when(histogram.getMax()).thenReturn(2L); + when(histogram.getMean()).thenReturn(3.0); + when(histogram.getMin()).thenReturn(4L); + when(histogram.getStdDev()).thenReturn(5.0); + + final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMedian()).thenReturn(6.0); + when(snapshot.get75thPercentile()).thenReturn(7.0); + when(snapshot.get95thPercentile()).thenReturn(8.0); + when(snapshot.get98thPercentile()).thenReturn(9.0); + when(snapshot.get99thPercentile()).thenReturn(10.0); + when(snapshot.get999thPercentile()).thenReturn(11.0); + + when(histogram.getSnapshot()).thenReturn(snapshot); + + reporter.report(this.map(), + this.map(), + map("test.histogram", histogram), + this.map(), + this.map()); + + verify(logger).info(marker, + "type=HISTOGRAM, name={}, count={}, min={}, max={}, mean={}, stddev={}, median={}, p75={}, p95={}, p98={}, p999={}", + "test.histogram", + 1L, + 4L, + 2L, + 3.0, + 5.0, + 6.0, + 7.0, + 8.0, + 9.0, + 10.0, + 11.0); + } + + @Test + public void reportsMeterValues() throws Exception { + final Meter meter = mock(Meter.class); + when(meter.getCount()).thenReturn(1L); + when(meter.getMeanRate()).thenReturn(2.0); + when(meter.getOneMinuteRate()).thenReturn(3.0); + when(meter.getFiveMinuteRate()).thenReturn(4.0); + when(meter.getFifteenMinuteRate()).thenReturn(5.0); + + reporter.report(this.map(), + this.map(), + this.map(), + map("test.meter", meter), + this.map()); + + verify(logger).info(marker, + "type=METER, name={}, count={}, mean_rate={}, m1={}, m5={}, m15={}, rate_unit={}", + "test.meter", + 1L, + 2.0, + 3.0, + 4.0, + 5.0, + "events/second"); + } + + @Test + public void reportsTimerValues() throws Exception { + final Timer timer = mock(Timer.class); + when(timer.getCount()).thenReturn(1L); + when(timer.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); + when(timer.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); + when(timer.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); + when(timer.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); + + when(timer.getMeanRate()).thenReturn(2.0); + when(timer.getOneMinuteRate()).thenReturn(3.0); + when(timer.getFiveMinuteRate()).thenReturn(4.0); + when(timer.getFifteenMinuteRate()).thenReturn(5.0); + + final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); + when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); + when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); + when(snapshot.get98thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(800)); + when(snapshot.get99thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(900)); + when(snapshot.get999thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS + .toNanos(1000)); + + when(timer.getSnapshot()).thenReturn(snapshot); + + reporter.report(this.map(), + this.map(), + this.map(), + this.map(), + map("test.another.timer", timer)); + + verify(logger).info(marker, + "type=TIMER, name={}, count={}, min={}, max={}, mean={}, stddev={}, median={}, p75={}, p95={}, p98={}, p999={}, mean_rate={}, m1={}, m5={}, m15={}, rate_unit={}, duration_unit={}", + "test.another.timer", + 1L, + 300.0, + 100.0, + 200.0, + 400.0, + 500.0, + 600.0, + 700.0, + 800.0, + 900.0, + 1000.0, + 2.0, + 3.0, + 4.0, + 5.0, + "events/second", + "milliseconds"); + } + + private SortedMap map() { + return new TreeMap(); + } + + private SortedMap map(String name, T metric) { + final TreeMap map = new TreeMap(); + map.put(name, metric); + return map; + } + + private Gauge gauge(T value) { + final Gauge gauge = mock(Gauge.class); + when(gauge.getValue()).thenReturn(value); + return gauge; + } +} From 53f592a995cc1fa36e4d763b47de2eb419d55571 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Fri, 15 Mar 2013 12:59:11 -0700 Subject: [PATCH 0068/2558] Added tests and docs for MetricRegistry.name. Closes #349. --- .../com/yammer/metrics/MetricRegistry.java | 17 ++++++++- .../metrics/tests/MetricRegistryTest.java | 38 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java index 99ed21d263..54fbf8246d 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -9,6 +9,13 @@ * A registry of metric instances. */ public class MetricRegistry { + /** + * Concatenates elements to form a dotted name, eliding any null values or empty strings. + * + * @param name the first element of the name + * @param names the remaining elements of the name + * @return {@code name} and {@code names} concatenated by periods + */ public static String name(String name, String... names) { final StringBuilder builder = new StringBuilder(); append(builder, name); @@ -18,8 +25,16 @@ public static String name(String name, String... names) { return builder.toString(); } + /** + * Concatenates a class name and elements to form a dotted name, eliding any null values or + * empty strings. + * + * @param klass the first element of the name + * @param names the remaining elements of the name + * @return {@code klass} and {@code names} concatenated by periods + */ public static String name(Class klass, String... names) { - return name(klass.getCanonicalName(), names); + return name(klass.getName(), names); } private static void append(StringBuilder builder, String part) { diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java index 51e5966782..8bb852ea6b 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java @@ -7,6 +7,7 @@ import java.util.HashMap; import java.util.Map; +import static com.yammer.metrics.MetricRegistry.name; import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.data.MapEntry.entry; import static org.mockito.Mockito.*; @@ -280,4 +281,41 @@ public Map getMetrics() { assertThat(registry.getNames()) .containsOnly("my.gauge", "my.counter"); } + + @Test + public void concatenatesStringsToFormADottedName() throws Exception { + assertThat(name("one", "two", "three")) + .isEqualTo("one.two.three"); + } + + @Test + public void elidesNullValuesFromNames() throws Exception { + assertThat(name("one", null, "three")) + .isEqualTo("one.three"); + } + + @Test + public void elidesEmptyStringsFromNames() throws Exception { + assertThat(name("one", "", "three")) + .isEqualTo("one.three"); + } + + @Test + public void concatenatesClassNamesWithStringsToFormADottedName() throws Exception { + assertThat(name(MetricRegistryTest.class, "one", "two")) + .isEqualTo("com.yammer.metrics.tests.MetricRegistryTest.one.two"); + } + + @Test + public void concatenatesClassesWithoutCanonicalNamesWithStrings() throws Exception { + final Gauge g = new Gauge() { + @Override + public String getValue() { + return null; + } + }; + + assertThat(name(g.getClass(), "one", "two")) + .isEqualTo("com.yammer.metrics.tests.MetricRegistryTest$3.one.two"); + } } From 19376159e37efaf2546d30b6eff99eb185994f36 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 17 Mar 2013 17:20:01 -0700 Subject: [PATCH 0069/2558] Added tests and docs for CsvReporter. Closes #336. --- .../java/com/yammer/metrics/CsvReporter.java | 5 +- .../yammer/metrics/tests/CsvReporterTest.java | 206 ++++++++++++++++++ 2 files changed, 209 insertions(+), 2 deletions(-) create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java diff --git a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java index 83afbbb6a0..eb2a057b1b 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java @@ -10,8 +10,9 @@ import java.util.SortedMap; import java.util.concurrent.TimeUnit; -// TODO: 3/12/13 -- test and document CsvReporter - +/** + * A reporter which creates a comma-separated values file of the measurements for each metric. + */ public class CsvReporter extends AbstractPollingReporter { private static final Logger LOGGER = LoggerFactory.getLogger(CsvReporter.class); private static final Charset UTF_8 = Charset.forName("UTF-8"); diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java new file mode 100644 index 0000000000..561f1950b4 --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java @@ -0,0 +1,206 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.*; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.*; +import java.nio.CharBuffer; +import java.util.Locale; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.concurrent.TimeUnit; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class CsvReporterTest { + @Rule public final TemporaryFolder folder = new TemporaryFolder(); + + private final MetricRegistry registry = mock(MetricRegistry.class); + private final Clock clock = mock(Clock.class); + + private File dataDirectory; + private CsvReporter reporter; + + @Before + public void setUp() throws Exception { + when(clock.getTime()).thenReturn(19910191000L); + + this.dataDirectory = folder.newFolder(); + + this.reporter = new CsvReporter(registry, + dataDirectory, + Locale.US, + TimeUnit.SECONDS, + TimeUnit.MILLISECONDS, + clock); + } + + @Test + public void reportsGaugeValues() throws Exception { + final Gauge gauge = mock(Gauge.class); + when(gauge.getValue()).thenReturn(1); + + reporter.report(map("gauge", gauge), + this.map(), + this.map(), + this.map(), + this.map()); + + assertThat(fileContents("gauge.csv")) + .isEqualTo(csv( + "t,value", + "19910191,1" + )); + } + + @Test + public void reportsCounterValues() throws Exception { + final Counter counter = mock(Counter.class); + when(counter.getCount()).thenReturn(100L); + + reporter.report(this.map(), + map("test.counter", counter), + this.map(), + this.map(), + this.map()); + + assertThat(fileContents("test.counter.csv")) + .isEqualTo(csv( + "t,count", + "19910191,100" + )); + } + + @Test + public void reportsHistogramValues() throws Exception { + final Histogram histogram = mock(Histogram.class); + when(histogram.getCount()).thenReturn(1L); + when(histogram.getMax()).thenReturn(2L); + when(histogram.getMean()).thenReturn(3.0); + when(histogram.getMin()).thenReturn(4L); + when(histogram.getStdDev()).thenReturn(5.0); + + final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMedian()).thenReturn(6.0); + when(snapshot.get75thPercentile()).thenReturn(7.0); + when(snapshot.get95thPercentile()).thenReturn(8.0); + when(snapshot.get98thPercentile()).thenReturn(9.0); + when(snapshot.get99thPercentile()).thenReturn(10.0); + when(snapshot.get999thPercentile()).thenReturn(11.0); + + when(histogram.getSnapshot()).thenReturn(snapshot); + + reporter.report(this.map(), + this.map(), + map("test.histogram", histogram), + this.map(), + this.map()); + + assertThat(fileContents("test.histogram.csv")) + .isEqualTo(csv( + "t,count,max,mean,min,stddev,p50,p75,p95,p98,p99,p999", + "19910191,1,2,3.000000,4,5.000000,6.000000,7.000000,8.000000,9.000000,10.000000,11.000000" + )); + } + + @Test + public void reportsMeterValues() throws Exception { + final Meter meter = mock(Meter.class); + when(meter.getCount()).thenReturn(1L); + when(meter.getMeanRate()).thenReturn(2.0); + when(meter.getOneMinuteRate()).thenReturn(3.0); + when(meter.getFiveMinuteRate()).thenReturn(4.0); + when(meter.getFifteenMinuteRate()).thenReturn(5.0); + + reporter.report(this.map(), + this.map(), + this.map(), + map("test.meter", meter), + this.map()); + + assertThat(fileContents("test.meter.csv")) + .isEqualTo(csv( + "t,count,mean_rate,m1_rate,m5_rate,m15_rate,rate_unit", + "19910191,1,2.000000,3.000000,4.000000,5.000000,events/second" + )); + } + + @Test + public void reportsTimerValues() throws Exception { + final Timer timer = mock(Timer.class); + when(timer.getCount()).thenReturn(1L); + when(timer.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); + when(timer.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); + when(timer.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); + when(timer.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); + + when(timer.getMeanRate()).thenReturn(2.0); + when(timer.getOneMinuteRate()).thenReturn(3.0); + when(timer.getFiveMinuteRate()).thenReturn(4.0); + when(timer.getFifteenMinuteRate()).thenReturn(5.0); + + final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); + when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); + when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); + when(snapshot.get98thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(800)); + when(snapshot.get99thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(900)); + when(snapshot.get999thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(1000)); + + when(timer.getSnapshot()).thenReturn(snapshot); + + reporter.report(this.map(), + this.map(), + this.map(), + this.map(), + map("test.another.timer", timer)); + + assertThat(fileContents("test.another.timer.csv")) + .isEqualTo(csv( + "t,count,max,mean,min,stddev,p50,p75,p95,p98,p99,p999,mean_rate,m1_rate,m5_rate,m15_rate,rate_unit,duration_unit", + "19910191,1,100.000000,200.000000,300.000000,400.000000,500.000000,600.000000,700.000000,800.000000,900.000000,1000.000000,2.000000,3.000000,4.000000,5.000000,calls/second,milliseconds" + )); + } + + private String csv(String... lines) { + final StringBuilder builder = new StringBuilder(); + for (String line : lines) { + builder.append(line).append(String.format("%n")); + } + return builder.toString(); + } + + private String fileContents(String filename) throws IOException { + final StringBuilder builder = new StringBuilder(); + final FileInputStream input = new FileInputStream(new File(dataDirectory, filename)); + try { + final InputStreamReader reader = new InputStreamReader(input); + final BufferedReader bufferedReader = new BufferedReader(reader); + final CharBuffer buf = CharBuffer.allocate(1024); + while (bufferedReader.read(buf) != -1) { + buf.flip(); + builder.append(buf); + buf.clear(); + } + } finally { + input.close(); + } + return builder.toString(); + } + + private SortedMap map() { + return new TreeMap(); + } + + private SortedMap map(String name, T metric) { + final TreeMap map = new TreeMap(); + map.put(name, metric); + return map; + } + +} From 60b3c69ed4eff275a45b0908c2e31a107f37b80b Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 17 Mar 2013 18:28:50 -0700 Subject: [PATCH 0070/2558] Added tests and docs for ConsoleReporter. Closes #335. --- .../com/yammer/metrics/ConsoleReporter.java | 142 +++++++---- .../metrics/tests/ConsoleReporterTest.java | 240 ++++++++++++++++++ .../yammer/metrics/tests/CsvReporterTest.java | 1 - 3 files changed, 333 insertions(+), 50 deletions(-) create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java diff --git a/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java b/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java index f7466dea8c..73a22d8dc3 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java @@ -3,10 +3,11 @@ import java.io.PrintStream; import java.text.DateFormat; import java.util.*; +import java.util.concurrent.TimeUnit; -// TODO: 3/10/13 -- write tests -// TODO: 3/10/13 -- write docs - +/** + * A reporter which outputs measurements to a {@link PrintStream}, like STDOUT. + */ public class ConsoleReporter extends AbstractPollingReporter { private static final int CONSOLE_WIDTH = 80; @@ -14,12 +15,29 @@ public class ConsoleReporter extends AbstractPollingReporter { private final Locale locale; private final Clock clock; private final DateFormat dateFormat; - + private final double durationFactor; + private final String durationUnit; + private final double rateFactor; + private final String rateUnit; + + /** + * Creates a new {@link ConsoleReporter}. + * + * @param registry the registry containing the metrics to report + * @param output the print stream to be written to + * @param locale the local in which data should be formatted + * @param clock a clock + * @param timeZone the time zone in which dates should be presented + * @param rateUnit the unit in which rates should be presented + * @param durationUnit the unit in which durations should be presented + */ public ConsoleReporter(MetricRegistry registry, PrintStream output, Locale locale, Clock clock, - TimeZone timeZone) { + TimeZone timeZone, + TimeUnit rateUnit, + TimeUnit durationUnit) { super(registry, "console-reporter"); this.output = output; this.locale = locale; @@ -28,6 +46,10 @@ public ConsoleReporter(MetricRegistry registry, DateFormat.MEDIUM, locale); dateFormat.setTimeZone(timeZone); + this.rateFactor = rateUnit.toSeconds(1); + this.rateUnit = calculateRateUnit(rateUnit); + this.durationFactor = 1.0 / durationUnit.toNanos(1); + this.durationUnit = durationUnit.toString().toLowerCase(Locale.US); } @Override @@ -40,45 +62,63 @@ public void report(SortedMap gauges, printWithBanner(dateTime, '='); output.println(); - printWithBanner("-- Gauges", '-'); - for (Map.Entry entry : gauges.entrySet()) { - output.println(entry.getKey()); - printGauge(entry); + if (!gauges.isEmpty()) { + printWithBanner("-- Gauges", '-'); + for (Map.Entry entry : gauges.entrySet()) { + output.println(entry.getKey()); + printGauge(entry); + } + output.println(); } - output.println(); - printWithBanner("-- Counters", '-'); - for (Map.Entry entry : counters.entrySet()) { - output.println(entry.getKey()); - printCounter(entry); + if (!counters.isEmpty()) { + printWithBanner("-- Counters", '-'); + for (Map.Entry entry : counters.entrySet()) { + output.println(entry.getKey()); + printCounter(entry); + } + output.println(); } - output.println(); - printWithBanner("-- Histograms", '-'); - for (Map.Entry entry : histograms.entrySet()) { - output.println(entry.getKey()); - printHistogram(entry.getValue()); + if (!histograms.isEmpty()) { + printWithBanner("-- Histograms", '-'); + for (Map.Entry entry : histograms.entrySet()) { + output.println(entry.getKey()); + printHistogram(entry.getValue()); + } + output.println(); } - output.println(); - printWithBanner("-- Meters", '-'); - for (Map.Entry entry : meters.entrySet()) { - output.println(entry.getKey()); - printMetered(entry.getValue()); + if (!meters.isEmpty()) { + printWithBanner("-- Meters", '-'); + for (Map.Entry entry : meters.entrySet()) { + output.println(entry.getKey()); + printMeter(entry.getValue()); + } + output.println(); } - output.println(); - printWithBanner("-- Timers", '-'); - for (Map.Entry entry : timers.entrySet()) { - output.println(entry.getKey()); - printTimer(entry.getValue()); + if (!timers.isEmpty()) { + printWithBanner("-- Timers", '-'); + for (Map.Entry entry : timers.entrySet()) { + output.println(entry.getKey()); + printTimer(entry.getValue()); + } + output.println(); } - output.println(); output.println(); output.flush(); } + private void printMeter(Meter meter) { + output.printf(locale, " count = %d%n", meter.getCount()); + output.printf(locale, " mean rate = %2.2f events/%s%n", meter.getMeanRate() * rateFactor, rateUnit); + output.printf(locale, " 1-minute rate = %2.2f events/%s%n", meter.getOneMinuteRate() * rateFactor, rateUnit); + output.printf(locale, " 5-minute rate = %2.2f events/%s%n", meter.getFiveMinuteRate() * rateFactor, rateUnit); + output.printf(locale, " 15-minute rate = %2.2f events/%s%n", meter.getFifteenMinuteRate() * rateFactor, rateUnit); + } + private void printCounter(Map.Entry entry) { output.printf(locale, " count = %d%n", entry.getValue().getCount()); } @@ -93,20 +133,7 @@ private void printHistogram(Histogram histogram) { output.printf(locale, " max = %d%n", histogram.getMax()); output.printf(locale, " mean = %2.2f%n", histogram.getMean()); output.printf(locale, " stddev = %2.2f%n", histogram.getStdDev()); - printSnapshot(histogram.getSnapshot()); - } - - private void printTimer(Timer timer) { - final Snapshot snapshot = timer.getSnapshot(); - printMetered(timer); - output.printf(locale, " min = %d%n", timer.getMin()); - output.printf(locale, " max = %d%n", timer.getMax()); - output.printf(locale, " mean = %2.2f%n", timer.getMean()); - output.printf(locale, " stddev = %2.2f%n", timer.getStdDev()); - printSnapshot(snapshot); - } - - private void printSnapshot(Snapshot snapshot) { + Snapshot snapshot = histogram.getSnapshot(); output.printf(locale, " median = %2.2f%n", snapshot.getMedian()); output.printf(locale, " 75%% <= %2.2f%n", snapshot.get75thPercentile()); output.printf(locale, " 95%% <= %2.2f%n", snapshot.get95thPercentile()); @@ -115,12 +142,24 @@ private void printSnapshot(Snapshot snapshot) { output.printf(locale, " 99.9%% <= %2.2f%n", snapshot.get999thPercentile()); } - private void printMetered(Metered timer) { + private void printTimer(Timer timer) { + final Snapshot snapshot = timer.getSnapshot(); output.printf(locale, " count = %d%n", timer.getCount()); - output.printf(locale, " mean rate = %2.2f%n", timer.getMeanRate()); - output.printf(locale, " 1-minute rate = %2.2f%n", timer.getOneMinuteRate()); - output.printf(locale, " 5-minute rate = %2.2f%n", timer.getFiveMinuteRate()); - output.printf(locale, " 15-minute rate = %2.2f%n", timer.getFifteenMinuteRate()); + output.printf(locale, " mean rate = %2.2f calls/%s%n", timer.getMeanRate() * rateFactor, rateUnit); + output.printf(locale, " 1-minute rate = %2.2f calls/%s%n", timer.getOneMinuteRate() * rateFactor, rateUnit); + output.printf(locale, " 5-minute rate = %2.2f calls/%s%n", timer.getFiveMinuteRate() * rateFactor, rateUnit); + output.printf(locale, " 15-minute rate = %2.2f calls/%s%n", timer.getFifteenMinuteRate() * rateFactor, rateUnit); + + output.printf(locale, " min = %2.2f %s%n", timer.getMin() * durationFactor, durationUnit); + output.printf(locale, " max = %2.2f %s%n", timer.getMax() * durationFactor, durationUnit); + output.printf(locale, " mean = %2.2f %s%n", timer.getMean() * durationFactor, durationUnit); + output.printf(locale, " stddev = %2.2f %s%n", timer.getStdDev() * durationFactor, durationUnit); + output.printf(locale, " median = %2.2f %s%n", snapshot.getMedian() * durationFactor, durationUnit); + output.printf(locale, " 75%% <= %2.2f %s%n", snapshot.get75thPercentile() * durationFactor, durationUnit); + output.printf(locale, " 95%% <= %2.2f %s%n", snapshot.get95thPercentile() * durationFactor, durationUnit); + output.printf(locale, " 98%% <= %2.2f %s%n", snapshot.get98thPercentile() * durationFactor, durationUnit); + output.printf(locale, " 99%% <= %2.2f %s%n", snapshot.get99thPercentile() * durationFactor, durationUnit); + output.printf(locale, " 99.9%% <= %2.2f %s%n", snapshot.get999thPercentile() * durationFactor, durationUnit); } private void printWithBanner(String s, char c) { @@ -131,4 +170,9 @@ private void printWithBanner(String s, char c) { } output.println(); } + + private String calculateRateUnit(TimeUnit unit) { + final String s = unit.toString().toLowerCase(Locale.US); + return s.substring(0, s.length() - 1); + } } diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java new file mode 100644 index 0000000000..2a0344923c --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java @@ -0,0 +1,240 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.*; +import org.junit.Before; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.util.Locale; +import java.util.SortedMap; +import java.util.TimeZone; +import java.util.TreeMap; +import java.util.concurrent.TimeUnit; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ConsoleReporterTest { + private final MetricRegistry registry = mock(MetricRegistry.class); + private final Clock clock = mock(Clock.class); + private final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + private final PrintStream output = new PrintStream(bytes); + private final ConsoleReporter reporter = new ConsoleReporter(registry, + output, + Locale.US, + clock, + TimeZone.getTimeZone("PST"), + TimeUnit.SECONDS, + TimeUnit.MILLISECONDS); + + @Before + public void setUp() throws Exception { + when(clock.getTime()).thenReturn(1363568676000L); + } + + @Test + public void reportsGaugeValues() throws Exception { + final Gauge gauge = mock(Gauge.class); + when(gauge.getValue()).thenReturn(1); + + reporter.report(map("gauge", gauge), + this.map(), + this.map(), + this.map(), + this.map()); + + assertThat(consoleOutput()) + .isEqualTo(lines( + "3/17/13 6:04:36 PM =============================================================", + "", + "-- Gauges ----------------------------------------------------------------------", + "gauge", + " value = 1", + "", + "" + )); + } + + @Test + public void reportsCounterValues() throws Exception { + final Counter counter = mock(Counter.class); + when(counter.getCount()).thenReturn(100L); + + reporter.report(this.map(), + map("test.counter", counter), + this.map(), + this.map(), + this.map()); + + assertThat(consoleOutput()) + .isEqualTo(lines( + "3/17/13 6:04:36 PM =============================================================", + "", + "-- Counters --------------------------------------------------------------------", + "test.counter", + " count = 100", + "", + "" + )); + } + + @Test + public void reportsHistogramValues() throws Exception { + final Histogram histogram = mock(Histogram.class); + when(histogram.getCount()).thenReturn(1L); + when(histogram.getMax()).thenReturn(2L); + when(histogram.getMean()).thenReturn(3.0); + when(histogram.getMin()).thenReturn(4L); + when(histogram.getStdDev()).thenReturn(5.0); + + final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMedian()).thenReturn(6.0); + when(snapshot.get75thPercentile()).thenReturn(7.0); + when(snapshot.get95thPercentile()).thenReturn(8.0); + when(snapshot.get98thPercentile()).thenReturn(9.0); + when(snapshot.get99thPercentile()).thenReturn(10.0); + when(snapshot.get999thPercentile()).thenReturn(11.0); + + when(histogram.getSnapshot()).thenReturn(snapshot); + + reporter.report(this.map(), + this.map(), + map("test.histogram", histogram), + this.map(), + this.map()); + + assertThat(consoleOutput()) + .isEqualTo(lines( + "3/17/13 6:04:36 PM =============================================================", + "", + "-- Histograms ------------------------------------------------------------------", + "test.histogram", + " count = 1", + " min = 4", + " max = 2", + " mean = 3.00", + " stddev = 5.00", + " median = 6.00", + " 75% <= 7.00", + " 95% <= 8.00", + " 98% <= 9.00", + " 99% <= 10.00", + " 99.9% <= 11.00", + "", + "" + )); + } + + @Test + public void reportsMeterValues() throws Exception { + final Meter meter = mock(Meter.class); + when(meter.getCount()).thenReturn(1L); + when(meter.getMeanRate()).thenReturn(2.0); + when(meter.getOneMinuteRate()).thenReturn(3.0); + when(meter.getFiveMinuteRate()).thenReturn(4.0); + when(meter.getFifteenMinuteRate()).thenReturn(5.0); + + reporter.report(this.map(), + this.map(), + this.map(), + map("test.meter", meter), + this.map()); + + assertThat(consoleOutput()) + .isEqualTo(lines( + "3/17/13 6:04:36 PM =============================================================", + "", + "-- Meters ----------------------------------------------------------------------", + "test.meter", + " count = 1", + " mean rate = 2.00 events/second", + " 1-minute rate = 3.00 events/second", + " 5-minute rate = 4.00 events/second", + " 15-minute rate = 5.00 events/second", + "", + "" + )); + } + + @Test + public void reportsTimerValues() throws Exception { + final Timer timer = mock(Timer.class); + when(timer.getCount()).thenReturn(1L); + when(timer.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); + when(timer.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); + when(timer.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); + when(timer.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); + + when(timer.getMeanRate()).thenReturn(2.0); + when(timer.getOneMinuteRate()).thenReturn(3.0); + when(timer.getFiveMinuteRate()).thenReturn(4.0); + when(timer.getFifteenMinuteRate()).thenReturn(5.0); + + final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); + when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); + when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); + when(snapshot.get98thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(800)); + when(snapshot.get99thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(900)); + when(snapshot.get999thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS + .toNanos(1000)); + + when(timer.getSnapshot()).thenReturn(snapshot); + + reporter.report(this.map(), + this.map(), + this.map(), + this.map(), + map("test.another.timer", timer)); + + assertThat(consoleOutput()) + .isEqualTo(lines( + "3/17/13 6:04:36 PM =============================================================", + "", + "-- Timers ----------------------------------------------------------------------", + "test.another.timer", + " count = 1", + " mean rate = 2.00 calls/second", + " 1-minute rate = 3.00 calls/second", + " 5-minute rate = 4.00 calls/second", + " 15-minute rate = 5.00 calls/second", + " min = 300.00 milliseconds", + " max = 100.00 milliseconds", + " mean = 200.00 milliseconds", + " stddev = 400.00 milliseconds", + " median = 500.00 milliseconds", + " 75% <= 600.00 milliseconds", + " 95% <= 700.00 milliseconds", + " 98% <= 800.00 milliseconds", + " 99% <= 900.00 milliseconds", + " 99.9% <= 1000.00 milliseconds", + "", + "" + )); + } + + private String lines(String... lines) { + final StringBuilder builder = new StringBuilder(); + for (String line : lines) { + builder.append(line).append(String.format("%n")); + } + return builder.toString(); + } + + private String consoleOutput() throws UnsupportedEncodingException { + return bytes.toString("UTF-8"); + } + + private SortedMap map() { + return new TreeMap(); + } + + private SortedMap map(String name, T metric) { + final TreeMap map = new TreeMap(); + map.put(name, metric); + return map; + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java index 561f1950b4..573dc933b4 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java @@ -202,5 +202,4 @@ private SortedMap map(String name, T metric) { map.put(name, metric); return map; } - } From 3b6778d84126c5920d99c7f9631f16d932194765 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 17 Mar 2013 19:19:32 -0700 Subject: [PATCH 0071/2558] Remove unused cast. --- .../src/main/java/com/yammer/metrics/UniformSample.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java b/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java index 9f27ebc9d6..b01723331e 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java @@ -74,7 +74,7 @@ private static long nextLong(long n) { @Override public Snapshot getSnapshot() { - final int s = (int) size(); + final int s = size(); final List copy = new ArrayList(s); for (int i = 0; i < s; i++) { copy.add(values.get(i)); From c7731a5a6ed376341810aea2fd16f746f30307f5 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 17 Mar 2013 19:19:40 -0700 Subject: [PATCH 0072/2558] Make field final. --- .../jersey/InstrumentedResourceMethodDispatchAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java b/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java index e9cd1f2f36..d8d007dfcc 100644 --- a/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java +++ b/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java @@ -14,7 +14,7 @@ public class InstrumentedResourceMethodDispatchAdapter implements ResourceMethodDispatchAdapter { // TODO: 3/10/13 -- figure out how to coordinate on registry names - private MetricRegistry registry; + private final MetricRegistry registry; /** * Construct a resource method dispatch adapter using the given From 72859e149d01377db522eb0a9d4bde8c7494ff0d Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 18 Mar 2013 07:04:14 -0700 Subject: [PATCH 0073/2558] Added tests and docs for JmxReporter. Closes #338. --- .../java/com/yammer/metrics/JmxReporter.java | 52 ++--- .../yammer/metrics/tests/JmxReporterTest.java | 207 ++++++++++++++++++ 2 files changed, 234 insertions(+), 25 deletions(-) create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java diff --git a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java index a734117002..727bab4ae5 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java @@ -5,9 +5,9 @@ import javax.management.*; -// TODO: 3/10/13 -- write tests -// TODO: 3/10/13 -- write docs - +/** + * A reporter which listens for new metrics and exposes them as namespaced MBeans. + */ public class JmxReporter { private static final Logger LOGGER = LoggerFactory.getLogger(JmxReporter.class); @@ -22,7 +22,7 @@ public interface MetricMBean { private abstract static class AbstractBean implements MetricMBean { private final ObjectName objectName; - protected AbstractBean(ObjectName objectName) { + AbstractBean(ObjectName objectName) { this.objectName = objectName; } @@ -79,9 +79,9 @@ public long getCount() { public interface JmxHistogramMBean extends MetricMBean { long getCount(); - double getMin(); + long getMin(); - double getMax(); + long getMax(); double getMean(); @@ -128,12 +128,12 @@ public long getCount() { } @Override - public double getMin() { + public long getMin() { return metric.getMin(); } @Override - public double getMax() { + public long getMax() { return metric.getMax(); } @@ -247,12 +247,12 @@ public double get50thPercentile() { } @Override - public double getMin() { + public long getMin() { return metric.getMin(); } @Override - public double getMax() { + public long getMax() { return metric.getMax(); } @@ -313,9 +313,7 @@ public void onGaugeAdded(String name, Gauge gauge) { mBeanServer.registerMBean(new JmxGauge(gauge, objectName), objectName); } catch (InstanceAlreadyExistsException e) { LOGGER.debug("Unable to register gauge", e); - } catch (MBeanRegistrationException e) { - LOGGER.warn("Unable to register gauge", e); - } catch (NotCompliantMBeanException e) { + } catch (JMException e) { LOGGER.warn("Unable to register gauge", e); } } @@ -338,9 +336,7 @@ public void onCounterAdded(String name, Counter counter) { mBeanServer.registerMBean(new JmxCounter(counter, objectName), objectName); } catch (InstanceAlreadyExistsException e) { LOGGER.debug("Unable to register gauge", e); - } catch (MBeanRegistrationException e) { - LOGGER.warn("Unable to register gauge", e); - } catch (NotCompliantMBeanException e) { + } catch (JMException e) { LOGGER.warn("Unable to register gauge", e); } } @@ -363,9 +359,7 @@ public void onHistogramAdded(String name, Histogram histogram) { mBeanServer.registerMBean(new JmxHistogram(histogram, objectName), objectName); } catch (InstanceAlreadyExistsException e) { LOGGER.debug("Unable to register histogram", e); - } catch (MBeanRegistrationException e) { - LOGGER.warn("Unable to register histogram", e); - } catch (NotCompliantMBeanException e) { + } catch (JMException e) { LOGGER.warn("Unable to register histogram", e); } } @@ -388,9 +382,7 @@ public void onMeterAdded(String name, Meter meter) { mBeanServer.registerMBean(new JmxMeter(meter, objectName), objectName); } catch (InstanceAlreadyExistsException e) { LOGGER.debug("Unable to register meter", e); - } catch (MBeanRegistrationException e) { - LOGGER.warn("Unable to register meter", e); - } catch (NotCompliantMBeanException e) { + } catch (JMException e) { LOGGER.warn("Unable to register meter", e); } } @@ -413,9 +405,7 @@ public void onTimerAdded(String name, Timer timer) { mBeanServer.registerMBean(new JmxTimer(timer, objectName), objectName); } catch (InstanceAlreadyExistsException e) { LOGGER.debug("Unable to register timer", e); - } catch (MBeanRegistrationException e) { - LOGGER.warn("Unable to register timer", e); - } catch (NotCompliantMBeanException e) { + } catch (JMException e) { LOGGER.warn("Unable to register timer", e); } } @@ -448,15 +438,27 @@ private ObjectName createName(String type, String name) { private final MetricRegistry registry; private final MetricRegistryListener listener; + /** + * Creates a new {@link JmxReporter}. + * + * @param mBeanServer the platform's {@link MBeanServer} + * @param registry the registry containing the metrics to report + */ public JmxReporter(MBeanServer mBeanServer, MetricRegistry registry) { this.registry = registry; this.listener = new JmxListener(mBeanServer, registry.getName()); } + /** + * Starts the reporter. + */ public void start() { registry.addListener(listener); } + /** + * Stops the reporter. + */ public void stop() { registry.removeListener(listener); } diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java new file mode 100644 index 0000000000..afb22ae5d5 --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java @@ -0,0 +1,207 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.*; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import javax.management.*; +import java.lang.management.ManagementFactory; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.fest.assertions.api.Assertions.entry; +import static org.mockito.Mockito.*; + +public class JmxReporterTest { + private final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + private final String name = UUID.randomUUID().toString().replaceAll("[{\\-}]", ""); + private final MetricRegistry registry = spy(new MetricRegistry(name)); + + private final JmxReporter reporter = new JmxReporter(mBeanServer, registry); + + private final Gauge gauge = mock(Gauge.class); + private final Counter counter = mock(Counter.class); + private final Histogram histogram = mock(Histogram.class); + private final Meter meter = mock(Meter.class); + private final Timer timer = mock(Timer.class); + + @Before + public void setUp() throws Exception { + when(gauge.getValue()).thenReturn(1); + + when(counter.getCount()).thenReturn(100L); + + when(histogram.getCount()).thenReturn(1L); + when(histogram.getMax()).thenReturn(2L); + when(histogram.getMean()).thenReturn(3.0); + when(histogram.getMin()).thenReturn(4L); + when(histogram.getStdDev()).thenReturn(5.0); + + final Snapshot hSnapshot = mock(Snapshot.class); + when(hSnapshot.getMedian()).thenReturn(6.0); + when(hSnapshot.get75thPercentile()).thenReturn(7.0); + when(hSnapshot.get95thPercentile()).thenReturn(8.0); + when(hSnapshot.get98thPercentile()).thenReturn(9.0); + when(hSnapshot.get99thPercentile()).thenReturn(10.0); + when(hSnapshot.get999thPercentile()).thenReturn(11.0); + + when(histogram.getSnapshot()).thenReturn(hSnapshot); + + when(meter.getCount()).thenReturn(1L); + when(meter.getMeanRate()).thenReturn(2.0); + when(meter.getOneMinuteRate()).thenReturn(3.0); + when(meter.getFiveMinuteRate()).thenReturn(4.0); + when(meter.getFifteenMinuteRate()).thenReturn(5.0); + + when(timer.getCount()).thenReturn(1L); + when(timer.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); + when(timer.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); + when(timer.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); + when(timer.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); + + when(timer.getMeanRate()).thenReturn(2.0); + when(timer.getOneMinuteRate()).thenReturn(3.0); + when(timer.getFiveMinuteRate()).thenReturn(4.0); + when(timer.getFifteenMinuteRate()).thenReturn(5.0); + + final Snapshot tSnapshot = mock(Snapshot.class); + when(tSnapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); + when(tSnapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); + when(tSnapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); + when(tSnapshot.get98thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(800)); + when(tSnapshot.get99thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(900)); + when(tSnapshot.get999thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(1000)); + + when(timer.getSnapshot()).thenReturn(tSnapshot); + + registry.register("gauge", gauge); + registry.register("test.counter", counter); + registry.register("test.histogram", histogram); + registry.register("test.meter", meter); + registry.register("test.another.timer", timer); + + reporter.start(); + } + + @After + public void tearDown() throws Exception { + reporter.stop(); + } + + @Test + public void registersMBeansForGauges() throws Exception { + final AttributeList attributes = getAttributes("gauge", "Value"); + + assertThat(values(attributes)) + .contains(entry("Value", 1)); + } + + @Test + public void registersMBeansForCounters() throws Exception { + final AttributeList attributes = getAttributes("test.counter", "Count"); + + assertThat(values(attributes)) + .contains(entry("Count", 100L)); + } + + @Test + public void registersMBeansForHistograms() throws Exception { + final AttributeList attributes = getAttributes("test.histogram", + "Count", + "Max", + "Mean", + "Min", + "StdDev", + "50thPercentile", + "75thPercentile", + "95thPercentile", + "98thPercentile", + "99thPercentile", + "999thPercentile"); + + assertThat(values(attributes)) + .contains(entry("Count", 1L)) + .contains(entry("Max", 2L)) + .contains(entry("Mean", 3.0)) + .contains(entry("Min", 4L)) + .contains(entry("StdDev", 5.0)) + .contains(entry("50thPercentile", 6.0)) + .contains(entry("75thPercentile", 7.0)) + .contains(entry("95thPercentile", 8.0)) + .contains(entry("98thPercentile", 9.0)) + .contains(entry("99thPercentile", 10.0)) + .contains(entry("999thPercentile", 11.0)); + } + + @Test + public void registersMBeansForMeters() throws Exception { + final AttributeList attributes = getAttributes("test.meter", + "Count", + "MeanRate", + "OneMinuteRate", + "FiveMinuteRate", + "FifteenMinuteRate"); + + assertThat(values(attributes)) + .contains(entry("Count", 1L)) + .contains(entry("MeanRate", 2.0)) + .contains(entry("OneMinuteRate", 3.0)) + .contains(entry("FiveMinuteRate", 4.0)) + .contains(entry("FifteenMinuteRate", 5.0)); + } + + @Test + public void registersMBeansForTimers() throws Exception { + final AttributeList attributes = getAttributes("test.another.timer", + "Count", + "MeanRate", + "OneMinuteRate", + "FiveMinuteRate", + "FifteenMinuteRate", + "Max", + "Mean", + "Min", + "StdDev", + "50thPercentile", + "75thPercentile", + "95thPercentile", + "98thPercentile", + "99thPercentile", + "999thPercentile"); + + assertThat(values(attributes)) + .contains(entry("Count", 1L)) + .contains(entry("MeanRate", 2.0)) + .contains(entry("OneMinuteRate", 3.0)) + .contains(entry("FiveMinuteRate", 4.0)) + .contains(entry("FifteenMinuteRate", 5.0)) + .contains(entry("Max", 100000000L)) + .contains(entry("Mean", 2.0e8)) + .contains(entry("Min", 300000000L)) + .contains(entry("StdDev", 4.0e8)) + .contains(entry("50thPercentile", 5.0e8)) + .contains(entry("75thPercentile", 6.0e8)) + .contains(entry("95thPercentile", 7.0e8)) + .contains(entry("98thPercentile", 8.0e8)) + .contains(entry("99thPercentile", 9.0e8)) + .contains(entry("999thPercentile", 10.0e8)); + } + + private AttributeList getAttributes(String name, String... attributeNames) throws JMException { + final ObjectName n = new ObjectName(this.name, "name", name); + return mBeanServer.getAttributes(n, attributeNames); + } + + private SortedMap values(AttributeList attributes) { + final TreeMap values = new TreeMap(); + for (Object o : attributes) { + final Attribute attribute = (Attribute) o; + values.put(attribute.getName(), attribute.getValue()); + } + return values; + } +} From f8f75406a1d8074090c59d2d6ee21134be28d206 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 18 Mar 2013 07:17:40 -0700 Subject: [PATCH 0074/2558] Collapse some nested if-statements. --- .../yammer/metrics/ExponentiallyDecayingSample.java | 10 ++++------ .../com/yammer/metrics/jvm/ThreadStatesGaugeSet.java | 6 ++---- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java b/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java index a6ba909319..223520e9e0 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java @@ -98,12 +98,10 @@ public void update(long value, long timestamp) { values.put(priority, value); } else { Double first = values.firstKey(); - if (first < priority) { - if (values.putIfAbsent(priority, value) == null) { - // ensure we always remove an item - while (values.remove(first) == null) { - first = values.firstKey(); - } + if (first < priority && values.putIfAbsent(priority, value) == null) { + // ensure we always remove an item + while (values.remove(first) == null) { + first = values.firstKey(); } } } diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeSet.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeSet.java index 35b0808fe9..5e55029bda 100644 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeSet.java +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeSet.java @@ -82,10 +82,8 @@ private int getThreadCount(Thread.State state) { final ThreadInfo[] allThreads = threads.getThreadInfo(threads.getAllThreadIds()); int count = 0; for (ThreadInfo info : allThreads) { - if (info != null) { - if (info.getThreadState() == state) { - count++; - } + if (info != null && info.getThreadState() == state) { + count++; } } return count; From 63f3e93d3cc31f530d5e4c885914c98395e9be1a Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 18 Mar 2013 07:18:04 -0700 Subject: [PATCH 0075/2558] Elide some redundant modifiers. --- metrics-core/src/main/java/com/yammer/metrics/Gauge.java | 2 +- .../src/main/java/com/yammer/metrics/MetricRegistry.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/Gauge.java b/metrics-core/src/main/java/com/yammer/metrics/Gauge.java index fe279c5e6f..dbd23ed83c 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Gauge.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Gauge.java @@ -21,5 +21,5 @@ public interface Gauge extends Metric { * * @return the metric's current value */ - public abstract T getValue(); + T getValue(); } diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java index 54fbf8246d..2e56fdefac 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -343,7 +343,7 @@ private void notifyListenerOfRemovedMetric(String name, Metric metric, MetricReg * A quick and easy way of capturing the notion of default metrics. */ private interface MetricBuilder { - final MetricBuilder COUNTERS = new MetricBuilder() { + MetricBuilder COUNTERS = new MetricBuilder() { @Override public Counter newMetric(MetricRegistry registry) { return new Counter(); @@ -355,7 +355,7 @@ public boolean isInstance(Metric metric) { } }; - final MetricBuilder HISTOGRAMS = new MetricBuilder() { + MetricBuilder HISTOGRAMS = new MetricBuilder() { @Override public Histogram newMetric(MetricRegistry registry) { return new Histogram(SampleType.BIASED); @@ -367,7 +367,7 @@ public boolean isInstance(Metric metric) { } }; - final MetricBuilder METERS = new MetricBuilder() { + MetricBuilder METERS = new MetricBuilder() { @Override public Meter newMetric(MetricRegistry registry) { return new Meter(registry.getClock()); @@ -379,7 +379,7 @@ public boolean isInstance(Metric metric) { } }; - final MetricBuilder TIMERS = new MetricBuilder() { + MetricBuilder TIMERS = new MetricBuilder() { @Override public Timer newMetric(MetricRegistry registry) { return new Timer(registry.getClock()); From bfa3ef58e1fbc0dace39283214fce33b6a18dd12 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 18 Mar 2013 07:18:23 -0700 Subject: [PATCH 0076/2558] Fix logging statements in JmxReporter. --- .../src/main/java/com/yammer/metrics/JmxReporter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java index 727bab4ae5..a2bb54a158 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java @@ -335,9 +335,9 @@ public void onCounterAdded(String name, Counter counter) { final ObjectName objectName = createName("counters", name); mBeanServer.registerMBean(new JmxCounter(counter, objectName), objectName); } catch (InstanceAlreadyExistsException e) { - LOGGER.debug("Unable to register gauge", e); + LOGGER.debug("Unable to register counter", e); } catch (JMException e) { - LOGGER.warn("Unable to register gauge", e); + LOGGER.warn("Unable to register counter", e); } } From 55bde5cfff9e0bc17b8651a30075fdb3616eab48 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 18 Mar 2013 07:18:36 -0700 Subject: [PATCH 0077/2558] Make sure InstrumentedResourceMethodDispatchProvider doesn't suppress errors. --- .../jersey/InstrumentedResourceMethodDispatchProvider.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java b/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java index 85182d63be..6648fb5814 100644 --- a/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java +++ b/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java @@ -67,6 +67,8 @@ private ExceptionMeteredRequestDispatcher(RequestDispatcher underlying, public void dispatch(Object resource, HttpContext httpContext) { try { underlying.dispatch(resource, httpContext); + } catch (Error e) { + throw e; } catch (Throwable e) { if (exceptionClass.isAssignableFrom(e.getClass()) || (e.getCause() != null && exceptionClass.isAssignableFrom(e.getCause().getClass()))) { From 8458c0a6de6c0083f9d09bde40cecd60f8ccb5f6 Mon Sep 17 00:00:00 2001 From: Mathijs Vogelzang Date: Mon, 18 Mar 2013 18:28:45 +0100 Subject: [PATCH 0078/2558] Allow custom prefix name for Jetty InstrumentedHandler --- .../metrics/jetty8/InstrumentedHandler.java | 74 +++++++++++-------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java index 489eea53b3..4ffdd9318f 100644 --- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java +++ b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java @@ -43,33 +43,45 @@ public class InstrumentedHandler extends HandlerWrapper { private final ContinuationListener listener; /** - * Create a new instrumented handler using a given metrics registry. + * Create a new instrumented handler using a given metrics registry. The name of the metric + * will be derived from the class of the Handler. * * @param registry the registry for the metrics * @param underlying the handler about which metrics will be collected */ public InstrumentedHandler(MetricRegistry registry, Handler underlying) { + this(registry, underlying, underlying.getClass()); + } + + /** + * Create a new instrumented handler using a given metrics registry and a custom prefix. + * + * @param registry the registry for the metrics + * @param underlying the handler about which metrics will be collected + * @param prefix the prefix to use for the metrics names + */ + public InstrumentedHandler(MetricRegistry registry, Handler underlying, String prefix) { super(); - this.dispatches = registry.timer(name(underlying.getClass(), "dispatches")); - this.requests = registry.meter(name(underlying.getClass(), "requests")); - this.resumes = registry.meter(name(underlying.getClass(), "resumes")); - this.suspends = registry.meter(name(underlying.getClass(), "suspends")); - this.expires = registry.meter(name(underlying.getClass(), "expires")); - - this.activeRequests = registry.counter(name(underlying.getClass(), "active-requests")); - this.activeSuspendedRequests = registry.counter(name(underlying.getClass(), + this.dispatches = registry.timer(name(prefix, "dispatches")); + this.requests = registry.meter(name(prefix, "requests")); + this.resumes = registry.meter(name(prefix, "resumes")); + this.suspends = registry.meter(name(prefix, "suspends")); + this.expires = registry.meter(name(prefix, "expires")); + + this.activeRequests = registry.counter(name(prefix, "active-requests")); + this.activeSuspendedRequests = registry.counter(name(prefix, "active-suspended-requests")); - this.activeDispatches = registry.counter(name(underlying.getClass(), "active-dispatches")); + this.activeDispatches = registry.counter(name(prefix, "active-dispatches")); this.responses = new Meter[]{ - registry.meter(name(underlying.getClass(), "1xx-responses")), // 1xx - registry.meter(name(underlying.getClass(), "2xx-responses")), // 2xx - registry.meter(name(underlying.getClass(), "3xx-responses")), // 3xx - registry.meter(name(underlying.getClass(), "4xx-responses")), // 4xx - registry.meter(name(underlying.getClass(), "5xx-responses")) // 5xx + registry.meter(name(prefix, "1xx-responses")), // 1xx + registry.meter(name(prefix, "2xx-responses")), // 2xx + registry.meter(name(prefix, "3xx-responses")), // 3xx + registry.meter(name(prefix, "4xx-responses")), // 4xx + registry.meter(name(prefix, "5xx-responses")) // 5xx }; - registry.register(name(underlying.getClass(), "percent-4xx-1m"), new RatioGauge() { + registry.register(name(prefix, "percent-4xx-1m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[3].getOneMinuteRate(), @@ -77,7 +89,7 @@ protected Ratio getRatio() { } }); - registry.register(name(underlying.getClass(), "percent-4xx-5m"), new RatioGauge() { + registry.register(name(prefix, "percent-4xx-5m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[3].getFiveMinuteRate(), @@ -85,7 +97,7 @@ protected Ratio getRatio() { } }); - registry.register(name(underlying.getClass(), "percent-4xx-15m"), new RatioGauge() { + registry.register(name(prefix, "percent-4xx-15m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[3].getFifteenMinuteRate(), @@ -93,7 +105,7 @@ protected Ratio getRatio() { } }); - registry.register(name(underlying.getClass(), "percent-5xx-1m"), new RatioGauge() { + registry.register(name(prefix, "percent-5xx-1m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[4].getOneMinuteRate(), @@ -101,7 +113,7 @@ protected Ratio getRatio() { } }); - registry.register(name(underlying.getClass(), "percent-5xx-5m"), new RatioGauge() { + registry.register(name(prefix, "percent-5xx-5m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[4].getFiveMinuteRate(), @@ -109,7 +121,7 @@ protected Ratio getRatio() { } }); - registry.register(name(underlying.getClass(), "percent-5xx-15m"), new RatioGauge() { + registry.register(name(prefix, "percent-5xx-15m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[4].getFifteenMinuteRate(), @@ -133,16 +145,16 @@ public void onTimeout(Continuation continuation) { } }; - this.getRequests = registry.timer(name(underlying.getClass(), "get-requests")); - this.postRequests = registry.timer(name(underlying.getClass(), "post-requests")); - this.headRequests = registry.timer(name(underlying.getClass(), "head-requests")); - this.putRequests = registry.timer(name(underlying.getClass(), "put-requests")); - this.deleteRequests = registry.timer(name(underlying.getClass(), "delete-requests")); - this.optionsRequests = registry.timer(name(underlying.getClass(), "options-requests")); - this.traceRequests = registry.timer(name(underlying.getClass(), "trace-requests")); - this.connectRequests = registry.timer(name(underlying.getClass(), "connect-requests")); - this.patchRequests = registry.timer(name(underlying.getClass(), "patch-requests")); - this.otherRequests = registry.timer(name(underlying.getClass(), "other-requests")); + this.getRequests = registry.timer(name(prefix, "get-requests")); + this.postRequests = registry.timer(name(prefix, "post-requests")); + this.headRequests = registry.timer(name(prefix, "head-requests")); + this.putRequests = registry.timer(name(prefix, "put-requests")); + this.deleteRequests = registry.timer(name(prefix, "delete-requests")); + this.optionsRequests = registry.timer(name(prefix, "options-requests")); + this.traceRequests = registry.timer(name(prefix, "trace-requests")); + this.connectRequests = registry.timer(name(prefix, "connect-requests")); + this.patchRequests = registry.timer(name(prefix, "patch-requests")); + this.otherRequests = registry.timer(name(prefix, "other-requests")); setHandler(underlying); } From 7ef7ac7ce0d815ffd578f4e60bff5b41f5a324bd Mon Sep 17 00:00:00 2001 From: Mathijs Vogelzang Date: Mon, 18 Mar 2013 18:43:04 +0100 Subject: [PATCH 0079/2558] Fix getClass() -> getClass().getName() --- .../java/com/yammer/metrics/jetty8/InstrumentedHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java index 4ffdd9318f..a1c4b087a1 100644 --- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java +++ b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java @@ -50,7 +50,7 @@ public class InstrumentedHandler extends HandlerWrapper { * @param underlying the handler about which metrics will be collected */ public InstrumentedHandler(MetricRegistry registry, Handler underlying) { - this(registry, underlying, underlying.getClass()); + this(registry, underlying, underlying.getClass().getName()); } /** From 0f24a95b24d2abd4ea03ef77dc0e7082a15dbf80 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 18 Mar 2013 14:42:19 -0700 Subject: [PATCH 0080/2558] Add metric filters. --- .../metrics/AbstractPollingReporter.java | 17 +++-- .../com/yammer/metrics/ConsoleReporter.java | 6 +- .../java/com/yammer/metrics/CsvReporter.java | 6 +- .../java/com/yammer/metrics/MetricFilter.java | 25 +++++++ .../com/yammer/metrics/MetricRegistry.java | 67 +++++++++++++++++-- .../com/yammer/metrics/Slf4jReporter.java | 16 ++--- .../tests/AbstractPollingReporterTest.java | 4 +- .../metrics/tests/ConsoleReporterTest.java | 3 +- .../yammer/metrics/tests/CsvReporterTest.java | 3 +- .../metrics/tests/MetricFilterTest.java | 17 +++++ .../metrics/tests/Slf4jReporterTest.java | 9 +-- .../metrics/ganglia/GangliaReporter.java | 5 +- .../ganglia/tests/GangliaReporterTest.java | 3 +- .../metrics/graphite/GraphiteReporter.java | 5 +- .../graphite/tests/GraphiteReporterTest.java | 3 +- 15 files changed, 146 insertions(+), 43 deletions(-) create mode 100644 metrics-core/src/main/java/com/yammer/metrics/MetricFilter.java create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/MetricFilterTest.java diff --git a/metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java b/metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java index 9858d0f15c..b98d1215fb 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java @@ -35,15 +35,18 @@ public Thread newThread(Runnable r) { private final MetricRegistry registry; private final ScheduledExecutorService executor; + private final MetricFilter filter; /** * Creates a new {@link AbstractPollingReporter} instance. * - * @param registry the {@link MetricRegistry} containing the metrics this reporter will report + * @param registry the {@link com.yammer.metrics.MetricRegistry} containing the metrics this reporter will report * @param name the reporter's name + * @param filter the filter for which metrics to report */ - protected AbstractPollingReporter(MetricRegistry registry, String name) { + protected AbstractPollingReporter(MetricRegistry registry, String name, MetricFilter filter) { this.registry = registry; + this.filter = filter; this.executor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory(name)); } @@ -57,11 +60,11 @@ public void start(long period, TimeUnit unit) { executor.scheduleAtFixedRate(new Runnable() { @Override public void run() { - report(registry.getGauges(), - registry.getCounters(), - registry.getHistograms(), - registry.getMeters(), - registry.getTimers()); + report(registry.getGauges(filter), + registry.getCounters(filter), + registry.getHistograms(filter), + registry.getMeters(filter), + registry.getTimers(filter)); } }, period, period, unit); } diff --git a/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java b/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java index 73a22d8dc3..53219357ae 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java @@ -30,6 +30,7 @@ public class ConsoleReporter extends AbstractPollingReporter { * @param timeZone the time zone in which dates should be presented * @param rateUnit the unit in which rates should be presented * @param durationUnit the unit in which durations should be presented + * @param filter the metric filter to match */ public ConsoleReporter(MetricRegistry registry, PrintStream output, @@ -37,8 +38,9 @@ public ConsoleReporter(MetricRegistry registry, Clock clock, TimeZone timeZone, TimeUnit rateUnit, - TimeUnit durationUnit) { - super(registry, "console-reporter"); + TimeUnit durationUnit, + MetricFilter filter) { + super(registry, "console-reporter", filter); this.output = output; this.locale = locale; this.clock = clock; diff --git a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java index eb2a057b1b..4d1dcb503d 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java @@ -31,14 +31,16 @@ public class CsvReporter extends AbstractPollingReporter { * @param registry the {@link MetricRegistry} containing the metrics this reporter will report * @param directory the directory in which CSV files will be created * @param locale the locale to use for formatting + * @param filter the metric filter to match */ public CsvReporter(MetricRegistry registry, File directory, Locale locale, TimeUnit rateUnit, TimeUnit durationUnit, - Clock clock) { - super(registry, "csv-reporter"); + Clock clock, + MetricFilter filter) { + super(registry, "csv-reporter", filter); this.directory = directory; this.locale = locale; this.clock = clock; diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricFilter.java b/metrics-core/src/main/java/com/yammer/metrics/MetricFilter.java new file mode 100644 index 0000000000..4e5a42741c --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricFilter.java @@ -0,0 +1,25 @@ +package com.yammer.metrics; + +/** + * A filter used to determine whether or not a metric should be reported, among other things. + */ +public interface MetricFilter { + /** + * Matches all metrics, regardless of type or name. + */ + MetricFilter ALL = new MetricFilter() { + @Override + public boolean matches(String name, Metric metric) { + return true; + } + }; + + /** + * Returns {@code true} if the metric matches the filter; {@code false} otherwise. + * + * @param name the metric's name + * @param metric the metric + * @return {@code true} if the metric matches the filter + */ + boolean matches(String name, Metric metric); +} diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java index 2e56fdefac..17728e932c 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -223,7 +223,17 @@ public SortedSet getNames() { * @return all the gauges in the registry */ public SortedMap getGauges() { - return getMetrics(Gauge.class); + return getGauges(MetricFilter.ALL); + } + + /** + * Returns a map of all the gauges in the registry and their names which match the given filter. + * + * @param filter the metric filter to match + * @return all the gauges in the registry + */ + public SortedMap getGauges(MetricFilter filter) { + return getMetrics(Gauge.class, filter); } /** @@ -232,7 +242,18 @@ public SortedMap getGauges() { * @return all the counters in the registry */ public SortedMap getCounters() { - return getMetrics(Counter.class); + return getCounters(MetricFilter.ALL); + } + + /** + * Returns a map of all the counters in the registry and their names which match the given + * filter. + * + * @param filter the metric filter to match + * @return all the counters in the registry + */ + public SortedMap getCounters(MetricFilter filter) { + return getMetrics(Counter.class, filter); } /** @@ -241,7 +262,18 @@ public SortedMap getCounters() { * @return all the histograms in the registry */ public SortedMap getHistograms() { - return getMetrics(Histogram.class); + return getHistograms(MetricFilter.ALL); + } + + /** + * Returns a map of all the histograms in the registry and their names which match the given + * filter. + * + * @param filter the metric filter to match + * @return all the histograms in the registry + */ + public SortedMap getHistograms(MetricFilter filter) { + return getMetrics(Histogram.class, filter); } /** @@ -250,7 +282,17 @@ public SortedMap getHistograms() { * @return all the meters in the registry */ public SortedMap getMeters() { - return getMetrics(Meter.class); + return getMeters(MetricFilter.ALL); + } + + /** + * Returns a map of all the meters in the registry and their names which match the given filter. + * + * @param filter the metric filter to match + * @return all the meters in the registry + */ + public SortedMap getMeters(MetricFilter filter) { + return getMetrics(Meter.class, filter); } /** @@ -259,7 +301,17 @@ public SortedMap getMeters() { * @return all the timers in the registry */ public SortedMap getTimers() { - return getMetrics(Timer.class); + return getTimers(MetricFilter.ALL); + } + + /** + * Returns a map of all the timers in the registry and their names which match the given filter. + * + * @param filter the metric filter to match + * @return all the timers in the registry + */ + public SortedMap getTimers(MetricFilter filter) { + return getMetrics(Timer.class, filter); } Clock getClock() { @@ -285,10 +337,11 @@ private T getOrAdd(String name, MetricBuilder builder) { } @SuppressWarnings("unchecked") - private SortedMap getMetrics(Class klass) { + private SortedMap getMetrics(Class klass, MetricFilter filter) { final TreeMap timers = new TreeMap(); for (Map.Entry entry : metrics.entrySet()) { - if (klass.isInstance(entry.getValue())) { + if (klass.isInstance(entry.getValue()) && filter.matches(entry.getKey(), + entry.getValue())) { timers.put(entry.getKey(), (T) entry.getValue()); } } diff --git a/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java b/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java index 9cf1f94586..1613bd9137 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java @@ -22,16 +22,6 @@ public class Slf4jReporter extends AbstractPollingReporter { private final double rateFactor; private final String rateUnit; - /** - * Construct a new SLF4J reporter. - * - * @param registry Metrics registry to report from. - * @param logger SLF4J {@link Logger} instance to send metrics reports to - */ - public Slf4jReporter(MetricRegistry registry, Logger logger) { - this(registry, logger, null, TimeUnit.SECONDS, TimeUnit.SECONDS); - } - /** * Construct a new SLF4J reporter. * @@ -41,13 +31,15 @@ public Slf4jReporter(MetricRegistry registry, Logger logger) { * none. * @param rateUnit the unit to which rates will be converted * @param durationUnit the unit to which durations will be converted + * @param filter the metric filter to match */ public Slf4jReporter(MetricRegistry registry, Logger logger, Marker marker, TimeUnit rateUnit, - TimeUnit durationUnit) { - super(registry, "logger-reporter"); + TimeUnit durationUnit, + MetricFilter filter) { + super(registry, "logger-reporter", filter); this.logger = logger; this.marker = marker; this.rateFactor = rateUnit.toSeconds(1); diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/AbstractPollingReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/AbstractPollingReporterTest.java index 41c9cd5b0b..2b060dbace 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/AbstractPollingReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/AbstractPollingReporterTest.java @@ -20,7 +20,9 @@ public class AbstractPollingReporterTest { private final MetricRegistry registry = new MetricRegistry("test"); private final Reporter underlying = mock(Reporter.class); - private final AbstractPollingReporter reporter = new AbstractPollingReporter(registry, "example") { + private final AbstractPollingReporter reporter = new AbstractPollingReporter(registry, + "example", + MetricFilter.ALL) { @Override public void report(SortedMap gauges, SortedMap counters, diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java index 2a0344923c..cfdbfba16d 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java @@ -28,7 +28,8 @@ public class ConsoleReporterTest { clock, TimeZone.getTimeZone("PST"), TimeUnit.SECONDS, - TimeUnit.MILLISECONDS); + TimeUnit.MILLISECONDS, + MetricFilter.ALL); @Before public void setUp() throws Exception { diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java index 573dc933b4..1b0408dd82 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java @@ -37,7 +37,8 @@ public void setUp() throws Exception { Locale.US, TimeUnit.SECONDS, TimeUnit.MILLISECONDS, - clock); + clock, + MetricFilter.ALL); } @Test diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricFilterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricFilterTest.java new file mode 100644 index 0000000000..69565431fb --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricFilterTest.java @@ -0,0 +1,17 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.Metric; +import com.yammer.metrics.MetricFilter; +import org.junit.Test; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; + +public class MetricFilterTest { + @Test + public void theAllFilterMatchesAllMetrics() throws Exception { + assertThat(MetricFilter.ALL.matches(anyString(), any(Metric.class))) + .isTrue(); + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java index 359c91aba1..96258eb20d 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java @@ -18,10 +18,11 @@ public class Slf4jReporterTest { private final Marker marker = mock(Marker.class); private final MetricRegistry registry = mock(MetricRegistry.class); private final Slf4jReporter reporter = new Slf4jReporter(registry, - logger, - marker, - TimeUnit.SECONDS, - TimeUnit.MILLISECONDS); + logger, + marker, + TimeUnit.SECONDS, + TimeUnit.MILLISECONDS, + MetricFilter.ALL); @Test public void reportsGaugeValues() throws Exception { diff --git a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java index 8761188e84..a169ad6abc 100644 --- a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java +++ b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java @@ -31,8 +31,9 @@ public GangliaReporter(MetricRegistry registry, int tMax, int dMax, TimeUnit rateUnit, - TimeUnit durationUnit) { - super(registry, "ganglia-reporter"); + TimeUnit durationUnit, + MetricFilter filter) { + super(registry, "ganglia-reporter", filter); this.ganglia = ganglia; this.tMax = tMax; this.dMax = dMax; diff --git a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java b/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java index 6b8f502f5a..ca5e56ff88 100644 --- a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java +++ b/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java @@ -21,7 +21,8 @@ public class GangliaReporterTest { 60, 0, TimeUnit.SECONDS, - TimeUnit.MILLISECONDS); + TimeUnit.MILLISECONDS, + MetricFilter.ALL); @Test public void reportsStringGaugeValues() throws Exception { diff --git a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java index 26c7d7f7a3..ee2f221b5a 100644 --- a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java +++ b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java @@ -25,8 +25,9 @@ public GraphiteReporter(MetricRegistry registry, Clock clock, String prefix, TimeUnit rateUnit, - TimeUnit durationUnit) { - super(registry, "graphite-reporter"); + TimeUnit durationUnit, + MetricFilter filter) { + super(registry, "graphite-reporter", filter); this.graphite = graphite; this.clock = clock; this.prefix = prefix; diff --git a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java index 6c6334fdbe..03b94f0ea9 100644 --- a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java +++ b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java @@ -23,7 +23,8 @@ public class GraphiteReporterTest { clock, "prefix", TimeUnit.SECONDS, - TimeUnit.MILLISECONDS); + TimeUnit.MILLISECONDS, + MetricFilter.ALL); @Before public void setUp() throws Exception { From 1ef4048763b342cccb0e311899d82ee9111ccb09 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 18 Mar 2013 14:52:24 -0700 Subject: [PATCH 0081/2558] Add filtering and full cleanup for JmxReporter. --- .../java/com/yammer/metrics/JmxReporter.java | 86 ++++++++++++++----- .../yammer/metrics/tests/JmxReporterTest.java | 15 +++- 2 files changed, 80 insertions(+), 21 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java index a2bb54a158..e9d55c353a 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java @@ -4,6 +4,8 @@ import org.slf4j.LoggerFactory; import javax.management.*; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; /** * A reporter which listens for new metrics and exposes them as namespaced MBeans. @@ -300,17 +302,24 @@ public long[] values() { private static class JmxListener implements MetricRegistryListener { private final String name; private final MBeanServer mBeanServer; + private final MetricFilter filter; + private final Set registered; - public JmxListener(MBeanServer mBeanServer, String name) { + public JmxListener(MBeanServer mBeanServer, String name, MetricFilter filter) { this.mBeanServer = mBeanServer; this.name = name; + this.filter = filter; + this.registered = new CopyOnWriteArraySet(); } @Override public void onGaugeAdded(String name, Gauge gauge) { try { - final ObjectName objectName = createName("gauges", name); - mBeanServer.registerMBean(new JmxGauge(gauge, objectName), objectName); + if (filter.matches(name, gauge)) { + final ObjectName objectName = createName("gauges", name); + mBeanServer.registerMBean(new JmxGauge(gauge, objectName), objectName); + registered.add(objectName); + } } catch (InstanceAlreadyExistsException e) { LOGGER.debug("Unable to register gauge", e); } catch (JMException e) { @@ -321,7 +330,9 @@ public void onGaugeAdded(String name, Gauge gauge) { @Override public void onGaugeRemoved(String name) { try { - mBeanServer.unregisterMBean(createName("gauges", name)); + final ObjectName objectName = createName("gauges", name); + mBeanServer.unregisterMBean(objectName); + registered.remove(objectName); } catch (InstanceNotFoundException e) { LOGGER.debug("Unable to unregister gauge", e); } catch (MBeanRegistrationException e) { @@ -332,8 +343,11 @@ public void onGaugeRemoved(String name) { @Override public void onCounterAdded(String name, Counter counter) { try { - final ObjectName objectName = createName("counters", name); - mBeanServer.registerMBean(new JmxCounter(counter, objectName), objectName); + if (filter.matches(name, counter)) { + final ObjectName objectName = createName("counters", name); + mBeanServer.registerMBean(new JmxCounter(counter, objectName), objectName); + registered.add(objectName); + } } catch (InstanceAlreadyExistsException e) { LOGGER.debug("Unable to register counter", e); } catch (JMException e) { @@ -344,7 +358,9 @@ public void onCounterAdded(String name, Counter counter) { @Override public void onCounterRemoved(String name) { try { - mBeanServer.unregisterMBean(createName("counters", name)); + final ObjectName objectName = createName("counters", name); + mBeanServer.unregisterMBean(objectName); + registered.remove(objectName); } catch (InstanceNotFoundException e) { LOGGER.debug("Unable to unregister counter", e); } catch (MBeanRegistrationException e) { @@ -355,8 +371,11 @@ public void onCounterRemoved(String name) { @Override public void onHistogramAdded(String name, Histogram histogram) { try { - final ObjectName objectName = createName("histograms", name); - mBeanServer.registerMBean(new JmxHistogram(histogram, objectName), objectName); + if (filter.matches(name, histogram)) { + final ObjectName objectName = createName("histograms", name); + mBeanServer.registerMBean(new JmxHistogram(histogram, objectName), objectName); + registered.add(objectName); + } } catch (InstanceAlreadyExistsException e) { LOGGER.debug("Unable to register histogram", e); } catch (JMException e) { @@ -367,7 +386,9 @@ public void onHistogramAdded(String name, Histogram histogram) { @Override public void onHistogramRemoved(String name) { try { - mBeanServer.unregisterMBean(createName("histograms", name)); + final ObjectName objectName = createName("histograms", name); + mBeanServer.unregisterMBean(objectName); + registered.remove(objectName); } catch (InstanceNotFoundException e) { LOGGER.debug("Unable to unregister histogram", e); } catch (MBeanRegistrationException e) { @@ -378,8 +399,11 @@ public void onHistogramRemoved(String name) { @Override public void onMeterAdded(String name, Meter meter) { try { - final ObjectName objectName = createName("meters", name); - mBeanServer.registerMBean(new JmxMeter(meter, objectName), objectName); + if (filter.matches(name, meter)) { + final ObjectName objectName = createName("meters", name); + mBeanServer.registerMBean(new JmxMeter(meter, objectName), objectName); + registered.add(objectName); + } } catch (InstanceAlreadyExistsException e) { LOGGER.debug("Unable to register meter", e); } catch (JMException e) { @@ -390,7 +414,9 @@ public void onMeterAdded(String name, Meter meter) { @Override public void onMeterRemoved(String name) { try { - mBeanServer.unregisterMBean(createName("meters", name)); + final ObjectName objectName = createName("meters", name); + mBeanServer.unregisterMBean(objectName); + registered.remove(objectName); } catch (InstanceNotFoundException e) { LOGGER.debug("Unable to unregister meter", e); } catch (MBeanRegistrationException e) { @@ -401,8 +427,11 @@ public void onMeterRemoved(String name) { @Override public void onTimerAdded(String name, Timer timer) { try { - final ObjectName objectName = createName("timers", name); - mBeanServer.registerMBean(new JmxTimer(timer, objectName), objectName); + if (filter.matches(name, timer)) { + final ObjectName objectName = createName("timers", name); + mBeanServer.registerMBean(new JmxTimer(timer, objectName), objectName); + registered.add(objectName); + } } catch (InstanceAlreadyExistsException e) { LOGGER.debug("Unable to register timer", e); } catch (JMException e) { @@ -413,7 +442,9 @@ public void onTimerAdded(String name, Timer timer) { @Override public void onTimerRemoved(String name) { try { - mBeanServer.unregisterMBean(createName("timers", name)); + final ObjectName objectName = createName("timers", name); + mBeanServer.unregisterMBean(objectName); + registered.add(objectName); } catch (InstanceNotFoundException e) { LOGGER.debug("Unable to unregister timer", e); } catch (MBeanRegistrationException e) { @@ -433,20 +464,34 @@ private ObjectName createName(String type, String name) { } } } + + void unregisterAll() { + for (ObjectName name : registered) { + try { + mBeanServer.unregisterMBean(name); + } catch (InstanceNotFoundException e) { + LOGGER.debug("Unable to unregister metric", e); + } catch (MBeanRegistrationException e) { + LOGGER.warn("Unable to unregister metric", e); + } + } + registered.clear(); + } } private final MetricRegistry registry; - private final MetricRegistryListener listener; + private final JmxListener listener; /** * Creates a new {@link JmxReporter}. * - * @param mBeanServer the platform's {@link MBeanServer} + * @param mBeanServer the platform's {@link javax.management.MBeanServer} * @param registry the registry containing the metrics to report + * @param filter the metric filter to match */ - public JmxReporter(MBeanServer mBeanServer, MetricRegistry registry) { + public JmxReporter(MBeanServer mBeanServer, MetricRegistry registry, MetricFilter filter) { this.registry = registry; - this.listener = new JmxListener(mBeanServer, registry.getName()); + this.listener = new JmxListener(mBeanServer, registry.getName(), filter); } /** @@ -461,5 +506,6 @@ public void start() { */ public void stop() { registry.removeListener(listener); + listener.unregisterAll(); } } diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java index afb22ae5d5..f30ebef04a 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java @@ -14,6 +14,7 @@ import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.api.Assertions.entry; +import static org.fest.assertions.api.Assertions.failBecauseExceptionWasNotThrown; import static org.mockito.Mockito.*; public class JmxReporterTest { @@ -21,7 +22,7 @@ public class JmxReporterTest { private final String name = UUID.randomUUID().toString().replaceAll("[{\\-}]", ""); private final MetricRegistry registry = spy(new MetricRegistry(name)); - private final JmxReporter reporter = new JmxReporter(mBeanServer, registry); + private final JmxReporter reporter = new JmxReporter(mBeanServer, registry, MetricFilter.ALL); private final Gauge gauge = mock(Gauge.class); private final Counter counter = mock(Counter.class); @@ -191,6 +192,18 @@ public void registersMBeansForTimers() throws Exception { .contains(entry("999thPercentile", 10.0e8)); } + @Test + public void cleansUpAfterItselfWhenStopped() throws Exception { + reporter.stop(); + + try { + getAttributes("gauge", "Value"); + failBecauseExceptionWasNotThrown(InstanceNotFoundException.class); + } catch (InstanceNotFoundException e) { + + } + } + private AttributeList getAttributes(String name, String... attributeNames) throws JMException { final ObjectName n = new ObjectName(this.name, "name", name); return mBeanServer.getAttributes(n, attributeNames); From b86b450aa6b42178ad6b0696ebbe6c7f242cec75 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 18 Mar 2013 17:04:57 -0700 Subject: [PATCH 0082/2558] Mark some stuff as done. --- TODO.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/TODO.md b/TODO.md index 349682001f..1a7cbf42e4 100644 --- a/TODO.md +++ b/TODO.md @@ -2,16 +2,10 @@ Things What Need Doing ====================== * Batch gauges? Sets of gauges which share a common name prefix and pre-read method (e.g., JVM shiz) -* Get back up to feature parity w/ JVM instrumentation, EOL ``VirtualMachineMetrics`` * Figure out what configuring Jetty w/ instrumented stuff looks like (JNDI?) * Figure out what configuring Ehcache w/ instrumented stuff looks like (JNDI?) * Go through the docs with a fine-toothed comb and make sure things make sense * Go through ``2.x-maintenance`` and make sure I didn't forget to forward-port something -* Add tests for ``JmxReporter`` -* Add tests for ``JxmAttributeGauge`` -* Add tests for ``LoggerReporter`` -* Add tests and rate/duration units for ``ConsoleReporter`` -* Add tests for ``CsvReporter`` * Go back and hit ``metrics-servlet`` with a stick * Add javadocs for pretty much everything * Do integration testing w/ Ganglia From 2c1e4e0d5a1b0f96e501d57c87f30cda4564257c Mon Sep 17 00:00:00 2001 From: Mathijs Vogelzang Date: Tue, 19 Mar 2013 09:47:53 +0100 Subject: [PATCH 0083/2558] use name( ) function instead of getName() directly --- .../java/com/yammer/metrics/jetty8/InstrumentedHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java index a1c4b087a1..9651d241ea 100644 --- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java +++ b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java @@ -50,7 +50,7 @@ public class InstrumentedHandler extends HandlerWrapper { * @param underlying the handler about which metrics will be collected */ public InstrumentedHandler(MetricRegistry registry, Handler underlying) { - this(registry, underlying, underlying.getClass().getName()); + this(registry, underlying, name(underlying.getClass())); } /** From 9d252a55c902537424745bb3f0f91c28dd4ed055 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 19 Mar 2013 10:42:43 -0700 Subject: [PATCH 0084/2558] Added CachedGauge. --- .../java/com/yammer/metrics/CachedGauge.java | 68 +++++++++++++++++++ .../yammer/metrics/tests/CachedGaugeTest.java | 42 ++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 metrics-core/src/main/java/com/yammer/metrics/CachedGauge.java create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/CachedGaugeTest.java diff --git a/metrics-core/src/main/java/com/yammer/metrics/CachedGauge.java b/metrics-core/src/main/java/com/yammer/metrics/CachedGauge.java new file mode 100644 index 0000000000..f7a4eacd8f --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/CachedGauge.java @@ -0,0 +1,68 @@ +package com.yammer.metrics; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +/** + * A {@link Gauge} implementation which caches its value for a period of time. + * + * @param the type of the gauge's value + */ +public abstract class CachedGauge implements Gauge { + private final Clock clock; + private final AtomicLong reloadAt; + private final long timeoutNS; + + private volatile T value; + + /** + * Creates a new cached gauge with the given timeout period. + * + * @param timeout the timeout + * @param timeoutUnit the unit of {@code timeout} + */ + protected CachedGauge(long timeout, TimeUnit timeoutUnit) { + this(Clock.defaultClock(), timeout, timeoutUnit); + } + + /** + * Creates a new cached gauge with the given clock and timeout period. + * + * @param clock the clock used to calculate the timeout + * @param timeout the timeout + * @param timeoutUnit the unit of {@code timeout} + */ + protected CachedGauge(Clock clock, long timeout, TimeUnit timeoutUnit) { + this.clock = clock; + this.reloadAt = new AtomicLong(0); + this.timeoutNS = timeoutUnit.toNanos(timeout); + } + + /** + * Loads the value and returns it. + * + * @return the new value + */ + protected abstract T loadValue(); + + @Override + public T getValue() { + if (shouldLoad()) { + this.value = loadValue(); + } + return value; + } + + private boolean shouldLoad() { + for (; ; ) { + final long time = clock.getTick(); + final long current = reloadAt.get(); + if (current > time) { + return false; + } + if (reloadAt.compareAndSet(current, time + timeoutNS)) { + return true; + } + } + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/CachedGaugeTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/CachedGaugeTest.java new file mode 100644 index 0000000000..e48664b57a --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/CachedGaugeTest.java @@ -0,0 +1,42 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.CachedGauge; +import com.yammer.metrics.Gauge; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.fest.assertions.api.Assertions.assertThat; + +public class CachedGaugeTest { + private final AtomicInteger value = new AtomicInteger(0); + private final Gauge gauge = new CachedGauge(100, TimeUnit.MILLISECONDS) { + @Override + protected Integer loadValue() { + return value.incrementAndGet(); + } + }; + + @Test + public void cachesTheValueForTheGivenPeriod() throws Exception { + assertThat(gauge.getValue()) + .isEqualTo(1); + assertThat(gauge.getValue()) + .isEqualTo(1); + } + + @Test + public void reloadsTheCachedValueAfterTheGivenPeriod() throws Exception { + assertThat(gauge.getValue()) + .isEqualTo(1); + + Thread.sleep(150); + + assertThat(gauge.getValue()) + .isEqualTo(2); + + assertThat(gauge.getValue()) + .isEqualTo(2); + } +} From 21d70c71f0441aa322612793a71869e3312c46d6 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 19 Mar 2013 10:47:42 -0700 Subject: [PATCH 0085/2558] Added DerivativeGauge. --- .../com/yammer/metrics/DerivativeGauge.java | 33 +++++++++++++++++++ .../metrics/tests/DerivativeGaugeTest.java | 28 ++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 metrics-core/src/main/java/com/yammer/metrics/DerivativeGauge.java create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/DerivativeGaugeTest.java diff --git a/metrics-core/src/main/java/com/yammer/metrics/DerivativeGauge.java b/metrics-core/src/main/java/com/yammer/metrics/DerivativeGauge.java new file mode 100644 index 0000000000..a617facb87 --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/DerivativeGauge.java @@ -0,0 +1,33 @@ +package com.yammer.metrics; + +/** + * A gauge whose value is derived from the value of another gauge. + * + * @param the base gauge's value type + * @param the derivative type + */ +public abstract class DerivativeGauge implements Gauge { + private final Gauge base; + + /** + * Creates a new derivative with the given base gauge. + * + * @param base the gauge from which to derive this gauge's value + */ + protected DerivativeGauge(Gauge base) { + this.base = base; + } + + @Override + public T getValue() { + return transform(base.getValue()); + } + + /** + * Transforms the value of the base gauge to the value of this gauge. + * + * @param value the value of the base gauge + * @return this gauge's value + */ + protected abstract T transform(F value); +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/DerivativeGaugeTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/DerivativeGaugeTest.java new file mode 100644 index 0000000000..6b129f2595 --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/DerivativeGaugeTest.java @@ -0,0 +1,28 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.DerivativeGauge; +import com.yammer.metrics.Gauge; +import org.junit.Test; + +import static org.fest.assertions.api.Assertions.assertThat; + +public class DerivativeGaugeTest { + private final Gauge gauge1 = new Gauge() { + @Override + public String getValue() { + return "woo"; + } + }; + private final Gauge gauge2 = new DerivativeGauge(gauge1) { + @Override + protected Integer transform(String value) { + return value.length(); + } + }; + + @Test + public void returnsATransformedValue() throws Exception { + assertThat(gauge2.getValue()) + .isEqualTo(3); + } +} From ab38941f62096127def00df14438f163dd246186 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sat, 23 Mar 2013 15:55:55 -0700 Subject: [PATCH 0086/2558] Fix test. Closes #355. --- .../test/java/com/yammer/metrics/tests/MetricFilterTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricFilterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricFilterTest.java index 69565431fb..00ae25de7c 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricFilterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricFilterTest.java @@ -5,13 +5,12 @@ import org.junit.Test; import static org.fest.assertions.api.Assertions.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; public class MetricFilterTest { @Test public void theAllFilterMatchesAllMetrics() throws Exception { - assertThat(MetricFilter.ALL.matches(anyString(), any(Metric.class))) + assertThat(MetricFilter.ALL.matches("", mock(Metric.class))) .isTrue(); } } From d9c11c1bac846642dd27a8d4949e07784e7d24c4 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 17:40:03 -0400 Subject: [PATCH 0087/2558] Pull in release notes from 2.x-maintenance. --- docs/source/about/release-notes.rst | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/source/about/release-notes.rst b/docs/source/about/release-notes.rst index 7d61279e8c..dc20835280 100644 --- a/docs/source/about/release-notes.rst +++ b/docs/source/about/release-notes.rst @@ -24,6 +24,32 @@ v3.0.0-SNAPSHOT * Changed package names in ``metrics-ganglia``, ``metrics-graphite``, and ``metrics-servlet``. * Removed ``metrics-guice`` and ``metrics-spring``. +.. _rel-2.2.0: + +v2.2.0: Nov 26 2012 +=================== + +* Removed all OSGi bundling. This will be back in 3.0. +* Added ``InstrumentedSslSelectChannelConnector`` and ``InstrumentedSslSocketConnector``. +* Made all metric names JMX-safe. +* Upgraded to Ehcache 2.6.2. +* Upgraded to Apache HttpClient 4.2.2. +* Upgraded to Jersey 1.15. +* Upgraded to Log4j 1.2.17. +* Upgraded to Logback 1.0.7. +* Upgraded to Spring 3.1.3. +* Upgraded to Jetty 8.1.8. +* Upgraded to SLF4J 1.7.2. +* Replaced usage of ``Unsafe`` in ``InstrumentedResourceMethodDispatchProvider`` with type erasure + trickery. + +.. _rel-2.1.5: + +v2.1.5: Nov 19 2012 +=================== + +* Upgraded to Jackson 2.1.1. + .. _rel-2.1.4: v2.1.4: Nov 15 2012 From 4918dc347ce31cce126a9562bdd78983572e48ae Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 19:04:45 -0400 Subject: [PATCH 0088/2558] Dropped InstrumentedEhcacheFactory. Tracking via #358 for 3.0.0 final. --- .../ehcache/InstrumentedEhcacheFactory.java | 17 ------- .../tests/ConfigInstrumentedEhcacheTest.java | 46 ------------------- 2 files changed, 63 deletions(-) delete mode 100644 metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcacheFactory.java delete mode 100644 metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/ConfigInstrumentedEhcacheTest.java diff --git a/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcacheFactory.java b/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcacheFactory.java deleted file mode 100644 index 38eb03e614..0000000000 --- a/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcacheFactory.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.yammer.metrics.ehcache; - -// TODO: 3/10/13 -- figure out how to coordinate on registry names - -//public class InstrumentedEhcacheFactory extends CacheDecoratorFactory { -// -// @Override -// public Ehcache createDecoratedEhcache(Ehcache cache, Properties properties) { -// return InstrumentedEhcache.instrument(cache); -// } -// -// @Override -// public Ehcache createDefaultDecoratedEhcache(Ehcache cache, Properties properties) { -// return InstrumentedEhcache.instrument(cache); -// } -// -//} diff --git a/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/ConfigInstrumentedEhcacheTest.java b/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/ConfigInstrumentedEhcacheTest.java deleted file mode 100644 index 929fecca93..0000000000 --- a/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/ConfigInstrumentedEhcacheTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.yammer.metrics.ehcache.tests; - -// TODO: 3/10/13 -- figure out how to coordinate on registry names - -//public class ConfigInstrumentedEhcacheTest { -// -// private static final CacheManager MANAGER = CacheManager.create(); -// -// private Ehcache cache; -// -// @Before -// public void setUp() throws Exception { -// cache = MANAGER.getEhcache("test-config"); -// if (cache == null) fail("Cache is not set correctly"); -// } -// -// @Test -// public void measuresGets() throws Exception { -// cache.get("woo"); -// -// final Timer gets = Metrics.defaultRegistry().newTimer(Cache.class, -// "get", -// "test-config", -// TimeUnit.MILLISECONDS, -// TimeUnit.SECONDS); -// -// assertThat(gets.count(), is(1L)); -// -// } -// -// @Test -// public void measuresPuts() throws Exception { -// -// cache.put(new Element("woo", "whee")); -// -// final Timer puts = Metrics.defaultRegistry().newTimer(Cache.class, -// "put", -// "test-config", -// TimeUnit.MILLISECONDS, -// TimeUnit.SECONDS); -// -// assertThat(puts.count(), is(1L)); -// -// } -// -//} From 47970a81a0762106a1eadc02681dd947f94f52dd Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 19:19:32 -0400 Subject: [PATCH 0089/2558] Tracking this TODO via #359 for 3.0.0 final. --- .../jersey/InstrumentedResourceMethodDispatchAdapter.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java b/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java index d8d007dfcc..5309fb04bb 100644 --- a/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java +++ b/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java @@ -12,8 +12,6 @@ */ @Provider public class InstrumentedResourceMethodDispatchAdapter implements ResourceMethodDispatchAdapter { - // TODO: 3/10/13 -- figure out how to coordinate on registry names - private final MetricRegistry registry; /** From 105b8e9cc8fa8b614b0c232dac45f4c7487b758a Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 19:21:45 -0400 Subject: [PATCH 0090/2558] Tracking this TODO via #360 for 3.0.0 final. --- .../java/com/yammer/metrics/servlet/WebappMetricsFilter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/metrics-servlet/src/main/java/com/yammer/metrics/servlet/WebappMetricsFilter.java b/metrics-servlet/src/main/java/com/yammer/metrics/servlet/WebappMetricsFilter.java index 45756d2aee..5bcb9fe3db 100644 --- a/metrics-servlet/src/main/java/com/yammer/metrics/servlet/WebappMetricsFilter.java +++ b/metrics-servlet/src/main/java/com/yammer/metrics/servlet/WebappMetricsFilter.java @@ -75,7 +75,6 @@ private MetricRegistry getMetricsFactory(FilterConfig filterConfig) { if (o instanceof MetricRegistry) { metricsRegistry = (MetricRegistry) o; } else { - // TODO: 3/10/13 -- figure out how to coordinate on registry names metricsRegistry = new MetricRegistry("servlet"); } return metricsRegistry; From 5fddd65ddf941a4dc96c6467e5253bdc1797883a Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 19:25:45 -0400 Subject: [PATCH 0091/2558] Added docs for AbstractPollingReporter. --- .../java/com/yammer/metrics/AbstractPollingReporter.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java b/metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java index b98d1215fb..7c737eda3e 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java @@ -6,6 +6,12 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +/** + * The abstract base class for all polling reporters (i.e., reporters which process a registry's + * metrics periodically). + * + * @see ConsoleReporter + */ public abstract class AbstractPollingReporter implements Reporter { /** * A simple named thread factory. @@ -69,6 +75,9 @@ public void run() { }, period, period, unit); } + /** + * Stops the reporter and shuts down its thread of execution. + */ public void stop() { executor.shutdown(); try { From bb8efde9d8aeb33477c0682944ac9ccff4aa2435 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 19:33:56 -0400 Subject: [PATCH 0092/2558] Inline Meter#tick(). Nothing else uses it. --- .../src/main/java/com/yammer/metrics/Meter.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/Meter.java b/metrics-core/src/main/java/com/yammer/metrics/Meter.java index 2fa2ca0b4e..4a2d8cea37 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Meter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Meter.java @@ -39,15 +39,6 @@ public Meter() { this.lastTick = new AtomicLong(startTime); } - /** - * Updates the moving averages. - */ - void tick() { - m1Rate.tick(); - m5Rate.tick(); - m15Rate.tick(); - } - /** * Mark the occurrence of an event. */ @@ -75,7 +66,9 @@ private void tickIfNecessary() { if (age > TICK_INTERVAL && lastTick.compareAndSet(oldTick, newTick)) { final long requiredTicks = age / TICK_INTERVAL; for (long i = 0; i < requiredTicks; i++) { - tick(); + m1Rate.tick(); + m5Rate.tick(); + m15Rate.tick(); } } } From affab45f9e7bff9333082a34019761d7d6775008 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 19:35:06 -0400 Subject: [PATCH 0093/2558] Remove no-op comments. --- .../main/java/com/yammer/metrics/Histogram.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/Histogram.java b/metrics-core/src/main/java/com/yammer/metrics/Histogram.java index 28c248dc31..95aee14d7d 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Histogram.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Histogram.java @@ -76,9 +76,6 @@ public long getCount() { return count.get(); } - /* (non-Javadoc) - * @see com.yammer.metrics.Summarizable#max() - */ @Override public long getMax() { if (getCount() > 0) { @@ -87,9 +84,6 @@ public long getMax() { return 0; } - /* (non-Javadoc) - * @see com.yammer.metrics.Summarizable#min() - */ @Override public long getMin() { if (getCount() > 0) { @@ -98,9 +92,6 @@ public long getMin() { return 0; } - /* (non-Javadoc) - * @see com.yammer.metrics.Summarizable#mean() - */ @Override public double getMean() { if (getCount() > 0) { @@ -109,9 +100,6 @@ public double getMean() { return 0.0; } - /* (non-Javadoc) - * @see com.yammer.metrics.Summarizable#stdDev() - */ @Override public double getStdDev() { if (getCount() > 0) { @@ -120,9 +108,6 @@ public double getStdDev() { return 0.0; } - /* (non-Javadoc) - * @see com.yammer.metrics.Summarizable#sum() - */ @Override public long getSum() { return sum.get(); From 6dd22e288f053af39df12817182566af735de11a Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 19:40:18 -0400 Subject: [PATCH 0094/2558] Added docs for MetricRegistryListener. --- .../metrics/MetricRegistryListener.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistryListener.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistryListener.java index bf0c88c92d..29548a1968 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistryListener.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistryListener.java @@ -6,23 +6,78 @@ * Listeners for events from the registry. Listeners must be thread-safe. */ public interface MetricRegistryListener extends EventListener { + /** + * Called when a {@link Gauge} is added to the registry. + * + * @param name the gauge's name + * @param gauge the gauge + */ void onGaugeAdded(String name, Gauge gauge); + /** + * Called when a {@link Gauge} is removed from the registry. + * + * @param name the gauge's name + */ void onGaugeRemoved(String name); + /** + * Called when a {@link Counter} is added to the registry. + * + * @param name the counter's name + * @param counter the counter + */ void onCounterAdded(String name, Counter counter); + /** + * Called when a {@link Counter} is removed from the registry. + * + * @param name the counter's name + */ void onCounterRemoved(String name); + /** + * Called when a {@link Histogram} is added to the registry. + * + * @param name the histogram's name + * @param histogram the histogram + */ void onHistogramAdded(String name, Histogram histogram); + /** + * Called when a {@link Histogram} is removed from the registry. + * + * @param name the histogram's name + */ void onHistogramRemoved(String name); + /** + * Called when a {@link Meter} is added to the registry. + * + * @param name the meter's name + * @param meter the meter + */ void onMeterAdded(String name, Meter meter); + /** + * Called when a {@link Meter} is removed from the registry. + * + * @param name the meter's name + */ void onMeterRemoved(String name); + /** + * Called when a {@link Timer} is added to the registry. + * + * @param name the timer's name + * @param timer the timer + */ void onTimerAdded(String name, Timer timer); + /** + * Called when a {@link Timer} is removed from the registry. + * + * @param name the timer's name + */ void onTimerRemoved(String name); } From a0a2482c993c1c8a861c88af80e77321456a0146 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 19:41:32 -0400 Subject: [PATCH 0095/2558] Added docs for MovingWindowSample. --- .../main/java/com/yammer/metrics/MovingWindowSample.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/metrics-core/src/main/java/com/yammer/metrics/MovingWindowSample.java b/metrics-core/src/main/java/com/yammer/metrics/MovingWindowSample.java index d9dcca8319..f9a6885091 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MovingWindowSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MovingWindowSample.java @@ -5,10 +5,19 @@ import static java.lang.Math.min; +/** + * A {@link Sample} implementation backed by a moving window that stores the last {@code N} + * measurements. + */ public class MovingWindowSample implements Sample { private final AtomicLongArray window; private final AtomicLong index; + /** + * Creates a new {@link MovingWindowSample} which stores the last {@code size} measurements. + * + * @param size the number of measurements to store + */ public MovingWindowSample(int size) { this.window = new AtomicLongArray(size); this.index = new AtomicLong(); From a256007754f55d64250fae56ed6b08996b8b43e9 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 19:47:25 -0400 Subject: [PATCH 0096/2558] Inline Reporter, as it's only used by AbstractPollingReporter. --- .../metrics/AbstractPollingReporter.java | 21 +++++++++++++-- .../java/com/yammer/metrics/Reporter.java | 11 -------- .../tests/AbstractPollingReporterTest.java | 27 +++++++++---------- 3 files changed, 32 insertions(+), 27 deletions(-) delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/Reporter.java diff --git a/metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java b/metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java index 7c737eda3e..fe7cdea068 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java @@ -1,5 +1,6 @@ package com.yammer.metrics; +import java.util.SortedMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; @@ -12,7 +13,7 @@ * * @see ConsoleReporter */ -public abstract class AbstractPollingReporter implements Reporter { +public abstract class AbstractPollingReporter { /** * A simple named thread factory. */ @@ -46,7 +47,8 @@ public Thread newThread(Runnable r) { /** * Creates a new {@link AbstractPollingReporter} instance. * - * @param registry the {@link com.yammer.metrics.MetricRegistry} containing the metrics this reporter will report + * @param registry the {@link com.yammer.metrics.MetricRegistry} containing the metrics this + * reporter will report * @param name the reporter's name * @param filter the filter for which metrics to report */ @@ -86,4 +88,19 @@ public void stop() { // do nothing } } + + /** + * Called periodically by the polling thread. Subclasses should report all the given metrics. + * + * @param gauges all of the gauges in the registry + * @param counters all of the counters in the registry + * @param histograms all of the histograms in the registry + * @param meters all of the meters in the registry + * @param timers all of the timers in the registry + */ + public abstract void report(SortedMap gauges, + SortedMap counters, + SortedMap histograms, + SortedMap meters, + SortedMap timers); } diff --git a/metrics-core/src/main/java/com/yammer/metrics/Reporter.java b/metrics-core/src/main/java/com/yammer/metrics/Reporter.java deleted file mode 100644 index 9f6d36e81f..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/Reporter.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.yammer.metrics; - -import java.util.SortedMap; - -public interface Reporter { - void report(SortedMap gauges, - SortedMap counters, - SortedMap histograms, - SortedMap meters, - SortedMap timers); -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/AbstractPollingReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/AbstractPollingReporterTest.java index 2b060dbace..4d1bcee916 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/AbstractPollingReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/AbstractPollingReporterTest.java @@ -19,19 +19,18 @@ public class AbstractPollingReporterTest { private final Timer timer = mock(Timer.class); private final MetricRegistry registry = new MetricRegistry("test"); - private final Reporter underlying = mock(Reporter.class); - private final AbstractPollingReporter reporter = new AbstractPollingReporter(registry, - "example", - MetricFilter.ALL) { - @Override - public void report(SortedMap gauges, - SortedMap counters, - SortedMap histograms, - SortedMap meters, - SortedMap timers) { - underlying.report(gauges, counters, histograms, meters, timers); - } - }; + private final AbstractPollingReporter reporter = spy( + new AbstractPollingReporter(registry, "example", MetricFilter.ALL) { + @Override + public void report(SortedMap gauges, + SortedMap counters, + SortedMap histograms, + SortedMap meters, + SortedMap timers) { + // nothing doing! + } + } + ); @Before public void setUp() throws Exception { @@ -51,7 +50,7 @@ public void tearDown() throws Exception { @Test public void pollsPeriodically() throws Exception { - verify(underlying, timeout(250).times(2)).report( + verify(reporter, timeout(250).times(2)).report( map("gauge", gauge), map("counter", counter), map("histogram", histogram), From 109bcb3bb38439c2fe398a67a82b02b81f007c02 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 19:52:14 -0400 Subject: [PATCH 0097/2558] Added docs and toString impl to Ratio. --- .../java/com/yammer/metrics/RatioGauge.java | 21 +++++++++++++++++++ .../yammer/metrics/tests/RatioGaugeTest.java | 8 +++++++ 2 files changed, 29 insertions(+) diff --git a/metrics-core/src/main/java/com/yammer/metrics/RatioGauge.java b/metrics-core/src/main/java/com/yammer/metrics/RatioGauge.java index e7468f3388..a94724cc64 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/RatioGauge.java +++ b/metrics-core/src/main/java/com/yammer/metrics/RatioGauge.java @@ -9,7 +9,17 @@ * If the denominator is zero, not a number, or infinite, the resulting ratio is not a number. */ public abstract class RatioGauge implements Gauge { + /** + * A ratio of one quantity to another. + */ public static class Ratio { + /** + * Creates a new ratio with the given numerator and denominator. + * + * @param numerator the numerator of the ratio + * @param denominator the denominator of the ratio + * @return {@code numerator:denominator} + */ public static Ratio of(double numerator, double denominator) { return new Ratio(numerator, denominator); } @@ -22,6 +32,12 @@ private Ratio(double numerator, double denominator) { this.denominator = denominator; } + /** + * Returns the ratio, which is either a {@code double} between 0 and 1 (inclusive) or + * {@code NaN}. + * + * @return the ratio + */ public double getValue() { final double d = denominator; if (isNaN(d) || isInfinite(d) || d == 0) { @@ -29,6 +45,11 @@ public double getValue() { } return numerator / d; } + + @Override + public String toString() { + return numerator + ":" + denominator; + } } /** diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/RatioGaugeTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/RatioGaugeTest.java index 091deb736a..4508328797 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/RatioGaugeTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/RatioGaugeTest.java @@ -6,6 +6,14 @@ import static org.fest.assertions.api.Assertions.assertThat; public class RatioGaugeTest { + @Test + public void ratiosAreHumanReadable() throws Exception { + final RatioGauge.Ratio ratio = RatioGauge.Ratio.of(100, 200); + + assertThat(ratio.toString()) + .isEqualTo("100.0:200.0"); + } + @Test public void calculatesTheRatioOfTheNumeratorToTheDenominator() throws Exception { final RatioGauge regular = new RatioGauge() { From 8db9b87996e32b6ce6242065fc7726c7bc32a331 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 19:56:13 -0400 Subject: [PATCH 0098/2558] Inlined some unused constants in Snapshot. --- .../main/java/com/yammer/metrics/Snapshot.java | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java b/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java index 80c525a9b6..a7ce5e8f9d 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java @@ -11,12 +11,6 @@ */ public class Snapshot { private static final Charset UTF_8 = Charset.forName("UTF-8"); - private static final double MEDIAN_Q = 0.5; - private static final double P75_Q = 0.75; - private static final double P95_Q = 0.95; - private static final double P98_Q = 0.98; - private static final double P99_Q = 0.99; - private static final double P999_Q = 0.999; private final long[] values; @@ -89,7 +83,7 @@ public int size() { * @return the median value in the distribution */ public double getMedian() { - return getValue(MEDIAN_Q); + return getValue(0.5); } /** @@ -98,7 +92,7 @@ public double getMedian() { * @return the value at the 75th percentile in the distribution */ public double get75thPercentile() { - return getValue(P75_Q); + return getValue(0.75); } /** @@ -107,7 +101,7 @@ public double get75thPercentile() { * @return the value at the 95th percentile in the distribution */ public double get95thPercentile() { - return getValue(P95_Q); + return getValue(0.95); } /** @@ -116,7 +110,7 @@ public double get95thPercentile() { * @return the value at the 98th percentile in the distribution */ public double get98thPercentile() { - return getValue(P98_Q); + return getValue(0.98); } /** @@ -125,7 +119,7 @@ public double get98thPercentile() { * @return the value at the 99th percentile in the distribution */ public double get99thPercentile() { - return getValue(P99_Q); + return getValue(0.99); } /** @@ -134,7 +128,7 @@ public double get99thPercentile() { * @return the value at the 99.9th percentile in the distribution */ public double get999thPercentile() { - return getValue(P999_Q); + return getValue(0.999); } /** From a14e5ac213fa8e8341209c693d4665dbb033b036 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 20:06:35 -0400 Subject: [PATCH 0099/2558] Added javadocs for GangliaReporter. --- .../yammer/metrics/ganglia/GangliaReporter.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java index a169ad6abc..3696a02516 100644 --- a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java +++ b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java @@ -15,6 +15,11 @@ import static com.yammer.metrics.MetricRegistry.name; +/** + * A reporter which announces metric values to a Ganglia cluster. + * + * @see Ganglia Monitoring System + */ public class GangliaReporter extends AbstractPollingReporter { private static final Logger LOGGER = LoggerFactory.getLogger(GangliaReporter.class); @@ -26,6 +31,17 @@ public class GangliaReporter extends AbstractPollingReporter { private final int tMax; private final int dMax; + /** + * Creates a new {@link GangliaReporter}. + * + * @param registry the registry to report + * @param ganglia a {@link GMetric} instance for the Ganglia cluster + * @param tMax the time in seconds between polling + * @param dMax the time in seconds after which the data should be considered unmonitored + * @param rateUnit the time unit to use for rates + * @param durationUnit the time unit to use for durations + * @param filter a filter for metrics + */ public GangliaReporter(MetricRegistry registry, GMetric ganglia, int tMax, From 3b4abd41e0c66a97687e5548c14e52ef90998518 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 20:11:13 -0400 Subject: [PATCH 0100/2558] Added Graphite#getFailures(). Closes #327. --- .../com/yammer/metrics/graphite/Graphite.java | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/Graphite.java b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/Graphite.java index 13327d0d8e..4946c0722e 100644 --- a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/Graphite.java +++ b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/Graphite.java @@ -17,6 +17,7 @@ public class Graphite implements Closeable { private Socket socket; private Writer writer; + private int failures; public Graphite(InetSocketAddress address, SocketFactory socketFactory) { this.address = address; @@ -33,13 +34,23 @@ public void connect() throws IOException { } public void write(String name, String value, long timestamp) throws IOException { - writer.write(sanitize(name)); - writer.write(' '); - writer.write(sanitize(value)); - writer.write(' '); - writer.write(Long.toString(timestamp)); - writer.write('\n'); - writer.flush(); + try { + writer.write(sanitize(name)); + writer.write(' '); + writer.write(sanitize(value)); + writer.write(' '); + writer.write(Long.toString(timestamp)); + writer.write('\n'); + writer.flush(); + this.failures = 0; + } catch (IOException e) { + failures++; + throw e; + } + } + + public int getFailures() { + return failures; } @Override From 046ddffbbf1048d7eb4d245c0d20333d3799e39d Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 20:12:48 -0400 Subject: [PATCH 0101/2558] Formatting fix for GangliaReporter. --- .../metrics/ganglia/GangliaReporter.java | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java index 3696a02516..259ace6f7c 100644 --- a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java +++ b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java @@ -34,13 +34,14 @@ public class GangliaReporter extends AbstractPollingReporter { /** * Creates a new {@link GangliaReporter}. * - * @param registry the registry to report - * @param ganglia a {@link GMetric} instance for the Ganglia cluster - * @param tMax the time in seconds between polling - * @param dMax the time in seconds after which the data should be considered unmonitored - * @param rateUnit the time unit to use for rates - * @param durationUnit the time unit to use for durations - * @param filter a filter for metrics + * @param registry the registry to report + * @param ganglia a {@link GMetric} instance for the Ganglia cluster + * @param tMax the time in seconds between polling + * @param dMax the time in seconds after which the data should be considered + * unmonitored + * @param rateUnit the time unit to use for rates + * @param durationUnit the time unit to use for durations + * @param filter a filter for metrics */ public GangliaReporter(MetricRegistry registry, GMetric ganglia, @@ -101,11 +102,26 @@ private void reportTimer(String name, Timer timer) { final Snapshot snapshot = timer.getSnapshot(); announce(name(name, "p50"), group, snapshot.getMedian() * durationFactor, durationUnit); - announce(name(name, "p75"), group, snapshot.get75thPercentile() * durationFactor, durationUnit); - announce(name(name, "p95"), group, snapshot.get95thPercentile() * durationFactor, durationUnit); - announce(name(name, "p98"), group, snapshot.get98thPercentile() * durationFactor, durationUnit); - announce(name(name, "p99"), group, snapshot.get99thPercentile() * durationFactor, durationUnit); - announce(name(name, "p999"), group, snapshot.get999thPercentile() * durationFactor, durationUnit); + announce(name(name, "p75"), + group, + snapshot.get75thPercentile() * durationFactor, + durationUnit); + announce(name(name, "p95"), + group, + snapshot.get95thPercentile() * durationFactor, + durationUnit); + announce(name(name, "p98"), + group, + snapshot.get98thPercentile() * durationFactor, + durationUnit); + announce(name(name, "p99"), + group, + snapshot.get99thPercentile() * durationFactor, + durationUnit); + announce(name(name, "p999"), + group, + snapshot.get999thPercentile() * durationFactor, + durationUnit); reportMetered(name, timer, group, "calls"); } catch (GangliaException e) { From a771907b570bca88e770337a866233f7d7b8d9be Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 20:26:54 -0400 Subject: [PATCH 0102/2558] Added docs for GraphiteReporter and Graphite. --- .../com/yammer/metrics/graphite/Graphite.java | 42 +++++++- .../metrics/graphite/GraphiteReporter.java | 95 +++++++++++++------ .../graphite/tests/GraphiteReporterTest.java | 78 +++++++-------- .../metrics/graphite/tests/GraphiteTest.java | 12 ++- 4 files changed, 154 insertions(+), 73 deletions(-) diff --git a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/Graphite.java b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/Graphite.java index 4946c0722e..9d717a6bd3 100644 --- a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/Graphite.java +++ b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/Graphite.java @@ -7,6 +7,9 @@ import java.nio.charset.Charset; import java.util.regex.Pattern; +/** + * A client to a Carbon server. + */ public class Graphite implements Closeable { private static final Pattern WHITESPACE = Pattern.compile("[\\s]+"); // this may be optimistic about Carbon/Graphite @@ -19,12 +22,34 @@ public class Graphite implements Closeable { private Writer writer; private int failures; + /** + * Creates a new client which connects to the given address using the default + * {@link SocketFactory}. + * + * @param address the address of the Carbon server + */ + public Graphite(InetSocketAddress address) { + this(address, SocketFactory.getDefault()); + } + + /** + * Creates a new client which connects to the given address and socket factory. + * + * @param address the address of the Carbon server + * @param socketFactory the socket factory + */ public Graphite(InetSocketAddress address, SocketFactory socketFactory) { this.address = address; this.socketFactory = socketFactory; } - public void connect() throws IOException { + /** + * Connects to the server. + * + * @throws IllegalStateException if the client is already connected + * @throws IOException if there is an error connecting + */ + public void connect() throws IllegalStateException, IOException { if (socket != null) { throw new IllegalStateException("Already connected"); } @@ -33,7 +58,15 @@ public void connect() throws IOException { this.writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), UTF_8)); } - public void write(String name, String value, long timestamp) throws IOException { + /** + * Sends the given measurement to the server. + * + * @param name the name of the metric + * @param value the value of the metric + * @param timestamp the timestamp of the metric + * @throws IOException if there was an error sending the metric + */ + public void send(String name, String value, long timestamp) throws IOException { try { writer.write(sanitize(name)); writer.write(' '); @@ -49,6 +82,11 @@ public void write(String name, String value, long timestamp) throws IOException } } + /** + * Returns the number of failed writes to the server. + * + * @return the number of failed writes to the server + */ public int getFailures() { return failures; } diff --git a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java index ee2f221b5a..386aa5363c 100644 --- a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java +++ b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java @@ -10,6 +10,11 @@ import java.util.SortedMap; import java.util.concurrent.TimeUnit; +/** + * A reporter which publishes metric values to a Graphite server. + * + * @see Graphite - Scalable Realtime Graphing + */ public class GraphiteReporter extends AbstractPollingReporter { private static final Logger LOGGER = LoggerFactory.getLogger(GraphiteReporter.class); @@ -19,7 +24,17 @@ public class GraphiteReporter extends AbstractPollingReporter { private final double rateFactor; private final double durationFactor; - + /** + * Creates a new reporter. + * + * @param registry the registry to report + * @param graphite the {@link Graphite} client + * @param clock the clock to use for timestamps + * @param prefix a prefix to be prepended to all metric names (can be {@code null}) + * @param rateUnit the time unit to use for rates + * @param durationUnit the time unit to use for durations + * @param filter a filter for metrics + */ public GraphiteReporter(MetricRegistry registry, Graphite graphite, Clock clock, @@ -78,54 +93,76 @@ public void report(SortedMap gauges, } private void reportTimer(String name, Timer timer, long timestamp) throws IOException { - graphite.write(prefix(name, "max"), format(timer.getMax() * durationFactor), timestamp); - graphite.write(prefix(name, "mean"), format(timer.getMean() * durationFactor), timestamp); - graphite.write(prefix(name, "min"), format(timer.getMin() * durationFactor), timestamp); - graphite.write(prefix(name, "stddev"), format(timer.getStdDev() * durationFactor), timestamp); + graphite.send(prefix(name, "max"), format(timer.getMax() * durationFactor), timestamp); + graphite.send(prefix(name, "mean"), format(timer.getMean() * durationFactor), timestamp); + graphite.send(prefix(name, "min"), format(timer.getMin() * durationFactor), timestamp); + graphite.send(prefix(name, "stddev"), + format(timer.getStdDev() * durationFactor), + timestamp); final Snapshot snapshot = timer.getSnapshot(); - graphite.write(prefix(name, "p50"), format(snapshot.getMedian() * durationFactor), timestamp); - graphite.write(prefix(name, "p75"), format(snapshot.get75thPercentile() * durationFactor), timestamp); - graphite.write(prefix(name, "p95"), format(snapshot.get95thPercentile() * durationFactor), timestamp); - graphite.write(prefix(name, "p98"), format(snapshot.get98thPercentile() * durationFactor), timestamp); - graphite.write(prefix(name, "p99"), format(snapshot.get99thPercentile() * durationFactor), timestamp); - graphite.write(prefix(name, "p999"), format(snapshot.get999thPercentile() * durationFactor), timestamp); + graphite.send(prefix(name, "p50"), + format(snapshot.getMedian() * durationFactor), + timestamp); + graphite.send(prefix(name, "p75"), + format(snapshot.get75thPercentile() * durationFactor), + timestamp); + graphite.send(prefix(name, "p95"), + format(snapshot.get95thPercentile() * durationFactor), + timestamp); + graphite.send(prefix(name, "p98"), + format(snapshot.get98thPercentile() * durationFactor), + timestamp); + graphite.send(prefix(name, "p99"), + format(snapshot.get99thPercentile() * durationFactor), + timestamp); + graphite.send(prefix(name, "p999"), + format(snapshot.get999thPercentile() * durationFactor), + timestamp); reportMetered(name, timer, timestamp); } private void reportMetered(String name, Metered meter, long timestamp) throws IOException { - graphite.write(prefix(name, "count"), format(meter.getCount()), timestamp); - graphite.write(prefix(name, "m1_rate"), format(meter.getOneMinuteRate() * rateFactor), timestamp); - graphite.write(prefix(name, "m5_rate"), format(meter.getFiveMinuteRate() * rateFactor), timestamp); - graphite.write(prefix(name, "m15_rate"), format(meter.getFifteenMinuteRate() * rateFactor), timestamp); - graphite.write(prefix(name, "mean_rate"), format(meter.getMeanRate() * rateFactor), timestamp); + graphite.send(prefix(name, "count"), format(meter.getCount()), timestamp); + graphite.send(prefix(name, "m1_rate"), + format(meter.getOneMinuteRate() * rateFactor), + timestamp); + graphite.send(prefix(name, "m5_rate"), + format(meter.getFiveMinuteRate() * rateFactor), + timestamp); + graphite.send(prefix(name, "m15_rate"), + format(meter.getFifteenMinuteRate() * rateFactor), + timestamp); + graphite.send(prefix(name, "mean_rate"), + format(meter.getMeanRate() * rateFactor), + timestamp); } private void reportHistogram(String name, Histogram histogram, long timestamp) throws IOException { - graphite.write(prefix(name, "count"), format(histogram.getCount()), timestamp); - graphite.write(prefix(name, "max"), format(histogram.getMax()), timestamp); - graphite.write(prefix(name, "mean"), format(histogram.getMean()), timestamp); - graphite.write(prefix(name, "min"), format(histogram.getMin()), timestamp); - graphite.write(prefix(name, "stddev"), format(histogram.getStdDev()), timestamp); + graphite.send(prefix(name, "count"), format(histogram.getCount()), timestamp); + graphite.send(prefix(name, "max"), format(histogram.getMax()), timestamp); + graphite.send(prefix(name, "mean"), format(histogram.getMean()), timestamp); + graphite.send(prefix(name, "min"), format(histogram.getMin()), timestamp); + graphite.send(prefix(name, "stddev"), format(histogram.getStdDev()), timestamp); final Snapshot snapshot = histogram.getSnapshot(); - graphite.write(prefix(name, "p50"), format(snapshot.getMedian()), timestamp); - graphite.write(prefix(name, "p75"), format(snapshot.get75thPercentile()), timestamp); - graphite.write(prefix(name, "p95"), format(snapshot.get95thPercentile()), timestamp); - graphite.write(prefix(name, "p98"), format(snapshot.get98thPercentile()), timestamp); - graphite.write(prefix(name, "p99"), format(snapshot.get99thPercentile()), timestamp); - graphite.write(prefix(name, "p999"), format(snapshot.get999thPercentile()), timestamp); + graphite.send(prefix(name, "p50"), format(snapshot.getMedian()), timestamp); + graphite.send(prefix(name, "p75"), format(snapshot.get75thPercentile()), timestamp); + graphite.send(prefix(name, "p95"), format(snapshot.get95thPercentile()), timestamp); + graphite.send(prefix(name, "p98"), format(snapshot.get98thPercentile()), timestamp); + graphite.send(prefix(name, "p99"), format(snapshot.get99thPercentile()), timestamp); + graphite.send(prefix(name, "p999"), format(snapshot.get999thPercentile()), timestamp); } private void reportCounter(String name, Counter counter, long timestamp) throws IOException { - graphite.write(prefix(name, "count"), format(counter.getCount()), timestamp); + graphite.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) { - graphite.write(prefix(name), value, timestamp); + graphite.send(prefix(name), value, timestamp); } } diff --git a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java index 03b94f0ea9..1cb79e3f79 100644 --- a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java +++ b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java @@ -41,7 +41,7 @@ public void doesNotReportStringGaugeValues() throws Exception { final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); - inOrder.verify(graphite, never()).write("prefix.gauge", "value", timestamp); + inOrder.verify(graphite, never()).send("prefix.gauge", "value", timestamp); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); @@ -57,7 +57,7 @@ public void reportsByteGaugeValues() throws Exception { final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); - inOrder.verify(graphite).write("prefix.gauge", "1", timestamp); + inOrder.verify(graphite).send("prefix.gauge", "1", timestamp); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); @@ -73,7 +73,7 @@ public void reportsShortGaugeValues() throws Exception { final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); - inOrder.verify(graphite).write("prefix.gauge", "1", timestamp); + inOrder.verify(graphite).send("prefix.gauge", "1", timestamp); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); @@ -89,7 +89,7 @@ public void reportsIntegerGaugeValues() throws Exception { final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); - inOrder.verify(graphite).write("prefix.gauge", "1", timestamp); + inOrder.verify(graphite).send("prefix.gauge", "1", timestamp); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); @@ -105,7 +105,7 @@ public void reportsLongGaugeValues() throws Exception { final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); - inOrder.verify(graphite).write("prefix.gauge", "1", timestamp); + inOrder.verify(graphite).send("prefix.gauge", "1", timestamp); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); @@ -121,7 +121,7 @@ public void reportsFloatGaugeValues() throws Exception { final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); - inOrder.verify(graphite).write("prefix.gauge", "1.10", timestamp); + inOrder.verify(graphite).send("prefix.gauge", "1.10", timestamp); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); @@ -137,7 +137,7 @@ public void reportsDoubleGaugeValues() throws Exception { final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); - inOrder.verify(graphite).write("prefix.gauge", "1.10", timestamp); + inOrder.verify(graphite).send("prefix.gauge", "1.10", timestamp); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); @@ -156,7 +156,7 @@ public void reportsCounters() throws Exception { final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); - inOrder.verify(graphite).write("prefix.counter.count", "100", timestamp); + inOrder.verify(graphite).send("prefix.counter.count", "100", timestamp); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); @@ -189,17 +189,17 @@ public void reportsHistograms() throws Exception { final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); - inOrder.verify(graphite).write("prefix.histogram.count", "1", timestamp); - inOrder.verify(graphite).write("prefix.histogram.max", "2", timestamp); - inOrder.verify(graphite).write("prefix.histogram.mean", "3.00", timestamp); - inOrder.verify(graphite).write("prefix.histogram.min", "4", timestamp); - inOrder.verify(graphite).write("prefix.histogram.stddev", "5.00", timestamp); - inOrder.verify(graphite).write("prefix.histogram.p50", "6.00", timestamp); - inOrder.verify(graphite).write("prefix.histogram.p75", "7.00", timestamp); - inOrder.verify(graphite).write("prefix.histogram.p95", "8.00", timestamp); - inOrder.verify(graphite).write("prefix.histogram.p98", "9.00", timestamp); - inOrder.verify(graphite).write("prefix.histogram.p99", "10.00", timestamp); - inOrder.verify(graphite).write("prefix.histogram.p999", "11.00", timestamp); + inOrder.verify(graphite).send("prefix.histogram.count", "1", timestamp); + inOrder.verify(graphite).send("prefix.histogram.max", "2", timestamp); + inOrder.verify(graphite).send("prefix.histogram.mean", "3.00", timestamp); + inOrder.verify(graphite).send("prefix.histogram.min", "4", timestamp); + inOrder.verify(graphite).send("prefix.histogram.stddev", "5.00", timestamp); + inOrder.verify(graphite).send("prefix.histogram.p50", "6.00", timestamp); + inOrder.verify(graphite).send("prefix.histogram.p75", "7.00", timestamp); + inOrder.verify(graphite).send("prefix.histogram.p95", "8.00", timestamp); + inOrder.verify(graphite).send("prefix.histogram.p98", "9.00", timestamp); + inOrder.verify(graphite).send("prefix.histogram.p99", "10.00", timestamp); + inOrder.verify(graphite).send("prefix.histogram.p999", "11.00", timestamp); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); @@ -222,11 +222,11 @@ public void reportsMeters() throws Exception { final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); - inOrder.verify(graphite).write("prefix.meter.count", "1", timestamp); - inOrder.verify(graphite).write("prefix.meter.m1_rate", "2.00", timestamp); - inOrder.verify(graphite).write("prefix.meter.m5_rate", "3.00", timestamp); - inOrder.verify(graphite).write("prefix.meter.m15_rate", "4.00", timestamp); - inOrder.verify(graphite).write("prefix.meter.mean_rate", "5.00", timestamp); + inOrder.verify(graphite).send("prefix.meter.count", "1", timestamp); + inOrder.verify(graphite).send("prefix.meter.m1_rate", "2.00", timestamp); + inOrder.verify(graphite).send("prefix.meter.m5_rate", "3.00", timestamp); + inOrder.verify(graphite).send("prefix.meter.m15_rate", "4.00", timestamp); + inOrder.verify(graphite).send("prefix.meter.mean_rate", "5.00", timestamp); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); @@ -265,21 +265,21 @@ public void reportsTimers() throws Exception { final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); - inOrder.verify(graphite).write("prefix.timer.max", "100.00", timestamp); - inOrder.verify(graphite).write("prefix.timer.mean", "200.00", timestamp); - inOrder.verify(graphite).write("prefix.timer.min", "300.00", timestamp); - inOrder.verify(graphite).write("prefix.timer.stddev", "400.00", timestamp); - inOrder.verify(graphite).write("prefix.timer.p50", "500.00", timestamp); - inOrder.verify(graphite).write("prefix.timer.p75", "600.00", timestamp); - inOrder.verify(graphite).write("prefix.timer.p95", "700.00", timestamp); - inOrder.verify(graphite).write("prefix.timer.p98", "800.00", timestamp); - inOrder.verify(graphite).write("prefix.timer.p99", "900.00", timestamp); - inOrder.verify(graphite).write("prefix.timer.p999", "1000.00", timestamp); - inOrder.verify(graphite).write("prefix.timer.count", "1", timestamp); - inOrder.verify(graphite).write("prefix.timer.m1_rate", "3.00", timestamp); - inOrder.verify(graphite).write("prefix.timer.m5_rate", "4.00", timestamp); - inOrder.verify(graphite).write("prefix.timer.m15_rate", "5.00", timestamp); - inOrder.verify(graphite).write("prefix.timer.mean_rate", "2.00", timestamp); + inOrder.verify(graphite).send("prefix.timer.max", "100.00", timestamp); + inOrder.verify(graphite).send("prefix.timer.mean", "200.00", timestamp); + inOrder.verify(graphite).send("prefix.timer.min", "300.00", timestamp); + inOrder.verify(graphite).send("prefix.timer.stddev", "400.00", timestamp); + inOrder.verify(graphite).send("prefix.timer.p50", "500.00", timestamp); + inOrder.verify(graphite).send("prefix.timer.p75", "600.00", timestamp); + inOrder.verify(graphite).send("prefix.timer.p95", "700.00", timestamp); + inOrder.verify(graphite).send("prefix.timer.p98", "800.00", timestamp); + inOrder.verify(graphite).send("prefix.timer.p99", "900.00", timestamp); + inOrder.verify(graphite).send("prefix.timer.p999", "1000.00", timestamp); + inOrder.verify(graphite).send("prefix.timer.count", "1", timestamp); + inOrder.verify(graphite).send("prefix.timer.m1_rate", "3.00", timestamp); + inOrder.verify(graphite).send("prefix.timer.m5_rate", "4.00", timestamp); + inOrder.verify(graphite).send("prefix.timer.m15_rate", "5.00", timestamp); + inOrder.verify(graphite).send("prefix.timer.mean_rate", "2.00", timestamp); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); diff --git a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteTest.java b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteTest.java index 221dd5b8eb..3be1dbf406 100644 --- a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteTest.java +++ b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteTest.java @@ -40,6 +40,12 @@ public void connectsToGraphite() throws Exception { verify(socketFactory).createSocket(address.getAddress(), address.getPort()); } + @Test + public void measuresFailures() throws Exception { + assertThat(graphite.getFailures()) + .isZero(); + } + @Test public void disconnectsFromGraphite() throws Exception { graphite.connect(); @@ -63,7 +69,7 @@ public void doesNotAllowDoubleConnections() throws Exception { @Test public void writesValuesToGraphite() throws Exception { graphite.connect(); - graphite.write("name", "value", 100); + graphite.send("name", "value", 100); assertThat(output.toString()) .isEqualTo("name value 100\n"); @@ -72,7 +78,7 @@ public void writesValuesToGraphite() throws Exception { @Test public void sanitizesNames() throws Exception { graphite.connect(); - graphite.write("name woo", "value", 100); + graphite.send("name woo", "value", 100); assertThat(output.toString()) .isEqualTo("name-woo value 100\n"); @@ -81,7 +87,7 @@ public void sanitizesNames() throws Exception { @Test public void sanitizesValues() throws Exception { graphite.connect(); - graphite.write("name", "value woo", 100); + graphite.send("name", "value woo", 100); assertThat(output.toString()) .isEqualTo("name value-woo 100\n"); From 4899a451bf39a3eaab509f4d23a39b6f16e88f61 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 20:37:49 -0400 Subject: [PATCH 0103/2558] Added docs for HealthCheckRegistry, plus a few tests. --- .../metrics/health/HealthCheckRegistry.java | 9 ++++++- .../health/tests/HealthCheckRegistryTest.java | 26 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java b/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java index 75cae7c84f..b15f219757 100644 --- a/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java +++ b/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java @@ -51,7 +51,14 @@ public SortedSet getNames() { return Collections.unmodifiableSortedSet(new TreeSet(healthChecks.keySet())); } - public HealthCheck.Result runHealthCheck(String name) { + /** + * Runs the health check with the given name. + * + * @param name the health check's name + * @return the result of the health check + * @throws NoSuchElementException if there is no health check with the given name + */ + public HealthCheck.Result runHealthCheck(String name) throws NoSuchElementException { final HealthCheck healthCheck = healthChecks.get(name); if (healthCheck == null) { throw new NoSuchElementException("No health check named " + name + " exists"); diff --git a/metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckRegistryTest.java b/metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckRegistryTest.java index 4af55f6c31..9610527ef7 100644 --- a/metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckRegistryTest.java +++ b/metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckRegistryTest.java @@ -6,12 +6,14 @@ import org.junit.Test; import java.util.Map; +import java.util.NoSuchElementException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.api.Assertions.entry; +import static org.fest.assertions.api.Assertions.failBecauseExceptionWasNotThrown; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -72,4 +74,28 @@ public void removesRegisteredHealthChecks() throws Exception { assertThat(results) .containsKey("hc2"); } + + @Test + public void hasASetOfHealthCheckNames() throws Exception { + assertThat(registry.getNames()) + .containsOnly("hc1", "hc2"); + } + + @Test + public void runsHealthChecksByName() throws Exception { + assertThat(registry.runHealthCheck("hc1")) + .isEqualTo(r1); + } + + @Test + public void doesNotRunNonexistentHealthChecks() throws Exception { + try { + registry.runHealthCheck("what"); + failBecauseExceptionWasNotThrown(NoSuchElementException.class); + } catch (NoSuchElementException e) { + assertThat(e.getMessage()) + .isEqualTo("No health check named what exists"); + } + + } } From 477a5c63c3cd75dbbe48450ca98ddaa4e84f5e4d Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 20:44:48 -0400 Subject: [PATCH 0104/2558] Added name scoping to InstrumentedHttpClient and friends. --- .../InstrumentedClientConnManager.java | 13 ++++++----- .../httpclient/InstrumentedHttpClient.java | 10 +++++--- .../InstrumentedRequestDirector.java | 23 ++++++++++--------- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedClientConnManager.java b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedClientConnManager.java index d530e0c9f5..f289bb2c6d 100644 --- a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedClientConnManager.java +++ b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedClientConnManager.java @@ -30,16 +30,17 @@ public InstrumentedClientConnManager(MetricRegistry metricsRegistry, SchemeRegistry registry, long connTTL, TimeUnit connTTLTimeUnit) { - this(metricsRegistry, registry, connTTL, connTTLTimeUnit, new SystemDefaultDnsResolver()); + this(metricsRegistry, registry, connTTL, connTTLTimeUnit, new SystemDefaultDnsResolver(), null); } public InstrumentedClientConnManager(MetricRegistry metricsRegistry, SchemeRegistry schemeRegistry, long connTTL, TimeUnit connTTLTimeUnit, - DnsResolver dnsResolver) { + DnsResolver dnsResolver, + String name) { super(schemeRegistry, connTTL, connTTLTimeUnit, dnsResolver); - metricsRegistry.register(name(ClientConnectionManager.class, "available-connections"), + metricsRegistry.register(name(ClientConnectionManager.class, name, "available-connections"), new Gauge() { @Override public Integer getValue() { @@ -47,7 +48,7 @@ public Integer getValue() { return getTotalStats().getAvailable(); } }); - metricsRegistry.register(name(ClientConnectionManager.class, "leased-connections"), + metricsRegistry.register(name(ClientConnectionManager.class, name, "leased-connections"), new Gauge() { @Override public Integer getValue() { @@ -55,7 +56,7 @@ public Integer getValue() { return getTotalStats().getLeased(); } }); - metricsRegistry.register(name(ClientConnectionManager.class, "max-connections"), + metricsRegistry.register(name(ClientConnectionManager.class, name, "max-connections"), new Gauge() { @Override public Integer getValue() { @@ -63,7 +64,7 @@ public Integer getValue() { return getTotalStats().getMax(); } }); - metricsRegistry.register(name(ClientConnectionManager.class, "pending-connections"), + metricsRegistry.register(name(ClientConnectionManager.class, name, "pending-connections"), new Gauge() { @Override public Integer getValue() { diff --git a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedHttpClient.java b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedHttpClient.java index ceeb1f7136..e69b3cfa3e 100644 --- a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedHttpClient.java +++ b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedHttpClient.java @@ -17,21 +17,24 @@ public class InstrumentedHttpClient extends DefaultHttpClient { private final Log log = LogFactory.getLog(getClass()); private final MetricRegistry registry; + private final String name; public InstrumentedHttpClient(MetricRegistry registry, InstrumentedClientConnManager manager, - HttpParams params) { + HttpParams params, + String name) { super(manager, params); this.registry = registry; + this.name = name; } public InstrumentedHttpClient(MetricRegistry registry, HttpParams params) { - this(registry, new InstrumentedClientConnManager(registry), params); + this(registry, new InstrumentedClientConnManager(registry), params, null); } public InstrumentedHttpClient(MetricRegistry registry) { - this(registry, new InstrumentedClientConnManager(registry), null); + this(registry, new InstrumentedClientConnManager(registry), null, null); } @Override @@ -49,6 +52,7 @@ protected RequestDirector createClientRequestDirector(HttpRequestExecutor reques HttpParams params) { return new InstrumentedRequestDirector( registry, + name, log, requestExec, conman, diff --git a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedRequestDirector.java b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedRequestDirector.java index 686f437f12..ee8fb9931c 100644 --- a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedRequestDirector.java +++ b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedRequestDirector.java @@ -36,6 +36,7 @@ class InstrumentedRequestDirector extends DefaultRequestDirector { private final Timer otherTimer; InstrumentedRequestDirector(MetricRegistry registry, + String name, Log log, HttpRequestExecutor requestExec, ClientConnectionManager conman, @@ -62,17 +63,17 @@ class InstrumentedRequestDirector extends DefaultRequestDirector { proxyAuthStrategy, userTokenHandler, params); - getTimer = registry.timer(name(HttpClient.class, "get-requests")); - postTimer = registry.timer(name(HttpClient.class, "post-requests")); - headTimer = registry.timer(name(HttpClient.class, "head-requests")); - putTimer = registry.timer(name(HttpClient.class, "put-requests")); - deleteTimer = registry.timer(name(HttpClient.class, "delete-requests")); - optionsTimer = registry.timer(name(HttpClient.class, "options-requests")); - traceTimer = registry.timer(name(HttpClient.class, "trace-requests")); - connectTimer = registry.timer(name(HttpClient.class, "connect-requests")); - moveTimer = registry.timer(name(HttpClient.class, "move-requests")); - patchTimer = registry.timer(name(HttpClient.class, "patch-requests")); - otherTimer = registry.timer(name(HttpClient.class, "other-requests")); + getTimer = registry.timer(name(HttpClient.class, name, "get-requests")); + postTimer = registry.timer(name(HttpClient.class, name, "post-requests")); + headTimer = registry.timer(name(HttpClient.class, name, "head-requests")); + putTimer = registry.timer(name(HttpClient.class, name, "put-requests")); + deleteTimer = registry.timer(name(HttpClient.class, name, "delete-requests")); + optionsTimer = registry.timer(name(HttpClient.class, name, "options-requests")); + traceTimer = registry.timer(name(HttpClient.class, name, "trace-requests")); + connectTimer = registry.timer(name(HttpClient.class, name, "connect-requests")); + moveTimer = registry.timer(name(HttpClient.class, name, "move-requests")); + patchTimer = registry.timer(name(HttpClient.class, name, "patch-requests")); + otherTimer = registry.timer(name(HttpClient.class, name, "other-requests")); } @Override From 9a0fc889f1f32406553a61b2e8ba02892f1421d2 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 20:45:38 -0400 Subject: [PATCH 0105/2558] Drop todos from docs. Too weird. --- docs/source/about/index.rst | 1 - docs/source/about/todos.rst | 7 ------- 2 files changed, 8 deletions(-) delete mode 100644 docs/source/about/todos.rst diff --git a/docs/source/about/index.rst b/docs/source/about/index.rst index 4577edacd0..bc4d6dceba 100644 --- a/docs/source/about/index.rst +++ b/docs/source/about/index.rst @@ -11,4 +11,3 @@ About Metrics contributors release-notes - todos diff --git a/docs/source/about/todos.rst b/docs/source/about/todos.rst deleted file mode 100644 index f38ac42722..0000000000 --- a/docs/source/about/todos.rst +++ /dev/null @@ -1,7 +0,0 @@ -.. _about-todos: - -################### -Documentation TODOs -################### - -.. todolist:: From 786ab757cb7e0f04e7be70a3d76fe7c43bef0c8e Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 20:57:41 -0400 Subject: [PATCH 0106/2558] Move JmxAttributeGauge to metrics-core. --- .../yammer/metrics}/JmxAttributeGauge.java | 19 +++++++++++++++---- .../yammer/metrics}/JvmAttributeGaugeSet.java | 6 +----- .../metrics}/tests/JmxAttributeGaugeTest.java | 4 ++-- .../tests/JvmAttributeGaugeSetTest.java | 4 ++-- .../metrics/jvm/BufferPoolMetricSet.java | 1 + 5 files changed, 21 insertions(+), 13 deletions(-) rename {metrics-jvm/src/main/java/com/yammer/metrics/jvm => metrics-core/src/main/java/com/yammer/metrics}/JmxAttributeGauge.java (64%) rename {metrics-jvm/src/main/java/com/yammer/metrics/jvm => metrics-core/src/main/java/com/yammer/metrics}/JvmAttributeGaugeSet.java (92%) rename {metrics-jvm/src/test/java/com/yammer/metrics/jvm => metrics-core/src/test/java/com/yammer/metrics}/tests/JmxAttributeGaugeTest.java (92%) rename {metrics-jvm/src/test/java/com/yammer/metrics/jvm => metrics-core/src/test/java/com/yammer/metrics}/tests/JvmAttributeGaugeSetTest.java (95%) diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/JmxAttributeGauge.java b/metrics-core/src/main/java/com/yammer/metrics/JmxAttributeGauge.java similarity index 64% rename from metrics-jvm/src/main/java/com/yammer/metrics/jvm/JmxAttributeGauge.java rename to metrics-core/src/main/java/com/yammer/metrics/JmxAttributeGauge.java index 9052261425..70830566e5 100644 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/JmxAttributeGauge.java +++ b/metrics-core/src/main/java/com/yammer/metrics/JmxAttributeGauge.java @@ -1,8 +1,9 @@ -package com.yammer.metrics.jvm; +package com.yammer.metrics; -import com.yammer.metrics.Gauge; - -import javax.management.*; +import javax.management.JMException; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import java.lang.management.ManagementFactory; /** * A {@link Gauge} implementation which queries a {@link MBeanServer} for an attribute of an object. @@ -12,6 +13,16 @@ public class JmxAttributeGauge implements Gauge { private final ObjectName objectName; private final String attributeName; + /** + * Creates a new JmxAttributeGauge. + * + * @param objectName the name of the object + * @param attributeName the name of the object's attribute + */ + public JmxAttributeGauge(ObjectName objectName, String attributeName) { + this(ManagementFactory.getPlatformMBeanServer(), objectName, attributeName); + } + /** * Creates a new JmxAttributeGauge. * diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/JvmAttributeGaugeSet.java b/metrics-core/src/main/java/com/yammer/metrics/JvmAttributeGaugeSet.java similarity index 92% rename from metrics-jvm/src/main/java/com/yammer/metrics/jvm/JvmAttributeGaugeSet.java rename to metrics-core/src/main/java/com/yammer/metrics/JvmAttributeGaugeSet.java index 0c120ed71c..035711add6 100644 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/JvmAttributeGaugeSet.java +++ b/metrics-core/src/main/java/com/yammer/metrics/JvmAttributeGaugeSet.java @@ -1,8 +1,4 @@ -package com.yammer.metrics.jvm; - -import com.yammer.metrics.Gauge; -import com.yammer.metrics.Metric; -import com.yammer.metrics.MetricSet; +package com.yammer.metrics; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/JmxAttributeGaugeTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/JmxAttributeGaugeTest.java similarity index 92% rename from metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/JmxAttributeGaugeTest.java rename to metrics-core/src/test/java/com/yammer/metrics/tests/JmxAttributeGaugeTest.java index e91b56b904..b5918b248e 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/JmxAttributeGaugeTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/JmxAttributeGaugeTest.java @@ -1,6 +1,6 @@ -package com.yammer.metrics.jvm.tests; +package com.yammer.metrics.tests; -import com.yammer.metrics.jvm.JmxAttributeGauge; +import com.yammer.metrics.JmxAttributeGauge; import org.junit.Test; import javax.management.AttributeNotFoundException; diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/JvmAttributeGaugeSetTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/JvmAttributeGaugeSetTest.java similarity index 95% rename from metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/JvmAttributeGaugeSetTest.java rename to metrics-core/src/test/java/com/yammer/metrics/tests/JvmAttributeGaugeSetTest.java index 45f9d8e92c..527c4eaaad 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/JvmAttributeGaugeSetTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/JvmAttributeGaugeSetTest.java @@ -1,7 +1,7 @@ -package com.yammer.metrics.jvm.tests; +package com.yammer.metrics.tests; import com.yammer.metrics.Gauge; -import com.yammer.metrics.jvm.JvmAttributeGaugeSet; +import com.yammer.metrics.JvmAttributeGaugeSet; import org.junit.Before; import org.junit.Test; diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolMetricSet.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolMetricSet.java index 1c7ac6d76e..b1d1b37c44 100644 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolMetricSet.java +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolMetricSet.java @@ -1,5 +1,6 @@ package com.yammer.metrics.jvm; +import com.yammer.metrics.JmxAttributeGauge; import com.yammer.metrics.Metric; import com.yammer.metrics.MetricSet; From 98240580717a3654db92f806d88e7e6bddb4196b Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 28 Mar 2013 21:55:18 -0400 Subject: [PATCH 0107/2558] Updated the docs for Metrics Core. --- docs/source/getting-started.rst | 2 +- docs/source/manual/core.rst | 271 +++++++++++++--------------- docs/source/manual/healthchecks.rst | 62 +++++++ docs/source/manual/index.rst | 4 +- docs/source/manual/scala.rst | 19 -- docs/source/manual/servlet.rst | 83 ++------- docs/source/manual/servlets.rst | 67 +++++++ docs/source/manual/webapps.rst | 20 -- 8 files changed, 273 insertions(+), 255 deletions(-) create mode 100644 docs/source/manual/healthchecks.rst delete mode 100644 docs/source/manual/scala.rst create mode 100644 docs/source/manual/servlets.rst delete mode 100644 docs/source/manual/webapps.rst diff --git a/docs/source/getting-started.rst b/docs/source/getting-started.rst index b8d45ccfd1..e30550b614 100644 --- a/docs/source/getting-started.rst +++ b/docs/source/getting-started.rst @@ -293,6 +293,6 @@ In addition to JMX and HTTP, Metrics also has reporters for the following output * ``STDOUT``, using :ref:`ConsoleReporter ` from ``metrics-core`` * ``CSV`` files, using :ref:`CsvReporter ` from ``metrics-core`` -* Slf4j loggers, using :ref:`LoggerReporter ` from ``metrics-core`` +* Slf4j loggers, using :ref:`Slf4jReporter ` from ``metrics-core`` * Ganglia, using :ref:`GangliaReporter ` from ``metrics-ganglia`` * Graphite, using :ref:`GraphiteReporter ` from ``metrics-graphite`` diff --git a/docs/source/manual/core.rst b/docs/source/manual/core.rst index da9b67b11a..165552723b 100644 --- a/docs/source/manual/core.rst +++ b/docs/source/manual/core.rst @@ -8,44 +8,42 @@ Metrics Core The central library for Metrics is ``metrics-core``, which provides some basic functionality: +* Metric :ref:`registries `. * The five metric types: :ref:`man-core-gauges`, :ref:`man-core-counters`, :ref:`man-core-histograms`, :ref:`man-core-meters`, and :ref:`man-core-timers`. -* :ref:`man-core-healthchecks` * Reporting metrics values via :ref:`JMX `, the - :ref:`console `, and :ref:`CSV ` files. + :ref:`console `, :ref:`CSV ` files, and + :ref:`SLF4J loggers `. -All metrics are created via either the ``Metrics`` class or a ``MetricsRegistry``. If your -application is running alongside other applications in a single JVM instance (e.g., multiple WARs -deployed to an application server), you should use per-application ``MetricsRegistry`` instances. If -your application is the sole occupant of the JVM instance (e.g., a Dropwizard_ application), feel -free to use the ``static`` factory methods on ``Metrics``. +.. _man-core-registries: -.. _Dropwizard: http://dropwizard.codahale.com/ +Metric Registries +================= -For this documentation, we'll assume you're using ``Metrics``, but the interfaces are much the same. +The starting point for Metrics is the ``MetricRegistry`` class, which is a collection of all the +metrics for your application (or a subset of your application). If your application is running +alongside other applications in a single JVM instance (e.g., multiple WARs deployed to an +application server), you should use per-application ``MetricRegistry`` instances with different +names. .. _man-core-names: Metric Names ============ -Each metric has a unique *metric name*, which consists of four pieces of information: +Each metric has a unique *name*, which is a simple dotted name, like ``com.example.Queue.size``. +This flexibility allows you to encode a wide variety of context directly into a metric's name. If +you have two instances of ``com.example.Queue``, you can give them more specific: +``com.example.Queue.requests.size`` vs. ``com.example.Queue.responses.size``, for example. -Group - The top-level grouping of the metric. When a metric belongs to a class, this defaults to the - class's *package name* (e.g., ``com.example.proj.auth``). -Type - The second-level grouping of the metric. When a metric belongs to a class, this defaults to the - class's *name* (e.g., ``SessionStore``). -Name - A short name describing the metric's purpose (e.g., ``session-count``). -Scope - An optional name describing the metric's scope. Useful for when you have multiple instances of a - class. +``MetricRegistry`` has a set of static helper methods for easily creating names: -The factory methods on ``Metrics`` and ``MetricsRegistry`` will accept either class/name, -class/name/scope, or ``MetricName`` instances with arbitrary inputs. +.. code-block:: java + + MetricRegistry.name(Queue.class, "requests", "size") + MetricRegistry.name(Queue.class, "responses", "size") +These methods will also elide any ``null`` values, allowing for easy optional scopes. .. _man-core-gauges: @@ -58,9 +56,9 @@ has a value which is maintained by a third-party library, you can easily expose .. code-block:: java - Metrics.newGauge(SessionStore.class, "cache-evictions", new Gauge() { + registry.register(name(SessionStore.class, "cache-evictions"), new Gauge() { @Override - public Integer value() { + public Integer getValue() { return cache.getEvictionsCount(); } }); @@ -74,14 +72,15 @@ JMX Gauges ---------- Given that many third-party library often expose metrics only via JMX, Metrics provides the -``JmxGauge`` class, which takes the object name of a JMX MBean and the name of an attribute and -produces a gauge implementation which returns the value of that attribute: +``JmxAttributeGauge`` class, which takes the object name of a JMX MBean and the name of an attribute +and produces a gauge implementation which returns the value of that attribute: .. code-block:: java - Metrics.newGauge(SessionStore.class, "cache-evictions", - new JmxGauge("net.sf.ehcache:type=Cache,scope=sessions,name=eviction-count", "Value")); + registry.register(name(SessionStore.class, "cache-evictions"), + new JmxAttributeGauge("net.sf.ehcache:type=Cache,scope=sessions,name=eviction-count", "Value")); +.. _man-core-gauges-ratio: Ratio Gauges ------------ @@ -99,22 +98,52 @@ A ratio gauge is a simple way to create a gauge which is the ratio between two n this.calls = calls; } - public double getNumerator() { - return hits.oneMinuteRate(); - } - - public double getDenominator() { - return calls.oneMinuteRate(); + @Override + public Ratio getValue() { + return Ratio.of(hits.oneMinuteRate(), + calls.oneMinuteRate()); } } -This custom gauge returns the ratio of cache hits to misses using a meter and a timer. +This gauge returns the ratio of cache hits to misses using a meter and a timer. + +.. _man-core-gauges-cached: + +Cached Gauges +------------- + +A cached gauge allows for a more efficient reporting of values which are expensive to calculate: -Percent Gauges --------------- +.. code-block:: java + + registry.register(name(Cache.class, cache.getName(), "size"), + new CachedGauge(10, TimeUnit.MINUTES) { + @Override + protected Long loadValue() { + // assume this does something which takes a long time + return cache.getSize(); + } + }); + +.. _man-core-gauges-derivative: + +Derivative Gauges +----------------- + +A derivative gauge allows you to derive values from other gauges' values: -A percent gauge is a ratio gauge where the result is normalized to a value between 0 and 100. It has -the same interface as a ratio gauge. +.. code-block:: java + + public class CacheSizeGauge extends DerivativeGauge { + public CacheSizeGauge(Gauge statsGauge) { + super(statsGauge); + } + + @Override + protected Long transform(CacheStats stats) { + return stats.getSize(); + } + } .. _man-core-counters: @@ -125,7 +154,7 @@ A counter is a simple incrementing and decrementing 64-bit integer: .. code-block:: java - final Counter evictions = Metrics.newCounter(SessionStore.class, "cache-evictions"); + final Counter evictions = registry.counter(name(SessionStore.class, "cache-evictions")); evictions.inc(); evictions.inc(3); evictions.dec(); @@ -143,7 +172,7 @@ returned by a search: .. code-block:: java - final Histogram resultCounts = Metrics.newHistogram(ProductDAO.class, "result-counts"); + final Histogram resultCounts = registry.histogram(name(ProductDAO.class, "result-counts"); resultCounts.update(results.size()); ``Histogram`` metrics allow you to measure not just easy things like the min, mean, max, and @@ -161,37 +190,44 @@ sample which is statistically representative of the data stream as a whole, we c easily calculate quantiles which are valid approximations of the actual quantiles. This technique is called **reservoir sampling**. -Metrics provides two types of histograms: :ref:`uniform ` -and :ref:`biased `. +Metrics provides a number of different ``Sample`` implementations, each of which is useful. .. _man-core-histograms-uniform: -Uniform Histograms ------------------- +Uniform Samples +--------------- -A uniform histogram produces quantiles which are valid for the entirely of the histogram's lifetime. -It will return a median value, for example, which is the median of all the values the histogram has -ever been updated with. It does this by using an algorithm called `Vitter's R`__), which randomly -selects values for the sample with linearly-decreasing probability. +A histogram with a uniform sample produces quantiles which are valid for the entirely of the +histogram's lifetime. It will return a median value, for example, which is the median of all the +values the histogram has ever been updated with. It does this by using an algorithm called +`Vitter's R`__), which randomly selects values for the sample with linearly-decreasing probability. .. __: http://www.cs.umd.edu/~samir/498/vitter.pdf Use a uniform histogram when you're interested in long-term measurements. Don't use one where you'd want to know if the distribution of the underlying data stream has changed recently. -.. _man-core-histograms-biased: +.. _man-core-histograms-exponential: -Biased Histograms ------------------ +Exponentially Decaying Samples +------------------------------ -A biased histogram produces quantiles which are representative of (roughly) the last five minutes of -data. It does so by using a `forward-decaying priority sample`__ with an exponential weighting -towards newer data. Unlike the uniform histogram, a biased histogram represents **recent data**, -allowing you to know very quickly if the distribution of the data has changed. -:ref:`man-core-timers` use biased histograms. +A histogram with an exponentially decaying sample produces quantiles which are representative of +(roughly) the last five minutes of data. It does so by using a `forward-decaying priority sample`__ +with an exponential weighting towards newer data. Unlike the uniform histogram, a biased histogram +represents **recent data**, allowing you to know very quickly if the distribution of the data has +changed. :ref:`man-core-timers` use histograms with exponentially decaying samples. .. __: http://www.research.att.com/people/Cormode_Graham/library/publications/CormodeShkapenyukSrivastavaXu09.pdf +.. _man-core-histograms-moving: + +Moving Window Samples +--------------------- + +A histogram with a moving window sample produces quantiles which are representative of the past +``N`` measurements. + .. _man-core-meters: Meters @@ -201,17 +237,10 @@ A meter measures the *rate* at which a set of events occur: .. code-block:: java - final Meter getRequests = Metrics.newMeter(WebProxy.class, "get-requests", "requests", TimeUnit.SECONDS); + final Meter getRequests = registry.meter(name(WebProxy.class, "get-requests", "requests")); getRequests.mark(); getRequests.mark(requests.size()); -A meter requires two additional pieces of information besides the name: the **event type** and the -**rate unit**. The event type simply describes the type of events which the meter is measuring. In -the above case, the meter is measuring proxied requests, and so its event type is ``"requests"``. -The rate unit is the unit of time denominating the rate. In the above case, the meter is measuring -the number of requests in each second, and so its rate unit is ``SECONDS``. When combined, the meter -is measuring requests per second. - Meters measure the rate of the events in a few different ways. The *mean* rate is the average rate of events. It's generally useful for trivia, but as it represents the total rate for your application's entire lifetime (e.g., the total number of requests handled, divided by the number of @@ -233,89 +262,22 @@ a :ref:`meter ` of the rate of its occurrence. .. code-block:: java - final Timer timer = Metrics.newTimer(WebProxy.class, "get-requests", TimeUnit.MILLISECONDS, TimeUnit.SECONDS); + final Timer timer = registry.timer(name(WebProxy.class, "get-requests")); - final TimerContext context = timer.time(); + final Timer.Context context = timer.time(); try { // handle request } finally { context.stop(); } -A timer requires two additional pieces of information besides the name: the **duration unit** and -the **rate unit**. The duration unit is the unit of time in which the durations of events will be -measured. In the above example, the duration unit is ``MILLISECONDS``, meaning the timed event's -duration will be measured in milliseconds. The rate unit in the above example is ``SECONDS``, -meaning the rate of the timed event is measured in calls/sec. - .. note:: - Regardless of the display duration unit of a timer, elapsed time for its events is measured - internally in nanoseconds, using Java's high-precision ``System.nanoTime()`` method. + Elapsed times for it events are measured internally in nanoseconds, using Java's high-precision + ``System.nanoTime()`` method. Its precision and accuracy vary depending on operating system and + hardware. -.. _man-core-healthchecks: -Health Checks -============= - -Metrics also provides you with a consistent, unified way of performing application health checks. A -health check is basically a small self-test which your application performs to verify that a -specific component or responsibility is performing correctly. - -To create a health check, extend the ``HealthCheck`` class: - -.. code-block:: java - - public class DatabaseHealthCheck extends HealthCheck { - private final Database database; - - public DatabaseHealthCheck(Database database) { - super("database"); - this.database = database; - } - - @Override - protected Result check() throws Exception { - if (database.ping()) { - return Result.healthy(); - } - return Result.unhealthy("Can't ping database"); - } - } - -In this example, we've created a health check for a ``Database`` class on which our application -depends. Our fictitious ``Database`` class has a ``#ping()`` method, which executes a safe test -query (e.g., ``SELECT 1``). ``#ping()`` returns ``true`` if the query returns the expected result, -returns ``false`` if it returns something else, and throws an exception if things have gone -seriously wrong. - -Our ``DatabaseHealthCheck``, then, takes a ``Database`` instance and in its ``#check()`` method, -attempts to ping the database. If it can, it returns a **healthy** result. If it can't, it returns -an **unhealthy** result. - -.. note:: - - Exceptions thrown inside a health check's ``#check()`` method are automatically caught and - turned into unhealthy results with the full stack trace. - -To register a health check, either use the ``HealthChecks`` singleton or a ``HealthCheckRegistry`` -instance: - -.. code-block:: java - - HealthChecks.register(new DatabaseHealthCheck(database)); - -You can also run the set of registered health checks: - -.. code-block:: java - - for (Entry entry : HealthChecks.run().entrySet()) { - if (entry.getValue().isHealthy()) { - System.out.println(entry.getKey() + ": PASS"); - } else { - System.out.println(entry.getKey() + ": FAIL"); - } - } .. _man-core-reporters: @@ -323,16 +285,16 @@ Reporters ========= Reporters are the way that your application exports all the measurements being made by its metrics. -``metrics-core`` comes with three ways of exporting your metrics: -:ref:`JMX `, :ref:`console `, and -:ref:`CSV `. +``metrics-core`` comes with four ways of exporting your metrics: +:ref:`JMX `, :ref:`console `, +:ref:`SLF4J `, and :ref:`CSV `. .. _man-core-reporters-jmx: JMX --- -By default, Metrics always registers your metrics as JMX MBeans. To explore this you can use +With ``JmxReporter``, you can expose your metrics as JMX MBeans. To explore this you can use VisualVM__ (which ships with most JDKs as ``jvisualvm``) with the VisualVM-MBeans plugins installed or JConsole (which ships with most JDKs as ``jconsole``): @@ -346,9 +308,11 @@ or JConsole (which ships with most JDKs as ``jconsole``): If you double-click any of the metric properties, VisualVM will start graphing the data for that property. Sweet, eh? -Reporting via JMX is always enabled, but we don't recommend that you try to gather metrics from your -production environment. JMX's RPC API is fragile and bonkers. For development purposes and browsing, -though, it can be very useful. +.. warning:: + + We don't recommend that you try to gather metrics from your production environment. JMX's RPC + API is fragile and bonkers. For development purposes and browsing, though, it can be very + useful. .. _man-core-reporters-console: @@ -360,7 +324,7 @@ registered metrics to the console: .. code-block:: java - ConsoleReporter.enable(1, TimeUnit.SECONDS); + // TODO: waiting on builders .. _man-core-reporters-csv: @@ -372,11 +336,22 @@ of ``.csv`` files in a given directory: .. code-block:: java - CsvReporter.enable(new File("work/measurements"), 1, TimeUnit.SECONDS); + // TODO: waiting on builders For each metric registered, a ``.csv`` file will be created, and every second its state will be written to it as a new row. +.. _man-core-reporters-slf4j: + +SLF4J +----- + +It's also possible to log metrics to an SLF4J logger: + +.. code-block:: java + + // TODO: waiting on builders + .. _man-core-reporters-other: Other Reporters @@ -384,7 +359,7 @@ Other Reporters Metrics has other reporter implementations, too: -* :ref:`MetricsServlet ` is a servlet which not only exposes your metrics as a JSON +* :ref:`MetricsServlet ` is a servlet which not only exposes your metrics as a JSON object, but it also runs your health checks, performs thread dumps, and exposes valuable JVM-level and OS-level information. * :ref:`GangliaReporter ` allows you to constantly stream metrics data to your diff --git a/docs/source/manual/healthchecks.rst b/docs/source/manual/healthchecks.rst new file mode 100644 index 0000000000..99974bdb5f --- /dev/null +++ b/docs/source/manual/healthchecks.rst @@ -0,0 +1,62 @@ +.. _man-healthchecks: + +############# +Health Checks +############# + +Metrics also provides you with a consistent, unified way of performing application health checks. A +health check is basically a small self-test which your application performs to verify that a +specific component or responsibility is performing correctly. + +To create a health check, extend the ``HealthCheck`` class: + +.. code-block:: java + + public class DatabaseHealthCheck extends HealthCheck { + private final Database database; + + public DatabaseHealthCheck(Database database) { + this.database = database; + } + + @Override + protected Result check() throws Exception { + if (database.ping()) { + return Result.healthy(); + } + return Result.unhealthy("Can't ping database"); + } + } + +In this example, we've created a health check for a ``Database`` class on which our application +depends. Our fictitious ``Database`` class has a ``#ping()`` method, which executes a safe test +query (e.g., ``SELECT 1``). ``#ping()`` returns ``true`` if the query returns the expected result, +returns ``false`` if it returns something else, and throws an exception if things have gone +seriously wrong. + +Our ``DatabaseHealthCheck``, then, takes a ``Database`` instance and in its ``#check()`` method, +attempts to ping the database. If it can, it returns a **healthy** result. If it can't, it returns +an **unhealthy** result. + +.. note:: + + Exceptions thrown inside a health check's ``#check()`` method are automatically caught and + turned into unhealthy results with the full stack trace. + +To register a health check, either use a ``HealthCheckRegistry`` instance: + +.. code-block:: java + + registry.register("database", new DatabaseHealthCheck(database)); + +You can also run the set of registered health checks: + +.. code-block:: java + + for (Entry entry : registry.runHealthChecks().entrySet()) { + if (entry.getValue().isHealthy()) { + System.out.println(entry.getKey() + ": OK"); + } else { + System.out.println(entry.getKey() + ": FAIL"); + } + } diff --git a/docs/source/manual/index.rst b/docs/source/manual/index.rst index 51e94d29d0..e4dfbcdb4a 100644 --- a/docs/source/manual/index.rst +++ b/docs/source/manual/index.rst @@ -12,6 +12,7 @@ User Manual :maxdepth: 1 core + healthchecks ehcache ganglia graphite @@ -21,7 +22,6 @@ User Manual jetty log4j logback - scala + servlets servlet - webapps diff --git a/docs/source/manual/scala.rst b/docs/source/manual/scala.rst deleted file mode 100644 index 5a4b690f94..0000000000 --- a/docs/source/manual/scala.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _manual-scala: - -############# -Scala Support -############# - -The ``metrics-scala_2.9.1`` module provides the ``Instrumented`` trait for Scala 2.9.1 applications: - -.. code-block:: scala - - class Example(db: Database) extends Instrumented { - private val loading = metrics.timer("loading") - - def loadStuff(): Seq[Row] = loading.time { - db.fetchRows() - } - } - -It also provides Scala-specific wrappers for each metric type. diff --git a/docs/source/manual/servlet.rst b/docs/source/manual/servlet.rst index 8c188467a5..7f0e45197f 100644 --- a/docs/source/manual/servlet.rst +++ b/docs/source/manual/servlet.rst @@ -1,67 +1,20 @@ .. _manual-servlet: -################ -Metrics Servlets -################ - -The ``metrics-servlet`` module provides a handful of useful servlets: - -.. _man-servlet-healthcheck: - -HealthCheckServlet -================== - -``HealthCheckServlet`` responds to ``GET`` requests by running all the [health checks](#health-checks) -and returning ``501 Not Implemented`` if no health checks are registered, ``200 OK`` if all pass, or -``500 Internal Service Error`` if one or more fail. The results are returned as a human-readable -``text/plain`` entity. - -If the servlet context has an attributed named -``com.yammer.metrics.servlet.HealthCheckServlet.registry`` which is a ``HealthCheckRegistry``, -``HealthCheckServlet`` will use that instead of the default ``HealthCheckRegistry``. - -.. _man-servlet-threaddump: - -ThreadDumpServlet -================= - -``ThreadDumpServlet`` responds to ``GET`` requests with a ``text/plain`` representation of all the live -threads in the JVM, their states, their stack traces, and the state of any locks they may be -waiting for. - -.. _man-servlet-metrics: - -MetricsServlet -============== - -``MetricsServlet`` exposes the state of the metrics in a particular registry as a JSON object. - -If the servlet context has an attributed named -``com.yammer.metrics.servlet.MetricsServlet.registry`` which is a ``MetricsRegistry``, -``MetricsServlet`` will use that instead of the default ``MetricsRegistry``. - -``MetricsServlet`` also takes an initialization parameter, ``show-jvm-metrics``, which if ``"false"`` will -disable the outputting of JVM-level information in the JSON object. - -.. _man-servlet-ping: - -PingServlet -=========== - -``PingServlet`` responds to ``GET`` requests with a ``text/plain``/``200 OK`` response of ``pong``. This is -useful for determining liveness for load balancers, etc. - -.. _man-servlet-admin: - -AdminServlet -============ - -``AdminServlet`` aggregates ``HealthCheckServlet``, ``ThreadDumpServlet``, ``MetricsServlet``, and -``PingServlet`` into a single, easy-to-use servlet which provides a set of URIs: - -* ``/``: an HTML admin menu with links to the following: - - * ``/healthcheck``: ``HealthCheckServlet`` - * ``/metrics``: ``MetricsServlet`` - * ``/ping``: ``PingServlet`` - * ``/threads``: ``ThreadDumpServlet`` +############################## +Instrumenting Web Applications +############################## + +The ``metrics-servlet`` module provides a Servlet filter which has meters for status codes, a counter +for the number of active requests, and a timer for request duration. You can use it in your +``web.xml`` like this: + +.. code-block:: xml + + + webappMetricsFilter + com.yammer.metrics.servlet.DefaultWebappMetricsFilter + + + webappMetricsFilter + /* + diff --git a/docs/source/manual/servlets.rst b/docs/source/manual/servlets.rst new file mode 100644 index 0000000000..99e71a392d --- /dev/null +++ b/docs/source/manual/servlets.rst @@ -0,0 +1,67 @@ +.. _manual-servlets: + +################ +Metrics Servlets +################ + +The ``metrics-servlets`` module provides a handful of useful servlets: + +.. _man-servlet-healthcheck: + +HealthCheckServlet +================== + +``HealthCheckServlet`` responds to ``GET`` requests by running all the [health checks](#health-checks) +and returning ``501 Not Implemented`` if no health checks are registered, ``200 OK`` if all pass, or +``500 Internal Service Error`` if one or more fail. The results are returned as a human-readable +``text/plain`` entity. + +If the servlet context has an attributed named +``com.yammer.metrics.servlet.HealthCheckServlet.registry`` which is a ``HealthCheckRegistry``, +``HealthCheckServlet`` will use that instead of the default ``HealthCheckRegistry``. + +.. _man-servlet-threaddump: + +ThreadDumpServlet +================= + +``ThreadDumpServlet`` responds to ``GET`` requests with a ``text/plain`` representation of all the live +threads in the JVM, their states, their stack traces, and the state of any locks they may be +waiting for. + +.. _man-servlet-metrics: + +MetricsServlet +============== + +``MetricsServlet`` exposes the state of the metrics in a particular registry as a JSON object. + +If the servlet context has an attributed named +``com.yammer.metrics.servlet.MetricsServlet.registry`` which is a ``MetricsRegistry``, +``MetricsServlet`` will use that instead of the default ``MetricsRegistry``. + +``MetricsServlet`` also takes an initialization parameter, ``show-jvm-metrics``, which if ``"false"`` will +disable the outputting of JVM-level information in the JSON object. + +.. _man-servlet-ping: + +PingServlet +=========== + +``PingServlet`` responds to ``GET`` requests with a ``text/plain``/``200 OK`` response of ``pong``. This is +useful for determining liveness for load balancers, etc. + +.. _man-servlet-admin: + +AdminServlet +============ + +``AdminServlet`` aggregates ``HealthCheckServlet``, ``ThreadDumpServlet``, ``MetricsServlet``, and +``PingServlet`` into a single, easy-to-use servlet which provides a set of URIs: + +* ``/``: an HTML admin menu with links to the following: + + * ``/healthcheck``: ``HealthCheckServlet`` + * ``/metrics``: ``MetricsServlet`` + * ``/ping``: ``PingServlet`` + * ``/threads``: ``ThreadDumpServlet`` diff --git a/docs/source/manual/webapps.rst b/docs/source/manual/webapps.rst deleted file mode 100644 index cf677a2419..0000000000 --- a/docs/source/manual/webapps.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. _manual-webapps: - -############################## -Instrumenting Web Applications -############################## - -The ``metrics-web`` module provides a Servlet filter which has meters for status codes, a counter -for the number of active requests, and a timer for request duration. You can use it in your -``web.xml`` like this: - -.. code-block:: xml - - - webappMetricsFilter - com.yammer.metrics.servlet.DefaultWebappMetricsFilter - - - webappMetricsFilter - /* - From 45945b8a248f0ac855eacb68021ba092f2831be4 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Fri, 29 Mar 2013 10:52:04 -0700 Subject: [PATCH 0108/2558] Simplify InstrumentedEhcache a bit. --- .../metrics/ehcache/InstrumentedEhcache.java | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcache.java b/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcache.java index a01e069971..4f78a4f9ec 100644 --- a/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcache.java +++ b/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcache.java @@ -118,7 +118,8 @@ public static Ehcache instrument(MetricRegistry registry, final Ehcache cache) { cache.setSampledStatisticsEnabled(true); cache.setStatisticsAccuracy(Statistics.STATISTICS_ACCURACY_NONE); - registry.register(name(cache.getClass(), cache.getName(), "hits"), + final String prefix = name(cache.getClass(), cache.getName()); + registry.register(name(prefix, "hits"), new Gauge() { @Override public Long getValue() { @@ -126,7 +127,7 @@ public Long getValue() { } }); - registry.register(name(cache.getClass(), cache.getName(), "in-memory-hits"), + registry.register(name(prefix, "in-memory-hits"), new Gauge() { @Override public Long getValue() { @@ -134,7 +135,7 @@ public Long getValue() { } }); - registry.register(name(cache.getClass(), cache.getName(), "off-heap-hits"), + registry.register(name(prefix, "off-heap-hits"), new Gauge() { @Override public Long getValue() { @@ -142,7 +143,7 @@ public Long getValue() { } }); - registry.register(name(cache.getClass(), cache.getName(), "on-disk-hits"), + registry.register(name(prefix, "on-disk-hits"), new Gauge() { @Override public Long getValue() { @@ -150,7 +151,7 @@ public Long getValue() { } }); - registry.register(name(cache.getClass(), cache.getName(), "misses"), + registry.register(name(prefix, "misses"), new Gauge() { @Override public Long getValue() { @@ -158,7 +159,7 @@ public Long getValue() { } }); - registry.register(name(cache.getClass(), cache.getName(), "in-memory-misses"), + registry.register(name(prefix, "in-memory-misses"), new Gauge() { @Override public Long getValue() { @@ -166,7 +167,7 @@ public Long getValue() { } }); - registry.register(name(cache.getClass(), cache.getName(), "off-heap-misses"), + registry.register(name(prefix, "off-heap-misses"), new Gauge() { @Override public Long getValue() { @@ -174,7 +175,7 @@ public Long getValue() { } }); - registry.register(name(cache.getClass(), cache.getName(), "on-disk-misses"), + registry.register(name(prefix, "on-disk-misses"), new Gauge() { @Override public Long getValue() { @@ -182,7 +183,7 @@ public Long getValue() { } }); - registry.register(name(cache.getClass(), cache.getName(), "objects"), + registry.register(name(prefix, "objects"), new Gauge() { @Override public Long getValue() { @@ -190,7 +191,7 @@ public Long getValue() { } }); - registry.register(name(cache.getClass(), cache.getName(), "in-memory-objects"), + registry.register(name(prefix, "in-memory-objects"), new Gauge() { @Override public Long getValue() { @@ -198,7 +199,7 @@ public Long getValue() { } }); - registry.register(name(cache.getClass(), cache.getName(), "off-heap-objects"), + registry.register(name(prefix, "off-heap-objects"), new Gauge() { @Override public Long getValue() { @@ -206,7 +207,7 @@ public Long getValue() { } }); - registry.register(name(cache.getClass(), cache.getName(), "on-disk-objects"), + registry.register(name(prefix, "on-disk-objects"), new Gauge() { @Override public Long getValue() { @@ -214,7 +215,7 @@ public Long getValue() { } }); - registry.register(name(cache.getClass(), cache.getName(), "mean-get-time"), + registry.register(name(prefix, "mean-get-time"), new Gauge() { @Override public Float getValue() { @@ -222,7 +223,7 @@ public Float getValue() { } }); - registry.register(name(cache.getClass(), cache.getName(), "mean-search-time"), + registry.register(name(prefix, "mean-search-time"), new Gauge() { @Override public Long getValue() { @@ -230,7 +231,7 @@ public Long getValue() { } }); - registry.register(name(cache.getClass(), cache.getName(), "eviction-count"), + registry.register(name(prefix, "eviction-count"), new Gauge() { @Override public Long getValue() { @@ -238,7 +239,7 @@ public Long getValue() { } }); - registry.register(name(cache.getClass(), cache.getName(), "searches-per-second"), + registry.register(name(prefix, "searches-per-second"), new Gauge() { @Override public Long getValue() { @@ -246,7 +247,7 @@ public Long getValue() { } }); - registry.register(name(cache.getClass(), cache.getName(), "writer-queue-size"), + registry.register(name(prefix, "writer-queue-size"), new Gauge() { @Override public Long getValue() { @@ -254,7 +255,7 @@ public Long getValue() { } }); - registry.register(name(cache.getClass(), cache.getName(), "accuracy"), + registry.register(name(prefix, "accuracy"), new Gauge() { @Override public String getValue() { From 8acabfe373c964f1a347c75169b38ba71f049450 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Fri, 29 Mar 2013 10:52:10 -0700 Subject: [PATCH 0109/2558] Update Ehcache docs. Add XML config back in when #358 is fixed. --- docs/source/manual/ehcache.rst | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/docs/source/manual/ehcache.rst b/docs/source/manual/ehcache.rst index 845c2dfb4c..43ac0f94ce 100644 --- a/docs/source/manual/ehcache.rst +++ b/docs/source/manual/ehcache.rst @@ -15,7 +15,7 @@ Instrumenting Ehcache final Cache c = new Cache(new CacheConfiguration("test", 100)); MANAGER.addCache(c); - this.cache = InstrumentedEhcache.instrument(c); + this.cache = InstrumentedEhcache.instrument(registry, c); Instrumenting an ``Ehcache`` instance creates gauges for all of the Ehcache-provided statistics: @@ -63,14 +63,5 @@ Instrumenting an ``Ehcache`` instance creates gauges for all of the Ehcache-prov It also adds full timers for the cache's ``get`` and ``put`` methods. -The metrics are all scoped to the cache's name. - -Configuring via XML -=================== - -If you're using an ``ehcache.xml`` to configure your cache, you can instrument it by using -``InstrumentedEhcacheFactory``: - -.. code-block:: xml - - +The metrics are all scoped to the cache's class and name, so a ``Cache`` instance named ``users`` +would have metric names like ``net.sf.ehcache.Cache.users.get``, etc. From 56a4e1f2bb9ec50319f4fb0365c2e5459406a423 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Fri, 29 Mar 2013 10:54:54 -0700 Subject: [PATCH 0110/2558] Updated metrics-jdbi docs. --- docs/source/manual/jdbi.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/source/manual/jdbi.rst b/docs/source/manual/jdbi.rst index 79ce169006..dd4a7daf80 100644 --- a/docs/source/manual/jdbi.rst +++ b/docs/source/manual/jdbi.rst @@ -14,7 +14,10 @@ To use it, just add a ``InstrumentedTimingCollector`` instance to your ``DBI``: .. code-block:: java final DBI dbi = new DBI(dataSource); - dbi.setTimingCollector(new InstrumentedTimingCollector()); + dbi.setTimingCollector(new InstrumentedTimingCollector(registry)); ``InstrumentedTimingCollector`` keeps per-SQL-object timing data, as well as general raw SQL timing -data. +data. The metric names for each query are constructed by an ``StatementNameStrategy`` instance, of +which there are many implementations. By default, ``StatementNameStrategy`` uses +``SmartNameStrategy``, which attempts to effectively handle both queries from bound objects and raw +SQL. From 99699c31721fe5a1a62baf77624ba9fca4aad1c0 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Fri, 29 Mar 2013 10:56:50 -0700 Subject: [PATCH 0111/2558] Tabs. Wat r u doing. Tabs. Stahp. --- docs/source/manual/jersey.rst | 16 ++++---- .../src/test/resources/ehcache.xml | 38 +++++++++---------- .../metrics/jetty8/InstrumentedHandler.java | 18 ++++----- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/docs/source/manual/jersey.rst b/docs/source/manual/jersey.rst index 732e3bd643..54f761a8bb 100644 --- a/docs/source/manual/jersey.rst +++ b/docs/source/manual/jersey.rst @@ -31,11 +31,11 @@ This is done by adding ``com.yammer.metrics.jersey`` as the value for the ``com. .. code-block:: xml - - Test Servlet - com.sun.jersey.spi.container.servlet.ServletContainer - - com.sun.jersey.config.property.packages - your.jersey.resources;com.yammer.metrics.jersey - - \ No newline at end of file + + Test Servlet + com.sun.jersey.spi.container.servlet.ServletContainer + + com.sun.jersey.config.property.packages + your.jersey.resources;com.yammer.metrics.jersey + + diff --git a/metrics-ehcache/src/test/resources/ehcache.xml b/metrics-ehcache/src/test/resources/ehcache.xml index 1adbd8730c..f09d0b7800 100644 --- a/metrics-ehcache/src/test/resources/ehcache.xml +++ b/metrics-ehcache/src/test/resources/ehcache.xml @@ -1,28 +1,28 @@ + xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false" + monitoring="autodetect" dynamicConfig="true"> - - - - - - - - + + + + + + + + - + - + diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java index 9651d241ea..e05ddaf020 100644 --- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java +++ b/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java @@ -18,8 +18,8 @@ import static org.eclipse.jetty.http.HttpMethods.*; /** - * A Jetty {@link Handler} which records various metrics about an underlying - * {@link Handler} instance. + * A Jetty {@link Handler} which records various metrics about an underlying {@link Handler} + * instance. */ public class InstrumentedHandler extends HandlerWrapper { private static final String PATCH = "PATCH"; @@ -43,23 +43,23 @@ public class InstrumentedHandler extends HandlerWrapper { private final ContinuationListener listener; /** - * Create a new instrumented handler using a given metrics registry. The name of the metric - * will be derived from the class of the Handler. + * Create a new instrumented handler using a given metrics registry. The name of the metric will + * be derived from the class of the Handler. * - * @param registry the registry for the metrics + * @param registry the registry for the metrics * @param underlying the handler about which metrics will be collected */ public InstrumentedHandler(MetricRegistry registry, Handler underlying) { - this(registry, underlying, name(underlying.getClass())); + this(registry, underlying, name(underlying.getClass())); } /** * Create a new instrumented handler using a given metrics registry and a custom prefix. * - * @param registry the registry for the metrics + * @param registry the registry for the metrics * @param underlying the handler about which metrics will be collected - * @param prefix the prefix to use for the metrics names - */ + * @param prefix the prefix to use for the metrics names + */ public InstrumentedHandler(MetricRegistry registry, Handler underlying, String prefix) { super(); this.dispatches = registry.timer(name(prefix, "dispatches")); From 16aa40e4c6c7b472111079296ccc631712d98502 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Fri, 29 Mar 2013 10:57:10 -0700 Subject: [PATCH 0112/2558] Trim metrics-jersey docs. Add XML config back in when #359 is fixed. --- docs/source/manual/jersey.rst | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/docs/source/manual/jersey.rst b/docs/source/manual/jersey.rst index 54f761a8bb..1d93474eb8 100644 --- a/docs/source/manual/jersey.rst +++ b/docs/source/manual/jersey.rst @@ -25,17 +25,3 @@ The ``show`` method in the above example will have a timer attached to it, measu in that method. Use of the ``@Metered`` and ``@ExceptionMetered`` annotations is also supported. - -Your ``web.xml`` file will need to be modified to register ``InstrumentedResourceMethodDispatchAdapter`` as a Provider in Jersey_. -This is done by adding ``com.yammer.metrics.jersey`` as the value for the ``com.sun.jersey.config.property.packages`` in ``init-param``. - -.. code-block:: xml - - - Test Servlet - com.sun.jersey.spi.container.servlet.ServletContainer - - com.sun.jersey.config.property.packages - your.jersey.resources;com.yammer.metrics.jersey - - From 08430636df964d7e8379a17e99d388ede06e506a Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Fri, 29 Mar 2013 11:13:51 -0700 Subject: [PATCH 0113/2558] Updated docs for metrics-log4j. Add XML config back in when #369 is fixed. --- docs/source/manual/log4j.rst | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/docs/source/manual/log4j.rst b/docs/source/manual/log4j.rst index 4c65733ff1..5552282759 100644 --- a/docs/source/manual/log4j.rst +++ b/docs/source/manual/log4j.rst @@ -7,32 +7,8 @@ Instrumenting Log4j The ``metrics-log4j`` module provides ``InstrumentedAppender``, a Log4j ``Appender`` implementation which records the rate of logged events by their logging level. -You can either add it to the root logger programmatically: +You can add it to the root logger programmatically: .. code-block:: java - LogManager.getRootLogger().addAppender(new InstrumentedAppender()); - -Or you can add it via Log4j's XML configuration: - -.. code-block:: xml - - - - - - - - - - - - - - - - - - - - + LogManager.getRootLogger().addAppender(new InstrumentedAppender(registry)); From 43108915586d754c1a2a839f2820575379d12fc8 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Fri, 29 Mar 2013 11:16:24 -0700 Subject: [PATCH 0114/2558] Update docs for metrics-logback. Add XML config back in when #368 is fixed. --- docs/source/manual/logback.rst | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/docs/source/manual/logback.rst b/docs/source/manual/logback.rst index fe193b9914..cf7ed71082 100644 --- a/docs/source/manual/logback.rst +++ b/docs/source/manual/logback.rst @@ -7,33 +7,14 @@ Instrumenting Logback The ``metrics-logback`` module provides ``InstrumentedAppender``, a Logback ``Appender`` implementation which records the rate of logged events by their logging level. -You can either add it to the root logger programmatically: +You add it to the root logger programmatically: .. code-block:: java final LoggerContext factory = (LoggerContext) LoggerFactory.getILoggerFactory(); final Logger root = factory.getLogger(Logger.ROOT_LOGGER_NAME); - final InstrumentedAppender metrics = new InstrumentedAppender(); + final InstrumentedAppender metrics = new InstrumentedAppender(registry); metrics.setContext(root.getLoggerContext()); metrics.start(); root.addAppender(metrics); - -Or you can add it via Logback's XML configuration: - -.. code-block:: xml - - - - - %-4relative [%thread] %-5level %logger{35} - %msg %n - - - - - - - - - - From f7ddfdb75468f1b9e84bfcc4fb63b1d70ebbfbb7 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Fri, 29 Mar 2013 15:39:34 -0700 Subject: [PATCH 0115/2558] Make MetricSet a Metric. This allows you to have nested metric sets. --- .../com/yammer/metrics/MetricRegistry.java | 35 ++++++++++--------- .../java/com/yammer/metrics/MetricSet.java | 3 +- .../metrics/tests/MetricRegistryTest.java | 31 ++++++++++++++-- 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java index 17728e932c..29f5a5cbcd 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -87,11 +87,15 @@ public MetricRegistry(String name, Clock clock) { */ @SuppressWarnings("unchecked") public T register(String name, T metric) throws IllegalArgumentException { - final Metric existing = metrics.putIfAbsent(name, metric); - if (existing == null) { - onMetricAdded(name, metric); + if (metric instanceof MetricSet) { + registerAll(name, (MetricSet) metric); } else { - throw new IllegalArgumentException("A metric named " + name + " already exists"); + final Metric existing = metrics.putIfAbsent(name, metric); + if (existing == null) { + onMetricAdded(name, metric); + } else { + throw new IllegalArgumentException("A metric named " + name + " already exists"); + } } return metric; } @@ -106,19 +110,6 @@ public void registerAll(MetricSet metrics) throws IllegalArgumentException { registerAll(null, metrics); } - /** - * Given a metric set, registers them using a prefix. - * - * @param prefix the prefix for all the names - * @param metrics a set of metrics - * @throws IllegalArgumentException if any of the names are already registered - */ - public void registerAll(String prefix, MetricSet metrics) throws IllegalArgumentException { - for (Map.Entry entry : metrics.getMetrics().entrySet()) { - register(name(prefix, entry.getKey()), entry.getValue()); - } - } - /** * Creates a new {@link Counter} and registers it under the given name. * @@ -392,6 +383,16 @@ private void notifyListenerOfRemovedMetric(String name, Metric metric, MetricReg } } + private void registerAll(String prefix, MetricSet metrics) throws IllegalArgumentException { + for (Map.Entry entry : metrics.getMetrics().entrySet()) { + if (entry.getValue() instanceof MetricSet) { + registerAll(name(prefix, entry.getKey()), (MetricSet) entry.getValue()); + } else { + register(name(prefix, entry.getKey()), entry.getValue()); + } + } + } + /** * A quick and easy way of capturing the notion of default metrics. */ diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricSet.java b/metrics-core/src/main/java/com/yammer/metrics/MetricSet.java index a842f4d746..4dba8af8fc 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricSet.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricSet.java @@ -6,9 +6,8 @@ * A set of named metrics. * * @see MetricRegistry#registerAll(MetricSet) - * @see MetricRegistry#registerAll(String,MetricSet) */ -public interface MetricSet { +public interface MetricSet extends Metric { /** * A map of metric names to metrics. * diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java index 8bb852ea6b..71f02b3caa 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java @@ -276,12 +276,39 @@ public Map getMetrics() { } }; - registry.registerAll("my", metrics); + registry.register("my", metrics); assertThat(registry.getNames()) .containsOnly("my.gauge", "my.counter"); } + @Test + public void registersRecursiveMetricSets() throws Exception { + final MetricSet inner = new MetricSet() { + @Override + public Map getMetrics() { + final Map metrics = new HashMap(); + metrics.put("gauge", gauge); + return metrics; + } + }; + + final MetricSet outer = new MetricSet() { + @Override + public Map getMetrics() { + final Map metrics = new HashMap(); + metrics.put("inner", inner); + metrics.put("counter", counter); + return metrics; + } + }; + + registry.register("my", outer); + + assertThat(registry.getNames()) + .containsOnly("my.inner.gauge", "my.counter"); + } + @Test public void concatenatesStringsToFormADottedName() throws Exception { assertThat(name("one", "two", "three")) @@ -316,6 +343,6 @@ public String getValue() { }; assertThat(name(g.getClass(), "one", "two")) - .isEqualTo("com.yammer.metrics.tests.MetricRegistryTest$3.one.two"); + .isEqualTo("com.yammer.metrics.tests.MetricRegistryTest$5.one.two"); } } From 27378f9f51097ed4fa67eebbc737817c92d00c14 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Fri, 29 Mar 2013 16:27:21 -0700 Subject: [PATCH 0116/2558] Metric registries don't need clocks. Meters and timers need clocks. SO LET THEM HAVE CLOCKS --- .../main/java/com/yammer/metrics/Meter.java | 2 +- .../com/yammer/metrics/MetricRegistry.java | 32 +++++-------------- .../main/java/com/yammer/metrics/Timer.java | 2 +- .../com/yammer/metrics/tests/TimerTest.java | 9 +++--- .../servlets/tests/MetricsServletTest.java | 10 +++--- 5 files changed, 18 insertions(+), 37 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/Meter.java b/metrics-core/src/main/java/com/yammer/metrics/Meter.java index 4a2d8cea37..7775d9e8c4 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Meter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Meter.java @@ -33,7 +33,7 @@ public Meter() { * * @param clock the clock to use for the meter ticks */ - Meter(Clock clock) { + public Meter(Clock clock) { this.clock = clock; this.startTime = this.clock.getTick(); this.lastTick = new AtomicLong(startTime); diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java index 29f5a5cbcd..5113de9981 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -46,7 +46,6 @@ private static void append(StringBuilder builder, String part) { } } - private final Clock clock; private final ConcurrentMap metrics; private final List listeners; private final String name; @@ -57,21 +56,10 @@ private static void append(StringBuilder builder, String part) { * @param name the name of the registry */ public MetricRegistry(String name) { - this(name, Clock.defaultClock()); - } - - /** - * Creates a new {@link MetricRegistry} with the given name and {@link Clock} instance. - * - * @param name the name of the registry - * @param clock a {@link Clock} instance - */ - public MetricRegistry(String name, Clock clock) { if (name == null || name.isEmpty()) { throw new IllegalArgumentException("A registry needs a name"); } this.name = name; - this.clock = clock; this.metrics = new ConcurrentHashMap(); this.listeners = new CopyOnWriteArrayList(); } @@ -305,10 +293,6 @@ public SortedMap getTimers(MetricFilter filter) { return getMetrics(Timer.class, filter); } - Clock getClock() { - return clock; - } - @SuppressWarnings("unchecked") private T getOrAdd(String name, MetricBuilder builder) { final Metric metric = metrics.get(name); @@ -316,7 +300,7 @@ private T getOrAdd(String name, MetricBuilder builder) { return (T) metric; } else if (metric == null) { try { - return register(name, builder.newMetric(this)); + return register(name, builder.newMetric()); } catch (IllegalArgumentException e) { final Metric added = metrics.get(name); if (builder.isInstance(added)) { @@ -399,7 +383,7 @@ private void registerAll(String prefix, MetricSet metrics) throws IllegalArgumen private interface MetricBuilder { MetricBuilder COUNTERS = new MetricBuilder() { @Override - public Counter newMetric(MetricRegistry registry) { + public Counter newMetric() { return new Counter(); } @@ -411,7 +395,7 @@ public boolean isInstance(Metric metric) { MetricBuilder HISTOGRAMS = new MetricBuilder() { @Override - public Histogram newMetric(MetricRegistry registry) { + public Histogram newMetric() { return new Histogram(SampleType.BIASED); } @@ -423,8 +407,8 @@ public boolean isInstance(Metric metric) { MetricBuilder METERS = new MetricBuilder() { @Override - public Meter newMetric(MetricRegistry registry) { - return new Meter(registry.getClock()); + public Meter newMetric() { + return new Meter(); } @Override @@ -435,8 +419,8 @@ public boolean isInstance(Metric metric) { MetricBuilder TIMERS = new MetricBuilder() { @Override - public Timer newMetric(MetricRegistry registry) { - return new Timer(registry.getClock()); + public Timer newMetric() { + return new Timer(); } @Override @@ -445,7 +429,7 @@ public boolean isInstance(Metric metric) { } }; - T newMetric(MetricRegistry registry); + T newMetric(); boolean isInstance(Metric metric); } diff --git a/metrics-core/src/main/java/com/yammer/metrics/Timer.java b/metrics-core/src/main/java/com/yammer/metrics/Timer.java index 1c46393a43..a4dd925579 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Timer.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Timer.java @@ -24,7 +24,7 @@ public Timer() { * * @param clock the clock used to calculate duration */ - Timer(Clock clock) { + public Timer(Clock clock) { this.meter = new Meter(clock); this.clock = clock; } diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java index 1a4be59070..906f302ec1 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java @@ -1,9 +1,8 @@ package com.yammer.metrics.tests; -import com.yammer.metrics.MetricRegistry; +import com.yammer.metrics.Clock; import com.yammer.metrics.Snapshot; import com.yammer.metrics.Timer; -import com.yammer.metrics.Clock; import org.junit.Test; import java.util.concurrent.Callable; @@ -13,7 +12,7 @@ import static org.fest.assertions.api.Assertions.offset; public class TimerTest { - private final MetricRegistry registry = new MetricRegistry("test", new Clock() { + private final Clock clock = new Clock() { // a mock clock that increments its ticker by 50msec per call private long val = 0; @@ -21,8 +20,8 @@ public class TimerTest { public long getTick() { return val += 50000000; } - }); - private final Timer timer = registry.timer("timer"); + }; + private final Timer timer = new Timer(clock); @Test public void aBlankTimer() throws Exception { diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java index 6c909b1e65..b230d38daa 100644 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java @@ -1,8 +1,6 @@ package com.yammer.metrics.servlets.tests; -import com.yammer.metrics.Clock; -import com.yammer.metrics.Gauge; -import com.yammer.metrics.MetricRegistry; +import com.yammer.metrics.*; import com.yammer.metrics.servlets.MetricsServlet; import org.eclipse.jetty.testing.ServletTester; import org.junit.Before; @@ -16,7 +14,7 @@ public class MetricsServletTest extends AbstractServletTest { private final Clock clock = mock(Clock.class); - private final MetricRegistry registry = new MetricRegistry("test", clock); + private final MetricRegistry registry = new MetricRegistry("test"); @Override protected void setUp(ServletTester tester) { @@ -36,8 +34,8 @@ public Long getValue() { }); registry.counter("c").inc(); registry.histogram("h").update(1); - registry.meter("m").mark(); - registry.timer("t").update(1, TimeUnit.SECONDS); + registry.register("m", new Meter(clock)).mark(); + registry.register("t", new Timer(clock)).update(1, TimeUnit.SECONDS); request.setMethod("GET"); request.setURI("/metrics"); From 9b1870eda3ac9e70cb946ee09ca5f250a4b57c62 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Fri, 29 Mar 2013 16:42:36 -0700 Subject: [PATCH 0117/2558] Improve tests for Meter. Closes #348. --- .../main/java/com/yammer/metrics/Meter.java | 9 ++-- .../com/yammer/metrics/tests/MeterTest.java | 46 +++++++++++++++---- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/Meter.java b/metrics-core/src/main/java/com/yammer/metrics/Meter.java index 7775d9e8c4..d4ac5cbb4b 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Meter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Meter.java @@ -8,6 +8,7 @@ * exponentially-weighted moving average throughputs. * * @see EMA + * @see EWMA */ public class Meter implements Metered { private static final long TICK_INTERVAL = TimeUnit.SECONDS.toNanos(5); @@ -95,8 +96,8 @@ public double getMeanRate() { if (getCount() == 0) { return 0.0; } else { - final long elapsed = (clock.getTick() - startTime); - return convertNsRate(getCount() / (double) elapsed); + final double elapsed = (clock.getTick() - startTime); + return getCount() / elapsed * TimeUnit.SECONDS.toNanos(1); } } @@ -105,8 +106,4 @@ public double getOneMinuteRate() { tickIfNecessary(); return m1Rate.getRate(TimeUnit.SECONDS); } - - private double convertNsRate(double ratePerNs) { - return ratePerNs * (double) TimeUnit.SECONDS.toNanos(1); - } } diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MeterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/MeterTest.java index 8d68910984..2389390f3f 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/MeterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/MeterTest.java @@ -1,30 +1,60 @@ package com.yammer.metrics.tests; +import com.yammer.metrics.Clock; import com.yammer.metrics.Meter; -import com.yammer.metrics.MetricRegistry; +import org.junit.Before; import org.junit.Test; +import java.util.concurrent.TimeUnit; + import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.api.Assertions.offset; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class MeterTest { - private final MetricRegistry registry = new MetricRegistry("test"); - private final Meter meter = registry.meter("things"); + private final Clock clock = mock(Clock.class); + private final Meter meter = new Meter(clock); + + @Before + public void setUp() throws Exception { + when(clock.getTick()).thenReturn(0L, TimeUnit.SECONDS.toNanos(10)); + + } @Test - public void aBlankMeter() throws Exception { + public void startsOutWithNoRatesOrCount() throws Exception { assertThat(meter.getCount()) .isZero(); assertThat(meter.getMeanRate()) .isEqualTo(0.0, offset(0.001)); + + assertThat(meter.getOneMinuteRate()) + .isEqualTo(0.0, offset(0.001)); + + assertThat(meter.getFiveMinuteRate()) + .isEqualTo(0.0, offset(0.001)); + + assertThat(meter.getFifteenMinuteRate()) + .isEqualTo(0.0, offset(0.001)); } @Test - public void aMeterWithThreeEvents() throws Exception { - meter.mark(3); + public void marksEventsAndUpdatesRatesAndCount() throws Exception { + meter.mark(); + meter.mark(2); - assertThat(meter.getCount()) - .isEqualTo(3); + assertThat(meter.getMeanRate()) + .isEqualTo(0.3, offset(0.001)); + + assertThat(meter.getOneMinuteRate()) + .isEqualTo(0.1840, offset(0.001)); + + assertThat(meter.getFiveMinuteRate()) + .isEqualTo(0.1966, offset(0.001)); + + assertThat(meter.getFifteenMinuteRate()) + .isEqualTo(0.1988, offset(0.001)); } } From d9ed5b5e60384133b3e1f0de1e95824bd42c6b89 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Fri, 29 Mar 2013 16:42:48 -0700 Subject: [PATCH 0118/2558] Clean up HistogramTest. --- .../test/java/com/yammer/metrics/tests/HistogramTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java index cf20e1083e..fb49d9174b 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java @@ -1,16 +1,15 @@ package com.yammer.metrics.tests; -import com.yammer.metrics.Snapshot; import com.yammer.metrics.Histogram; -import com.yammer.metrics.MetricRegistry; +import com.yammer.metrics.SampleType; +import com.yammer.metrics.Snapshot; import org.junit.Test; import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.api.Assertions.offset; public class HistogramTest { - private final MetricRegistry registry = new MetricRegistry("test"); - private final Histogram histogram = registry.histogram("histogram"); + private final Histogram histogram = new Histogram(SampleType.UNIFORM); @Test public void anEmptyHistogram() throws Exception { From a6c784a110d4beb6340b6b1d2af6ca37e145b6bd Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Fri, 29 Mar 2013 16:51:06 -0700 Subject: [PATCH 0119/2558] Added docs about MetricSet. --- docs/source/manual/core.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/source/manual/core.rst b/docs/source/manual/core.rst index 165552723b..c6e50c4264 100644 --- a/docs/source/manual/core.rst +++ b/docs/source/manual/core.rst @@ -277,7 +277,14 @@ a :ref:`meter ` of the rate of its occurrence. ``System.nanoTime()`` method. Its precision and accuracy vary depending on operating system and hardware. +.. _man-core-sets: +Metric Sets +=========== + +Metrics can also be grouped together into reusable metric sets using the ``MetricSet`` interface. +This allows library authors to provide a single entry point for the instrumentation of a wide +variety of functionality. .. _man-core-reporters: From 06ad81b4585c3d9954d8a9a46e82521596f14aba Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Fri, 29 Mar 2013 16:59:24 -0700 Subject: [PATCH 0120/2558] Added docs for metrics-jvm and metrics-json. --- docs/source/manual/index.rst | 2 ++ docs/source/manual/json.rst | 12 ++++++++++++ docs/source/manual/jvm.rst | 16 ++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 docs/source/manual/json.rst create mode 100644 docs/source/manual/jvm.rst diff --git a/docs/source/manual/index.rst b/docs/source/manual/index.rst index e4dfbcdb4a..30300fd690 100644 --- a/docs/source/manual/index.rst +++ b/docs/source/manual/index.rst @@ -22,6 +22,8 @@ User Manual jetty log4j logback + jvm + json servlets servlet diff --git a/docs/source/manual/json.rst b/docs/source/manual/json.rst new file mode 100644 index 0000000000..a973c9a2b3 --- /dev/null +++ b/docs/source/manual/json.rst @@ -0,0 +1,12 @@ +.. _manual-json: + +############ +JSON Support +############ + +Metrics comes with ``metrics-json``, which features two reusable modules for Jackson_. + +.. _Jackson: http://wiki.fasterxml.com/JacksonHome + +This allows for the serialization of all metric types and health checks to a standard, +easily-parsable JSON format. diff --git a/docs/source/manual/jvm.rst b/docs/source/manual/jvm.rst new file mode 100644 index 0000000000..03f7b18456 --- /dev/null +++ b/docs/source/manual/jvm.rst @@ -0,0 +1,16 @@ +.. _manual-jvm: + +################### +JVM Instrumentation +################### + +The ``metrics-jvm`` module contains a number of reusable gauges and +:ref:`metric sets ` which allow you to easily instrument JVM internals. + +Supported metrics include: + +* Run count and elapsed times for all supported garbage collectors +* Memory usage for all memory pools, including off-heap memory +* Breakdown of thread states, including deadlocks +* File descriptor usage +* Buffer pool sizes and utilization (Java 7 only) From 4b16dbaead255eea9484b99aed8cd55ae75f0c03 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Fri, 29 Mar 2013 17:17:02 -0700 Subject: [PATCH 0121/2558] Pin Maven refs to the Sphinx release config. --- docs/source/conf.py | 4 ++-- docs/source/getting-started.rst | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index ec9b552d2b..5c8c5d64be 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -48,9 +48,9 @@ # built documents. # # The short X.Y version. -version = '2.1' +version = '3.0' # The full version, including alpha/beta/rc tags. -release = '2.1.3' +release = '3.0.0-BETA1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/source/getting-started.rst b/docs/source/getting-started.rst index e30550b614..251106ffeb 100644 --- a/docs/source/getting-started.rst +++ b/docs/source/getting-started.rst @@ -23,10 +23,15 @@ Just add the ``metrics-core`` library as a dependency: com.yammer.metrics metrics-core - 3.0.0 + ${metrics.version} +.. note:: + + Make sure you have a ``metrics.version`` property declared in your POM with the current version, + which is |release|. + Now it's time to add some metrics to your application! .. _gs-registry: @@ -279,9 +284,14 @@ To use this servlet, include the ``metrics-servlets`` module as a dependency: com.yammer.metrics metrics-servlets - 3.0.0 + ${metrics.version} +.. note:: + + Make sure you have a ``metrics.version`` property declared in your POM with the current version, + which is |release|. + From there on, you can map the servlet to whatever path you see fit. .. _gs-other: From 6b313cfb38978f3be0cb6e0712367dbf2a77d7b8 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sat, 30 Mar 2013 17:39:15 -0700 Subject: [PATCH 0122/2558] Added a builder for ConsoleReporter. Closes #361. --- .../com/yammer/metrics/ConsoleReporter.java | 160 +++++++++++++++--- .../metrics/tests/ConsoleReporterTest.java | 17 +- 2 files changed, 148 insertions(+), 29 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java b/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java index 53219357ae..099b7a81c7 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java @@ -6,9 +6,139 @@ import java.util.concurrent.TimeUnit; /** - * A reporter which outputs measurements to a {@link PrintStream}, like STDOUT. + * A reporter which outputs measurements to a {@link PrintStream}, like {@code System.out}. */ public class ConsoleReporter extends AbstractPollingReporter { + /** + * Returns a new {@link Builder} for {@link ConsoleReporter}. + * + * @param registry the registry to report + * @return a {@link Builder} instance for a {@link ConsoleReporter} + */ + public static Builder forRegistry(MetricRegistry registry) { + return new Builder(registry); + } + + /** + * A builder for {@link ConsoleReporter} instances. Defaults to using the default locale and + * time zone, writing to {@code System.out}, converting rates to events/second, converting + * durations to milliseconds, and not filtering metrics. + */ + public static class Builder { + private final MetricRegistry registry; + private PrintStream output; + private Locale locale; + private Clock clock; + private TimeZone timeZone; + private TimeUnit rateUnit; + private TimeUnit durationUnit; + private MetricFilter filter; + + private Builder(MetricRegistry registry) { + this.registry = registry; + this.output = System.out; + this.locale = Locale.getDefault(); + this.clock = Clock.defaultClock(); + this.timeZone = TimeZone.getDefault(); + this.rateUnit = TimeUnit.SECONDS; + this.durationUnit = TimeUnit.MILLISECONDS; + this.filter = MetricFilter.ALL; + } + + /** + * Write to the given {@link PrintStream}. + * + * @param output a {@link PrintStream} instance. + * @return {@code this} + */ + public Builder outputTo(PrintStream output) { + this.output = output; + return this; + } + + /** + * Format numbers for the given {@link Locale}. + * + * @param locale a {@link Locale} + * @return {@code this} + */ + public Builder formattedFor(Locale locale) { + this.locale = locale; + return this; + } + + /** + * 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; + } + + /** + * Use the given {@link TimeZone} for the time. + * + * @param timeZone a {@link TimeZone} + * @return {@code this} + */ + public Builder formattedFor(TimeZone timeZone) { + this.timeZone = timeZone; + 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 ConsoleReporter} with the given properties. + * + * @return a {@link ConsoleReporter} + */ + public ConsoleReporter build() { + return new ConsoleReporter(registry, + output, + locale, + clock, + timeZone, + rateUnit, + durationUnit, + filter); + } + } + private static final int CONSOLE_WIDTH = 80; private final PrintStream output; @@ -20,26 +150,14 @@ public class ConsoleReporter extends AbstractPollingReporter { private final double rateFactor; private final String rateUnit; - /** - * Creates a new {@link ConsoleReporter}. - * - * @param registry the registry containing the metrics to report - * @param output the print stream to be written to - * @param locale the local in which data should be formatted - * @param clock a clock - * @param timeZone the time zone in which dates should be presented - * @param rateUnit the unit in which rates should be presented - * @param durationUnit the unit in which durations should be presented - * @param filter the metric filter to match - */ - public ConsoleReporter(MetricRegistry registry, - PrintStream output, - Locale locale, - Clock clock, - TimeZone timeZone, - TimeUnit rateUnit, - TimeUnit durationUnit, - MetricFilter filter) { + private ConsoleReporter(MetricRegistry registry, + PrintStream output, + Locale locale, + Clock clock, + TimeZone timeZone, + TimeUnit rateUnit, + TimeUnit durationUnit, + MetricFilter filter) { super(registry, "console-reporter", filter); this.output = output; this.locale = locale; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java index cfdbfba16d..5cad84b2c3 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java @@ -22,14 +22,15 @@ public class ConsoleReporterTest { private final Clock clock = mock(Clock.class); private final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); private final PrintStream output = new PrintStream(bytes); - private final ConsoleReporter reporter = new ConsoleReporter(registry, - output, - Locale.US, - clock, - TimeZone.getTimeZone("PST"), - TimeUnit.SECONDS, - TimeUnit.MILLISECONDS, - MetricFilter.ALL); + private final ConsoleReporter reporter = ConsoleReporter.forRegistry(registry) + .outputTo(output) + .formattedFor(Locale.US) + .withClock(clock) + .formattedFor(TimeZone.getTimeZone("PST")) + .convertRatesTo(TimeUnit.SECONDS) + .convertDurationsTo(TimeUnit.MILLISECONDS) + .filter(MetricFilter.ALL) + .build(); @Before public void setUp() throws Exception { From 97c45f0e11174192bdebf583fcecd0aa95154026 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sat, 30 Mar 2013 17:57:01 -0700 Subject: [PATCH 0123/2558] Added a builder for CsvReporter. Closes #362. --- .../java/com/yammer/metrics/CsvReporter.java | 140 ++++++++++++++++-- .../yammer/metrics/tests/CsvReporterTest.java | 15 +- 2 files changed, 132 insertions(+), 23 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java index 4d1dcb503d..0d593dc4eb 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java @@ -14,6 +14,122 @@ * A reporter which creates a comma-separated values file of the measurements for each metric. */ public class CsvReporter extends AbstractPollingReporter { + /** + * Returns a new {@link Builder} for {@link CsvReporter}. + * + * @param registry the registry to report + * @return a {@link Builder} instance for a {@link CsvReporter} + */ + public static Builder forRegistry(MetricRegistry registry) { + return new Builder(registry); + } + + /** + * A builder for {@link CsvReporter} instances. Defaults to using the default locale, writing to + * the current directory, converting rates to events/second, converting durations to + * milliseconds, and not filtering metrics. + */ + public static class Builder { + private final MetricRegistry registry; + private File directory; + private Locale locale; + private TimeUnit rateUnit; + private TimeUnit durationUnit; + private Clock clock; + private MetricFilter filter; + + private Builder(MetricRegistry registry) { + this.registry = registry; + this.directory = new File("."); + this.locale = Locale.getDefault(); + this.rateUnit = TimeUnit.SECONDS; + this.durationUnit = TimeUnit.MILLISECONDS; + this.clock = Clock.defaultClock(); + this.filter = MetricFilter.ALL; + } + + /** + * Create {@code .csv} files in the given directory. + * + * @param directory a directory + * @return {@code this} + */ + public Builder outputTo(File directory) { + this.directory = directory; + return this; + } + + /** + * Format numbers for the given {@link Locale}. + * + * @param locale a {@link Locale} + * @return {@code this} + */ + public Builder formatFor(Locale locale) { + this.locale = locale; + 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; + } + + /** + * 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; + } + + /** + * 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 CsvReporter} with the given properties. + * + * @return a {@link CsvReporter} + */ + public CsvReporter build() { + return new CsvReporter(registry, + directory, + locale, + rateUnit, + durationUnit, + clock, + filter); + } + } + private static final Logger LOGGER = LoggerFactory.getLogger(CsvReporter.class); private static final Charset UTF_8 = Charset.forName("UTF-8"); @@ -25,21 +141,13 @@ public class CsvReporter extends AbstractPollingReporter { private final double rateFactor; private final String rateUnit; - /** - * Creates a new {@link CsvReporter} instance. - * - * @param registry the {@link MetricRegistry} containing the metrics this reporter will report - * @param directory the directory in which CSV files will be created - * @param locale the locale to use for formatting - * @param filter the metric filter to match - */ - public CsvReporter(MetricRegistry registry, - File directory, - Locale locale, - TimeUnit rateUnit, - TimeUnit durationUnit, - Clock clock, - MetricFilter filter) { + private CsvReporter(MetricRegistry registry, + File directory, + Locale locale, + TimeUnit rateUnit, + TimeUnit durationUnit, + Clock clock, + MetricFilter filter) { super(registry, "csv-reporter", filter); this.directory = directory; this.locale = locale; @@ -151,7 +259,7 @@ private void report(long timestamp, String name, String header, String line, Obj final File file = new File(directory, sanitize(name) + ".csv"); final boolean fileAlreadyExists = file.exists(); if (fileAlreadyExists || file.createNewFile()) { - final PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file, true), UTF_8)); + final PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file,true), UTF_8)); try { if (!fileAlreadyExists) { out.println("t," + header); diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java index 1b0408dd82..1a1eca9838 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java @@ -32,13 +32,14 @@ public void setUp() throws Exception { this.dataDirectory = folder.newFolder(); - this.reporter = new CsvReporter(registry, - dataDirectory, - Locale.US, - TimeUnit.SECONDS, - TimeUnit.MILLISECONDS, - clock, - MetricFilter.ALL); + this.reporter = CsvReporter.forRegistry(registry) + .outputTo(dataDirectory) + .formatFor(Locale.US) + .convertRatesTo(TimeUnit.SECONDS) + .convertDurationsTo(TimeUnit.MILLISECONDS) + .withClock(clock) + .filter(MetricFilter.ALL) + .build(); } @Test From 8bb0f824b7eb661eef73576e6ac92bf601791a98 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sat, 30 Mar 2013 18:26:29 -0700 Subject: [PATCH 0124/2558] Added a builder for JmxReporter. --- .../java/com/yammer/metrics/JmxReporter.java | 65 ++++++++++++++++--- .../yammer/metrics/tests/JmxReporterTest.java | 5 +- 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java index e9d55c353a..956527b77e 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java @@ -4,6 +4,7 @@ import org.slf4j.LoggerFactory; import javax.management.*; +import java.lang.management.ManagementFactory; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; @@ -11,6 +12,61 @@ * A reporter which listens for new metrics and exposes them as namespaced MBeans. */ public class JmxReporter { + /** + * Returns a new {@link Builder} for {@link JmxReporter}. + * + * @param registry the registry to report + * @return a {@link Builder} instance for a {@link JmxReporter} + */ + public static Builder forRegistry(MetricRegistry registry) { + return new Builder(registry); + } + + /** + * A builder for {@link CsvReporter} instances. Defaults to using the default MBean server and + * not filtering metrics. + */ + public static class Builder { + private final MetricRegistry registry; + private MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + private MetricFilter filter = MetricFilter.ALL; + + private Builder(MetricRegistry registry) { + this.registry = registry; + } + + /** + * Register MBeans with the given {@link MBeanServer}. + * + * @param mBeanServer an {@link MBeanServer} + * @return {@code this} + */ + public Builder registerWith(MBeanServer mBeanServer) { + this.mBeanServer = mBeanServer; + 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 JmxReporter} with the given properties. + * + * @return a {@link JmxReporter} + */ + public JmxReporter build() { + return new JmxReporter(mBeanServer, registry, filter); + } + } + private static final Logger LOGGER = LoggerFactory.getLogger(JmxReporter.class); // CHECKSTYLE:OFF @@ -482,14 +538,7 @@ void unregisterAll() { private final MetricRegistry registry; private final JmxListener listener; - /** - * Creates a new {@link JmxReporter}. - * - * @param mBeanServer the platform's {@link javax.management.MBeanServer} - * @param registry the registry containing the metrics to report - * @param filter the metric filter to match - */ - public JmxReporter(MBeanServer mBeanServer, MetricRegistry registry, MetricFilter filter) { + private JmxReporter(MBeanServer mBeanServer, MetricRegistry registry, MetricFilter filter) { this.registry = registry; this.listener = new JmxListener(mBeanServer, registry.getName(), filter); } diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java index f30ebef04a..7ffbe9a1c0 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java @@ -22,7 +22,10 @@ public class JmxReporterTest { private final String name = UUID.randomUUID().toString().replaceAll("[{\\-}]", ""); private final MetricRegistry registry = spy(new MetricRegistry(name)); - private final JmxReporter reporter = new JmxReporter(mBeanServer, registry, MetricFilter.ALL); + private final JmxReporter reporter = JmxReporter.forRegistry(registry) + .registerWith(mBeanServer) + .filter(MetricFilter.ALL) + .build(); private final Gauge gauge = mock(Gauge.class); private final Counter counter = mock(Counter.class); From c8a587a2a6a0169dcb69e833962d2e7edc5524e3 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sat, 30 Mar 2013 18:46:05 -0700 Subject: [PATCH 0125/2558] Added a builder for Slf4jReporter. Closes #363. --- .../com/yammer/metrics/Slf4jReporter.java | 131 +++++++++++++++--- .../metrics/tests/Slf4jReporterTest.java | 13 +- 2 files changed, 117 insertions(+), 27 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java b/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java index 1613bd9137..4012b86752 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java @@ -1,6 +1,7 @@ package com.yammer.metrics; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.slf4j.Marker; import java.util.Locale; @@ -9,12 +10,109 @@ import java.util.concurrent.TimeUnit; /** - * Metrics reporter class for logging metrics values to a SLF4J {@link Logger} periodically, similar - * to how {@link ConsoleReporter} or {@link CsvReporter} function, but using the SLF4J framework - * instead. It also supports specifying a {@link Marker} instance that can be used by custom - * appenders and filters for the bound logging toolkit to further process metrics reports. + * A reporter class for logging metrics values to a SLF4J {@link Logger} periodically, similar to + * {@link ConsoleReporter} or {@link CsvReporter}, but using the SLF4J framework instead. It also + * supports specifying a {@link Marker} instance that can be used by custom appenders and filters + * for the bound logging toolkit to further process metrics reports. */ public class Slf4jReporter extends AbstractPollingReporter { + /** + * Returns a new {@link Builder} for {@link Slf4jReporter}. + * + * @param registry the registry to report + * @return a {@link Builder} instance for a {@link Slf4jReporter} + */ + public static Builder forRegistry(MetricRegistry registry) { + return new Builder(registry); + } + + /** + * A builder for {@link CsvReporter} instances. Defaults to logging to {@code metrics}, not + * using a marker, converting rates to events/second, converting durations to milliseconds, and + * not filtering metrics. + */ + public static class Builder { + private MetricRegistry registry; + private Logger logger; + private Marker marker; + private TimeUnit rateUnit; + private TimeUnit durationUnit; + private MetricFilter filter; + + private Builder(MetricRegistry registry) { + this.registry = registry; + this.logger = LoggerFactory.getLogger("metrics"); + this.marker = null; + this.rateUnit = TimeUnit.SECONDS; + this.durationUnit = TimeUnit.MILLISECONDS; + this.filter = MetricFilter.ALL; + } + + /** + * Log metrics to the given logger. + * + * @param logger an SLF4J {@link Logger} + * @return {@code this} + */ + public Builder outputTo(Logger logger) { + this.logger = logger; + return this; + } + + /** + * Mark all logged metrics with the given marker. + * + * @param marker an SLF4J {@link Marker} + * @return {@code this} + */ + public Builder markWith(Marker marker) { + this.marker = marker; + 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 Slf4jReporter} with the given properties. + * + * @return a {@link Slf4jReporter} + */ + public Slf4jReporter build() { + return new Slf4jReporter(registry, logger, marker, rateUnit, durationUnit, filter); + } + } + private final Logger logger; private final Marker marker; private final double durationFactor; @@ -22,23 +120,12 @@ public class Slf4jReporter extends AbstractPollingReporter { private final double rateFactor; private final String rateUnit; - /** - * Construct a new SLF4J reporter. - * - * @param registry Metrics registry to report from. - * @param logger SLF4J {@link Logger} instance to send metrics reports to - * @param marker SLF4J {@link Marker} instance to log with metrics class, or null if - * none. - * @param rateUnit the unit to which rates will be converted - * @param durationUnit the unit to which durations will be converted - * @param filter the metric filter to match - */ - public Slf4jReporter(MetricRegistry registry, - Logger logger, - Marker marker, - TimeUnit rateUnit, - TimeUnit durationUnit, - MetricFilter filter) { + private Slf4jReporter(MetricRegistry registry, + Logger logger, + Marker marker, + TimeUnit rateUnit, + TimeUnit durationUnit, + MetricFilter filter) { super(registry, "logger-reporter", filter); this.logger = logger; this.marker = marker; @@ -143,4 +230,6 @@ private String calculateRateUnit(TimeUnit unit) { final String s = unit.toString().toLowerCase(Locale.US); return s.substring(0, s.length() - 1); } + + } diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java index 96258eb20d..14275cdf18 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java @@ -17,12 +17,13 @@ public class Slf4jReporterTest { private final Logger logger = mock(Logger.class); private final Marker marker = mock(Marker.class); private final MetricRegistry registry = mock(MetricRegistry.class); - private final Slf4jReporter reporter = new Slf4jReporter(registry, - logger, - marker, - TimeUnit.SECONDS, - TimeUnit.MILLISECONDS, - MetricFilter.ALL); + private final Slf4jReporter reporter = Slf4jReporter.forRegistry(registry) + .outputTo(logger) + .markWith(marker) + .convertRatesTo(TimeUnit.SECONDS) + .convertDurationsTo(TimeUnit.MILLISECONDS) + .filter(MetricFilter.ALL) + .build(); @Test public void reportsGaugeValues() throws Exception { From cc5adb75415323c0ff5eadd5db7cd577467dab4c Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sat, 30 Mar 2013 18:48:31 -0700 Subject: [PATCH 0126/2558] Make the directory required for CsvReporter. --- .../java/com/yammer/metrics/CsvReporter.java | 24 +++++-------------- .../yammer/metrics/tests/CsvReporterTest.java | 3 +-- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java index 0d593dc4eb..6e219f148f 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java @@ -25,13 +25,11 @@ public static Builder forRegistry(MetricRegistry registry) { } /** - * A builder for {@link CsvReporter} instances. Defaults to using the default locale, writing to - * the current directory, converting rates to events/second, converting durations to - * milliseconds, and not filtering metrics. + * A builder for {@link CsvReporter} instances. Defaults to using the default locale, converting + * rates to events/second, converting durations to milliseconds, and not filtering metrics. */ public static class Builder { private final MetricRegistry registry; - private File directory; private Locale locale; private TimeUnit rateUnit; private TimeUnit durationUnit; @@ -40,7 +38,6 @@ public static class Builder { private Builder(MetricRegistry registry) { this.registry = registry; - this.directory = new File("."); this.locale = Locale.getDefault(); this.rateUnit = TimeUnit.SECONDS; this.durationUnit = TimeUnit.MILLISECONDS; @@ -48,17 +45,6 @@ private Builder(MetricRegistry registry) { this.filter = MetricFilter.ALL; } - /** - * Create {@code .csv} files in the given directory. - * - * @param directory a directory - * @return {@code this} - */ - public Builder outputTo(File directory) { - this.directory = directory; - return this; - } - /** * Format numbers for the given {@link Locale}. * @@ -115,11 +101,13 @@ public Builder filter(MetricFilter filter) { } /** - * Builds a {@link CsvReporter} with the given properties. + * Builds a {@link CsvReporter} with the given properties, writing {@code .csv} files to the + * given directory. * + * @param directory the directory in which the {@code .csv} files will be created * @return a {@link CsvReporter} */ - public CsvReporter build() { + public CsvReporter build(File directory) { return new CsvReporter(registry, directory, locale, diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java index 1a1eca9838..9520a6befd 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java @@ -33,13 +33,12 @@ public void setUp() throws Exception { this.dataDirectory = folder.newFolder(); this.reporter = CsvReporter.forRegistry(registry) - .outputTo(dataDirectory) .formatFor(Locale.US) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .withClock(clock) .filter(MetricFilter.ALL) - .build(); + .build(dataDirectory); } @Test From 00e8e34dbf871ffaf101616c3439da444c722c7c Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sat, 30 Mar 2013 18:57:52 -0700 Subject: [PATCH 0127/2558] Added a builder for GangliaReporter. Closes #365. --- .../metrics/ganglia/GangliaReporter.java | 126 +++++++++++++++--- .../ganglia/tests/GangliaReporterTest.java | 14 +- 2 files changed, 114 insertions(+), 26 deletions(-) diff --git a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java index 259ace6f7c..e9c9ea3ef9 100644 --- a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java +++ b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java @@ -21,6 +21,106 @@ * @see Ganglia Monitoring System */ public class GangliaReporter extends AbstractPollingReporter { + /** + * Returns a new {@link Builder} for {@link GangliaReporter}. + * + * @param registry the registry to report + * @return a {@link Builder} instance for a {@link GangliaReporter} + */ + public static Builder forRegistry(MetricRegistry registry) { + return new Builder(registry); + } + + /** + * A builder for {@link CsvReporter} instances. Defaults to using a {@code tmax} of {@code 60}, + * a {@code dmax} of {@code 0}, converting rates to events/second, converting durations to + * milliseconds, and not filtering metrics. + */ + public static class Builder { + private final MetricRegistry registry; + private int tMax; + private int dMax; + private TimeUnit rateUnit; + private TimeUnit durationUnit; + private MetricFilter filter; + + private Builder(MetricRegistry registry) { + this.registry = registry; + this.tMax = 60; + this.dMax = 0; + this.rateUnit = TimeUnit.SECONDS; + this.durationUnit = TimeUnit.MILLISECONDS; + this.filter = MetricFilter.ALL; + } + + /** + * Use the given {@code tmax} value when announcing metrics. + * + * @param tMax the desired gmond {@code tmax} value + * @return {@code this} + */ + public Builder withTMax(int tMax) { + this.tMax = tMax; + return this; + } + + /** + * Use the given {@code dmax} value when announcing metrics. + * + * @param dMax the desired gmond {@code dmax} value + * @return {@code this} + */ + public Builder withDMax(int dMax) { + this.dMax = dMax; + 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 GangliaReporter} with the given properties, announcing metrics to the + * given {@link GMetric} client. + * + * @param ganglia the client to use for announcing metrics + * @return a {@link GangliaReporter} + */ + public GangliaReporter build(GMetric ganglia) { + return new GangliaReporter(registry, + ganglia, tMax, dMax, rateUnit, durationUnit, filter); + } + } + private static final Logger LOGGER = LoggerFactory.getLogger(GangliaReporter.class); private final GMetric ganglia; @@ -31,25 +131,13 @@ public class GangliaReporter extends AbstractPollingReporter { private final int tMax; private final int dMax; - /** - * Creates a new {@link GangliaReporter}. - * - * @param registry the registry to report - * @param ganglia a {@link GMetric} instance for the Ganglia cluster - * @param tMax the time in seconds between polling - * @param dMax the time in seconds after which the data should be considered - * unmonitored - * @param rateUnit the time unit to use for rates - * @param durationUnit the time unit to use for durations - * @param filter a filter for metrics - */ - public GangliaReporter(MetricRegistry registry, - GMetric ganglia, - int tMax, - int dMax, - TimeUnit rateUnit, - TimeUnit durationUnit, - MetricFilter filter) { + private GangliaReporter(MetricRegistry registry, + GMetric ganglia, + int tMax, + int dMax, + TimeUnit rateUnit, + TimeUnit durationUnit, + MetricFilter filter) { super(registry, "ganglia-reporter", filter); this.ganglia = ganglia; this.tMax = tMax; diff --git a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java b/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java index ca5e56ff88..dc5f37d1b8 100644 --- a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java +++ b/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java @@ -16,13 +16,13 @@ public class GangliaReporterTest { private final GMetric ganglia = mock(GMetric.class); private final MetricRegistry registry = mock(MetricRegistry.class); - private final GangliaReporter reporter = new GangliaReporter(registry, - ganglia, - 60, - 0, - TimeUnit.SECONDS, - TimeUnit.MILLISECONDS, - MetricFilter.ALL); + private final GangliaReporter reporter = GangliaReporter.forRegistry(registry) + .withTMax(60) + .withDMax(0) + .convertRatesTo(TimeUnit.SECONDS) + .convertDurationsTo(TimeUnit.MILLISECONDS) + .filter(MetricFilter.ALL) + .build(ganglia); @Test public void reportsStringGaugeValues() throws Exception { From e6c792c96e36ab6e0b53da6a2fa56c396429e435 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sat, 30 Mar 2013 19:06:41 -0700 Subject: [PATCH 0128/2558] Added a builder for GraphiteReporter. Closes #366. --- .../metrics/graphite/GraphiteReporter.java | 130 +++++++++++++++--- .../graphite/tests/GraphiteReporterTest.java | 14 +- 2 files changed, 119 insertions(+), 25 deletions(-) diff --git a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java index 386aa5363c..41adb339f9 100644 --- a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java +++ b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java @@ -16,6 +16,111 @@ * @see Graphite - Scalable Realtime Graphing */ public class GraphiteReporter extends AbstractPollingReporter { + /** + * Returns a new {@link Builder} for {@link GraphiteReporter}. + * + * @param registry the registry to report + * @return a {@link Builder} instance for a {@link GraphiteReporter} + */ + public static Builder forRegistry(MetricRegistry registry) { + return new Builder(registry); + } + + /** + * A builder for {@link GraphiteReporter} 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 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 GraphiteReporter} with the given properties, sending metrics using the + * given {@link Graphite} client. + * + * @param graphite a {@link Graphite} client + * @return a {@link GraphiteReporter} + */ + public GraphiteReporter build(Graphite graphite) { + return new GraphiteReporter(registry, + graphite, + clock, + prefix, + rateUnit, + durationUnit, + filter); + } + } + private static final Logger LOGGER = LoggerFactory.getLogger(GraphiteReporter.class); private final Graphite graphite; @@ -24,24 +129,13 @@ public class GraphiteReporter extends AbstractPollingReporter { private final double rateFactor; private final double durationFactor; - /** - * Creates a new reporter. - * - * @param registry the registry to report - * @param graphite the {@link Graphite} client - * @param clock the clock to use for timestamps - * @param prefix a prefix to be prepended to all metric names (can be {@code null}) - * @param rateUnit the time unit to use for rates - * @param durationUnit the time unit to use for durations - * @param filter a filter for metrics - */ - public GraphiteReporter(MetricRegistry registry, - Graphite graphite, - Clock clock, - String prefix, - TimeUnit rateUnit, - TimeUnit durationUnit, - MetricFilter filter) { + private GraphiteReporter(MetricRegistry registry, + Graphite graphite, + Clock clock, + String prefix, + TimeUnit rateUnit, + TimeUnit durationUnit, + MetricFilter filter) { super(registry, "graphite-reporter", filter); this.graphite = graphite; this.clock = clock; diff --git a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java index 1cb79e3f79..6ea0f2d238 100644 --- a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java +++ b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java @@ -18,13 +18,13 @@ public class GraphiteReporterTest { private final Clock clock = mock(Clock.class); private final Graphite graphite = mock(Graphite.class); private final MetricRegistry registry = mock(MetricRegistry.class); - private final GraphiteReporter reporter = new GraphiteReporter(registry, - graphite, - clock, - "prefix", - TimeUnit.SECONDS, - TimeUnit.MILLISECONDS, - MetricFilter.ALL); + private final GraphiteReporter reporter = GraphiteReporter.forRegistry(registry) + .withClock(clock) + .prefixedWith("prefix") + .convertRatesTo(TimeUnit.SECONDS) + .convertDurationsTo(TimeUnit.MILLISECONDS) + .filter(MetricFilter.ALL) + .build(graphite); @Before public void setUp() throws Exception { From 7f698182ade2154289ff3398dc3c73c856b1a168 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sat, 30 Mar 2013 19:26:41 -0700 Subject: [PATCH 0129/2558] Update docs for metrics-ganglia. --- docs/source/manual/ganglia.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/source/manual/ganglia.rst b/docs/source/manual/ganglia.rst index 14d1bb2373..ff5ac8cfad 100644 --- a/docs/source/manual/ganglia.rst +++ b/docs/source/manual/ganglia.rst @@ -11,4 +11,9 @@ constantly stream metric values to a Ganglia_ server: .. code-block:: java - GangliaReporter.enable(1, TimeUnit.MINUTES, "ganglia.example.com", 8649); + final GMetric ganglia = new GMetric("ganglia.example.com", 8649, UDPAddressingMode.MULTICAST, 1); + final GangliaReporter reporter = GangliaReporter.forRegistry(registry) + .convertRatesTo(TimeUnit.SECONDS) + .convertDurationsTo(TimeUnit.MILLISECONDS) + .build(ganglia); + reporter.start(1, TimeUnit.MINUTES); From c51416d0718fb9ea7ed5a40331a09f495ecb0f5a Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sat, 30 Mar 2013 19:30:18 -0700 Subject: [PATCH 0130/2558] Update docs for metrics-graphite. --- docs/source/manual/graphite.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/source/manual/graphite.rst b/docs/source/manual/graphite.rst index 66aa696d6b..4e2f5f99b4 100644 --- a/docs/source/manual/graphite.rst +++ b/docs/source/manual/graphite.rst @@ -11,4 +11,11 @@ constantly stream metric values to a Graphite_ server: .. code-block:: java - GraphiteReporter.enable(1, TimeUnit.MINUTES, "graphite.example.com", 2003); + final Graphite graphite = new Graphite(new InetSocketAddress("graphite.example.com", 2003)); + final GraphiteReporter reporter = GraphiteReporter.forRegistry(registry) + .prefixedWith("web1.example.com") + .convertRatesTo(TimeUnit.SECONDS) + .convertDurationsTo(TimeUnit.MILLISECONDS) + .filter(MetricFilter.ALL) + .build(graphite); + reporter.start(1, TimeUnit.MINUTES); From 9114cdada352df4fe24df502bca599907f607d62 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 10:10:11 -0700 Subject: [PATCH 0131/2558] Added docs for the core reporters. --- docs/source/manual/core.rst | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/docs/source/manual/core.rst b/docs/source/manual/core.rst index c6e50c4264..7d5c5f7920 100644 --- a/docs/source/manual/core.rst +++ b/docs/source/manual/core.rst @@ -321,6 +321,13 @@ or JConsole (which ships with most JDKs as ``jconsole``): API is fragile and bonkers. For development purposes and browsing, though, it can be very useful. +To report metrics via JMX: + +.. code-block:: java + + final JmxReporter reporter = JmxReporter.forRegistry(registry).build(); + reporter.start(); + .. _man-core-reporters-console: Console @@ -331,7 +338,11 @@ registered metrics to the console: .. code-block:: java - // TODO: waiting on builders + final ConsoleReporter reporter = ConsoleReporter.forRegistry(registry) + .convertRatesTo(TimeUnit.SECONDS) + .convertDurationsTo(TimeUnit.MILLISECONDS) + .build(); + reporter.start(1, TimeUnit.MINUTES); .. _man-core-reporters-csv: @@ -343,7 +354,12 @@ of ``.csv`` files in a given directory: .. code-block:: java - // TODO: waiting on builders + final CsvReporter reporter = CsvReporter.forRegistry(registry) + .formatFor(Locale.US) + .convertRatesTo(TimeUnit.SECONDS) + .convertDurationsTo(TimeUnit.MILLISECONDS) + .build(new File("~/projects/data/")); + reporter.start(1, TimeUnit.SECONDS); For each metric registered, a ``.csv`` file will be created, and every second its state will be written to it as a new row. @@ -357,7 +373,12 @@ It's also possible to log metrics to an SLF4J logger: .. code-block:: java - // TODO: waiting on builders + final Slf4jReporter reporter = Slf4jReporter.forRegistry(registry) + .outputTo(LoggerFactory.getLogger("com.example.metrics")) + .convertRatesTo(TimeUnit.SECONDS) + .convertDurationsTo(TimeUnit.MILLISECONDS) + .build(); + reporter.start(1, TimeUnit.MINUTES); .. _man-core-reporters-other: From b1aca662031d1b95b1010709c7e122302639706e Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 10:14:32 -0700 Subject: [PATCH 0132/2558] Added Timers backed with arbitrary Samples. --- .../main/java/com/yammer/metrics/Timer.java | 21 ++++++++++++++----- .../com/yammer/metrics/tests/TimerTest.java | 3 ++- .../servlets/tests/MetricsServletTest.java | 2 +- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/Timer.java b/metrics-core/src/main/java/com/yammer/metrics/Timer.java index a4dd925579..e2d5afdc52 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Timer.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Timer.java @@ -9,24 +9,35 @@ */ public class Timer implements Metered, Sampling, Summarizable { private final Meter meter; - private final Histogram histogram = new Histogram(SampleType.BIASED); + private final Histogram histogram; private final Clock clock; /** * Creates a new {@link Timer}. */ public Timer() { - this(Clock.defaultClock()); + this(SampleType.BIASED.newSample()); } /** - * Creates a new {@link Timer}. + * Creates a new {@link Timer} that uses the given {@link Sample}. + * + * @param sample the {@link Sample} implementation the timer should use + */ + public Timer(Sample sample) { + this(sample, Clock.defaultClock()); + } + + /** + * Creates a new {@link Timer} that uses the given {@link Sample} and {@link Clock}. * - * @param clock the clock used to calculate duration + * @param sample the {@link Sample} implementation the timer should use + * @param clock the {@link Clock} implementation the timer should use */ - public Timer(Clock clock) { + public Timer(Sample sample, Clock clock) { this.meter = new Meter(clock); this.clock = clock; + this.histogram = new Histogram(sample); } /** diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java index 906f302ec1..2893eec182 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java @@ -1,6 +1,7 @@ package com.yammer.metrics.tests; import com.yammer.metrics.Clock; +import com.yammer.metrics.SampleType; import com.yammer.metrics.Snapshot; import com.yammer.metrics.Timer; import org.junit.Test; @@ -21,7 +22,7 @@ public long getTick() { return val += 50000000; } }; - private final Timer timer = new Timer(clock); + private final Timer timer = new Timer(SampleType.BIASED.newSample(), clock); @Test public void aBlankTimer() throws Exception { diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java index b230d38daa..e7c39a4aa3 100644 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java @@ -35,7 +35,7 @@ public Long getValue() { registry.counter("c").inc(); registry.histogram("h").update(1); registry.register("m", new Meter(clock)).mark(); - registry.register("t", new Timer(clock)).update(1, TimeUnit.SECONDS); + registry.register("t", new Timer(SampleType.BIASED.newSample(), clock)).update(1, TimeUnit.SECONDS); request.setMethod("GET"); request.setURI("/metrics"); From 3de0d969a5c272fb772a8e5e25210cf92c0716d4 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 10:28:32 -0700 Subject: [PATCH 0133/2558] Drop SampleType. Just accept Sample instances. --- .../metrics/ExponentiallyDecayingSample.java | 15 ++++++++ .../java/com/yammer/metrics/Histogram.java | 9 ----- .../com/yammer/metrics/MetricRegistry.java | 2 +- .../java/com/yammer/metrics/SampleType.java | 34 ------------------- .../main/java/com/yammer/metrics/Timer.java | 2 +- .../com/yammer/metrics/UniformSample.java | 11 ++++++ .../yammer/metrics/tests/HistogramTest.java | 4 +-- .../com/yammer/metrics/tests/TimerTest.java | 4 +-- .../servlets/tests/MetricsServletTest.java | 3 +- 9 files changed, 34 insertions(+), 50 deletions(-) delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/SampleType.java diff --git a/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java b/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java index 223520e9e0..6ea681e370 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java @@ -19,7 +19,22 @@ * Proceedings of the 2009 IEEE International Conference on Data Engineering (2009) */ public class ExponentiallyDecayingSample implements Sample { + private static final int DEFAULT_SAMPLE_SIZE = 1028; + private static final double DEFAULT_ALPHA = 0.015; + + /** + * Creates a new {@link ExponentiallyDecayingSample} of 1028 elements, which offers a 99.9% + * confidence level with a 5% margin of error assuming a normal distribution, and an alpha + * factor of 0.015, which heavily biases the sample to the past 5 minutes of measurements. + * + * @return a new {@link ExponentiallyDecayingSample} + */ + public static ExponentiallyDecayingSample create() { + return new ExponentiallyDecayingSample(DEFAULT_SAMPLE_SIZE, DEFAULT_ALPHA); + } + private static final long RESCALE_THRESHOLD = TimeUnit.HOURS.toNanos(1); + private final ConcurrentSkipListMap values; private final ReentrantReadWriteLock lock; private final double alpha; diff --git a/metrics-core/src/main/java/com/yammer/metrics/Histogram.java b/metrics-core/src/main/java/com/yammer/metrics/Histogram.java index 95aee14d7d..8160aa8b25 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Histogram.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Histogram.java @@ -21,15 +21,6 @@ public class Histogram implements Metric, Sampling, Summarizable { private final AtomicReference variance; // M, S private final AtomicLong count; - /** - * Creates a new {@link Histogram} with the given sample type. - * - * @param type the type of sample to use - */ - public Histogram(SampleType type) { - this(type.newSample()); - } - /** * Creates a new {@link Histogram} with the given sample. * diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java index 5113de9981..0c975ab82d 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -396,7 +396,7 @@ public boolean isInstance(Metric metric) { MetricBuilder HISTOGRAMS = new MetricBuilder() { @Override public Histogram newMetric() { - return new Histogram(SampleType.BIASED); + return new Histogram(ExponentiallyDecayingSample.create()); } @Override diff --git a/metrics-core/src/main/java/com/yammer/metrics/SampleType.java b/metrics-core/src/main/java/com/yammer/metrics/SampleType.java deleted file mode 100644 index ca7180d5d7..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/SampleType.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.yammer.metrics; - -/** - * The type of sampling the histogram should be performing. - */ -public enum SampleType { - /** - * Uses a uniform sample of 1028 elements, which offers a 99.9% confidence level with a 5% - * margin of error assuming a normal distribution. - */ - UNIFORM { - @Override - public Sample newSample() { - return new UniformSample(DEFAULT_SAMPLE_SIZE); - } - }, - - /** - * Uses an exponentially decaying sample of 1028 elements, which offers a 99.9% confidence - * level with a 5% margin of error assuming a normal distribution, and an alpha factor of - * 0.015, which heavily biases the sample to the past 5 minutes of measurements. - */ - BIASED { - @Override - public Sample newSample() { - return new ExponentiallyDecayingSample(DEFAULT_SAMPLE_SIZE, DEFAULT_ALPHA); - } - }; - - private static final int DEFAULT_SAMPLE_SIZE = 1028; - private static final double DEFAULT_ALPHA = 0.015; - - public abstract Sample newSample(); -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/Timer.java b/metrics-core/src/main/java/com/yammer/metrics/Timer.java index e2d5afdc52..d2393ab197 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Timer.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Timer.java @@ -16,7 +16,7 @@ public class Timer implements Metered, Sampling, Summarizable { * Creates a new {@link Timer}. */ public Timer() { - this(SampleType.BIASED.newSample()); + this(ExponentiallyDecayingSample.create()); } /** diff --git a/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java b/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java index b01723331e..144f12b488 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java @@ -12,10 +12,21 @@ * @see Random Sampling with a Reservoir */ public class UniformSample implements Sample { + private static final int DEFAULT_SAMPLE_SIZE = 1028; private static final int BITS_PER_LONG = 63; private final AtomicLong count = new AtomicLong(); private final AtomicLongArray values; + /** + * Creates a new uniform sample of 1028 elements, which offers a 99.9% confidence level with a + * 5% margin of error assuming a normal distribution. + * + * @return a new {@link UniformSample} + */ + public static UniformSample create() { + return new UniformSample(DEFAULT_SAMPLE_SIZE); + } + /** * Creates a new {@link UniformSample}. * diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java index fb49d9174b..12410c8fc7 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java @@ -1,15 +1,15 @@ package com.yammer.metrics.tests; import com.yammer.metrics.Histogram; -import com.yammer.metrics.SampleType; import com.yammer.metrics.Snapshot; +import com.yammer.metrics.UniformSample; import org.junit.Test; import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.api.Assertions.offset; public class HistogramTest { - private final Histogram histogram = new Histogram(SampleType.UNIFORM); + private final Histogram histogram = new Histogram(UniformSample.create()); @Test public void anEmptyHistogram() throws Exception { diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java index 2893eec182..294512bf63 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java @@ -1,7 +1,7 @@ package com.yammer.metrics.tests; import com.yammer.metrics.Clock; -import com.yammer.metrics.SampleType; +import com.yammer.metrics.ExponentiallyDecayingSample; import com.yammer.metrics.Snapshot; import com.yammer.metrics.Timer; import org.junit.Test; @@ -22,7 +22,7 @@ public long getTick() { return val += 50000000; } }; - private final Timer timer = new Timer(SampleType.BIASED.newSample(), clock); + private final Timer timer = new Timer(ExponentiallyDecayingSample.create(), clock); @Test public void aBlankTimer() throws Exception { diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java index e7c39a4aa3..b39f3502d1 100644 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java @@ -35,7 +35,8 @@ public Long getValue() { registry.counter("c").inc(); registry.histogram("h").update(1); registry.register("m", new Meter(clock)).mark(); - registry.register("t", new Timer(SampleType.BIASED.newSample(), clock)).update(1, TimeUnit.SECONDS); + registry.register("t", new Timer(ExponentiallyDecayingSample.create(), clock)) + .update(1, TimeUnit.SECONDS); request.setMethod("GET"); request.setURI("/metrics"); From 545f7fb5a3e858c098f11bd72321ac9efbf69c2c Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 11:21:45 -0700 Subject: [PATCH 0134/2558] Added TimeSlidingWindowSample. --- .../metrics/TimeSlidingWindowSample.java | 82 +++++++++++++++++++ .../tests/TimeSlidingWindowSampleTest.java | 48 +++++++++++ 2 files changed, 130 insertions(+) create mode 100644 metrics-core/src/main/java/com/yammer/metrics/TimeSlidingWindowSample.java create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/TimeSlidingWindowSampleTest.java diff --git a/metrics-core/src/main/java/com/yammer/metrics/TimeSlidingWindowSample.java b/metrics-core/src/main/java/com/yammer/metrics/TimeSlidingWindowSample.java new file mode 100644 index 0000000000..57e41cafc2 --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/TimeSlidingWindowSample.java @@ -0,0 +1,82 @@ +package com.yammer.metrics; + +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +/** + * A {@link Sample} implementation which stores only the measurements made in the last {@code N} + * seconds (or other time unit). + */ +public class TimeSlidingWindowSample implements Sample { + // allow for this many duplicate ticks before overwriting measurements + private static final int COLLISION_BUFFER = 100; + + private final Clock clock; + private final ConcurrentSkipListMap measurements; + private final long window; + private final AtomicLong lastTick; + + /** + * Creates a new {@link TimeSlidingWindowSample} with the given window of time. + * + * @param window the window of time + * @param windowUnit the unit of {@code window} + */ + public TimeSlidingWindowSample(long window, TimeUnit windowUnit) { + this(window, windowUnit, Clock.defaultClock()); + } + + /** + * Creates a new {@link TimeSlidingWindowSample} with the given clock and window of time. + * + * @param window the window of time + * @param windowUnit the unit of {@code window} + * @param clock the {@link Clock} to use + */ + public TimeSlidingWindowSample(long window, TimeUnit windowUnit, Clock clock) { + this.clock = clock; + this.measurements = new ConcurrentSkipListMap(); + this.window = windowUnit.toNanos(window) * COLLISION_BUFFER; + this.lastTick = new AtomicLong(); + } + + @Override + public void clear() { + measurements.clear(); + } + + @Override + public int size() { + trim(); + return measurements.size(); + } + + @Override + public void update(long value) { + measurements.put(getTick(), value); + trim(); + } + + @Override + public Snapshot getSnapshot() { + trim(); + return new Snapshot(measurements.values()); + } + + private long getTick() { + for (; ; ) { + final long oldTick = lastTick.get(); + final long tick = clock.getTick() * COLLISION_BUFFER; + // ensure the tick is strictly incrementing even if there are duplicate ticks + final long newTick = tick > oldTick ? tick : oldTick + 1; + if (lastTick.compareAndSet(oldTick, newTick)) { + return newTick; + } + } + } + + private void trim() { + measurements.headMap(getTick() - window).clear(); + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/TimeSlidingWindowSampleTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/TimeSlidingWindowSampleTest.java new file mode 100644 index 0000000000..c942ee1371 --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/TimeSlidingWindowSampleTest.java @@ -0,0 +1,48 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.Clock; +import com.yammer.metrics.TimeSlidingWindowSample; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TimeSlidingWindowSampleTest { + private final Clock clock = mock(Clock.class); + private final TimeSlidingWindowSample sample = new TimeSlidingWindowSample(10, TimeUnit.NANOSECONDS, clock); + + @Test + public void storesMeasurementsWithDuplicateTicks() throws Exception { + when(clock.getTick()).thenReturn(20L); + + sample.update(1); + sample.update(2); + + assertThat(sample.getSnapshot().getValues()) + .containsOnly(1, 2); + } + + @Test + public void boundsMeasurementsToATimeWindow() throws Exception { + when(clock.getTick()).thenReturn(0L); + sample.update(1); + + when(clock.getTick()).thenReturn(5L); + sample.update(2); + + when(clock.getTick()).thenReturn(10L); + sample.update(3); + + when(clock.getTick()).thenReturn(15L); + sample.update(4); + + when(clock.getTick()).thenReturn(20L); + sample.update(5); + + assertThat(sample.getSnapshot().getValues()) + .containsOnly(4, 5); + } +} From 976b15cd8ecd46696393a03add09d7c4e1b879dd Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 11:24:13 -0700 Subject: [PATCH 0135/2558] Standardize on "sliding window". So, SlidingWindowSample and SlidingTimeWindowSample. --- .../yammer/metrics/MovingWindowSample.java | 50 ------------------- ...mple.java => SlidingTimeWindowSample.java} | 14 +++--- .../yammer/metrics/SlidingWindowSample.java | 50 +++++++++++++++++++ ....java => SlidingTimeWindowSampleTest.java} | 6 +-- ...Test.java => SlidingWindowSampleTest.java} | 6 +-- 5 files changed, 63 insertions(+), 63 deletions(-) delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/MovingWindowSample.java rename metrics-core/src/main/java/com/yammer/metrics/{TimeSlidingWindowSample.java => SlidingTimeWindowSample.java} (81%) create mode 100644 metrics-core/src/main/java/com/yammer/metrics/SlidingWindowSample.java rename metrics-core/src/test/java/com/yammer/metrics/tests/{TimeSlidingWindowSampleTest.java => SlidingTimeWindowSampleTest.java} (84%) rename metrics-core/src/test/java/com/yammer/metrics/tests/{MovingWindowSampleTest.java => SlidingWindowSampleTest.java} (79%) diff --git a/metrics-core/src/main/java/com/yammer/metrics/MovingWindowSample.java b/metrics-core/src/main/java/com/yammer/metrics/MovingWindowSample.java deleted file mode 100644 index f9a6885091..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/MovingWindowSample.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.yammer.metrics; - -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicLongArray; - -import static java.lang.Math.min; - -/** - * A {@link Sample} implementation backed by a moving window that stores the last {@code N} - * measurements. - */ -public class MovingWindowSample implements Sample { - private final AtomicLongArray window; - private final AtomicLong index; - - /** - * Creates a new {@link MovingWindowSample} which stores the last {@code size} measurements. - * - * @param size the number of measurements to store - */ - public MovingWindowSample(int size) { - this.window = new AtomicLongArray(size); - this.index = new AtomicLong(); - } - - @Override - public void clear() { - index.set(0); - } - - @Override - public int size() { - return (int) min(index.get(), window.length()); - } - - @Override - public void update(long value) { - final int i = (int) (index.getAndIncrement() % window.length()); - window.set(i, value); - } - - @Override - public Snapshot getSnapshot() { - final long[] values = new long[size()]; - for (int i = 0; i < values.length; i++) { - values[i] = window.get(i); - } - return new Snapshot(values); - } -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/TimeSlidingWindowSample.java b/metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowSample.java similarity index 81% rename from metrics-core/src/main/java/com/yammer/metrics/TimeSlidingWindowSample.java rename to metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowSample.java index 57e41cafc2..703fb3426e 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/TimeSlidingWindowSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowSample.java @@ -5,10 +5,10 @@ import java.util.concurrent.atomic.AtomicLong; /** - * A {@link Sample} implementation which stores only the measurements made in the last {@code N} - * seconds (or other time unit). + * A {@link Sample} implementation backed by a sliding window that stores only the measurements made + * in the last {@code N} seconds (or other time unit). */ -public class TimeSlidingWindowSample implements Sample { +public class SlidingTimeWindowSample implements Sample { // allow for this many duplicate ticks before overwriting measurements private static final int COLLISION_BUFFER = 100; @@ -18,23 +18,23 @@ public class TimeSlidingWindowSample implements Sample { private final AtomicLong lastTick; /** - * Creates a new {@link TimeSlidingWindowSample} with the given window of time. + * Creates a new {@link SlidingTimeWindowSample} with the given window of time. * * @param window the window of time * @param windowUnit the unit of {@code window} */ - public TimeSlidingWindowSample(long window, TimeUnit windowUnit) { + public SlidingTimeWindowSample(long window, TimeUnit windowUnit) { this(window, windowUnit, Clock.defaultClock()); } /** - * Creates a new {@link TimeSlidingWindowSample} with the given clock and window of time. + * Creates a new {@link SlidingTimeWindowSample} with the given clock and window of time. * * @param window the window of time * @param windowUnit the unit of {@code window} * @param clock the {@link Clock} to use */ - public TimeSlidingWindowSample(long window, TimeUnit windowUnit, Clock clock) { + public SlidingTimeWindowSample(long window, TimeUnit windowUnit, Clock clock) { this.clock = clock; this.measurements = new ConcurrentSkipListMap(); this.window = windowUnit.toNanos(window) * COLLISION_BUFFER; diff --git a/metrics-core/src/main/java/com/yammer/metrics/SlidingWindowSample.java b/metrics-core/src/main/java/com/yammer/metrics/SlidingWindowSample.java new file mode 100644 index 0000000000..68223618e4 --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/SlidingWindowSample.java @@ -0,0 +1,50 @@ +package com.yammer.metrics; + +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongArray; + +import static java.lang.Math.min; + +/** + * A {@link Sample} implementation backed by a sliding window that stores the last {@code N} + * measurements. + */ +public class SlidingWindowSample implements Sample { + private final AtomicLongArray measurements; + private final AtomicLong count; + + /** + * Creates a new {@link SlidingWindowSample} which stores the last {@code size} measurements. + * + * @param size the number of measurements to store + */ + public SlidingWindowSample(int size) { + this.measurements = new AtomicLongArray(size); + this.count = new AtomicLong(); + } + + @Override + public void clear() { + count.set(0); + } + + @Override + public int size() { + return (int) min(count.get(), measurements.length()); + } + + @Override + public void update(long value) { + final int i = (int) (count.getAndIncrement() % measurements.length()); + measurements.set(i, value); + } + + @Override + public Snapshot getSnapshot() { + final long[] values = new long[size()]; + for (int i = 0; i < values.length; i++) { + values[i] = measurements.get(i); + } + return new Snapshot(values); + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/TimeSlidingWindowSampleTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingTimeWindowSampleTest.java similarity index 84% rename from metrics-core/src/test/java/com/yammer/metrics/tests/TimeSlidingWindowSampleTest.java rename to metrics-core/src/test/java/com/yammer/metrics/tests/SlidingTimeWindowSampleTest.java index c942ee1371..4c0358b0ed 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/TimeSlidingWindowSampleTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingTimeWindowSampleTest.java @@ -1,7 +1,7 @@ package com.yammer.metrics.tests; import com.yammer.metrics.Clock; -import com.yammer.metrics.TimeSlidingWindowSample; +import com.yammer.metrics.SlidingTimeWindowSample; import org.junit.Test; import java.util.concurrent.TimeUnit; @@ -10,9 +10,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -public class TimeSlidingWindowSampleTest { +public class SlidingTimeWindowSampleTest { private final Clock clock = mock(Clock.class); - private final TimeSlidingWindowSample sample = new TimeSlidingWindowSample(10, TimeUnit.NANOSECONDS, clock); + private final SlidingTimeWindowSample sample = new SlidingTimeWindowSample(10, TimeUnit.NANOSECONDS, clock); @Test public void storesMeasurementsWithDuplicateTicks() throws Exception { diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MovingWindowSampleTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingWindowSampleTest.java similarity index 79% rename from metrics-core/src/test/java/com/yammer/metrics/tests/MovingWindowSampleTest.java rename to metrics-core/src/test/java/com/yammer/metrics/tests/SlidingWindowSampleTest.java index 20529aa5e2..dafe011552 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/MovingWindowSampleTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingWindowSampleTest.java @@ -1,12 +1,12 @@ package com.yammer.metrics.tests; -import com.yammer.metrics.MovingWindowSample; +import com.yammer.metrics.SlidingWindowSample; import org.junit.Test; import static org.fest.assertions.api.Assertions.assertThat; -public class MovingWindowSampleTest { - private final MovingWindowSample sample = new MovingWindowSample(3); +public class SlidingWindowSampleTest { + private final SlidingWindowSample sample = new SlidingWindowSample(3); @Test public void handlesSmallSamples() throws Exception { From 25244eeda64c6dcdf59889a6584851a91039d8e3 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 11:29:22 -0700 Subject: [PATCH 0136/2558] Drop Sample#clear(). --- .../metrics/ExponentiallyDecayingSample.java | 25 ++++--------------- .../main/java/com/yammer/metrics/Sample.java | 5 ---- .../metrics/SlidingTimeWindowSample.java | 5 ---- .../yammer/metrics/SlidingWindowSample.java | 5 ---- .../com/yammer/metrics/UniformSample.java | 5 ---- 5 files changed, 5 insertions(+), 40 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java b/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java index 6ea681e370..34147e01b8 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java @@ -39,9 +39,9 @@ public static ExponentiallyDecayingSample create() { private final ReentrantReadWriteLock lock; private final double alpha; private final int reservoirSize; - private final AtomicLong count = new AtomicLong(0); + private final AtomicLong count; private volatile long startTime; - private final AtomicLong nextScaleTime = new AtomicLong(0); + private final AtomicLong nextScaleTime; private final Clock clock; /** @@ -68,20 +68,9 @@ public ExponentiallyDecayingSample(int reservoirSize, double alpha, Clock clock) this.alpha = alpha; this.reservoirSize = reservoirSize; this.clock = clock; - clear(); - } - - @Override - public void clear() { - lockForRescale(); - try { - values.clear(); - count.set(0); - this.startTime = currentTimeInSeconds(); - nextScaleTime.set(clock.getTick() + RESCALE_THRESHOLD); - } finally { - unlockForRescale(); - } + this.count = new AtomicLong(0); + this.startTime = currentTimeInSeconds(); + this.nextScaleTime = new AtomicLong(clock.getTick() + RESCALE_THRESHOLD); } @Override @@ -101,9 +90,7 @@ public void update(long value) { * @param timestamp the epoch timestamp of {@code value} in seconds */ public void update(long value, long timestamp) { - rescaleIfNeeded(); - lockForRegularUsage(); try { final double priority = weight(timestamp - startTime) / ThreadLocalRandom.current() @@ -123,8 +110,6 @@ public void update(long value, long timestamp) { } finally { unlockForRegularUsage(); } - - } private void rescaleIfNeeded() { diff --git a/metrics-core/src/main/java/com/yammer/metrics/Sample.java b/metrics-core/src/main/java/com/yammer/metrics/Sample.java index da1dd9e157..f02be0529c 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Sample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Sample.java @@ -4,11 +4,6 @@ * A statistically representative sample of a data stream. */ public interface Sample { - /** - * Clears all recorded values. - */ - void clear(); - /** * Returns the number of values recorded. * diff --git a/metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowSample.java b/metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowSample.java index 703fb3426e..be93e279d8 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowSample.java @@ -41,11 +41,6 @@ public SlidingTimeWindowSample(long window, TimeUnit windowUnit, Clock clock) { this.lastTick = new AtomicLong(); } - @Override - public void clear() { - measurements.clear(); - } - @Override public int size() { trim(); diff --git a/metrics-core/src/main/java/com/yammer/metrics/SlidingWindowSample.java b/metrics-core/src/main/java/com/yammer/metrics/SlidingWindowSample.java index 68223618e4..9e0e46acd7 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/SlidingWindowSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/SlidingWindowSample.java @@ -23,11 +23,6 @@ public SlidingWindowSample(int size) { this.count = new AtomicLong(); } - @Override - public void clear() { - count.set(0); - } - @Override public int size() { return (int) min(count.get(), measurements.length()); diff --git a/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java b/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java index 144f12b488..84b0d2dc25 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java @@ -34,11 +34,6 @@ public static UniformSample create() { */ public UniformSample(int reservoirSize) { this.values = new AtomicLongArray(reservoirSize); - clear(); - } - - @Override - public void clear() { for (int i = 0; i < values.length(); i++) { values.set(i, 0); } From d9ec302a869080bc10baef3c1e4f6d02db38d26a Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 11:43:00 -0700 Subject: [PATCH 0137/2558] Updates docs for the sliding window samples. --- docs/source/manual/core.rst | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/source/manual/core.rst b/docs/source/manual/core.rst index 7d5c5f7920..9dca74bce7 100644 --- a/docs/source/manual/core.rst +++ b/docs/source/manual/core.rst @@ -220,14 +220,28 @@ changed. :ref:`man-core-timers` use histograms with exponentially decaying sampl .. __: http://www.research.att.com/people/Cormode_Graham/library/publications/CormodeShkapenyukSrivastavaXu09.pdf -.. _man-core-histograms-moving: +.. _man-core-histograms-sliding: -Moving Window Samples ---------------------- +Sliding Window Samples +---------------------- -A histogram with a moving window sample produces quantiles which are representative of the past +A histogram with a sliding window sample produces quantiles which are representative of the past ``N`` measurements. +.. _man-core-histograms-sliding-time: + +Sliding Time Window Samples +--------------------------- + +A histogram with a sliding time window sample produces quantiles which are strictly representative +of the past ``N`` seconds (or other time period). + +.. warning:: + + While ``SlidingTimeWindowSample`` is easier to understand than ``ExponentiallyDecayingSample``, + it is not bounded in size, so using it to sample a high-frequency process can require a + significant amount of memory. + .. _man-core-meters: Meters From 1c36a4dc71e7bc965362fcb7652104f6d846fa69 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 11:43:29 -0700 Subject: [PATCH 0138/2558] Ditch the factory methods for ExponentiallyDecayingSample and UniformSample. Easier here to just use constructors. --- .../metrics/ExponentiallyDecayingSample.java | 21 ++++++++----------- .../com/yammer/metrics/MetricRegistry.java | 2 +- .../main/java/com/yammer/metrics/Timer.java | 2 +- .../com/yammer/metrics/UniformSample.java | 10 ++++----- .../yammer/metrics/tests/HistogramTest.java | 2 +- .../com/yammer/metrics/tests/TimerTest.java | 2 +- .../servlets/tests/MetricsServletTest.java | 2 +- 7 files changed, 18 insertions(+), 23 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java b/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java index 34147e01b8..da4d43b9d2 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java @@ -21,18 +21,6 @@ public class ExponentiallyDecayingSample implements Sample { private static final int DEFAULT_SAMPLE_SIZE = 1028; private static final double DEFAULT_ALPHA = 0.015; - - /** - * Creates a new {@link ExponentiallyDecayingSample} of 1028 elements, which offers a 99.9% - * confidence level with a 5% margin of error assuming a normal distribution, and an alpha - * factor of 0.015, which heavily biases the sample to the past 5 minutes of measurements. - * - * @return a new {@link ExponentiallyDecayingSample} - */ - public static ExponentiallyDecayingSample create() { - return new ExponentiallyDecayingSample(DEFAULT_SAMPLE_SIZE, DEFAULT_ALPHA); - } - private static final long RESCALE_THRESHOLD = TimeUnit.HOURS.toNanos(1); private final ConcurrentSkipListMap values; @@ -44,6 +32,15 @@ public static ExponentiallyDecayingSample create() { private final AtomicLong nextScaleTime; private final Clock clock; + /** + * Creates a new {@link ExponentiallyDecayingSample} of 1028 elements, which offers a 99.9% + * confidence level with a 5% margin of error assuming a normal distribution, and an alpha + * factor of 0.015, which heavily biases the sample to the past 5 minutes of measurements. + */ + public ExponentiallyDecayingSample() { + this(DEFAULT_SAMPLE_SIZE, DEFAULT_ALPHA); + } + /** * Creates a new {@link ExponentiallyDecayingSample}. * diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java index 0c975ab82d..4d5cb177e3 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -396,7 +396,7 @@ public boolean isInstance(Metric metric) { MetricBuilder HISTOGRAMS = new MetricBuilder() { @Override public Histogram newMetric() { - return new Histogram(ExponentiallyDecayingSample.create()); + return new Histogram(new ExponentiallyDecayingSample()); } @Override diff --git a/metrics-core/src/main/java/com/yammer/metrics/Timer.java b/metrics-core/src/main/java/com/yammer/metrics/Timer.java index d2393ab197..293c7518bb 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Timer.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Timer.java @@ -16,7 +16,7 @@ public class Timer implements Metered, Sampling, Summarizable { * Creates a new {@link Timer}. */ public Timer() { - this(ExponentiallyDecayingSample.create()); + this(new ExponentiallyDecayingSample()); } /** diff --git a/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java b/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java index 84b0d2dc25..6332c0fc4a 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java @@ -18,13 +18,11 @@ public class UniformSample implements Sample { private final AtomicLongArray values; /** - * Creates a new uniform sample of 1028 elements, which offers a 99.9% confidence level with a - * 5% margin of error assuming a normal distribution. - * - * @return a new {@link UniformSample} + * Creates a new {@link UniformSample} of 1028 elements, which offers a 99.9% confidence level + * with a 5% margin of error assuming a normal distribution. */ - public static UniformSample create() { - return new UniformSample(DEFAULT_SAMPLE_SIZE); + public UniformSample() { + this(DEFAULT_SAMPLE_SIZE); } /** diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java index 12410c8fc7..9e35d03586 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java @@ -9,7 +9,7 @@ import static org.fest.assertions.api.Assertions.offset; public class HistogramTest { - private final Histogram histogram = new Histogram(UniformSample.create()); + private final Histogram histogram = new Histogram(new UniformSample()); @Test public void anEmptyHistogram() throws Exception { diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java index 294512bf63..09f73377c3 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java @@ -22,7 +22,7 @@ public long getTick() { return val += 50000000; } }; - private final Timer timer = new Timer(ExponentiallyDecayingSample.create(), clock); + private final Timer timer = new Timer(new ExponentiallyDecayingSample(), clock); @Test public void aBlankTimer() throws Exception { diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java index b39f3502d1..122d1bb44c 100644 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java @@ -35,7 +35,7 @@ public Long getValue() { registry.counter("c").inc(); registry.histogram("h").update(1); registry.register("m", new Meter(clock)).mark(); - registry.register("t", new Timer(ExponentiallyDecayingSample.create(), clock)) + registry.register("t", new Timer(new ExponentiallyDecayingSample(), clock)) .update(1, TimeUnit.SECONDS); request.setMethod("GET"); From 3fa688240d531df18966e3cb6ca63e454cb002e1 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 12:03:37 -0700 Subject: [PATCH 0139/2558] Drop TODO.md. All of these are tickets, basically. --- TODO.md | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 TODO.md diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 1a7cbf42e4..0000000000 --- a/TODO.md +++ /dev/null @@ -1,16 +0,0 @@ -Things What Need Doing -====================== - -* Batch gauges? Sets of gauges which share a common name prefix and pre-read method (e.g., JVM shiz) -* Figure out what configuring Jetty w/ instrumented stuff looks like (JNDI?) -* Figure out what configuring Ehcache w/ instrumented stuff looks like (JNDI?) -* Go through the docs with a fine-toothed comb and make sure things make sense -* Go through ``2.x-maintenance`` and make sure I didn't forget to forward-port something -* Go back and hit ``metrics-servlet`` with a stick -* Add javadocs for pretty much everything -* Do integration testing w/ Ganglia -* Do integration testing w/ Graphite -* Figure out a consistent builder API for complicated reporter constructor (no more accordion - constructors). - -Do you see something here which is near and dear to your heart? WELL COME ON DOWN. From 558ce0e0d2e84dd4832ebd31d4180a2b42762314 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 13:05:41 -0700 Subject: [PATCH 0140/2558] Added Caliper benchmarks for Sample types. --- .../metrics/benchmarks/SampleBenchmark.java | 49 +++++++++++++++++++ pom.xml | 6 +++ 2 files changed, 55 insertions(+) create mode 100644 metrics-core/src/test/java/com/yammer/metrics/benchmarks/SampleBenchmark.java diff --git a/metrics-core/src/test/java/com/yammer/metrics/benchmarks/SampleBenchmark.java b/metrics-core/src/test/java/com/yammer/metrics/benchmarks/SampleBenchmark.java new file mode 100644 index 0000000000..747ffd9d7a --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/benchmarks/SampleBenchmark.java @@ -0,0 +1,49 @@ +package com.yammer.metrics.benchmarks; + +import com.google.caliper.Runner; +import com.google.caliper.SimpleBenchmark; +import com.yammer.metrics.ExponentiallyDecayingSample; +import com.yammer.metrics.SlidingTimeWindowSample; +import com.yammer.metrics.SlidingWindowSample; +import com.yammer.metrics.UniformSample; + +import java.util.concurrent.TimeUnit; + +public class SampleBenchmark extends SimpleBenchmark { + public static void main(String[] args) throws Exception { + new Runner().run(SampleBenchmark.class.getName()); + } + + private final UniformSample uniform = new UniformSample(); + private final ExponentiallyDecayingSample exponential = new ExponentiallyDecayingSample(); + private final SlidingWindowSample sliding = new SlidingWindowSample(1000); + private final SlidingTimeWindowSample slidingTime = new SlidingTimeWindowSample(1, TimeUnit.SECONDS); + + @SuppressWarnings("UnusedDeclaration") + public void timeUniformSample(int reps) { + for (int i = 0; i < reps; i++) { + uniform.update(i); + } + } + + @SuppressWarnings("UnusedDeclaration") + public void timeExponentiallyDecayingSample(int reps) { + for (int i = 0; i < reps; i++) { + exponential.update(i); + } + } + + @SuppressWarnings("UnusedDeclaration") + public void timeSlidingWindowSample(int reps) { + for (int i = 0; i < reps; i++) { + sliding.update(i); + } + } + + @SuppressWarnings("UnusedDeclaration") + public void timeSlidingTimeWindowSample(int reps) { + for (int i = 0; i < reps; i++) { + slidingTime.update(i); + } + } +} diff --git a/pom.xml b/pom.xml index bcd1171a25..fec54c96f8 100644 --- a/pom.xml +++ b/pom.xml @@ -117,6 +117,12 @@ ${slf4j.version} test + + com.google.caliper + caliper + 0.5-rc1 + test + From cd68246fa3135e3f10064e8d4409913e83f10c25 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 15:57:18 -0700 Subject: [PATCH 0141/2558] Added benchmarks for counters and meters. --- .../metrics/benchmarks/CounterBenchmark.java | 20 +++++++++++++++++++ .../metrics/benchmarks/MeterBenchmark.java | 20 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 metrics-core/src/test/java/com/yammer/metrics/benchmarks/CounterBenchmark.java create mode 100644 metrics-core/src/test/java/com/yammer/metrics/benchmarks/MeterBenchmark.java diff --git a/metrics-core/src/test/java/com/yammer/metrics/benchmarks/CounterBenchmark.java b/metrics-core/src/test/java/com/yammer/metrics/benchmarks/CounterBenchmark.java new file mode 100644 index 0000000000..f9994797b9 --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/benchmarks/CounterBenchmark.java @@ -0,0 +1,20 @@ +package com.yammer.metrics.benchmarks; + +import com.google.caliper.Runner; +import com.google.caliper.SimpleBenchmark; +import com.yammer.metrics.Counter; + +public class CounterBenchmark extends SimpleBenchmark { + public static void main(String[] args) throws Exception { + new Runner().run(CounterBenchmark.class.getName()); + } + + private final Counter counter = new Counter(); + + @SuppressWarnings("unused") + public void timeIncrement(int reps) { + for (int i = 0; i < reps; i++) { + counter.inc(i); + } + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/benchmarks/MeterBenchmark.java b/metrics-core/src/test/java/com/yammer/metrics/benchmarks/MeterBenchmark.java new file mode 100644 index 0000000000..f2fdb2f4cc --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/benchmarks/MeterBenchmark.java @@ -0,0 +1,20 @@ +package com.yammer.metrics.benchmarks; + +import com.google.caliper.Runner; +import com.google.caliper.SimpleBenchmark; +import com.yammer.metrics.Meter; + +public class MeterBenchmark extends SimpleBenchmark { + public static void main(String[] args) throws Exception { + new Runner().run(MeterBenchmark.class.getName()); + } + + private final Meter meter = new Meter(); + + @SuppressWarnings("unused") + public void timeMark(int reps) { + for (int i = 0; i < reps; i++) { + meter.mark(i); + } + } +} From d3a8f5dfa2c307e78ed3b14c1cdbbb32db2ee6ec Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 16:14:09 -0700 Subject: [PATCH 0142/2558] Mature optimization. --- .../com/yammer/metrics/SlidingTimeWindowSample.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowSample.java b/metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowSample.java index be93e279d8..5f8f5953b3 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowSample.java @@ -10,12 +10,15 @@ */ public class SlidingTimeWindowSample implements Sample { // allow for this many duplicate ticks before overwriting measurements - private static final int COLLISION_BUFFER = 100; + private static final int COLLISION_BUFFER = 256; + // only trim on updating once every N + private static final int TRIM_THRESHOLD = 256; private final Clock clock; private final ConcurrentSkipListMap measurements; private final long window; private final AtomicLong lastTick; + private final AtomicLong count; /** * Creates a new {@link SlidingTimeWindowSample} with the given window of time. @@ -39,6 +42,7 @@ public SlidingTimeWindowSample(long window, TimeUnit windowUnit, Clock clock) { this.measurements = new ConcurrentSkipListMap(); this.window = windowUnit.toNanos(window) * COLLISION_BUFFER; this.lastTick = new AtomicLong(); + this.count = new AtomicLong(); } @Override @@ -49,8 +53,10 @@ public int size() { @Override public void update(long value) { + if (count.incrementAndGet() % TRIM_THRESHOLD == 0) { + trim(); + } measurements.put(getTick(), value); - trim(); } @Override From 6dede62c5fd4470038f4843f6e18d104d296deb5 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 19:46:36 -0700 Subject: [PATCH 0143/2558] Update some javadocs. --- metrics-core/src/main/java/com/yammer/metrics/EWMA.java | 1 + metrics-core/src/main/java/com/yammer/metrics/Meter.java | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/EWMA.java b/metrics-core/src/main/java/com/yammer/metrics/EWMA.java index 845a029296..4bef9f58da 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/EWMA.java +++ b/metrics-core/src/main/java/com/yammer/metrics/EWMA.java @@ -12,6 +12,7 @@ * It Works * @see UNIX Load Average Part 2: Not * Your Average Average + * @see EMA */ public class EWMA { private static final int INTERVAL = 5; diff --git a/metrics-core/src/main/java/com/yammer/metrics/Meter.java b/metrics-core/src/main/java/com/yammer/metrics/Meter.java index d4ac5cbb4b..f68cb5e752 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Meter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Meter.java @@ -7,7 +7,6 @@ * A meter metric which measures mean throughput and one-, five-, and fifteen-minute * exponentially-weighted moving average throughputs. * - * @see EMA * @see EWMA */ public class Meter implements Metered { From 84e09b9e06b538a2e327dca842840db79ec7583f Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 19:54:55 -0700 Subject: [PATCH 0144/2558] Make Timer.Context closeable. This way it can play nicely with Java 7's try-with-resources. --- .../main/java/com/yammer/metrics/Timer.java | 60 ++++++++++--------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/Timer.java b/metrics-core/src/main/java/com/yammer/metrics/Timer.java index 293c7518bb..df90e0005d 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Timer.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Timer.java @@ -1,5 +1,6 @@ package com.yammer.metrics; +import java.io.Closeable; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; @@ -8,6 +9,38 @@ * throughput statistics via {@link Meter}. */ public class Timer implements Metered, Sampling, Summarizable { + /** + * A timing context. + * + * @see Timer#time() + */ + public static class Context implements Closeable { + private final Timer timer; + private final Clock clock; + private final long startTime; + + private Context(Timer timer, Clock clock) { + this.timer = timer; + this.clock = clock; + this.startTime = clock.getTick(); + } + + /** + * Stops recording the elapsed time, updates the timer and returns the elapsed time in + * nanoseconds. + */ + public long stop() { + final long elapsed = clock.getTick() - startTime; + timer.update(elapsed, TimeUnit.NANOSECONDS); + return elapsed; + } + + @Override + public void close() { + stop(); + } + } + private final Meter meter; private final Histogram histogram; private final Clock clock; @@ -164,31 +197,4 @@ private void update(long duration) { meter.mark(); } } - - /** - * A timing context. - * - * @see Timer#time() - */ - public static class Context { - private final Timer timer; - private final Clock clock; - private final long startTime; - - private Context(Timer timer, Clock clock) { - this.timer = timer; - this.clock = clock; - this.startTime = clock.getTick(); - } - - /** - * Stops recording the elapsed time, updates the timer and returns the elapsed time in - * nanoseconds. - */ - public long stop() { - final long elapsedNanos = clock.getTick() - startTime; - timer.update(elapsedNanos, TimeUnit.NANOSECONDS); - return elapsedNanos; - } - } } From 140c5a99b9ec9f1b3bc8c02a8dbcf0e322e663f3 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 20:22:26 -0700 Subject: [PATCH 0145/2558] Renamed Sample to Reservoir. Should have followed @joewilliams's lead on this a while ago with Folsom. --- docs/source/manual/core.rst | 49 ++++++------- ...va => ExponentiallyDecayingReservoir.java} | 50 +++++++------- .../java/com/yammer/metrics/Histogram.java | 14 ++-- .../com/yammer/metrics/MetricRegistry.java | 2 +- .../metrics/{Sample.java => Reservoir.java} | 10 +-- ...e.java => SlidingTimeWindowReservoir.java} | 12 ++-- ...ample.java => SlidingWindowReservoir.java} | 8 +-- .../java/com/yammer/metrics/Snapshot.java | 6 +- .../main/java/com/yammer/metrics/Timer.java | 18 ++--- ...iformSample.java => UniformReservoir.java} | 20 +++--- .../benchmarks/ReservoirBenchmark.java | 48 +++++++++++++ .../metrics/benchmarks/SampleBenchmark.java | 49 ------------- ...> ExponentiallyDecayingReservoirTest.java} | 69 ++++++++++--------- .../yammer/metrics/tests/HistogramTest.java | 5 +- ...va => SlidingTimeWindowReservoirTest.java} | 24 +++---- .../tests/SlidingWindowReservoirTest.java | 30 ++++++++ .../tests/SlidingWindowSampleTest.java | 30 -------- .../com/yammer/metrics/tests/TimerTest.java | 5 +- ...pleTest.java => UniformReservoirTest.java} | 14 ++-- .../servlets/tests/MetricsServletTest.java | 2 +- 20 files changed, 235 insertions(+), 230 deletions(-) rename metrics-core/src/main/java/com/yammer/metrics/{ExponentiallyDecayingSample.java => ExponentiallyDecayingReservoir.java} (77%) rename metrics-core/src/main/java/com/yammer/metrics/{Sample.java => Reservoir.java} (56%) rename metrics-core/src/main/java/com/yammer/metrics/{SlidingTimeWindowSample.java => SlidingTimeWindowReservoir.java} (81%) rename metrics-core/src/main/java/com/yammer/metrics/{SlidingWindowSample.java => SlidingWindowReservoir.java} (76%) rename metrics-core/src/main/java/com/yammer/metrics/{UniformSample.java => UniformReservoir.java} (77%) create mode 100644 metrics-core/src/test/java/com/yammer/metrics/benchmarks/ReservoirBenchmark.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/benchmarks/SampleBenchmark.java rename metrics-core/src/test/java/com/yammer/metrics/tests/{ExponentiallyDecayingSampleTest.java => ExponentiallyDecayingReservoirTest.java} (55%) rename metrics-core/src/test/java/com/yammer/metrics/tests/{SlidingTimeWindowSampleTest.java => SlidingTimeWindowReservoirTest.java} (61%) create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/SlidingWindowReservoirTest.java delete mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/SlidingWindowSampleTest.java rename metrics-core/src/test/java/com/yammer/metrics/tests/{UniformSampleTest.java => UniformReservoirTest.java} (60%) diff --git a/docs/source/manual/core.rst b/docs/source/manual/core.rst index 9dca74bce7..cc8bbbb875 100644 --- a/docs/source/manual/core.rst +++ b/docs/source/manual/core.rst @@ -186,21 +186,22 @@ works for small data sets, or batch processing systems, but not for high-through services. The solution for this is to sample the data as it goes through. By maintaining a small, manageable -sample which is statistically representative of the data stream as a whole, we can quickly and +reservoir which is statistically representative of the data stream as a whole, we can quickly and easily calculate quantiles which are valid approximations of the actual quantiles. This technique is called **reservoir sampling**. -Metrics provides a number of different ``Sample`` implementations, each of which is useful. +Metrics provides a number of different ``Reservoir`` implementations, each of which is useful. .. _man-core-histograms-uniform: -Uniform Samples ---------------- +Uniform Reservoirs +------------------ -A histogram with a uniform sample produces quantiles which are valid for the entirely of the +A histogram with a uniform reservoir produces quantiles which are valid for the entirely of the histogram's lifetime. It will return a median value, for example, which is the median of all the values the histogram has ever been updated with. It does this by using an algorithm called -`Vitter's R`__), which randomly selects values for the sample with linearly-decreasing probability. +`Vitter's R`__), which randomly selects values for the reservoir with linearly-decreasing +probability. .. __: http://www.cs.umd.edu/~samir/498/vitter.pdf @@ -209,38 +210,40 @@ want to know if the distribution of the underlying data stream has changed recen .. _man-core-histograms-exponential: -Exponentially Decaying Samples ------------------------------- +Exponentially Decaying Reservoirs +--------------------------------- -A histogram with an exponentially decaying sample produces quantiles which are representative of -(roughly) the last five minutes of data. It does so by using a `forward-decaying priority sample`__ -with an exponential weighting towards newer data. Unlike the uniform histogram, a biased histogram -represents **recent data**, allowing you to know very quickly if the distribution of the data has -changed. :ref:`man-core-timers` use histograms with exponentially decaying samples. +A histogram with an exponentially decaying reservoir produces quantiles which are representative of +(roughly) the last five minutes of data. It does so by using a +`forward-decaying priority reservoir`__ with an exponential weighting towards newer data. Unlike the +uniform reservoir, an exponentially decaying reservoir represents **recent data**, allowing you to +know very quickly if the distribution of the data has changed. :ref:`man-core-timers` use histograms +with exponentially decaying reservoirs by default. .. __: http://www.research.att.com/people/Cormode_Graham/library/publications/CormodeShkapenyukSrivastavaXu09.pdf .. _man-core-histograms-sliding: -Sliding Window Samples ----------------------- +Sliding Window Reservoirs +------------------------- -A histogram with a sliding window sample produces quantiles which are representative of the past +A histogram with a sliding window reservoir produces quantiles which are representative of the past ``N`` measurements. .. _man-core-histograms-sliding-time: -Sliding Time Window Samples ---------------------------- +Sliding Time Window Reservoirs +------------------------------ -A histogram with a sliding time window sample produces quantiles which are strictly representative -of the past ``N`` seconds (or other time period). +A histogram with a sliding time window reservoir produces quantiles which are strictly +representative of the past ``N`` seconds (or other time period). .. warning:: - While ``SlidingTimeWindowSample`` is easier to understand than ``ExponentiallyDecayingSample``, - it is not bounded in size, so using it to sample a high-frequency process can require a - significant amount of memory. + While ``SlidingTimeWindowReservoir`` is easier to understand than + ``ExponentiallyDecayingReservoir``, it is not bounded in size, so using it to sample a + high-frequency process can require a significant amount of memory. Because it records every + measurement, it's also the slowest reservoir type. .. _man-core-meters: diff --git a/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java b/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingReservoir.java similarity index 77% rename from metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java rename to metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingReservoir.java index da4d43b9d2..8b31da979c 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingReservoir.java @@ -10,60 +10,60 @@ import static java.lang.Math.min; /** - * An exponentially-decaying random sample of {@code long}s. Uses Cormode et al's forward-decaying - * priority reservoir sampling method to produce a statistically representative sample, - * exponentially biased towards newer entries. + * An exponentially-decaying random reservoir of {@code long}s. Uses Cormode et al's + * forward-decaying priority reservoir sampling method to produce a statistically representative + * sampling reservoir, exponentially biased towards newer entries. * * @see * Cormode et al. Forward Decay: A Practical Time Decay Model for Streaming Systems. ICDE '09: * Proceedings of the 2009 IEEE International Conference on Data Engineering (2009) */ -public class ExponentiallyDecayingSample implements Sample { - private static final int DEFAULT_SAMPLE_SIZE = 1028; +public class ExponentiallyDecayingReservoir implements Reservoir { + private static final int DEFAULT_SIZE = 1028; private static final double DEFAULT_ALPHA = 0.015; private static final long RESCALE_THRESHOLD = TimeUnit.HOURS.toNanos(1); private final ConcurrentSkipListMap values; private final ReentrantReadWriteLock lock; private final double alpha; - private final int reservoirSize; + private final int size; private final AtomicLong count; private volatile long startTime; private final AtomicLong nextScaleTime; private final Clock clock; /** - * Creates a new {@link ExponentiallyDecayingSample} of 1028 elements, which offers a 99.9% + * Creates a new {@link ExponentiallyDecayingReservoir} of 1028 elements, which offers a 99.9% * confidence level with a 5% margin of error assuming a normal distribution, and an alpha - * factor of 0.015, which heavily biases the sample to the past 5 minutes of measurements. + * factor of 0.015, which heavily biases the reservoir to the past 5 minutes of measurements. */ - public ExponentiallyDecayingSample() { - this(DEFAULT_SAMPLE_SIZE, DEFAULT_ALPHA); + public ExponentiallyDecayingReservoir() { + this(DEFAULT_SIZE, DEFAULT_ALPHA); } /** - * Creates a new {@link ExponentiallyDecayingSample}. + * Creates a new {@link ExponentiallyDecayingReservoir}. * - * @param reservoirSize the number of samples to keep in the sampling reservoir - * @param alpha the exponential decay factor; the higher this is, the more biased the - * sample will be towards newer values + * @param size the number of samples to keep in the sampling reservoir + * @param alpha the exponential decay factor; the higher this is, the more biased the reservoir + * will be towards newer values */ - public ExponentiallyDecayingSample(int reservoirSize, double alpha) { - this(reservoirSize, alpha, Clock.defaultClock()); + public ExponentiallyDecayingReservoir(int size, double alpha) { + this(size, alpha, Clock.defaultClock()); } /** - * Creates a new {@link ExponentiallyDecayingSample}. + * Creates a new {@link ExponentiallyDecayingReservoir}. * - * @param reservoirSize the number of samples to keep in the sampling reservoir - * @param alpha the exponential decay factor; the higher this is, the more biased the - * sample will be towards newer values + * @param size the number of samples to keep in the sampling reservoir + * @param alpha the exponential decay factor; the higher this is, the more biased the reservoir + * will be towards newer values */ - public ExponentiallyDecayingSample(int reservoirSize, double alpha, Clock clock) { + public ExponentiallyDecayingReservoir(int size, double alpha, Clock clock) { this.values = new ConcurrentSkipListMap(); this.lock = new ReentrantReadWriteLock(); this.alpha = alpha; - this.reservoirSize = reservoirSize; + this.size = size; this.clock = clock; this.count = new AtomicLong(0); this.startTime = currentTimeInSeconds(); @@ -72,7 +72,7 @@ public ExponentiallyDecayingSample(int reservoirSize, double alpha, Clock clock) @Override public int size() { - return (int) min(reservoirSize, count.get()); + return (int) min(size, count.get()); } @Override @@ -81,7 +81,7 @@ public void update(long value) { } /** - * Adds an old value with a fixed timestamp to the sample. + * Adds an old value with a fixed timestamp to the reservoir. * * @param value the value to be added * @param timestamp the epoch timestamp of {@code value} in seconds @@ -93,7 +93,7 @@ public void update(long value, long timestamp) { final double priority = weight(timestamp - startTime) / ThreadLocalRandom.current() .nextDouble(); final long newCount = count.incrementAndGet(); - if (newCount <= reservoirSize) { + if (newCount <= size) { values.put(priority, value); } else { Double first = values.firstKey(); diff --git a/metrics-core/src/main/java/com/yammer/metrics/Histogram.java b/metrics-core/src/main/java/com/yammer/metrics/Histogram.java index 8160aa8b25..84634f491d 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Histogram.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Histogram.java @@ -12,7 +12,7 @@ * variance */ public class Histogram implements Metric, Sampling, Summarizable { - private final Sample sample; + private final Reservoir reservoir; private final AtomicLong min; private final AtomicLong max; private final AtomicLong sum; @@ -22,12 +22,12 @@ public class Histogram implements Metric, Sampling, Summarizable { private final AtomicLong count; /** - * Creates a new {@link Histogram} with the given sample. + * Creates a new {@link Histogram} with the given reservoir. * - * @param sample the sample to create a histogram from + * @param reservoir the reservoir to create a histogram from */ - public Histogram(Sample sample) { - this.sample = sample; + public Histogram(Reservoir reservoir) { + this.reservoir = reservoir; this.min = new AtomicLong(Long.MAX_VALUE); this.max = new AtomicLong(Long.MIN_VALUE); this.sum = new AtomicLong(0); @@ -51,7 +51,7 @@ public void update(int value) { */ public void update(long value) { count.incrementAndGet(); - sample.update(value); + reservoir.update(value); setMax(value); setMin(value); sum.getAndAdd(value); @@ -106,7 +106,7 @@ public long getSum() { @Override public Snapshot getSnapshot() { - return sample.getSnapshot(); + return reservoir.getSnapshot(); } private double getVariance() { diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java index 4d5cb177e3..1f123183fa 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -396,7 +396,7 @@ public boolean isInstance(Metric metric) { MetricBuilder HISTOGRAMS = new MetricBuilder() { @Override public Histogram newMetric() { - return new Histogram(new ExponentiallyDecayingSample()); + return new Histogram(new ExponentiallyDecayingReservoir()); } @Override diff --git a/metrics-core/src/main/java/com/yammer/metrics/Sample.java b/metrics-core/src/main/java/com/yammer/metrics/Reservoir.java similarity index 56% rename from metrics-core/src/main/java/com/yammer/metrics/Sample.java rename to metrics-core/src/main/java/com/yammer/metrics/Reservoir.java index f02be0529c..368a1e3009 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Sample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Reservoir.java @@ -1,9 +1,9 @@ package com.yammer.metrics; /** - * A statistically representative sample of a data stream. + * A statistically representative reservoir of a data stream. */ -public interface Sample { +public interface Reservoir { /** * Returns the number of values recorded. * @@ -12,16 +12,16 @@ public interface Sample { int size(); /** - * Adds a new recorded value to the sample. + * Adds a new recorded value to the reservoir. * * @param value a new recorded value */ void update(long value); /** - * Returns a snapshot of the sample's values. + * Returns a snapshot of the reservoir's values. * - * @return a snapshot of the sample's values + * @return a snapshot of the reservoir's values */ Snapshot getSnapshot(); } diff --git a/metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowSample.java b/metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowReservoir.java similarity index 81% rename from metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowSample.java rename to metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowReservoir.java index 5f8f5953b3..df6ffbdc84 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowReservoir.java @@ -5,10 +5,10 @@ import java.util.concurrent.atomic.AtomicLong; /** - * A {@link Sample} implementation backed by a sliding window that stores only the measurements made + * A {@link Reservoir} implementation backed by a sliding window that stores only the measurements made * in the last {@code N} seconds (or other time unit). */ -public class SlidingTimeWindowSample implements Sample { +public class SlidingTimeWindowReservoir implements Reservoir { // allow for this many duplicate ticks before overwriting measurements private static final int COLLISION_BUFFER = 256; // only trim on updating once every N @@ -21,23 +21,23 @@ public class SlidingTimeWindowSample implements Sample { private final AtomicLong count; /** - * Creates a new {@link SlidingTimeWindowSample} with the given window of time. + * Creates a new {@link SlidingTimeWindowReservoir} with the given window of time. * * @param window the window of time * @param windowUnit the unit of {@code window} */ - public SlidingTimeWindowSample(long window, TimeUnit windowUnit) { + public SlidingTimeWindowReservoir(long window, TimeUnit windowUnit) { this(window, windowUnit, Clock.defaultClock()); } /** - * Creates a new {@link SlidingTimeWindowSample} with the given clock and window of time. + * Creates a new {@link SlidingTimeWindowReservoir} with the given clock and window of time. * * @param window the window of time * @param windowUnit the unit of {@code window} * @param clock the {@link Clock} to use */ - public SlidingTimeWindowSample(long window, TimeUnit windowUnit, Clock clock) { + public SlidingTimeWindowReservoir(long window, TimeUnit windowUnit, Clock clock) { this.clock = clock; this.measurements = new ConcurrentSkipListMap(); this.window = windowUnit.toNanos(window) * COLLISION_BUFFER; diff --git a/metrics-core/src/main/java/com/yammer/metrics/SlidingWindowSample.java b/metrics-core/src/main/java/com/yammer/metrics/SlidingWindowReservoir.java similarity index 76% rename from metrics-core/src/main/java/com/yammer/metrics/SlidingWindowSample.java rename to metrics-core/src/main/java/com/yammer/metrics/SlidingWindowReservoir.java index 9e0e46acd7..43cd153518 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/SlidingWindowSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/SlidingWindowReservoir.java @@ -6,19 +6,19 @@ import static java.lang.Math.min; /** - * A {@link Sample} implementation backed by a sliding window that stores the last {@code N} + * A {@link Reservoir} implementation backed by a sliding window that stores the last {@code N} * measurements. */ -public class SlidingWindowSample implements Sample { +public class SlidingWindowReservoir implements Reservoir { private final AtomicLongArray measurements; private final AtomicLong count; /** - * Creates a new {@link SlidingWindowSample} which stores the last {@code size} measurements. + * Creates a new {@link SlidingWindowReservoir} which stores the last {@code size} measurements. * * @param size the number of measurements to store */ - public SlidingWindowSample(int size) { + public SlidingWindowReservoir(int size) { this.measurements = new AtomicLongArray(size); this.count = new AtomicLong(); } diff --git a/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java b/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java index a7ce5e8f9d..f8ac7cfff1 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java @@ -17,7 +17,7 @@ public class Snapshot { /** * Create a new {@link Snapshot} with the given values. * - * @param values an unordered set of values in the sample + * @param values an unordered set of values in the reservoir */ public Snapshot(Collection values) { final Object[] copy = values.toArray(); @@ -31,7 +31,7 @@ public Snapshot(Collection values) { /** * Create a new {@link Snapshot} with the given values. * - * @param values an unordered set of values in the sample + * @param values an unordered set of values in the reservoir */ public Snapshot(long[] values) { this.values = Arrays.copyOf(values, values.length); @@ -141,7 +141,7 @@ public long[] getValues() { } /** - * Writes the values of the sample to the given stream. + * Writes the values of the snapshot to the given stream. * * @param output an output stream */ diff --git a/metrics-core/src/main/java/com/yammer/metrics/Timer.java b/metrics-core/src/main/java/com/yammer/metrics/Timer.java index df90e0005d..1d50d38f96 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Timer.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Timer.java @@ -49,28 +49,28 @@ public void close() { * Creates a new {@link Timer}. */ public Timer() { - this(new ExponentiallyDecayingSample()); + this(new ExponentiallyDecayingReservoir()); } /** - * Creates a new {@link Timer} that uses the given {@link Sample}. + * Creates a new {@link Timer} that uses the given {@link Reservoir}. * - * @param sample the {@link Sample} implementation the timer should use + * @param reservoir the {@link Reservoir} implementation the timer should use */ - public Timer(Sample sample) { - this(sample, Clock.defaultClock()); + public Timer(Reservoir reservoir) { + this(reservoir, Clock.defaultClock()); } /** - * Creates a new {@link Timer} that uses the given {@link Sample} and {@link Clock}. + * Creates a new {@link Timer} that uses the given {@link Reservoir} and {@link Clock}. * - * @param sample the {@link Sample} implementation the timer should use + * @param reservoir the {@link Reservoir} implementation the timer should use * @param clock the {@link Clock} implementation the timer should use */ - public Timer(Sample sample, Clock clock) { + public Timer(Reservoir reservoir, Clock clock) { this.meter = new Meter(clock); this.clock = clock; - this.histogram = new Histogram(sample); + this.histogram = new Histogram(reservoir); } /** diff --git a/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java b/metrics-core/src/main/java/com/yammer/metrics/UniformReservoir.java similarity index 77% rename from metrics-core/src/main/java/com/yammer/metrics/UniformSample.java rename to metrics-core/src/main/java/com/yammer/metrics/UniformReservoir.java index 6332c0fc4a..cd37029b3b 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/UniformSample.java +++ b/metrics-core/src/main/java/com/yammer/metrics/UniformReservoir.java @@ -6,32 +6,32 @@ import java.util.concurrent.atomic.AtomicLongArray; /** - * A random sample of a stream of {@code long}s. Uses Vitter's Algorithm R to produce a + * A random sampling reservoir of a stream of {@code long}s. Uses Vitter's Algorithm R to produce a * statistically representative sample. * * @see Random Sampling with a Reservoir */ -public class UniformSample implements Sample { - private static final int DEFAULT_SAMPLE_SIZE = 1028; +public class UniformReservoir implements Reservoir { + private static final int DEFAULT_SIZE = 1028; private static final int BITS_PER_LONG = 63; private final AtomicLong count = new AtomicLong(); private final AtomicLongArray values; /** - * Creates a new {@link UniformSample} of 1028 elements, which offers a 99.9% confidence level + * Creates a new {@link UniformReservoir} of 1028 elements, which offers a 99.9% confidence level * with a 5% margin of error assuming a normal distribution. */ - public UniformSample() { - this(DEFAULT_SAMPLE_SIZE); + public UniformReservoir() { + this(DEFAULT_SIZE); } /** - * Creates a new {@link UniformSample}. + * Creates a new {@link UniformReservoir}. * - * @param reservoirSize the number of samples to keep in the sampling reservoir + * @param size the number of samples to keep in the sampling reservoir */ - public UniformSample(int reservoirSize) { - this.values = new AtomicLongArray(reservoirSize); + public UniformReservoir(int size) { + this.values = new AtomicLongArray(size); for (int i = 0; i < values.length(); i++) { values.set(i, 0); } diff --git a/metrics-core/src/test/java/com/yammer/metrics/benchmarks/ReservoirBenchmark.java b/metrics-core/src/test/java/com/yammer/metrics/benchmarks/ReservoirBenchmark.java new file mode 100644 index 0000000000..00b085ae64 --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/benchmarks/ReservoirBenchmark.java @@ -0,0 +1,48 @@ +package com.yammer.metrics.benchmarks; + +import com.google.caliper.Runner; +import com.google.caliper.SimpleBenchmark; +import com.yammer.metrics.*; +import com.yammer.metrics.ExponentiallyDecayingReservoir; +import com.yammer.metrics.SlidingWindowReservoir; + +import java.util.concurrent.TimeUnit; + +public class ReservoirBenchmark extends SimpleBenchmark { + public static void main(String[] args) throws Exception { + new Runner().run(ReservoirBenchmark.class.getName()); + } + + private final UniformReservoir uniform = new UniformReservoir(); + private final ExponentiallyDecayingReservoir exponential = new ExponentiallyDecayingReservoir(); + private final SlidingWindowReservoir sliding = new SlidingWindowReservoir(1000); + private final SlidingTimeWindowReservoir slidingTime = new SlidingTimeWindowReservoir(1, TimeUnit.SECONDS); + + @SuppressWarnings("UnusedDeclaration") + public void timeUniformReservoir(int reps) { + for (int i = 0; i < reps; i++) { + uniform.update(i); + } + } + + @SuppressWarnings("UnusedDeclaration") + public void timeExponentiallyDecayingReservoir(int reps) { + for (int i = 0; i < reps; i++) { + exponential.update(i); + } + } + + @SuppressWarnings("UnusedDeclaration") + public void timeSlidingWindowReservoir(int reps) { + for (int i = 0; i < reps; i++) { + sliding.update(i); + } + } + + @SuppressWarnings("UnusedDeclaration") + public void timeSlidingTimeWindowReservoir(int reps) { + for (int i = 0; i < reps; i++) { + slidingTime.update(i); + } + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/benchmarks/SampleBenchmark.java b/metrics-core/src/test/java/com/yammer/metrics/benchmarks/SampleBenchmark.java deleted file mode 100644 index 747ffd9d7a..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/benchmarks/SampleBenchmark.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.yammer.metrics.benchmarks; - -import com.google.caliper.Runner; -import com.google.caliper.SimpleBenchmark; -import com.yammer.metrics.ExponentiallyDecayingSample; -import com.yammer.metrics.SlidingTimeWindowSample; -import com.yammer.metrics.SlidingWindowSample; -import com.yammer.metrics.UniformSample; - -import java.util.concurrent.TimeUnit; - -public class SampleBenchmark extends SimpleBenchmark { - public static void main(String[] args) throws Exception { - new Runner().run(SampleBenchmark.class.getName()); - } - - private final UniformSample uniform = new UniformSample(); - private final ExponentiallyDecayingSample exponential = new ExponentiallyDecayingSample(); - private final SlidingWindowSample sliding = new SlidingWindowSample(1000); - private final SlidingTimeWindowSample slidingTime = new SlidingTimeWindowSample(1, TimeUnit.SECONDS); - - @SuppressWarnings("UnusedDeclaration") - public void timeUniformSample(int reps) { - for (int i = 0; i < reps; i++) { - uniform.update(i); - } - } - - @SuppressWarnings("UnusedDeclaration") - public void timeExponentiallyDecayingSample(int reps) { - for (int i = 0; i < reps; i++) { - exponential.update(i); - } - } - - @SuppressWarnings("UnusedDeclaration") - public void timeSlidingWindowSample(int reps) { - for (int i = 0; i < reps; i++) { - sliding.update(i); - } - } - - @SuppressWarnings("UnusedDeclaration") - public void timeSlidingTimeWindowSample(int reps) { - for (int i = 0; i < reps; i++) { - slidingTime.update(i); - } - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/ExponentiallyDecayingSampleTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/ExponentiallyDecayingReservoirTest.java similarity index 55% rename from metrics-core/src/test/java/com/yammer/metrics/tests/ExponentiallyDecayingSampleTest.java rename to metrics-core/src/test/java/com/yammer/metrics/tests/ExponentiallyDecayingReservoirTest.java index 39ccfcef26..c19b48e53c 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/ExponentiallyDecayingSampleTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/ExponentiallyDecayingReservoirTest.java @@ -1,7 +1,7 @@ package com.yammer.metrics.tests; import com.yammer.metrics.Clock; -import com.yammer.metrics.ExponentiallyDecayingSample; +import com.yammer.metrics.ExponentiallyDecayingReservoir; import com.yammer.metrics.Snapshot; import org.junit.Test; @@ -9,33 +9,33 @@ import static org.fest.assertions.api.Assertions.assertThat; -public class ExponentiallyDecayingSampleTest { +public class ExponentiallyDecayingReservoirTest { @Test - public void aSampleOf100OutOf1000Elements() throws Exception { - final ExponentiallyDecayingSample sample = new ExponentiallyDecayingSample(100, 0.99); + public void aReservoirOf100OutOf1000Elements() throws Exception { + final ExponentiallyDecayingReservoir reservoir = new ExponentiallyDecayingReservoir(100, 0.99); for (int i = 0; i < 1000; i++) { - sample.update(i); + reservoir.update(i); } - assertThat(sample.size()) + assertThat(reservoir.size()) .isEqualTo(100); - final Snapshot snapshot = sample.getSnapshot(); + final Snapshot snapshot = reservoir.getSnapshot(); assertThat(snapshot.size()) .isEqualTo(100); - assertAllValuesBetween(sample, 0, 1000); + assertAllValuesBetween(reservoir, 0, 1000); } @Test - public void aSampleOf100OutOf10Elements() throws Exception { - final ExponentiallyDecayingSample sample = new ExponentiallyDecayingSample(100, 0.99); + public void aReservoirOf100OutOf10Elements() throws Exception { + final ExponentiallyDecayingReservoir reservoir = new ExponentiallyDecayingReservoir(100, 0.99); for (int i = 0; i < 10; i++) { - sample.update(i); + reservoir.update(i); } - final Snapshot snapshot = sample.getSnapshot(); + final Snapshot snapshot = reservoir.getSnapshot(); assertThat(snapshot.size()) .isEqualTo(10); @@ -43,68 +43,69 @@ public void aSampleOf100OutOf10Elements() throws Exception { assertThat(snapshot.size()) .isEqualTo(10); - assertAllValuesBetween(sample, 0, 10); + assertAllValuesBetween(reservoir, 0, 10); } @Test - public void aHeavilyBiasedSampleOf100OutOf1000Elements() throws Exception { - final ExponentiallyDecayingSample sample = new ExponentiallyDecayingSample(1000, 0.01); + public void aHeavilyBiasedReservoirOf100OutOf1000Elements() throws Exception { + final ExponentiallyDecayingReservoir reservoir = new ExponentiallyDecayingReservoir(1000, 0.01); for (int i = 0; i < 100; i++) { - sample.update(i); + reservoir.update(i); } - assertThat(sample.size()) + assertThat(reservoir.size()) .isEqualTo(100); - final Snapshot snapshot = sample.getSnapshot(); + final Snapshot snapshot = reservoir.getSnapshot(); assertThat(snapshot.size()) .isEqualTo(100); - assertAllValuesBetween(sample, 0, 100); + assertAllValuesBetween(reservoir, 0, 100); } @Test public void longPeriodsOfInactivityShouldNotCorruptSamplingState() { final ManualClock clock = new ManualClock(); - final ExponentiallyDecayingSample sample = new ExponentiallyDecayingSample(10, - 0.015, - clock); + final ExponentiallyDecayingReservoir reservoir = new ExponentiallyDecayingReservoir(10, + 0.015, + clock); // add 1000 values at a rate of 10 values/second for (int i = 0; i < 1000; i++) { - sample.update(1000 + i); + reservoir.update(1000 + i); clock.addMillis(100); } - assertThat(sample.getSnapshot().size()) + assertThat(reservoir.getSnapshot().size()) .isEqualTo(10); - assertAllValuesBetween(sample, 1000, 2000); + assertAllValuesBetween(reservoir, 1000, 2000); // wait for 15 hours and add another value. // this should trigger a rescale. Note that the number of samples will be reduced to 2 // because of the very small scaling factor that will make all existing priorities equal to // zero after rescale. clock.addHours(15); - sample.update(2000); - assertThat(sample.getSnapshot().size()) + reservoir.update(2000); + assertThat(reservoir.getSnapshot().size()) .isEqualTo(2); - assertAllValuesBetween(sample, 1000, 3000); + assertAllValuesBetween(reservoir, 1000, 3000); // add 1000 values at a rate of 10 values/second for (int i = 0; i < 1000; i++) { - sample.update(3000 + i); + reservoir.update(3000 + i); clock.addMillis(100); } - assertThat(sample.getSnapshot().size()) + assertThat(reservoir.getSnapshot().size()) .isEqualTo(10); - assertAllValuesBetween(sample, 3000, 4000); + assertAllValuesBetween(reservoir, 3000, 4000); } - private static void assertAllValuesBetween(ExponentiallyDecayingSample sample, - double min, double max) { - for (double i : sample.getSnapshot().getValues()) { + private static void assertAllValuesBetween(ExponentiallyDecayingReservoir reservoir, + double min, + double max) { + for (double i : reservoir.getSnapshot().getValues()) { assertThat(i) .isLessThan(max) .isGreaterThanOrEqualTo(min); diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java index 9e35d03586..94ddfbf856 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java @@ -2,14 +2,15 @@ import com.yammer.metrics.Histogram; import com.yammer.metrics.Snapshot; -import com.yammer.metrics.UniformSample; +import com.yammer.metrics.UniformReservoir; +import com.yammer.metrics.UniformReservoir; import org.junit.Test; import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.api.Assertions.offset; public class HistogramTest { - private final Histogram histogram = new Histogram(new UniformSample()); + private final Histogram histogram = new Histogram(new UniformReservoir()); @Test public void anEmptyHistogram() throws Exception { diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingTimeWindowSampleTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingTimeWindowReservoirTest.java similarity index 61% rename from metrics-core/src/test/java/com/yammer/metrics/tests/SlidingTimeWindowSampleTest.java rename to metrics-core/src/test/java/com/yammer/metrics/tests/SlidingTimeWindowReservoirTest.java index 4c0358b0ed..03617f8eaa 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingTimeWindowSampleTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingTimeWindowReservoirTest.java @@ -1,7 +1,7 @@ package com.yammer.metrics.tests; import com.yammer.metrics.Clock; -import com.yammer.metrics.SlidingTimeWindowSample; +import com.yammer.metrics.SlidingTimeWindowReservoir; import org.junit.Test; import java.util.concurrent.TimeUnit; @@ -10,39 +10,39 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -public class SlidingTimeWindowSampleTest { +public class SlidingTimeWindowReservoirTest { private final Clock clock = mock(Clock.class); - private final SlidingTimeWindowSample sample = new SlidingTimeWindowSample(10, TimeUnit.NANOSECONDS, clock); + private final SlidingTimeWindowReservoir reservoir = new SlidingTimeWindowReservoir(10, TimeUnit.NANOSECONDS, clock); @Test public void storesMeasurementsWithDuplicateTicks() throws Exception { when(clock.getTick()).thenReturn(20L); - sample.update(1); - sample.update(2); + reservoir.update(1); + reservoir.update(2); - assertThat(sample.getSnapshot().getValues()) + assertThat(reservoir.getSnapshot().getValues()) .containsOnly(1, 2); } @Test public void boundsMeasurementsToATimeWindow() throws Exception { when(clock.getTick()).thenReturn(0L); - sample.update(1); + reservoir.update(1); when(clock.getTick()).thenReturn(5L); - sample.update(2); + reservoir.update(2); when(clock.getTick()).thenReturn(10L); - sample.update(3); + reservoir.update(3); when(clock.getTick()).thenReturn(15L); - sample.update(4); + reservoir.update(4); when(clock.getTick()).thenReturn(20L); - sample.update(5); + reservoir.update(5); - assertThat(sample.getSnapshot().getValues()) + assertThat(reservoir.getSnapshot().getValues()) .containsOnly(4, 5); } } diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingWindowReservoirTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingWindowReservoirTest.java new file mode 100644 index 0000000000..156954f2c3 --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingWindowReservoirTest.java @@ -0,0 +1,30 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.SlidingWindowReservoir; +import org.junit.Test; + +import static org.fest.assertions.api.Assertions.assertThat; + +public class SlidingWindowReservoirTest { + private final SlidingWindowReservoir reservoir = new SlidingWindowReservoir(3); + + @Test + public void handlesSmallDataStreams() throws Exception { + reservoir.update(1); + reservoir.update(2); + + assertThat(reservoir.getSnapshot().getValues()) + .containsOnly(1, 2); + } + + @Test + public void onlyKeepsTheMostRecentFromBigDataStreams() throws Exception { + reservoir.update(1); + reservoir.update(2); + reservoir.update(3); + reservoir.update(4); + + assertThat(reservoir.getSnapshot().getValues()) + .containsOnly(2, 3, 4); + } +} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingWindowSampleTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingWindowSampleTest.java deleted file mode 100644 index dafe011552..0000000000 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingWindowSampleTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.yammer.metrics.tests; - -import com.yammer.metrics.SlidingWindowSample; -import org.junit.Test; - -import static org.fest.assertions.api.Assertions.assertThat; - -public class SlidingWindowSampleTest { - private final SlidingWindowSample sample = new SlidingWindowSample(3); - - @Test - public void handlesSmallSamples() throws Exception { - sample.update(1); - sample.update(2); - - assertThat(sample.getSnapshot().getValues()) - .containsOnly(1, 2); - } - - @Test - public void onlyKeepsTheMostRecentFromBigSamples() throws Exception { - sample.update(1); - sample.update(2); - sample.update(3); - sample.update(4); - - assertThat(sample.getSnapshot().getValues()) - .containsOnly(2, 3, 4); - } -} diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java index 09f73377c3..0cf9a1388a 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java @@ -1,7 +1,8 @@ package com.yammer.metrics.tests; import com.yammer.metrics.Clock; -import com.yammer.metrics.ExponentiallyDecayingSample; +import com.yammer.metrics.ExponentiallyDecayingReservoir; +import com.yammer.metrics.ExponentiallyDecayingReservoir; import com.yammer.metrics.Snapshot; import com.yammer.metrics.Timer; import org.junit.Test; @@ -22,7 +23,7 @@ public long getTick() { return val += 50000000; } }; - private final Timer timer = new Timer(new ExponentiallyDecayingSample(), clock); + private final Timer timer = new Timer(new ExponentiallyDecayingReservoir(), clock); @Test public void aBlankTimer() throws Exception { diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/UniformSampleTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/UniformReservoirTest.java similarity index 60% rename from metrics-core/src/test/java/com/yammer/metrics/tests/UniformSampleTest.java rename to metrics-core/src/test/java/com/yammer/metrics/tests/UniformReservoirTest.java index 9c5923d225..3d692cd678 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/UniformSampleTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/UniformReservoirTest.java @@ -1,23 +1,23 @@ package com.yammer.metrics.tests; -import com.yammer.metrics.UniformSample; import com.yammer.metrics.Snapshot; +import com.yammer.metrics.UniformReservoir; import org.junit.Test; import static org.fest.assertions.api.Assertions.assertThat; -public class UniformSampleTest { +public class UniformReservoirTest { @Test @SuppressWarnings("unchecked") - public void aSampleOf100OutOf1000Elements() throws Exception { - final UniformSample sample = new UniformSample(100); + public void aReservoirOf100OutOf1000Elements() throws Exception { + final UniformReservoir reservoir = new UniformReservoir(100); for (int i = 0; i < 1000; i++) { - sample.update(i); + reservoir.update(i); } - final Snapshot snapshot = sample.getSnapshot(); + final Snapshot snapshot = reservoir.getSnapshot(); - assertThat(sample.size()) + assertThat(reservoir.size()) .isEqualTo(100); assertThat(snapshot.size()) diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java index 122d1bb44c..843f553d7a 100644 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java @@ -35,7 +35,7 @@ public Long getValue() { registry.counter("c").inc(); registry.histogram("h").update(1); registry.register("m", new Meter(clock)).mark(); - registry.register("t", new Timer(new ExponentiallyDecayingSample(), clock)) + registry.register("t", new Timer(new ExponentiallyDecayingReservoir(), clock)) .update(1, TimeUnit.SECONDS); request.setMethod("GET"); From 0d577751fbf29b146e036442e3fb8d78bfa2ea93 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 20:25:22 -0700 Subject: [PATCH 0146/2558] Remove the old, outdated Markdown docs. --- CHANGELOG.md | 400 ------------------------------------------------ CONTRIBUTORS.md | 39 ----- 2 files changed, 439 deletions(-) delete mode 100644 CHANGELOG.md delete mode 100644 CONTRIBUTORS.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index dd906fd1b9..0000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,400 +0,0 @@ -v2.0.2: Feb 09 2012 -=================== - -* `InstrumentationModule` in `metrics-guice` now uses the default `MetricsRegistry` and - `HealthCheckRegistry`. - - -v2.0.1: Feb 08 2012 -=================== - -* Fixed a concurrency bug in `JmxReporter`. - - -v2.0.0: Feb 07 2012 -=================== - -* Upgraded to Jackson 1.9.4. -* Upgraded to Jetty 7.6.0. -* Added escaping for garbage collector and memory pool names in `GraphiteReporter`. -* Fixed the inability to start and stop multiple reporter instances. -* Switched to using a backported version of `ThreadLocalRandom` for `UniformSample` and - `ExponentiallyDecayingSample` to reduce lock contention on random number generation. -* Removed `Ordered` from `TimedAnnotationBeanPostProcessor` in `metrics-spring`. -* Upgraded to JDBI 2.31.1. -* Upgraded to Ehcache 2.5.1. -* Added `#timerContext()` to Scala `Timer`. - - -v2.0.0-RC0: Jan 19 2012 -======================= - -* Added FindBugs checks to the build process. -* Fixed the catching of `Error` instances thrown during health checks. -* Added `enable` static methods to `CsvReporter` and changed `CsvReporter(File, MetricsRegistry)` - to `CsvReporter(MetricsRegistry, File)`. -* Slimmed down `InstrumentedEhcache`. -* Hid the internals of `GangliaReporter`. -* Hid the internals of `metrics-guice`. -* Changed `metrics-httpclient` to consistently associate metrics with the `org.apache` class being - extended. -* Hid the internals of `metrics-httpclient`. -* Rewrote `InstrumentedAppender` in `metrics-log4j`. It no longer forwards events to an appender. - Instead, you can just attach it to your root logger to instrument logging. -* Rewrote `InstrumentedAppender` in `metrics-logback`. No major API changes. -* Fixed bugs with `@ExceptionMetered`-annotated resource methods in `metrics-jersey`. -* Fixed bugs generating `Snapshot` instances from concurrently modified collections. -* Fixed edge case in `MetricsServlet`'s thread dumps where one thread could be missed. -* Added `RatioGauge` and `PercentGauge`. -* Changed `InstrumentedQueuedThreadPool`'s `percent-idle` gauge to be a ratio. -* Decomposed `MetricsServlet` into a set of focused servlets: `HealthCheckServlet`, - `MetricsServlet`, `PingServlet`, and `ThreadDumpServlet`. The top-level servlet which provides the - HTML menu page is now `AdminServlet`. -* Added `metrics-spring`. - - -v2.0.0-BETA19: Jan 07 2012 -========================== - -* Added absolute memory usage to `MetricsServlet`. -* Extracted `@Timed` etc. to `metrics-annotations`. -* Added `metrics-jersey`, which provides a class allowing you to automatically instrument all - `@Timed`, `@Metered`, and `@ExceptionMetered`-annotated resource methods. -* Moved all classes in `metrics-scala` from `com.yammer.metrics` to `com.yammer.metrics.scala`. -* Renamed `CounterMetric` to `Counter`. -* Renamed `GaugeMetric` to `Gauge`. -* Renamed `HistogramMetric` to `Histogram`. -* Renamed `MeterMetric` to `Meter`. -* Renamed `TimerMetric` to `Timer`. -* Added `ToggleGauge`, which returns `1` the first time it's called and `0` every time after that. -* Now licensed under Apache License 2.0. -* Converted `VirtualMachineMetrics` to a non-singleton class. -* Removed `Utils`. -* Removed deprecated constructors from `Meter` and `Timer`. -* Removed `LoggerMemoryLeakFix`. -* `DeathRattleExceptionHandler` now logs to SLF4J, not syserr. -* Added `MetricsRegistry#groupedMetrics()`. -* Removed `Metrics#allMetrics()`. -* Removed `Metrics#remove(MetricName)`. -* Removed `MetricsRegistry#threadPools()` and `#newMeterTickThreadPool()` and added - `#newScheduledThreadPool`. -* Added `MetricsRegistry#shutdown()`. -* Renamed `ThreadPools#shutdownThreadPools()` to `#shutdown()`. -* Replaced `HealthCheck`'s abstract `name` method with a required constructor parameter. -* `HealthCheck#check()` is now `protected`. -* Moved `DeadlockHealthCheck` from `com.yammer.metrics.core` to `com.yammer.metrics.utils`. -* Added `HealthCheckRegistry#unregister(HealthCheck)`. -* Fixed typo in `VirtualMachineMetrics` and `MetricsServlet`: `commited` to `committed`. -* Changed `MetricsRegistry#createName` to `protected`. -* All metric types are created exclusively through `MetricsRegistry` now. -* `Metrics.newJmxGauge` and `MetricsRegistry.newJmxGauge` are deprecated. -* Fixed heap metrics in `VirtualMachineMetrics`. -* Added `Snapshot`, which calculates quantiles. -* Renamed `Percentiled` to `Sampling` and dropped `percentile` and `percentiles` in favor of - producing `Snapshot` instances. This affects both `Histogram` and `Timer`. -* Renamed `Summarized` to `Summarizable`. -* Changed order of `CsvReporter`'s construction parameters. -* Renamed `VirtualMachineMetrics.GarbageCollector` to `VirtualMachineMetrics.GarbageCollectorStats`. -* Moved Guice/Servlet support from `metrics-servlet` to `metrics-guice`. -* Removed `metrics-aop`. -* Removed `newJmxGauge` from both `Metrics` and `MetricsRegistry`. Just use `JmxGauge`. -* Moved `JmxGauge` to `com.yammer.metrics.util`. -* Moved `MetricPredicate` to `com.yammer.metrics.core`. -* Moved `NameThreadFactory` into `ThreadPools` and made `ThreadPools` package-visible. -* Removed `Timer#values()`, `Histogram#values()`, and `Sample#values()`. Use `getSnapshot()` instead. -* Removed `Timer#dump(File)` and `Histogram#dump(File)`, and `Sample#dump(File)`. Use - `Snapshot#dump(File)` instead. - - -v2.0.0-BETA18: Dec 16 2011 -========================== - -* Added `DeathRattleExceptionHandler`. -* Fixed NPE in `VirtualMachineMetrics`. -* Added decorators for connectors and thread pools in `metrics-jetty`. -* Added `TimerMetric#time()` and `TimerContext`. -* Added a shorter factory method for millisecond/second timers. -* Switched tests to JUnit. -* Improved logging in `GangliaReporter`. -* Improved random number generation for `UniformSample`. -* Added `metrics-httpclient` for instrumenting Apache HttpClient 4.1. -* Massively overhauled the reporting code. -* Added support for instrumented, non-`public` methods in `metrics-guice`. -* Added `@ExceptionMetered` to `metrics-guice`. -* Added group prefixes to `GangliaReporter`. -* Added `CvsReporter`, which outputs metric values to `.csv` files. -* Improved metric name sanitization in `GangliaReporter`. -* Added `Metrics.shutdown()` and improved metrics lifecycle behavior. -* Added `metrics-web`. -* Upgraded to ehcache 2.5.0. -* Many, many refactorings. -* `metrics-servlet` now responds with `501 Not Implememented` when no health checks have been - registered. -* Many internal refactorings for testability. -* Added histogram counts to `metrics-servlet`. -* Fixed a race condition in `ExponentiallyDecayingSample`. -* Added timezone and locale support to `ConsoleReporter`. -* Added `metrics-aop` for Guiceless support of method annotations. -* Added `metrics-jdbi` which adds instrumentation to [JDBI](http://www.jdbi.org). -* Fixed NPE for metrics which belong to classes in the default package. -* Now deploying artifacts to Maven Central. - - -v2.0.0-BETA17: Oct 07 2011 -========================== - -* Added an option message to successful health check results. -* Fixed locale issues in `GraphiteReporter`. -* Added `GangliaReporter`. -* Added per-HTTP method timers to `InstrumentedHandler` in `metrics-jetty`. -* Fixed a thread pool leak for meters. -* Added `#dump(File)` to `HistogramMetric` and `TimerMetric`. -* Upgraded to Jackson 1.9.x. -* Upgraded to slf4j 1.6.2. -* Upgraded to logback 0.9.30. -* Upgraded to ehcache 2.4.5. -* Surfaced `Metrics.removeMetric()`. - - -v2.0.0-BETA16: Aug 23 2011 -========================== - -* Fixed a bug in GC monitoring. - - -v2.0.0-BETA15: Aug 15 2011 -========================== - -* Fixed dependency scopes for `metrics-jetty`. -* Added time and VM version to `vm` output of `MetricsServlet`. -* Dropped `com.sun.mangement`-based GC instrumentation in favor of a - `java.lang.management`-based one. `getLastGcInfo` has a nasty native memory - leak in it, plus it often returned incorrect data. -* Upgraded to Jackson 1.8.5. -* Upgraded to Jetty 7.4.5. -* Added sanitization for metric names in `GraphiteReporter`. -* Extracted out a `Clock` interface for timers for non-wall-clock timing. -* Extracted out most of the remaining statics into `MetricsRegistry` and - `HealthCheckRegistry`. -* Added an init parameter to `MetricsServlet` for disabling the `jvm` section. -* Added a Guice module for `MetricsServlet`. -* Added dynamic metric names. -* Upgraded to ehcache 2.4.5. -* Upgraded to logback 0.9.29. -* Allowed for the removal of metrics. -* Added the ability to filter metrics exposed by a reporter to those which match - a given predicate. - - -v2.0.0-BETA14: Jul 05 2011 -========================== - -* Moved to Maven for a build system and extracted the Scala façade to a - `metrics-scala` module which is now the only cross-built module. All other - modules dropped the Scala version suffix in their `artifactId`s. -* Fixed non-heap metric name in `GraphiteReporter`. -* Fixed stability error in `GraphiteReporter` when dealing with unavailable - servers. -* Fixed error with anonymous, instrumented classes. -* Fixed error in `MetricsServlet` when a gauge throws an exception. -* Fixed error with bogus GC run times. -* Link to the pretty JSON output from the `MetricsServlet` menu page. -* Fixed potential race condition in histograms' variance calculations. -* Fixed memory pool reporting for the G1 collector. - - -v2.0.0-BETA13: May 13 2011 -========================== - -* Fixed a bug in the initial startup phase of the `JmxReporter`. -* Added `metrics-ehcache`, for the instrumentation of `Ehcache` instances. -* Fixed a typo in `metrics-jetty`'s `InstrumentedHandler`. -* Added name prefixes to `GraphiteReporter`. -* Added JVM metrics reporting to `GraphiteReporter`. -* Actually fixed `MetricsServlet`'s links when the servlet has a non-root - context path. -* Now cross-building for Scala 2.9.0. -* Added `pretty` query parameter for `MetricsServlet` to format the JSON object - for human consumption. -* Added `no-cache` headers to the `MetricsServlet` responses. - - -v2.0.0-BETA12: May 09 2011 -========================== - -* Upgraded to Jackson 1.7.6. -* Added a new instrumented Log4J appender. -* Added a new instrumented Logback appender. Thanks to Bruce Mitchener - (@waywardmonkeys) for the patch. -* Added a new reporter for the [Graphite](http://graphite.wikidot.com) - aggregation system. Thanks to Mahesh Tiyyagura (@tmahesh) for the patch. -* Added scoped metric names. -* Added Scala 2.9.0.RC{2,3,4} as build targets. -* Added meters to Jetty handler for the percent of responses which have `4xx` or - `5xx` status codes. -* Changed the Servlet API to be a `provided` dependency. Thanks to Mårten - Gustafson (@chids) for the patch. -* Separated project into modules: - * `metrics-core`: A dependency-less project with all the core metrics. - * `metrics-graphite`: A reporter for the [Graphite](http://graphite.wikidot.com) - aggregation system. - * `metrics-guice`: Guice AOP support. - * `metrics-jetty`: An instrumented Jetty handler. - * `metrics-log4j`: An instrumented Log4J appender. - * `metrics-logback`: An instrumented Logback appender. - * `metrics-servlet`: The Metrics servlet with context listener. - - -v2.0.0-BETA11: Apr 27 2011 -========================== - -* Added thread state and deadlock detection metrics. -* Fix `VirtualMachineMetrics`' initialization. -* Context path fixes for the servlet. -* Added the `@Gauge` annotation. -* Big reworking of the exponentially-weighted moving average code for meters. - Thanks to JD Maturen (@sku) and John Ewart (@johnewart) for pointing this out. -* Upgraded to Guice 3.0. -* Upgraded to Jackson 1.7.5. -* Upgraded to Jetty 7.4.0. -* Big rewrite of the servlet's thread dump code. -* Fixed race condition in `ExponentiallyDecayingSample`. Thanks to Martin - Traverso (@martint) for the patch. -* Lots of spelling fixes in Javadocs. Thanks to Bruce Mitchener - (@waywardmonkeys) for the patch. -* Added Scala 2.9.0.RC1 as a build target. Thanks to Bruce Mitchener - (@waywardmonkeys) for the patch. -* Patched a hilarious memory leak in `java.util.logging`. - - -v2.0.0-BETA10: Mar 25 2011 -========================== - -* Added Guice AOP annotations. -* Added `HealthCheck#name()`. -* Added `Metrics.newJmxGauge()`. -* Moved health checks into `HealthChecks`. -* Upgraded to Jackson 1.7.3 and Jetty 7.3.1. - -v2.0.0-BETA10: Mar 25 2011 -========================== - -* Added Guice AOP annotations: `@Timed` and `@Metered`. -* Added `HealthCheck#name()`. -* Added `Metrics.newJmxGauge()`. -* Moved health checks into `HealthChecks`. -* Upgraded to Jackson 1.7.3 and Jetty 7.3.1. - -v2.0.0-BETA9: Mar 14 2011 -========================= - -* Fixed `JmxReporter` lag. -* Added default arguments to timers and meters. -* Added default landing page to the servlet. -* Improved the performance of `ExponentiallyDecayingSample`. -* Fixed an integer overflow bug in `UniformSample`. -* Added linear scaling to `ExponentiallyDecayingSample`. - -v2.0.0-BETA8: Mar 01 2011 -========================= - -* Added histograms. -* Added biased sampling for timers. -* Added dumping of timer/histogram samples via the servlet. -* Added dependency on `jackon-mapper`. -* Added classname filtering for the servlet. -* Added URI configuration for the servlet. - - -v2.0.0-BETA7: Jan 12 2011 -========================= - -* Added `JettyHandler`. -* Made the `Servlet` dependency optional. - -v2.0.0-BETA6: Jan 12 2011 -========================= - -* Fix `JmxReporter` initialization. - -v2.0.0-BETA5: Jan 11 2011 -========================= - -* Dropped `Counter#++` and `Counter#--`. -* Added `Timer#update`. -* Upgraded to Jackson 1.7.0. -* Made JMX reporting implicit. -* Added health checks. - -v2.0.0-BETA3: Dec 23 2010 -========================= - -* Fixed thread names and some docs. - -v2.0.0-BETA2: Dec 22 2010 -========================= - -* Fixed a memory leak in `MeterMetric`. - -v2.0.0-BETA1: Dec 22 2010 -========================= - -* Total rewrite in Java. - -v1.0.7: Sep 21 2010 -=================== - -* Added `median` to `Timer`. -* Added `p95` to `Timer` (95th percentile). -* Added `p98` to `Timer` (98th percentile). -* Added `p99` to `Timer` (99th percentile). - -v1.0.6: Jul 15 2010 -=================== - -* Now compiled exclusively for 2.8.0 final. - -v1.0.5: Jun 01 2010 -=================== - -* Documentation fix. -* Added `TimedToggle`, which may or may not be useful at all. -* Now cross-building for RC2 and RC3. - -v1.0.4: Apr 27 2010 -=================== - -* Blank `Timer`s (i.e., those which have recorded no timings yet) no longer - explode when asked for metrics for that which does not yet exist. -* Nested classes, companion objects, and singletons don't have trailing `$`s - messing up JMX's good looks. - -v1.0.3: Apr 16 2010 -=================== - -* Fixed some issues with the [implicit.ly](http://implicit.ly) plumbing. -* Tweaked the sample size for `Timer`, giving it 99.9% confidence level with a - %5 margin of error (for a normally distributed variable, which it almost - certainly isn't.) -* `Sample#iterator` returns only the recorded data, not a bunch of zeros. -* Moved units of `Timer`, `Meter`, and `LoadMeter` to their own attributes, - which allows for easy export of Metrics data via JMX to things like - [Ganglia](http://ganglia.sourceforge.net/) or whatever. - -v1.0.2: Mar 08 2010 -=================== - -* `Timer` now uses Welford's algorithm for calculating running variance, which - means no more hilariously wrong standard deviations (e.g., `NaN`). -* `Timer` now supports `+=(Long)` for pre-recorded, nanosecond-precision - timings. - -v1.0.1: Mar 05 2010 -=================== - -* changed `Sample` to use an `AtomicReferenceArray` - -v1.0.0: Feb 27 2010 -=================== - -* Initial release diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md deleted file mode 100644 index 0638b1bf7d..0000000000 --- a/CONTRIBUTORS.md +++ /dev/null @@ -1,39 +0,0 @@ -Many Many Thanks To -=================== - -* Alex Lambert (@bifflabs) -* Brian Roberts (@flicken) -* Bruce Mitchener (@waywardmonkeys) -* C. Scott Andreas (@cscotta) -* Charles Care (@ccare) -* Chris Burroughs (@cburroughs) -* Ciamac Moallemi (@ciamac) -* Cliff Moon (@cliffmoon) -* Collin VanDyck (@collinvandyck) -* Dag Liodden (@daggerrz) -* Drew Stephens (@dinomite) -* Eric Daigneault (@Newtopian) -* François Beausoleil (@francois) -* Gerolf Seitz (@seitz) -* Jackson Davis (@jcdavis) -* James Casey (@jamesc) -* JD Maturen (@sku) -* Jeff Hodges (@jmhodges) -* Jesper Blomquist (jebl01) -* John Ewart (@johnewart) -* John Wang (@javasoze) -* Kevin Clark (@kevinclark) -* Mahesh Tiyyagura (@tmahesh) -* Martin Traverso (@martint) -* Matt Abrams (@abramsm) -* Matt Ryall (@mattryall) -* Matthew Gilliard (@mjg123) -* Matthew O'Connor (@oconnor0) -* Mårten Gustafson (@chids) -* Neil Prosser (@neilprosser) -* Robby Walker (@robbywalker) -* Ryan Kennedy (@ryankennedy) -* Ryan W Tenney (@ryantenney) -* Shaneal Manek (@smanek) -* Thomas Dudziak (@tomdz) -* Tobias Lidskog (@tobli) From 8acfb926f324c84fcaa6d345145f16e505a912fc Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 20:25:35 -0700 Subject: [PATCH 0147/2558] It is the future now. --- README.md | 2 +- docs/source/conf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b4b6f9b12d..4dae141360 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,6 @@ For more information, please see [the documentation](http://metrics.codahale.com License ------- -Copyright (c) 2010-2012 Coda Hale, Yammer.com +Copyright (c) 2010-2013 Coda Hale, Yammer.com Published under Apache Software License 2.0, see LICENSE diff --git a/docs/source/conf.py b/docs/source/conf.py index 5c8c5d64be..f4c46037de 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -41,7 +41,7 @@ # General information about the project. project = u'Metrics' -copyright = u'2010-2012, Coda Hale, Yammer Inc.' +copyright = u'2010-2013, Coda Hale, Yammer Inc.' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From 852f3e4417f92e5d2102f59b536d7b6b86c14710 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 20:28:27 -0700 Subject: [PATCH 0148/2558] Make registry fields final in reporter builders. --- .../src/main/java/com/yammer/metrics/Slf4jReporter.java | 2 +- .../main/java/com/yammer/metrics/graphite/GraphiteReporter.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java b/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java index 4012b86752..fa60c87f05 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java @@ -32,7 +32,7 @@ public static Builder forRegistry(MetricRegistry registry) { * not filtering metrics. */ public static class Builder { - private MetricRegistry registry; + private final MetricRegistry registry; private Logger logger; private Marker marker; private TimeUnit rateUnit; diff --git a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java index 41adb339f9..184ff4988f 100644 --- a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java +++ b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java @@ -32,7 +32,7 @@ public static Builder forRegistry(MetricRegistry registry) { * not filtering metrics. */ public static class Builder { - private MetricRegistry registry; + private final MetricRegistry registry; private Clock clock; private String prefix; private TimeUnit rateUnit; From 43587458328b08a1bac8a1ee269ae92e95c527a0 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 20:31:44 -0700 Subject: [PATCH 0149/2558] Oh hello huge chunk of dead code! --- .../yammer/metrics/servlets/AdminServlet.java | 104 ------------------ 1 file changed, 104 deletions(-) diff --git a/metrics-servlets/src/main/java/com/yammer/metrics/servlets/AdminServlet.java b/metrics-servlets/src/main/java/com/yammer/metrics/servlets/AdminServlet.java index eb682d3e46..44eed9da48 100755 --- a/metrics-servlets/src/main/java/com/yammer/metrics/servlets/AdminServlet.java +++ b/metrics-servlets/src/main/java/com/yammer/metrics/servlets/AdminServlet.java @@ -103,107 +103,3 @@ private static String getParam(String initParam, String defaultValue) { return initParam == null ? defaultValue : initParam; } } - -//import javax.servlet.ServletConfig; -//import javax.servlet.ServletException; -//import javax.servlet.http.HttpServlet; -//import javax.servlet.http.HttpServletRequest; -//import javax.servlet.http.HttpServletResponse; -//import java.io.IOException; -//import java.io.PrintWriter; -//import java.text.MessageFormat; -// -//public class AdminServlet extends HttpServlet { -// private static final long serialVersionUID = 1363903248255082791L; -// - -// -// private final HealthCheckServlet healthCheckServlet; -// private final MetricsServlet metricsServlet; -// private final PingServlet pingServlet; -// private final ThreadDumpServlet threadDumpServlet; -// -// private String metricsUri; -// private String pingUri; -// private String threadsUri; -// private String healthcheckUri; -// private String serviceName; -// -// public AdminServlet() { -// this(new HealthCheckServlet(), new MetricsServlet(), new PingServlet(), -// new ThreadDumpServlet(), DEFAULT_HEALTHCHECK_URI, DEFAULT_METRICS_URI, -// DEFAULT_PING_URI, DEFAULT_THREADS_URI); -// } -// -// public AdminServlet(HealthCheckServlet healthCheckServlet, -// MetricsServlet metricsServlet, -// PingServlet pingServlet, -// ThreadDumpServlet threadDumpServlet, -// String healthcheckUri, -// String metricsUri, -// String pingUri, -// String threadsUri) { -// this.healthCheckServlet = healthCheckServlet; -// this.metricsServlet = metricsServlet; -// this.pingServlet = pingServlet; -// this.threadDumpServlet = threadDumpServlet; -// -// this.metricsUri = metricsUri; -// this.pingUri = pingUri; -// this.threadsUri = threadsUri; -// this.healthcheckUri = healthcheckUri; -// } -// -// @Override -// public void init(ServletConfig config) throws ServletException { -// super.init(config); -// healthCheckServlet.init(config); -// metricsServlet.init(config); -// pingServlet.init(config); -// threadDumpServlet.init(config); -// -// //final ServletContext context = config.getServletContext(); -// this.metricsUri = getParam(config.getInitParameter("metrics-uri"), this.metricsUri); -// this.pingUri = getParam(config.getInitParameter("ping-uri"), this.pingUri); -// this.threadsUri = getParam(config.getInitParameter("threads-uri"), this.threadsUri); -// this.healthcheckUri = getParam(config.getInitParameter("healthcheck-uri"), this.healthcheckUri); -// this.serviceName = getParam(config.getInitParameter("service-name"), this.serviceName); -// } -// -// public void setServiceName(String serviceName) { -// this.serviceName = serviceName; -// } -// -// @Override -// protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { -// resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); -// final String uri = req.getPathInfo(); -// final String path = req.getContextPath() + req.getServletPath(); -// if (uri == null || uri.equals("/")) { -// resp.setStatus(HttpServletResponse.SC_OK); -// resp.setContentType(CONTENT_TYPE); -// final PrintWriter writer = resp.getWriter(); -// try { -// writer.println(MessageFormat.format(TEMPLATE, path, metricsUri, path, pingUri, path, -// threadsUri, path, healthcheckUri, -// serviceName == null ? "" : " (" + serviceName + ")")); -// } finally { -// writer.close(); -// } -// } else if (uri.equals(healthcheckUri)) { -// healthCheckServlet.service(req, resp); -// } else if (uri.startsWith(metricsUri)) { -// metricsServlet.service(req, resp); -// } else if (uri.equals(pingUri)) { -// pingServlet.service(req, resp); -// } else if (uri.equals(threadsUri)) { -// threadDumpServlet.service(req, resp); -// } else { -// resp.sendError(HttpServletResponse.SC_NOT_FOUND); -// } -// } -// -// private static String getParam(String initParam, String defaultValue) { -// return initParam == null ? defaultValue : initParam; -// } -//} From 01ebfa2491e9d292f8cdfc47ba0354cbb885d8e3 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 20:38:05 -0700 Subject: [PATCH 0150/2558] Log debug error with buffer pool loading. --- .../java/com/yammer/metrics/jvm/BufferPoolMetricSet.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolMetricSet.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolMetricSet.java index b1d1b37c44..7259f6b3b0 100644 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolMetricSet.java +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolMetricSet.java @@ -3,6 +3,8 @@ import com.yammer.metrics.JmxAttributeGauge; import com.yammer.metrics.Metric; import com.yammer.metrics.MetricSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.management.JMException; import javax.management.MBeanServer; @@ -19,6 +21,7 @@ * These JMX objects are only available on Java 7 and above. */ public class BufferPoolMetricSet implements MetricSet { + private static final Logger LOGGER = LoggerFactory.getLogger(BufferPoolMetricSet.class); private static final String[] ATTRIBUTES = { "Count", "MemoryUsed", "TotalCapacity" }; private static final String[] NAMES = { "count", "used", "capacity" }; private static final String[] POOLS = { "direct", "mapped" }; @@ -42,7 +45,7 @@ public Map getMetrics() { gauges.put(name(pool, name), new JmxAttributeGauge(mBeanServer, on, attribute)); } catch (JMException ignored) { - + LOGGER.debug("Unable to load buffer pool MBeans, possibly running on Java 6"); } } } From 3b5065607636347c3d4be0f26e9ea1c8c3bbc17f Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 21:25:53 -0700 Subject: [PATCH 0151/2558] Added rate and duration units to JmxReporter. --- .../java/com/yammer/metrics/JmxReporter.java | 140 ++++++++++++++---- .../yammer/metrics/tests/JmxReporterTest.java | 34 +++-- 2 files changed, 134 insertions(+), 40 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java index 956527b77e..5048bb07f8 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java @@ -5,8 +5,10 @@ import javax.management.*; import java.lang.management.ManagementFactory; +import java.util.Locale; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.TimeUnit; /** * A reporter which listens for new metrics and exposes them as namespaced MBeans. @@ -29,10 +31,14 @@ public static Builder forRegistry(MetricRegistry registry) { public static class Builder { private final MetricRegistry registry; private MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + private TimeUnit rateUnit; + private TimeUnit durationUnit; private MetricFilter filter = MetricFilter.ALL; private Builder(MetricRegistry registry) { this.registry = registry; + this.rateUnit = TimeUnit.SECONDS; + this.durationUnit = TimeUnit.MILLISECONDS; } /** @@ -46,6 +52,28 @@ public Builder registerWith(MBeanServer mBeanServer) { 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. * @@ -63,7 +91,7 @@ public Builder filter(MetricFilter filter) { * @return a {@link JmxReporter} */ public JmxReporter build() { - return new JmxReporter(mBeanServer, registry, filter); + return new JmxReporter(mBeanServer, registry, filter, rateUnit, durationUnit); } } @@ -248,15 +276,21 @@ public interface JmxMeterMBean extends MetricMBean { double getFiveMinuteRate(); double getFifteenMinuteRate(); + + String getRateUnit(); } //CHECKSTYLE:ON private static class JmxMeter extends AbstractBean implements JmxMeterMBean { private final Metered metric; + private final double rateFactor; + private final String rateUnit; - private JmxMeter(Metered metric, ObjectName objectName) { + private JmxMeter(Metered metric, ObjectName objectName, TimeUnit rateUnit) { super(objectName); this.metric = metric; + this.rateFactor = rateUnit.toSeconds(1); + this.rateUnit = "events/" + calculateRateUnit(rateUnit); } @Override @@ -266,105 +300,157 @@ public long getCount() { @Override public double getMeanRate() { - return metric.getMeanRate(); + return metric.getMeanRate() * rateFactor; } @Override public double getOneMinuteRate() { - return metric.getOneMinuteRate(); + return metric.getOneMinuteRate() * rateFactor; } @Override public double getFiveMinuteRate() { - return metric.getFiveMinuteRate(); + return metric.getFiveMinuteRate() * rateFactor; } @Override public double getFifteenMinuteRate() { - return metric.getFifteenMinuteRate(); + return metric.getFifteenMinuteRate() * rateFactor; + } + + @Override + public String getRateUnit() { + return rateUnit; + } + + private String calculateRateUnit(TimeUnit unit) { + final String s = unit.toString().toLowerCase(Locale.US); + return s.substring(0, s.length() - 1); } } // CHECKSTYLE:OFF @SuppressWarnings("UnusedDeclaration") - public interface JmxTimerMBean extends JmxMeterMBean, JmxHistogramMBean { + public interface JmxTimerMBean extends JmxMeterMBean { + double getMin(); + + double getMax(); + + double getMean(); + + double getStdDev(); + + double get50thPercentile(); + + double get75thPercentile(); + + double get95thPercentile(); + + double get98thPercentile(); + + double get99thPercentile(); + + double get999thPercentile(); + + long[] values(); + String getDurationUnit(); } // CHECKSTYLE:ON static class JmxTimer extends JmxMeter implements JmxTimerMBean { private final Timer metric; - - private JmxTimer(Timer metric, ObjectName objectName) { - super(metric, objectName); + private final double durationFactor; + private final String durationUnit; + + private JmxTimer(Timer metric, + ObjectName objectName, + TimeUnit rateUnit, + TimeUnit durationUnit) { + super(metric, objectName, rateUnit); this.metric = metric; + this.durationFactor = 1.0 / durationUnit.toNanos(1); + this.durationUnit = durationUnit.toString().toLowerCase(Locale.US); } @Override public double get50thPercentile() { - return metric.getSnapshot().getMedian(); + return metric.getSnapshot().getMedian() * durationFactor; } @Override - public long getMin() { - return metric.getMin(); + public double getMin() { + return metric.getMin() * durationFactor; } @Override - public long getMax() { - return metric.getMax(); + public double getMax() { + return metric.getMax() * durationFactor; } @Override public double getMean() { - return metric.getMean(); + return metric.getMean() * durationFactor; } @Override public double getStdDev() { - return metric.getStdDev(); + return metric.getStdDev() * durationFactor; } @Override public double get75thPercentile() { - return metric.getSnapshot().get75thPercentile(); + return metric.getSnapshot().get75thPercentile() * durationFactor; } @Override public double get95thPercentile() { - return metric.getSnapshot().get95thPercentile(); + return metric.getSnapshot().get95thPercentile() * durationFactor; } @Override public double get98thPercentile() { - return metric.getSnapshot().get98thPercentile(); + return metric.getSnapshot().get98thPercentile() * durationFactor; } @Override public double get99thPercentile() { - return metric.getSnapshot().get99thPercentile(); + return metric.getSnapshot().get99thPercentile() * durationFactor; } @Override public double get999thPercentile() { - return metric.getSnapshot().get999thPercentile(); + return metric.getSnapshot().get999thPercentile() * durationFactor; } @Override public long[] values() { return metric.getSnapshot().getValues(); } + + @Override + public String getDurationUnit() { + return durationUnit; + } } private static class JmxListener implements MetricRegistryListener { private final String name; private final MBeanServer mBeanServer; private final MetricFilter filter; + private final TimeUnit rateUnit; + private final TimeUnit durationUnit; private final Set registered; - public JmxListener(MBeanServer mBeanServer, String name, MetricFilter filter) { + private JmxListener(MBeanServer mBeanServer, + String name, + MetricFilter filter, + TimeUnit rateUnit, + TimeUnit durationUnit) { this.mBeanServer = mBeanServer; this.name = name; this.filter = filter; + this.rateUnit = rateUnit; + this.durationUnit = durationUnit; this.registered = new CopyOnWriteArraySet(); } @@ -457,7 +543,7 @@ public void onMeterAdded(String name, Meter meter) { try { if (filter.matches(name, meter)) { final ObjectName objectName = createName("meters", name); - mBeanServer.registerMBean(new JmxMeter(meter, objectName), objectName); + mBeanServer.registerMBean(new JmxMeter(meter, objectName, rateUnit), objectName); registered.add(objectName); } } catch (InstanceAlreadyExistsException e) { @@ -485,7 +571,7 @@ public void onTimerAdded(String name, Timer timer) { try { if (filter.matches(name, timer)) { final ObjectName objectName = createName("timers", name); - mBeanServer.registerMBean(new JmxTimer(timer, objectName), objectName); + mBeanServer.registerMBean(new JmxTimer(timer, objectName, rateUnit, durationUnit), objectName); registered.add(objectName); } } catch (InstanceAlreadyExistsException e) { @@ -538,9 +624,9 @@ void unregisterAll() { private final MetricRegistry registry; private final JmxListener listener; - private JmxReporter(MBeanServer mBeanServer, MetricRegistry registry, MetricFilter filter) { + private JmxReporter(MBeanServer mBeanServer, MetricRegistry registry, MetricFilter filter, TimeUnit rateUnit, TimeUnit durationUnit) { this.registry = registry; - this.listener = new JmxListener(mBeanServer, registry.getName(), filter); + this.listener = new JmxListener(mBeanServer, registry.getName(), filter, rateUnit, durationUnit); } /** diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java index 7ffbe9a1c0..9f78c8b525 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java @@ -24,6 +24,8 @@ public class JmxReporterTest { private final JmxReporter reporter = JmxReporter.forRegistry(registry) .registerWith(mBeanServer) + .convertDurationsTo(TimeUnit.MILLISECONDS) + .convertRatesTo(TimeUnit.SECONDS) .filter(MetricFilter.ALL) .build(); @@ -148,14 +150,16 @@ public void registersMBeansForMeters() throws Exception { "MeanRate", "OneMinuteRate", "FiveMinuteRate", - "FifteenMinuteRate"); + "FifteenMinuteRate", + "RateUnit"); assertThat(values(attributes)) .contains(entry("Count", 1L)) .contains(entry("MeanRate", 2.0)) .contains(entry("OneMinuteRate", 3.0)) .contains(entry("FiveMinuteRate", 4.0)) - .contains(entry("FifteenMinuteRate", 5.0)); + .contains(entry("FifteenMinuteRate", 5.0)) + .contains(entry("RateUnit", "events/second")); } @Test @@ -175,7 +179,9 @@ public void registersMBeansForTimers() throws Exception { "95thPercentile", "98thPercentile", "99thPercentile", - "999thPercentile"); + "999thPercentile", + "RateUnit", + "DurationUnit"); assertThat(values(attributes)) .contains(entry("Count", 1L)) @@ -183,16 +189,18 @@ public void registersMBeansForTimers() throws Exception { .contains(entry("OneMinuteRate", 3.0)) .contains(entry("FiveMinuteRate", 4.0)) .contains(entry("FifteenMinuteRate", 5.0)) - .contains(entry("Max", 100000000L)) - .contains(entry("Mean", 2.0e8)) - .contains(entry("Min", 300000000L)) - .contains(entry("StdDev", 4.0e8)) - .contains(entry("50thPercentile", 5.0e8)) - .contains(entry("75thPercentile", 6.0e8)) - .contains(entry("95thPercentile", 7.0e8)) - .contains(entry("98thPercentile", 8.0e8)) - .contains(entry("99thPercentile", 9.0e8)) - .contains(entry("999thPercentile", 10.0e8)); + .contains(entry("Max", 100.0)) + .contains(entry("Mean", 200.0)) + .contains(entry("Min", 300.0)) + .contains(entry("StdDev", 400.0)) + .contains(entry("50thPercentile", 500.0)) + .contains(entry("75thPercentile", 600.0)) + .contains(entry("95thPercentile", 700.0)) + .contains(entry("98thPercentile", 800.0)) + .contains(entry("99thPercentile", 900.0)) + .contains(entry("999thPercentile", 1000.0)) + .contains(entry("RateUnit", "events/second")) + .contains(entry("DurationUnit", "milliseconds")); } @Test From 0f50f8be3eaebefddc9113f48cd999f4a46716d2 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 31 Mar 2013 21:47:23 -0700 Subject: [PATCH 0152/2558] Added ThreadDeadlockHealthCheck. Closes #343. --- metrics-healthchecks/pom.xml | 9 ++++ .../health/jvm/ThreadDeadlockHealthCheck.java | 38 +++++++++++++ .../tests/ThreadDeadlockHealthCheckTest.java | 54 +++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 metrics-healthchecks/src/main/java/com/yammer/metrics/health/jvm/ThreadDeadlockHealthCheck.java create mode 100644 metrics-healthchecks/src/test/java/com/yammer/metrics/health/jvm/tests/ThreadDeadlockHealthCheckTest.java diff --git a/metrics-healthchecks/pom.xml b/metrics-healthchecks/pom.xml index e598732d52..86872f175f 100644 --- a/metrics-healthchecks/pom.xml +++ b/metrics-healthchecks/pom.xml @@ -13,4 +13,13 @@ metrics-healthchecks Metrics Health Checks bundle + + + + com.yammer.metrics + metrics-jvm + ${project.version} + true + + diff --git a/metrics-healthchecks/src/main/java/com/yammer/metrics/health/jvm/ThreadDeadlockHealthCheck.java b/metrics-healthchecks/src/main/java/com/yammer/metrics/health/jvm/ThreadDeadlockHealthCheck.java new file mode 100644 index 0000000000..d8489186ff --- /dev/null +++ b/metrics-healthchecks/src/main/java/com/yammer/metrics/health/jvm/ThreadDeadlockHealthCheck.java @@ -0,0 +1,38 @@ +package com.yammer.metrics.health.jvm; + +import com.yammer.metrics.health.HealthCheck; +import com.yammer.metrics.jvm.ThreadDeadlockDetector; + +import java.util.Set; + +/** + * A health check which returns healthy if no threads are deadlocked. + */ +public class ThreadDeadlockHealthCheck extends HealthCheck { + private final ThreadDeadlockDetector detector; + + /** + * Creates a new health check. + */ + public ThreadDeadlockHealthCheck() { + this(new ThreadDeadlockDetector()); + } + + /** + * Creates a new health check with the given detector. + * + * @param detector a thread deadlock detector + */ + public ThreadDeadlockHealthCheck(ThreadDeadlockDetector detector) { + this.detector = detector; + } + + @Override + protected Result check() throws Exception { + final Set threads = detector.getDeadlockedThreads(); + if (threads.isEmpty()) { + return Result.healthy(); + } + return Result.unhealthy(threads.toString()); + } +} diff --git a/metrics-healthchecks/src/test/java/com/yammer/metrics/health/jvm/tests/ThreadDeadlockHealthCheckTest.java b/metrics-healthchecks/src/test/java/com/yammer/metrics/health/jvm/tests/ThreadDeadlockHealthCheckTest.java new file mode 100644 index 0000000000..a8acee18f9 --- /dev/null +++ b/metrics-healthchecks/src/test/java/com/yammer/metrics/health/jvm/tests/ThreadDeadlockHealthCheckTest.java @@ -0,0 +1,54 @@ +package com.yammer.metrics.health.jvm.tests; + +import com.yammer.metrics.health.HealthCheck; +import com.yammer.metrics.health.jvm.ThreadDeadlockHealthCheck; +import com.yammer.metrics.jvm.ThreadDeadlockDetector; +import org.junit.Test; + +import java.util.Collections; +import java.util.Set; +import java.util.TreeSet; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ThreadDeadlockHealthCheckTest { + @Test + public void isHealthyIfNoThreadsAreDeadlocked() throws Exception { + final ThreadDeadlockDetector detector = mock(ThreadDeadlockDetector.class); + final ThreadDeadlockHealthCheck healthCheck = new ThreadDeadlockHealthCheck(detector); + + when(detector.getDeadlockedThreads()).thenReturn(Collections.emptySet()); + + assertThat(healthCheck.execute().isHealthy()) + .isTrue(); + } + + @Test + public void isUnhealthyIfThreadsAreDeadlocked() throws Exception { + final Set threads = new TreeSet(); + threads.add("one"); + threads.add("two"); + + final ThreadDeadlockDetector detector = mock(ThreadDeadlockDetector.class); + final ThreadDeadlockHealthCheck healthCheck = new ThreadDeadlockHealthCheck(detector); + + when(detector.getDeadlockedThreads()).thenReturn(threads); + + final HealthCheck.Result result = healthCheck.execute(); + + assertThat(result.isHealthy()) + .isFalse(); + + assertThat(result.getMessage()) + .isEqualTo("[one, two]"); + } + + @Test + public void automaticallyUsesThePlatformThreadBeans() throws Exception { + final ThreadDeadlockHealthCheck healthCheck = new ThreadDeadlockHealthCheck(); + assertThat(healthCheck.execute().isHealthy()) + .isTrue(); + } +} From ea9a4fb6163f51e9b5c07ede95ce9d49d9012795 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 09:17:57 -0700 Subject: [PATCH 0153/2558] Added min, max, mean, and stddev to Snapshot. --- .../java/com/yammer/metrics/Snapshot.java | 79 +++++++++++++++++-- .../yammer/metrics/tests/SnapshotTest.java | 56 +++++++++++++ 2 files changed, 127 insertions(+), 8 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java b/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java index f8ac7cfff1..57fb56e487 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java @@ -71,7 +71,7 @@ public double getValue(double quantile) { /** * Returns the number of values in the snapshot. * - * @return the number of values in the snapshot + * @return the number of values */ public int size() { return values.length; @@ -80,7 +80,7 @@ public int size() { /** * Returns the median value in the distribution. * - * @return the median value in the distribution + * @return the median value */ public double getMedian() { return getValue(0.5); @@ -89,7 +89,7 @@ public double getMedian() { /** * Returns the value at the 75th percentile in the distribution. * - * @return the value at the 75th percentile in the distribution + * @return the value at the 75th percentile */ public double get75thPercentile() { return getValue(0.75); @@ -98,7 +98,7 @@ public double get75thPercentile() { /** * Returns the value at the 95th percentile in the distribution. * - * @return the value at the 95th percentile in the distribution + * @return the value at the 95th percentile */ public double get95thPercentile() { return getValue(0.95); @@ -107,7 +107,7 @@ public double get95thPercentile() { /** * Returns the value at the 98th percentile in the distribution. * - * @return the value at the 98th percentile in the distribution + * @return the value at the 98th percentile */ public double get98thPercentile() { return getValue(0.98); @@ -116,7 +116,7 @@ public double get98thPercentile() { /** * Returns the value at the 99th percentile in the distribution. * - * @return the value at the 99th percentile in the distribution + * @return the value at the 99th percentile */ public double get99thPercentile() { return getValue(0.99); @@ -125,7 +125,7 @@ public double get99thPercentile() { /** * Returns the value at the 99.9th percentile in the distribution. * - * @return the value at the 99.9th percentile in the distribution + * @return the value at the 99.9th percentile */ public double get999thPercentile() { return getValue(0.999); @@ -134,12 +134,75 @@ public double get999thPercentile() { /** * Returns the entire set of values in the snapshot. * - * @return the entire set of values in the snapshot + * @return the entire set of values */ public long[] getValues() { return Arrays.copyOf(values, values.length); } + /** + * Returns the highest value in the snapshot. + * + * @return the highest value + */ + public long getMax() { + if (values.length == 0) { + return 0; + } + return values[values.length - 1]; + } + + /** + * Returns the lowest value in the snapshot. + * + * @return the lowest value + */ + public long getMin() { + if (values.length == 0) { + return 0; + } + return values[0]; + } + + /** + * Returns the arithmetic mean of the values in the snapshot. + * + * @return the arithmetic mean + */ + public double getMean() { + if (values.length == 0) { + return 0; + } + + double sum = 0; + for (long value : values) { + sum += value; + } + return sum / values.length; + } + + /** + * Returns the standard deviation of the values in the snapshot. + * + * @return the standard value + */ + public double getStdDev() { + if (values.length == 0) { + return 0; + } + + final double mean = getMean(); + double sum = 0; + + for (long value : values) { + final double diff = value - mean; + sum += diff * diff; + } + + final double variance = sum / (values.length - 1); + return Math.sqrt(variance); + } + /** * Writes the values of the snapshot to the given stream. * diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java index be834ed79f..838a549acc 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java @@ -119,4 +119,60 @@ public void dumpsToAStream() throws Exception { assertThat(output.toString()) .isEqualTo(String.format("1%n2%n3%n4%n5%n")); } + + @Test + public void calculatesTheMinimumValue() throws Exception { + assertThat(snapshot.getMin()) + .isEqualTo(1); + } + + @Test + public void calculatesTheMaximumValue() throws Exception { + assertThat(snapshot.getMax()) + .isEqualTo(5); + } + + @Test + public void calculatesTheMeanValue() throws Exception { + assertThat(snapshot.getMean()) + .isEqualTo(3.0); + } + + @Test + public void calculatesTheStdDev() throws Exception { + assertThat(snapshot.getStdDev()) + .isEqualTo(1.5811, offset(0.0001)); + } + + @Test + public void calculatesAMinOfZeroForAnEmptySnapshot() throws Exception { + final Snapshot snapshot = new Snapshot(new long[]{ }); + + assertThat(snapshot.getMin()) + .isZero(); + } + + @Test + public void calculatesAMaxOfZeroForAnEmptySnapshot() throws Exception { + final Snapshot snapshot = new Snapshot(new long[]{ }); + + assertThat(snapshot.getMax()) + .isZero(); + } + + @Test + public void calculatesAMeanOfZeroForAnEmptySnapshot() throws Exception { + final Snapshot snapshot = new Snapshot(new long[]{ }); + + assertThat(snapshot.getMean()) + .isZero(); + } + + @Test + public void calculatesAStdDevOfZeroForAnEmptySnapshot() throws Exception { + final Snapshot snapshot = new Snapshot(new long[]{ }); + + assertThat(snapshot.getStdDev()) + .isZero(); + } } From c8395c975d9f41a74322c2df98bd2bf43ce0ba08 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 11:14:34 -0700 Subject: [PATCH 0154/2558] Remove min/max/mean/stdev from Histogram + Timer. In addition to simplifying the code, this also means these values will be derived from the reservoir contents, allowing them to be recency-biased, etc. --- .../com/yammer/metrics/ConsoleReporter.java | 16 +-- .../java/com/yammer/metrics/CsvReporter.java | 16 +-- .../java/com/yammer/metrics/Histogram.java | 102 +--------------- .../java/com/yammer/metrics/JmxReporter.java | 16 +-- .../com/yammer/metrics/Slf4jReporter.java | 16 +-- .../java/com/yammer/metrics/Snapshot.java | 4 +- .../java/com/yammer/metrics/Summarizable.java | 41 ------- .../main/java/com/yammer/metrics/Timer.java | 59 +--------- .../metrics/tests/ConsoleReporterTest.java | 17 ++- .../yammer/metrics/tests/CsvReporterTest.java | 17 ++- .../yammer/metrics/tests/HistogramTest.java | 82 +++---------- .../yammer/metrics/tests/JmxReporterTest.java | 17 ++- .../metrics/tests/Slf4jReporterTest.java | 16 +-- .../yammer/metrics/tests/SnapshotTest.java | 8 ++ .../com/yammer/metrics/tests/TimerTest.java | 110 ++++++------------ .../metrics/ganglia/GangliaReporter.java | 20 ++-- .../ganglia/tests/GangliaReporterTest.java | 16 +-- .../metrics/graphite/GraphiteReporter.java | 23 ++-- .../graphite/tests/GraphiteReporterTest.java | 17 ++- .../InstrumentedTimingCollectorTest.java | 22 ++-- .../yammer/metrics/json/MetricsModule.java | 21 ++-- .../metrics/json/tests/MetricsModuleTest.java | 17 ++- 22 files changed, 199 insertions(+), 474 deletions(-) delete mode 100644 metrics-core/src/main/java/com/yammer/metrics/Summarizable.java diff --git a/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java b/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java index 099b7a81c7..b52198d4d1 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java @@ -249,11 +249,11 @@ private void printGauge(Map.Entry entry) { private void printHistogram(Histogram histogram) { output.printf(locale, " count = %d%n", histogram.getCount()); - output.printf(locale, " min = %d%n", histogram.getMin()); - output.printf(locale, " max = %d%n", histogram.getMax()); - output.printf(locale, " mean = %2.2f%n", histogram.getMean()); - output.printf(locale, " stddev = %2.2f%n", histogram.getStdDev()); Snapshot snapshot = histogram.getSnapshot(); + output.printf(locale, " min = %d%n", snapshot.getMin()); + output.printf(locale, " max = %d%n", snapshot.getMax()); + output.printf(locale, " mean = %2.2f%n", snapshot.getMean()); + output.printf(locale, " stddev = %2.2f%n", snapshot.getStdDev()); output.printf(locale, " median = %2.2f%n", snapshot.getMedian()); output.printf(locale, " 75%% <= %2.2f%n", snapshot.get75thPercentile()); output.printf(locale, " 95%% <= %2.2f%n", snapshot.get95thPercentile()); @@ -270,10 +270,10 @@ private void printTimer(Timer timer) { output.printf(locale, " 5-minute rate = %2.2f calls/%s%n", timer.getFiveMinuteRate() * rateFactor, rateUnit); output.printf(locale, " 15-minute rate = %2.2f calls/%s%n", timer.getFifteenMinuteRate() * rateFactor, rateUnit); - output.printf(locale, " min = %2.2f %s%n", timer.getMin() * durationFactor, durationUnit); - output.printf(locale, " max = %2.2f %s%n", timer.getMax() * durationFactor, durationUnit); - output.printf(locale, " mean = %2.2f %s%n", timer.getMean() * durationFactor, durationUnit); - output.printf(locale, " stddev = %2.2f %s%n", timer.getStdDev() * durationFactor, durationUnit); + output.printf(locale, " min = %2.2f %s%n", snapshot.getMin() * durationFactor, durationUnit); + output.printf(locale, " max = %2.2f %s%n", snapshot.getMax() * durationFactor, durationUnit); + output.printf(locale, " mean = %2.2f %s%n", snapshot.getMean() * durationFactor, durationUnit); + output.printf(locale, " stddev = %2.2f %s%n", snapshot.getStdDev() * durationFactor, durationUnit); output.printf(locale, " median = %2.2f %s%n", snapshot.getMedian() * durationFactor, durationUnit); output.printf(locale, " 75%% <= %2.2f %s%n", snapshot.get75thPercentile() * durationFactor, durationUnit); output.printf(locale, " 95%% <= %2.2f %s%n", snapshot.get95thPercentile() * durationFactor, durationUnit); diff --git a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java index 6e219f148f..d1175f2556 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java @@ -183,10 +183,10 @@ private void reportTimer(long timestamp, String name, Timer timer) { "count,max,mean,min,stddev,p50,p75,p95,p98,p99,p999,mean_rate,m1_rate,m5_rate,m15_rate,rate_unit,duration_unit", "%d,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,calls/%s,%s", timer.getCount(), - timer.getMax() * durationFactor, - timer.getMean() * durationFactor, - timer.getMin() * durationFactor, - timer.getStdDev() * durationFactor, + snapshot.getMax() * durationFactor, + snapshot.getMean() * durationFactor, + snapshot.getMin() * durationFactor, + snapshot.getStdDev() * durationFactor, snapshot.getMedian() * durationFactor, snapshot.get75thPercentile() * durationFactor, snapshot.get95thPercentile() * durationFactor, @@ -222,10 +222,10 @@ private void reportHistogram(long timestamp, String name, Histogram histogram) { "count,max,mean,min,stddev,p50,p75,p95,p98,p99,p999", "%d,%d,%f,%d,%f,%f,%f,%f,%f,%f,%f", histogram.getCount(), - histogram.getMax(), - histogram.getMean(), - histogram.getMin(), - histogram.getStdDev(), + snapshot.getMax(), + snapshot.getMean(), + snapshot.getMin(), + snapshot.getStdDev(), snapshot.getMedian(), snapshot.get75thPercentile(), snapshot.get95thPercentile(), diff --git a/metrics-core/src/main/java/com/yammer/metrics/Histogram.java b/metrics-core/src/main/java/com/yammer/metrics/Histogram.java index 84634f491d..b8329faa3b 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Histogram.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Histogram.java @@ -1,9 +1,6 @@ package com.yammer.metrics; import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReference; - -import static java.lang.Math.sqrt; /** * A metric which calculates the distribution of a value. @@ -11,14 +8,8 @@ * @see Accurately computing running * variance */ -public class Histogram implements Metric, Sampling, Summarizable { +public class Histogram implements Metric, Sampling { private final Reservoir reservoir; - private final AtomicLong min; - private final AtomicLong max; - private final AtomicLong sum; - // These are for the Welford algorithm for calculating running variance - // without floating-point doom. - private final AtomicReference variance; // M, S private final AtomicLong count; /** @@ -28,10 +19,6 @@ public class Histogram implements Metric, Sampling, Summarizable { */ public Histogram(Reservoir reservoir) { this.reservoir = reservoir; - this.min = new AtomicLong(Long.MAX_VALUE); - this.max = new AtomicLong(Long.MIN_VALUE); - this.sum = new AtomicLong(0); - this.variance = new AtomicReference(new double[]{-1, 0}); this.count = new AtomicLong(0); } @@ -52,10 +39,6 @@ public void update(int value) { public void update(long value) { count.incrementAndGet(); reservoir.update(value); - setMax(value); - setMin(value); - sum.getAndAdd(value); - updateVariance(value); } /** @@ -67,91 +50,8 @@ public long getCount() { return count.get(); } - @Override - public long getMax() { - if (getCount() > 0) { - return max.get(); - } - return 0; - } - - @Override - public long getMin() { - if (getCount() > 0) { - return min.get(); - } - return 0; - } - - @Override - public double getMean() { - if (getCount() > 0) { - return sum.get() / (double) getCount(); - } - return 0.0; - } - - @Override - public double getStdDev() { - if (getCount() > 0) { - return sqrt(getVariance()); - } - return 0.0; - } - - @Override - public long getSum() { - return sum.get(); - } - @Override public Snapshot getSnapshot() { return reservoir.getSnapshot(); } - - private double getVariance() { - if (getCount() <= 1) { - return 0.0; - } - return variance.get()[1] / (getCount() - 1); - } - - private void setMax(long potentialMax) { - boolean done = false; - while (!done) { - final long currentMax = max.get(); - done = currentMax >= potentialMax || max.compareAndSet(currentMax, potentialMax); - } - } - - private void setMin(long potentialMin) { - boolean done = false; - while (!done) { - final long currentMin = min.get(); - done = currentMin <= potentialMin || min.compareAndSet(currentMin, potentialMin); - } - } - - private void updateVariance(long value) { - while (true) { - final double[] oldValues = variance.get(); - final double[] newValues = new double[2]; - if (oldValues[0] == -1) { - newValues[0] = value; - newValues[1] = 0; - } else { - final double oldM = oldValues[0]; - final double oldS = oldValues[1]; - - final double newM = oldM + ((value - oldM) / getCount()); - final double newS = oldS + ((value - oldM) * (value - newM)); - - newValues[0] = newM; - newValues[1] = newS; - } - if (variance.compareAndSet(oldValues, newValues)) { - return; - } - } - } } diff --git a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java index 5048bb07f8..cdb9ba4e9f 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java @@ -215,22 +215,22 @@ public long getCount() { @Override public long getMin() { - return metric.getMin(); + return metric.getSnapshot().getMin(); } @Override public long getMax() { - return metric.getMax(); + return metric.getSnapshot().getMax(); } @Override public double getMean() { - return metric.getMean(); + return metric.getSnapshot().getMean(); } @Override public double getStdDev() { - return metric.getStdDev(); + return metric.getSnapshot().getStdDev(); } @Override @@ -379,22 +379,22 @@ public double get50thPercentile() { @Override public double getMin() { - return metric.getMin() * durationFactor; + return metric.getSnapshot().getMin() * durationFactor; } @Override public double getMax() { - return metric.getMax() * durationFactor; + return metric.getSnapshot().getMax() * durationFactor; } @Override public double getMean() { - return metric.getMean() * durationFactor; + return metric.getSnapshot().getMean() * durationFactor; } @Override public double getStdDev() { - return metric.getStdDev() * durationFactor; + return metric.getSnapshot().getStdDev() * durationFactor; } @Override diff --git a/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java b/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java index fa60c87f05..b5d5be7246 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java @@ -170,10 +170,10 @@ private void logTimer(String name, Timer timer) { "rate_unit={}, duration_unit={}", name, timer.getCount(), - timer.getMin() * durationFactor, - timer.getMax() * durationFactor, - timer.getMean() * durationFactor, - timer.getStdDev() * durationFactor, + snapshot.getMin() * durationFactor, + snapshot.getMax() * durationFactor, + snapshot.getMean() * durationFactor, + snapshot.getStdDev() * durationFactor, snapshot.getMedian() * durationFactor, snapshot.get75thPercentile() * durationFactor, snapshot.get95thPercentile() * durationFactor, @@ -206,10 +206,10 @@ private void logHistogram(String name, Histogram histogram) { "type=HISTOGRAM, name={}, count={}, min={}, max={}, mean={}, stddev={}, median={}, p75={}, p95={}, p98={}, p999={}", name, histogram.getCount(), - histogram.getMin(), - histogram.getMax(), - histogram.getMean(), - histogram.getStdDev(), + snapshot.getMin(), + snapshot.getMax(), + snapshot.getMean(), + snapshot.getStdDev(), snapshot.getMedian(), snapshot.get75thPercentile(), snapshot.get95thPercentile(), diff --git a/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java b/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java index 57fb56e487..68fe12d449 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java @@ -187,7 +187,9 @@ public double getMean() { * @return the standard value */ public double getStdDev() { - if (values.length == 0) { + // two-pass algorithm for variance, avoids numeric overflow + + if (values.length <= 1) { return 0; } diff --git a/metrics-core/src/main/java/com/yammer/metrics/Summarizable.java b/metrics-core/src/main/java/com/yammer/metrics/Summarizable.java deleted file mode 100644 index acc281eecb..0000000000 --- a/metrics-core/src/main/java/com/yammer/metrics/Summarizable.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.yammer.metrics; - -/** - * An object which can produce statistical summaries. - */ -public interface Summarizable { - /** - * Returns the largest recorded value. - * - * @return the largest recorded value - */ - long getMax(); - - /** - * Returns the smallest recorded value. - * - * @return the smallest recorded value - */ - long getMin(); - - /** - * Returns the arithmetic mean of all recorded values. - * - * @return the arithmetic mean of all recorded values - */ - double getMean(); - - /** - * Returns the standard deviation of all recorded values. - * - * @return the standard deviation of all recorded values - */ - double getStdDev(); - - /** - * Returns the sum of all recorded values. - * - * @return the sum of all recorded values - */ - long getSum(); -} diff --git a/metrics-core/src/main/java/com/yammer/metrics/Timer.java b/metrics-core/src/main/java/com/yammer/metrics/Timer.java index 1d50d38f96..d1992453ae 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Timer.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Timer.java @@ -8,7 +8,7 @@ * A timer metric which aggregates timing durations and provides duration statistics, plus * throughput statistics via {@link Meter}. */ -public class Timer implements Metered, Sampling, Summarizable { +public class Timer implements Metered, Sampling { /** * A timing context. * @@ -46,7 +46,8 @@ public void close() { private final Clock clock; /** - * Creates a new {@link Timer}. + * Creates a new {@link Timer} using an {@link ExponentiallyDecayingReservoir} and the default + * {@link Clock}. */ public Timer() { this(new ExponentiallyDecayingReservoir()); @@ -102,7 +103,7 @@ public T time(Callable event) throws Exception { } /** - * Returns a timing {@link Context}, which measures an elapsed time in nanoseconds. + * Returns a new {@link Context}. * * @return a new {@link Context} * @see Context @@ -136,59 +137,9 @@ public double getOneMinuteRate() { return meter.getOneMinuteRate(); } - /** - * Returns the longest recorded duration. - * - * @return the longest recorded duration - */ - @Override - public long getMax() { - return histogram.getMax(); - } - - /** - * Returns the shortest recorded duration. - * - * @return the shortest recorded duration - */ - @Override - public long getMin() { - return histogram.getMin(); - } - - /** - * Returns the arithmetic mean of all recorded durations. - * - * @return the arithmetic mean of all recorded durations - */ - @Override - public double getMean() { - return histogram.getMean(); - } - - /** - * Returns the standard deviation of all recorded durations. - * - * @return the standard deviation of all recorded durations - */ - @Override - public double getStdDev() { - return histogram.getStdDev(); - } - - /** - * Returns the sum of all recorded durations. - * - * @return the sum of all recorded durations - */ - @Override - public long getSum() { - return histogram.getSum(); - } - @Override public Snapshot getSnapshot() { - return new Snapshot(histogram.getSnapshot().getValues()); + return histogram.getSnapshot(); } private void update(long duration) { diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java index 5cad84b2c3..10575e890e 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java @@ -87,12 +87,12 @@ public void reportsCounterValues() throws Exception { public void reportsHistogramValues() throws Exception { final Histogram histogram = mock(Histogram.class); when(histogram.getCount()).thenReturn(1L); - when(histogram.getMax()).thenReturn(2L); - when(histogram.getMean()).thenReturn(3.0); - when(histogram.getMin()).thenReturn(4L); - when(histogram.getStdDev()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMax()).thenReturn(2L); + when(snapshot.getMean()).thenReturn(3.0); + when(snapshot.getMin()).thenReturn(4L); + when(snapshot.getStdDev()).thenReturn(5.0); when(snapshot.getMedian()).thenReturn(6.0); when(snapshot.get75thPercentile()).thenReturn(7.0); when(snapshot.get95thPercentile()).thenReturn(8.0); @@ -165,17 +165,16 @@ public void reportsMeterValues() throws Exception { public void reportsTimerValues() throws Exception { final Timer timer = mock(Timer.class); when(timer.getCount()).thenReturn(1L); - when(timer.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); - when(timer.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); - when(timer.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); - when(timer.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); - when(timer.getMeanRate()).thenReturn(2.0); when(timer.getOneMinuteRate()).thenReturn(3.0); when(timer.getFiveMinuteRate()).thenReturn(4.0); when(timer.getFifteenMinuteRate()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); + when(snapshot.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); + when(snapshot.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); + when(snapshot.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java index 9520a6befd..077026ce24 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java @@ -81,12 +81,12 @@ public void reportsCounterValues() throws Exception { public void reportsHistogramValues() throws Exception { final Histogram histogram = mock(Histogram.class); when(histogram.getCount()).thenReturn(1L); - when(histogram.getMax()).thenReturn(2L); - when(histogram.getMean()).thenReturn(3.0); - when(histogram.getMin()).thenReturn(4L); - when(histogram.getStdDev()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMax()).thenReturn(2L); + when(snapshot.getMean()).thenReturn(3.0); + when(snapshot.getMin()).thenReturn(4L); + when(snapshot.getStdDev()).thenReturn(5.0); when(snapshot.getMedian()).thenReturn(6.0); when(snapshot.get75thPercentile()).thenReturn(7.0); when(snapshot.get95thPercentile()).thenReturn(8.0); @@ -135,17 +135,16 @@ public void reportsMeterValues() throws Exception { public void reportsTimerValues() throws Exception { final Timer timer = mock(Timer.class); when(timer.getCount()).thenReturn(1L); - when(timer.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); - when(timer.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); - when(timer.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); - when(timer.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); - when(timer.getMeanRate()).thenReturn(2.0); when(timer.getOneMinuteRate()).thenReturn(3.0); when(timer.getFiveMinuteRate()).thenReturn(4.0); when(timer.getFifteenMinuteRate()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); + when(snapshot.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); + when(snapshot.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); + when(snapshot.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java index 94ddfbf856..7e1f868455 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java @@ -1,85 +1,41 @@ package com.yammer.metrics.tests; import com.yammer.metrics.Histogram; +import com.yammer.metrics.Reservoir; import com.yammer.metrics.Snapshot; -import com.yammer.metrics.UniformReservoir; -import com.yammer.metrics.UniformReservoir; import org.junit.Test; import static org.fest.assertions.api.Assertions.assertThat; -import static org.fest.assertions.api.Assertions.offset; +import static org.mockito.Mockito.*; public class HistogramTest { - private final Histogram histogram = new Histogram(new UniformReservoir()); + private final Reservoir reservoir = mock(Reservoir.class); + private final Histogram histogram = new Histogram(reservoir); @Test - public void anEmptyHistogram() throws Exception { + public void updatesTheCountOnUpdates() throws Exception { assertThat(histogram.getCount()) - .isEqualTo(0L); + .isZero(); - assertThat(histogram.getMax()) - .isEqualTo(0); - - assertThat(histogram.getMin()) - .isEqualTo(0); - - assertThat(histogram.getMean()) - .isEqualTo(0.0, offset(0.0001)); - - assertThat(histogram.getStdDev()) - .isEqualTo(0.0, offset(0.0001)); - - assertThat(histogram.getSum()) - .isEqualTo(0); - - final Snapshot snapshot = histogram.getSnapshot(); - - assertThat(snapshot.getMedian()) - .isEqualTo(0.0, offset(0.0001)); - - assertThat(snapshot.get75thPercentile()) - .isEqualTo(0.0, offset(0.0001)); - - assertThat(snapshot.get99thPercentile()) - .isEqualTo(0.0, offset(0.0001)); - - assertThat(snapshot.size()) - .isEqualTo(0); - } - - @Test - public void aHistogramWith1000Elements() throws Exception { - for (int i = 1; i <= 1000; i++) { - histogram.update(i); - } + histogram.update(1); assertThat(histogram.getCount()) - .isEqualTo(1000L); - - assertThat(histogram.getMax()) - .isEqualTo(1000); - - assertThat(histogram.getMin()) .isEqualTo(1); + } - assertThat(histogram.getMean()) - .isEqualTo(500.5, offset(0.0001)); - - assertThat(histogram.getStdDev()) - .isEqualTo(288.8194360957494, offset(0.0001)); - - assertThat(histogram.getSum()) - .isEqualTo(500500); - - final Snapshot snapshot = histogram.getSnapshot(); - - assertThat(snapshot.getMedian()).isEqualTo(500.5, offset(0.0001)); + @Test + public void returnsTheSnapshotFromTheReservoir() throws Exception { + final Snapshot snapshot = mock(Snapshot.class); + when(reservoir.getSnapshot()).thenReturn(snapshot); - assertThat(snapshot.get75thPercentile()).isEqualTo(750.75, offset(0.0001)); + assertThat(histogram.getSnapshot()) + .isEqualTo(snapshot); + } - assertThat(snapshot.get99thPercentile()).isEqualTo(990.99, offset(0.0001)); + @Test + public void updatesTheReservoir() throws Exception { + histogram.update(1); - assertThat(snapshot.size()) - .isEqualTo(1000); + verify(reservoir).update(1); } } diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java index 9f78c8b525..cba02cb6fb 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java @@ -42,12 +42,12 @@ public void setUp() throws Exception { when(counter.getCount()).thenReturn(100L); when(histogram.getCount()).thenReturn(1L); - when(histogram.getMax()).thenReturn(2L); - when(histogram.getMean()).thenReturn(3.0); - when(histogram.getMin()).thenReturn(4L); - when(histogram.getStdDev()).thenReturn(5.0); final Snapshot hSnapshot = mock(Snapshot.class); + when(hSnapshot.getMax()).thenReturn(2L); + when(hSnapshot.getMean()).thenReturn(3.0); + when(hSnapshot.getMin()).thenReturn(4L); + when(hSnapshot.getStdDev()).thenReturn(5.0); when(hSnapshot.getMedian()).thenReturn(6.0); when(hSnapshot.get75thPercentile()).thenReturn(7.0); when(hSnapshot.get95thPercentile()).thenReturn(8.0); @@ -64,17 +64,16 @@ public void setUp() throws Exception { when(meter.getFifteenMinuteRate()).thenReturn(5.0); when(timer.getCount()).thenReturn(1L); - when(timer.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); - when(timer.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); - when(timer.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); - when(timer.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); - when(timer.getMeanRate()).thenReturn(2.0); when(timer.getOneMinuteRate()).thenReturn(3.0); when(timer.getFiveMinuteRate()).thenReturn(4.0); when(timer.getFifteenMinuteRate()).thenReturn(5.0); final Snapshot tSnapshot = mock(Snapshot.class); + when(tSnapshot.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); + when(tSnapshot.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); + when(tSnapshot.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); + when(tSnapshot.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(tSnapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); when(tSnapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); when(tSnapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java index 14275cdf18..bc63a3bc7f 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java @@ -54,12 +54,12 @@ public void reportsCounterValues() throws Exception { public void reportsHistogramValues() throws Exception { final Histogram histogram = mock(Histogram.class); when(histogram.getCount()).thenReturn(1L); - when(histogram.getMax()).thenReturn(2L); - when(histogram.getMean()).thenReturn(3.0); - when(histogram.getMin()).thenReturn(4L); - when(histogram.getStdDev()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMax()).thenReturn(2L); + when(snapshot.getMean()).thenReturn(3.0); + when(snapshot.getMin()).thenReturn(4L); + when(snapshot.getStdDev()).thenReturn(5.0); when(snapshot.getMedian()).thenReturn(6.0); when(snapshot.get75thPercentile()).thenReturn(7.0); when(snapshot.get95thPercentile()).thenReturn(8.0); @@ -121,10 +121,6 @@ public void reportsMeterValues() throws Exception { public void reportsTimerValues() throws Exception { final Timer timer = mock(Timer.class); when(timer.getCount()).thenReturn(1L); - when(timer.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); - when(timer.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); - when(timer.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); - when(timer.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(timer.getMeanRate()).thenReturn(2.0); when(timer.getOneMinuteRate()).thenReturn(3.0); @@ -132,6 +128,10 @@ public void reportsTimerValues() throws Exception { when(timer.getFifteenMinuteRate()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); + when(snapshot.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); + when(snapshot.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); + when(snapshot.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java index 838a549acc..80b06769cc 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java @@ -175,4 +175,12 @@ public void calculatesAStdDevOfZeroForAnEmptySnapshot() throws Exception { assertThat(snapshot.getStdDev()) .isZero(); } + + @Test + public void calculatesAStdDevOfZeroForASingletonSnapshot() throws Exception { + final Snapshot snapshot = new Snapshot(new long[]{ 1 }); + + assertThat(snapshot.getStdDev()) + .isZero(); + } } diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java index 0cf9a1388a..f16eae92f9 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java @@ -1,8 +1,7 @@ package com.yammer.metrics.tests; import com.yammer.metrics.Clock; -import com.yammer.metrics.ExponentiallyDecayingReservoir; -import com.yammer.metrics.ExponentiallyDecayingReservoir; +import com.yammer.metrics.Reservoir; import com.yammer.metrics.Snapshot; import com.yammer.metrics.Timer; import org.junit.Test; @@ -12,8 +11,10 @@ import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.api.Assertions.offset; +import static org.mockito.Mockito.*; public class TimerTest { + private final Reservoir reservoir = mock(Reservoir.class); private final Clock clock = new Clock() { // a mock clock that increments its ticker by 50msec per call private long val = 0; @@ -23,36 +24,13 @@ public long getTick() { return val += 50000000; } }; - private final Timer timer = new Timer(new ExponentiallyDecayingReservoir(), clock); + private final Timer timer = new Timer(reservoir, clock); @Test - public void aBlankTimer() throws Exception { + public void hasRates() throws Exception { assertThat(timer.getCount()) .isZero(); - assertThat(timer.getMax()) - .isEqualTo(0); - - assertThat(timer.getMin()) - .isEqualTo(0); - - assertThat(timer.getMean()) - .isEqualTo(0.0, offset(0.001)); - - assertThat(timer.getStdDev()) - .isEqualTo(0.0, offset(0.001)); - - final Snapshot snapshot = timer.getSnapshot(); - - assertThat(snapshot.getMedian()) - .isEqualTo(0.0, offset(0.001)); - - assertThat(snapshot.get75thPercentile()) - .isEqualTo(0.0, offset(0.001)); - - assertThat(snapshot.get99thPercentile()) - .isEqualTo(0.0, offset(0.001)); - assertThat(timer.getMeanRate()) .isEqualTo(0.0, offset(0.001)); @@ -64,60 +42,21 @@ public void aBlankTimer() throws Exception { assertThat(timer.getFifteenMinuteRate()) .isEqualTo(0.0, offset(0.001)); - - assertThat(timer.getSnapshot().size()) - .isZero(); } @Test - public void timingASeriesOfEvents() throws Exception { - timer.update(10, TimeUnit.MILLISECONDS); - timer.update(20, TimeUnit.MILLISECONDS); - timer.update(20, TimeUnit.MILLISECONDS); - timer.update(30, TimeUnit.MILLISECONDS); - timer.update(40, TimeUnit.MILLISECONDS); - + public void updatesTheCountOnUpdates() throws Exception { assertThat(timer.getCount()) - .isEqualTo(5); - - assertThat(timer.getMax()) - .isEqualTo(40000000); - - assertThat(timer.getMin()) - .isEqualTo(10000000); - - assertThat(timer.getMean()) - .isEqualTo(24000000, offset(0.001)); - - assertThat(timer.getStdDev()) - .isEqualTo(11400000, offset(10000.0)); - - final Snapshot snapshot = timer.getSnapshot(); - - assertThat(snapshot.getMedian()) - .isEqualTo(20000000, offset(0.001)); - - assertThat(snapshot.get75thPercentile()) - .isEqualTo(35000000, offset(0.001)); - - assertThat(snapshot.get99thPercentile()) - .isEqualTo(40000000, offset(0.001)); - - assertThat(timer.getSnapshot().getValues()) - .containsOnly(10000000, 20000000, 20000000, 30000000, 40000000); - } + .isZero(); - @Test - public void timingVariantValues() throws Exception { - timer.update(Long.MAX_VALUE, TimeUnit.NANOSECONDS); - timer.update(0, TimeUnit.NANOSECONDS); + timer.update(1, TimeUnit.SECONDS); - assertThat(timer.getStdDev()) - .isEqualTo(6.521908912666392E18, offset(0.001)); + assertThat(timer.getCount()) + .isEqualTo(1); } @Test - public void timingCallableInstances() throws Exception { + public void timesCallableInstances() throws Exception { final String value = timer.time(new Callable() { @Override public String call() throws Exception { @@ -131,18 +70,35 @@ public String call() throws Exception { assertThat(value) .isEqualTo("one"); - assertThat(timer.getMax()) - .isEqualTo(50000000); + verify(reservoir).update(50000000); } @Test - public void timingContexts() throws Exception { + public void timesContexts() throws Exception { timer.time().stop(); assertThat(timer.getCount()) .isEqualTo(1); - assertThat(timer.getMax()) - .isEqualTo(50000000); + verify(reservoir).update(50000000); + } + + @Test + public void returnsTheSnapshotFromTheReservoir() throws Exception { + final Snapshot snapshot = mock(Snapshot.class); + when(reservoir.getSnapshot()).thenReturn(snapshot); + + assertThat(timer.getSnapshot()) + .isEqualTo(snapshot); + } + + @Test + public void ignoresNegativeValues() throws Exception { + timer.update(-1, TimeUnit.SECONDS); + + assertThat(timer.getCount()) + .isZero(); + + verifyZeroInteractions(reservoir); } } diff --git a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java index e9c9ea3ef9..bc998db350 100644 --- a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java +++ b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java @@ -183,12 +183,13 @@ public void report(SortedMap gauges, private void reportTimer(String name, Timer timer) { final String group = group(name); try { - announce(name(name, "max"), group, timer.getMax() * durationFactor, durationUnit); - announce(name(name, "mean"), group, timer.getMean() * durationFactor, durationUnit); - announce(name(name, "min"), group, timer.getMin() * durationFactor, durationUnit); - announce(name(name, "stddev"), group, timer.getStdDev() * durationFactor, durationUnit); - final Snapshot snapshot = timer.getSnapshot(); + + announce(name(name, "max"), group, snapshot.getMax() * durationFactor, durationUnit); + announce(name(name, "mean"), group, snapshot.getMean() * durationFactor, durationUnit); + announce(name(name, "min"), group, snapshot.getMin() * durationFactor, durationUnit); + announce(name(name, "stddev"), group, snapshot.getStdDev() * durationFactor, durationUnit); + announce(name(name, "p50"), group, snapshot.getMedian() * durationFactor, durationUnit); announce(name(name, "p75"), group, @@ -241,11 +242,10 @@ private void reportHistogram(String name, Histogram histogram) { final Snapshot snapshot = histogram.getSnapshot(); announce(name(name, "count"), group, histogram.getCount(), ""); - announce(name(name, "max"), group, histogram.getMax(), ""); - announce(name(name, "mean"), group, histogram.getMean(), ""); - announce(name(name, "min"), group, histogram.getMin(), ""); - announce(name(name, "stddev"), group, histogram.getStdDev(), ""); - + announce(name(name, "max"), group, snapshot.getMax(), ""); + announce(name(name, "mean"), group, snapshot.getMean(), ""); + announce(name(name, "min"), group, snapshot.getMin(), ""); + announce(name(name, "stddev"), group, snapshot.getStdDev(), ""); announce(name(name, "p50"), group, snapshot.getMedian(), ""); announce(name(name, "p75"), group, snapshot.get75thPercentile(), ""); announce(name(name, "p95"), group, snapshot.get95thPercentile(), ""); diff --git a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java b/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java index dc5f37d1b8..26dc34f802 100644 --- a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java +++ b/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java @@ -127,12 +127,12 @@ public void reportsCounterValues() throws Exception { public void reportsHistogramValues() throws Exception { final Histogram histogram = mock(Histogram.class); when(histogram.getCount()).thenReturn(1L); - when(histogram.getMax()).thenReturn(2L); - when(histogram.getMean()).thenReturn(3.0); - when(histogram.getMin()).thenReturn(4L); - when(histogram.getStdDev()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMax()).thenReturn(2L); + when(snapshot.getMean()).thenReturn(3.0); + when(snapshot.getMin()).thenReturn(4L); + when(snapshot.getStdDev()).thenReturn(5.0); when(snapshot.getMedian()).thenReturn(6.0); when(snapshot.get75thPercentile()).thenReturn(7.0); when(snapshot.get95thPercentile()).thenReturn(8.0); @@ -189,10 +189,6 @@ public void reportsMeterValues() throws Exception { public void reportsTimerValues() throws Exception { final Timer timer = mock(Timer.class); when(timer.getCount()).thenReturn(1L); - when(timer.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); - when(timer.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); - when(timer.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); - when(timer.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(timer.getMeanRate()).thenReturn(2.0); when(timer.getOneMinuteRate()).thenReturn(3.0); @@ -200,6 +196,10 @@ public void reportsTimerValues() throws Exception { when(timer.getFifteenMinuteRate()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); + when(snapshot.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); + when(snapshot.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); + when(snapshot.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); diff --git a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java index 184ff4988f..3a0db60018 100644 --- a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java +++ b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java @@ -187,14 +187,14 @@ public void report(SortedMap gauges, } private void reportTimer(String name, Timer timer, long timestamp) throws IOException { - graphite.send(prefix(name, "max"), format(timer.getMax() * durationFactor), timestamp); - graphite.send(prefix(name, "mean"), format(timer.getMean() * durationFactor), timestamp); - graphite.send(prefix(name, "min"), format(timer.getMin() * durationFactor), timestamp); + final Snapshot snapshot = timer.getSnapshot(); + + graphite.send(prefix(name, "max"), format(snapshot.getMax() * durationFactor), timestamp); + graphite.send(prefix(name, "mean"), format(snapshot.getMean() * durationFactor), timestamp); + graphite.send(prefix(name, "min"), format(snapshot.getMin() * durationFactor), timestamp); graphite.send(prefix(name, "stddev"), - format(timer.getStdDev() * durationFactor), + format(snapshot.getStdDev() * durationFactor), timestamp); - - final Snapshot snapshot = timer.getSnapshot(); graphite.send(prefix(name, "p50"), format(snapshot.getMedian() * durationFactor), timestamp); @@ -234,13 +234,12 @@ private void reportMetered(String name, Metered meter, long timestamp) throws IO } private void reportHistogram(String name, Histogram histogram, long timestamp) throws IOException { - graphite.send(prefix(name, "count"), format(histogram.getCount()), timestamp); - graphite.send(prefix(name, "max"), format(histogram.getMax()), timestamp); - graphite.send(prefix(name, "mean"), format(histogram.getMean()), timestamp); - graphite.send(prefix(name, "min"), format(histogram.getMin()), timestamp); - graphite.send(prefix(name, "stddev"), format(histogram.getStdDev()), timestamp); - final Snapshot snapshot = histogram.getSnapshot(); + graphite.send(prefix(name, "count"), format(histogram.getCount()), timestamp); + graphite.send(prefix(name, "max"), format(snapshot.getMax()), timestamp); + graphite.send(prefix(name, "mean"), format(snapshot.getMean()), timestamp); + graphite.send(prefix(name, "min"), format(snapshot.getMin()), timestamp); + graphite.send(prefix(name, "stddev"), format(snapshot.getStdDev()), timestamp); graphite.send(prefix(name, "p50"), format(snapshot.getMedian()), timestamp); graphite.send(prefix(name, "p75"), format(snapshot.get75thPercentile()), timestamp); graphite.send(prefix(name, "p95"), format(snapshot.get95thPercentile()), timestamp); diff --git a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java index 6ea0f2d238..ce3bd7aac0 100644 --- a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java +++ b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java @@ -166,12 +166,12 @@ public void reportsCounters() throws Exception { public void reportsHistograms() throws Exception { final Histogram histogram = mock(Histogram.class); when(histogram.getCount()).thenReturn(1L); - when(histogram.getMax()).thenReturn(2L); - when(histogram.getMean()).thenReturn(3.0); - when(histogram.getMin()).thenReturn(4L); - when(histogram.getStdDev()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMax()).thenReturn(2L); + when(snapshot.getMean()).thenReturn(3.0); + when(snapshot.getMin()).thenReturn(4L); + when(snapshot.getStdDev()).thenReturn(5.0); when(snapshot.getMedian()).thenReturn(6.0); when(snapshot.get75thPercentile()).thenReturn(7.0); when(snapshot.get95thPercentile()).thenReturn(8.0); @@ -236,17 +236,16 @@ public void reportsMeters() throws Exception { public void reportsTimers() throws Exception { final Timer timer = mock(Timer.class); when(timer.getCount()).thenReturn(1L); - when(timer.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); - when(timer.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); - when(timer.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); - when(timer.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); - when(timer.getMeanRate()).thenReturn(2.0); when(timer.getOneMinuteRate()).thenReturn(3.0); when(timer.getFiveMinuteRate()).thenReturn(4.0); when(timer.getFifteenMinuteRate()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); + when(snapshot.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); + when(snapshot.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); + when(snapshot.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); diff --git a/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java b/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java index e8620431e4..17a295ff54 100644 --- a/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java +++ b/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java @@ -37,7 +37,7 @@ public void updatesTimerForSqlObjects() throws Exception { assertThat(name) .isEqualTo(name(getClass(), "updatesTimerForSqlObjects")); - assertThat(timer.getMax()) + assertThat(timer.getSnapshot().getMax()) .isEqualTo(1000000000); } @@ -57,7 +57,7 @@ public void updatesTimerForSqlObjectsWithoutMethod() throws Exception { assertThat(name) .isEqualTo(name(getClass(), "SELECT 1")); - assertThat(timer.getMax()) + assertThat(timer.getSnapshot().getMax()) .isEqualTo(1000000000); } @@ -76,7 +76,7 @@ public void updatesTimerForRawSql() throws Exception { assertThat(name) .isEqualTo(name("sql", "raw", "SELECT 1")); - assertThat(timer.getMax()) + assertThat(timer.getSnapshot().getMax()) .isEqualTo(2000000000); } @@ -94,7 +94,7 @@ public void updatesTimerForNoRawSql() throws Exception { assertThat(name) .isEqualTo(name("sql", "empty")); - assertThat(timer.getMax()) + assertThat(timer.getSnapshot().getMax()) .isEqualTo(2000000000); } @@ -113,7 +113,7 @@ public void updatesTimerForNonSqlishRawSql() throws Exception { assertThat(name) .isEqualTo(name("sql", "raw", "don't know what it is but it's not SQL")); - assertThat(timer.getMax()) + assertThat(timer.getSnapshot().getMax()) .isEqualTo(3000000000L); } @@ -135,7 +135,7 @@ public void updatesTimerForContextClass() throws Exception { assertThat(name) .isEqualTo(name(getClass(), "updatesTimerForContextClass")); - assertThat(timer.getMax()) + assertThat(timer.getSnapshot().getMax()) .isEqualTo(3000000000L); } @@ -157,7 +157,7 @@ public void updatesTimerForTemplateFile() throws Exception { assertThat(name) .isEqualTo(name("foo", "bar", "updatesTimerForTemplateFile")); - assertThat(timer.getMax()) + assertThat(timer.getSnapshot().getMax()) .isEqualTo(4000000000L); } @@ -179,7 +179,7 @@ public void updatesTimerForContextGroupAndName() throws Exception { assertThat(name) .isEqualTo(name("my-group", "updatesTimerForContextGroupAndName", "")); - assertThat(timer.getMax()) + assertThat(timer.getSnapshot().getMax()) .isEqualTo(4000000000L); } @@ -202,7 +202,7 @@ public void updatesTimerForContextGroupTypeAndName() throws Exception { assertThat(name) .isEqualTo(name("my-group", "my-type", "updatesTimerForContextGroupTypeAndName")); - assertThat(timer.getMax()) + assertThat(timer.getSnapshot().getMax()) .isEqualTo(5000000000L); } @@ -226,7 +226,7 @@ public void updatesTimerForShortSqlObjectStrategy() throws Exception { .isEqualTo(name("jdbi", getClass().getSimpleName(), "updatesTimerForShortSqlObjectStrategy")); - assertThat(timer.getMax()) + assertThat(timer.getSnapshot().getMax()) .isEqualTo(1000000000); } @@ -250,7 +250,7 @@ public void updatesTimerForShortContextClassStrategy() throws Exception { .isEqualTo(name("jdbi", getClass().getSimpleName(), "updatesTimerForShortContextClassStrategy")); - assertThat(timer.getMax()) + assertThat(timer.getSnapshot().getMax()) .isEqualTo(3000000000L); } } diff --git a/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java b/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java index 7b9c4d7784..2f84b86121 100644 --- a/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java +++ b/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java @@ -66,12 +66,11 @@ public void serialize(Histogram histogram, JsonGenerator json, SerializerProvider provider) throws IOException { json.writeStartObject(); - json.writeNumberField("count", histogram.getCount()); - json.writeNumberField("max", histogram.getMax()); - json.writeNumberField("mean", histogram.getMean()); - json.writeNumberField("min", histogram.getMin()); - final Snapshot snapshot = histogram.getSnapshot(); + json.writeNumberField("count", histogram.getCount()); + json.writeNumberField("max", snapshot.getMax()); + json.writeNumberField("mean", snapshot.getMean()); + json.writeNumberField("min", snapshot.getMin()); json.writeNumberField("p50", snapshot.getMedian()); json.writeNumberField("p75", snapshot.get75thPercentile()); json.writeNumberField("p95", snapshot.get95thPercentile()); @@ -83,7 +82,7 @@ public void serialize(Histogram histogram, json.writeObjectField("values", snapshot.getValues()); } - json.writeNumberField("stddev", histogram.getStdDev()); + json.writeNumberField("stddev", snapshot.getStdDev()); json.writeEndObject(); } } @@ -136,12 +135,12 @@ public void serialize(Timer timer, JsonGenerator json, SerializerProvider provider) throws IOException { json.writeStartObject(); + final Snapshot snapshot = timer.getSnapshot(); json.writeNumberField("count", timer.getCount()); - json.writeNumberField("max", timer.getMax() * durationFactor); - json.writeNumberField("mean", timer.getMean() * durationFactor); - json.writeNumberField("min", timer.getMin() * durationFactor); + json.writeNumberField("max", snapshot.getMax() * durationFactor); + json.writeNumberField("mean", snapshot.getMean() * durationFactor); + json.writeNumberField("min", snapshot.getMin() * durationFactor); - final Snapshot snapshot = timer.getSnapshot(); json.writeNumberField("p50", snapshot.getMedian() * durationFactor); json.writeNumberField("p75", snapshot.get75thPercentile() * durationFactor); json.writeNumberField("p95", snapshot.get95thPercentile() * durationFactor); @@ -158,7 +157,7 @@ public void serialize(Timer timer, json.writeObjectField("values", scaledValues); } - json.writeNumberField("stddev", timer.getStdDev() * durationFactor); + json.writeNumberField("stddev", snapshot.getStdDev() * durationFactor); json.writeNumberField("m15_rate", timer.getOneMinuteRate() * rateFactor); json.writeNumberField("m1_rate", timer.getFifteenMinuteRate() * rateFactor); json.writeNumberField("m5_rate", timer.getFiveMinuteRate() * rateFactor); diff --git a/metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java b/metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java index 8d629bb2a0..596cef5bf3 100644 --- a/metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java +++ b/metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java @@ -54,12 +54,12 @@ public void serializesCounters() throws Exception { public void serializesHistograms() throws Exception { final Histogram histogram = mock(Histogram.class); when(histogram.getCount()).thenReturn(1L); - when(histogram.getMax()).thenReturn(2L); - when(histogram.getMean()).thenReturn(3.0); - when(histogram.getMin()).thenReturn(4L); - when(histogram.getStdDev()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMax()).thenReturn(2L); + when(snapshot.getMean()).thenReturn(3.0); + when(snapshot.getMin()).thenReturn(4L); + when(snapshot.getStdDev()).thenReturn(5.0); when(snapshot.getMedian()).thenReturn(6.0); when(snapshot.get75thPercentile()).thenReturn(7.0); when(snapshot.get95thPercentile()).thenReturn(8.0); @@ -126,17 +126,16 @@ public void serializesMeters() throws Exception { public void serializesTimers() throws Exception { final Timer timer = mock(Timer.class); when(timer.getCount()).thenReturn(1L); - when(timer.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); - when(timer.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); - when(timer.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); - when(timer.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); - when(timer.getMeanRate()).thenReturn(2.0); when(timer.getOneMinuteRate()).thenReturn(3.0); when(timer.getFiveMinuteRate()).thenReturn(4.0); when(timer.getFifteenMinuteRate()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); + when(snapshot.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); + when(snapshot.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); + when(snapshot.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); + when(snapshot.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); From c9e9a899c429d8c8fb88778bc2f91853a821f81b Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 11:59:31 -0700 Subject: [PATCH 0155/2558] Rename reporter base to ScheduledReporter. Also, moved all the rate/duration conversion logic up into it. --- .../com/yammer/metrics/ConsoleReporter.java | 53 ++++++---------- .../java/com/yammer/metrics/CsvReporter.java | 59 +++++++---------- ...ngReporter.java => ScheduledReporter.java} | 44 +++++++++++-- .../com/yammer/metrics/Slf4jReporter.java | 63 ++++++++----------- ...erTest.java => ScheduledReporterTest.java} | 10 ++- .../metrics/ganglia/GangliaReporter.java | 58 +++++++---------- .../metrics/graphite/GraphiteReporter.java | 36 +++++------ 7 files changed, 154 insertions(+), 169 deletions(-) rename metrics-core/src/main/java/com/yammer/metrics/{AbstractPollingReporter.java => ScheduledReporter.java} (71%) rename metrics-core/src/test/java/com/yammer/metrics/tests/{AbstractPollingReporterTest.java => ScheduledReporterTest.java} (85%) diff --git a/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java b/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java index b52198d4d1..d04c3a5fbe 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java @@ -8,7 +8,7 @@ /** * A reporter which outputs measurements to a {@link PrintStream}, like {@code System.out}. */ -public class ConsoleReporter extends AbstractPollingReporter { +public class ConsoleReporter extends ScheduledReporter { /** * Returns a new {@link Builder} for {@link ConsoleReporter}. * @@ -145,10 +145,6 @@ public ConsoleReporter build() { private final Locale locale; private final Clock clock; private final DateFormat dateFormat; - private final double durationFactor; - private final String durationUnit; - private final double rateFactor; - private final String rateUnit; private ConsoleReporter(MetricRegistry registry, PrintStream output, @@ -158,7 +154,7 @@ private ConsoleReporter(MetricRegistry registry, TimeUnit rateUnit, TimeUnit durationUnit, MetricFilter filter) { - super(registry, "console-reporter", filter); + super(registry, "console-reporter", filter, rateUnit, durationUnit); this.output = output; this.locale = locale; this.clock = clock; @@ -166,10 +162,6 @@ private ConsoleReporter(MetricRegistry registry, DateFormat.MEDIUM, locale); dateFormat.setTimeZone(timeZone); - this.rateFactor = rateUnit.toSeconds(1); - this.rateUnit = calculateRateUnit(rateUnit); - this.durationFactor = 1.0 / durationUnit.toNanos(1); - this.durationUnit = durationUnit.toString().toLowerCase(Locale.US); } @Override @@ -233,10 +225,10 @@ public void report(SortedMap gauges, private void printMeter(Meter meter) { output.printf(locale, " count = %d%n", meter.getCount()); - output.printf(locale, " mean rate = %2.2f events/%s%n", meter.getMeanRate() * rateFactor, rateUnit); - output.printf(locale, " 1-minute rate = %2.2f events/%s%n", meter.getOneMinuteRate() * rateFactor, rateUnit); - output.printf(locale, " 5-minute rate = %2.2f events/%s%n", meter.getFiveMinuteRate() * rateFactor, rateUnit); - output.printf(locale, " 15-minute rate = %2.2f events/%s%n", meter.getFifteenMinuteRate() * rateFactor, rateUnit); + output.printf(locale, " mean rate = %2.2f events/%s%n", convertRate(meter.getMeanRate()), getRateUnit()); + output.printf(locale, " 1-minute rate = %2.2f events/%s%n", convertRate(meter.getOneMinuteRate()), getRateUnit()); + output.printf(locale, " 5-minute rate = %2.2f events/%s%n", convertRate(meter.getFiveMinuteRate()), getRateUnit()); + output.printf(locale, " 15-minute rate = %2.2f events/%s%n", convertRate(meter.getFifteenMinuteRate()), getRateUnit()); } private void printCounter(Map.Entry entry) { @@ -265,21 +257,21 @@ private void printHistogram(Histogram histogram) { private void printTimer(Timer timer) { final Snapshot snapshot = timer.getSnapshot(); output.printf(locale, " count = %d%n", timer.getCount()); - output.printf(locale, " mean rate = %2.2f calls/%s%n", timer.getMeanRate() * rateFactor, rateUnit); - output.printf(locale, " 1-minute rate = %2.2f calls/%s%n", timer.getOneMinuteRate() * rateFactor, rateUnit); - output.printf(locale, " 5-minute rate = %2.2f calls/%s%n", timer.getFiveMinuteRate() * rateFactor, rateUnit); - output.printf(locale, " 15-minute rate = %2.2f calls/%s%n", timer.getFifteenMinuteRate() * rateFactor, rateUnit); + output.printf(locale, " mean rate = %2.2f calls/%s%n", convertRate(timer.getMeanRate()), getRateUnit()); + output.printf(locale, " 1-minute rate = %2.2f calls/%s%n", convertRate(timer.getOneMinuteRate()), getRateUnit()); + output.printf(locale, " 5-minute rate = %2.2f calls/%s%n", convertRate(timer.getFiveMinuteRate()), getRateUnit()); + output.printf(locale, " 15-minute rate = %2.2f calls/%s%n", convertRate(timer.getFifteenMinuteRate()), getRateUnit()); - output.printf(locale, " min = %2.2f %s%n", snapshot.getMin() * durationFactor, durationUnit); - output.printf(locale, " max = %2.2f %s%n", snapshot.getMax() * durationFactor, durationUnit); - output.printf(locale, " mean = %2.2f %s%n", snapshot.getMean() * durationFactor, durationUnit); - output.printf(locale, " stddev = %2.2f %s%n", snapshot.getStdDev() * durationFactor, durationUnit); - output.printf(locale, " median = %2.2f %s%n", snapshot.getMedian() * durationFactor, durationUnit); - output.printf(locale, " 75%% <= %2.2f %s%n", snapshot.get75thPercentile() * durationFactor, durationUnit); - output.printf(locale, " 95%% <= %2.2f %s%n", snapshot.get95thPercentile() * durationFactor, durationUnit); - output.printf(locale, " 98%% <= %2.2f %s%n", snapshot.get98thPercentile() * durationFactor, durationUnit); - output.printf(locale, " 99%% <= %2.2f %s%n", snapshot.get99thPercentile() * durationFactor, durationUnit); - output.printf(locale, " 99.9%% <= %2.2f %s%n", snapshot.get999thPercentile() * durationFactor, durationUnit); + output.printf(locale, " min = %2.2f %s%n", convertDuration(snapshot.getMin()), getDurationUnit()); + output.printf(locale, " max = %2.2f %s%n", convertDuration(snapshot.getMax()), getDurationUnit()); + output.printf(locale, " mean = %2.2f %s%n", convertDuration(snapshot.getMean()), getDurationUnit()); + output.printf(locale, " stddev = %2.2f %s%n", convertDuration(snapshot.getStdDev()), getDurationUnit()); + output.printf(locale, " median = %2.2f %s%n", convertDuration(snapshot.getMedian()), getDurationUnit()); + output.printf(locale, " 75%% <= %2.2f %s%n", convertDuration(snapshot.get75thPercentile()), getDurationUnit()); + output.printf(locale, " 95%% <= %2.2f %s%n", convertDuration(snapshot.get95thPercentile()), getDurationUnit()); + output.printf(locale, " 98%% <= %2.2f %s%n", convertDuration(snapshot.get98thPercentile()), getDurationUnit()); + output.printf(locale, " 99%% <= %2.2f %s%n", convertDuration(snapshot.get99thPercentile()), getDurationUnit()); + output.printf(locale, " 99.9%% <= %2.2f %s%n", convertDuration(snapshot.get999thPercentile()), getDurationUnit()); } private void printWithBanner(String s, char c) { @@ -290,9 +282,4 @@ private void printWithBanner(String s, char c) { } output.println(); } - - private String calculateRateUnit(TimeUnit unit) { - final String s = unit.toString().toLowerCase(Locale.US); - return s.substring(0, s.length() - 1); - } } diff --git a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java index d1175f2556..d4c71ab897 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java @@ -13,7 +13,7 @@ /** * A reporter which creates a comma-separated values file of the measurements for each metric. */ -public class CsvReporter extends AbstractPollingReporter { +public class CsvReporter extends ScheduledReporter { /** * Returns a new {@link Builder} for {@link CsvReporter}. * @@ -124,10 +124,6 @@ public CsvReporter build(File directory) { private final File directory; private final Locale locale; private final Clock clock; - private final double durationFactor; - private final String durationUnit; - private final double rateFactor; - private final String rateUnit; private CsvReporter(MetricRegistry registry, File directory, @@ -136,14 +132,10 @@ private CsvReporter(MetricRegistry registry, TimeUnit durationUnit, Clock clock, MetricFilter filter) { - super(registry, "csv-reporter", filter); + super(registry, "csv-reporter", filter, rateUnit, durationUnit); this.directory = directory; this.locale = locale; this.clock = clock; - this.rateFactor = rateUnit.toSeconds(1); - this.rateUnit = calculateRateUnit(rateUnit); - this.durationFactor = 1.0 / durationUnit.toNanos(1); - this.durationUnit = durationUnit.toString().toLowerCase(Locale.US); } @Override @@ -183,22 +175,22 @@ private void reportTimer(long timestamp, String name, Timer timer) { "count,max,mean,min,stddev,p50,p75,p95,p98,p99,p999,mean_rate,m1_rate,m5_rate,m15_rate,rate_unit,duration_unit", "%d,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,calls/%s,%s", timer.getCount(), - snapshot.getMax() * durationFactor, - snapshot.getMean() * durationFactor, - snapshot.getMin() * durationFactor, - snapshot.getStdDev() * durationFactor, - snapshot.getMedian() * durationFactor, - snapshot.get75thPercentile() * durationFactor, - snapshot.get95thPercentile() * durationFactor, - snapshot.get98thPercentile() * durationFactor, - snapshot.get99thPercentile() * durationFactor, - snapshot.get999thPercentile() * durationFactor, - timer.getMeanRate() * rateFactor, - timer.getOneMinuteRate() * rateFactor, - timer.getFiveMinuteRate() * rateFactor, - timer.getFifteenMinuteRate() * rateFactor, - rateUnit, - durationUnit); + convertDuration(snapshot.getMax()), + convertDuration(snapshot.getMean()), + convertDuration(snapshot.getMin()), + convertDuration(snapshot.getStdDev()), + convertDuration(snapshot.getMedian()), + convertDuration(snapshot.get75thPercentile()), + convertDuration(snapshot.get95thPercentile()), + convertDuration(snapshot.get98thPercentile()), + convertDuration(snapshot.get99thPercentile()), + convertDuration(snapshot.get999thPercentile()), + convertRate(timer.getMeanRate()), + convertRate(timer.getOneMinuteRate()), + convertRate(timer.getFiveMinuteRate()), + convertRate(timer.getFifteenMinuteRate()), + getRateUnit(), + getDurationUnit()); } private void reportMeter(long timestamp, String name, Meter meter) { @@ -207,11 +199,11 @@ private void reportMeter(long timestamp, String name, Meter meter) { "count,mean_rate,m1_rate,m5_rate,m15_rate,rate_unit", "%d,%f,%f,%f,%f,events/%s", meter.getCount(), - meter.getMeanRate() * rateFactor, - meter.getOneMinuteRate() * rateFactor, - meter.getFiveMinuteRate() * rateFactor, - meter.getFifteenMinuteRate() * rateFactor, - rateUnit); + convertRate(meter.getMeanRate()), + convertRate(meter.getOneMinuteRate()), + convertRate(meter.getFiveMinuteRate()), + convertRate(meter.getFifteenMinuteRate()), + getRateUnit()); } private void reportHistogram(long timestamp, String name, Histogram histogram) { @@ -262,11 +254,6 @@ private void report(long timestamp, String name, String header, String line, Obj } } - private String calculateRateUnit(TimeUnit unit) { - final String s = unit.toString().toLowerCase(Locale.US); - return s.substring(0, s.length() - 1); - } - protected String sanitize(String name) { return name; } diff --git a/metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java b/metrics-core/src/main/java/com/yammer/metrics/ScheduledReporter.java similarity index 71% rename from metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java rename to metrics-core/src/main/java/com/yammer/metrics/ScheduledReporter.java index fe7cdea068..88c207e337 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/AbstractPollingReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/ScheduledReporter.java @@ -1,5 +1,6 @@ package com.yammer.metrics; +import java.util.Locale; import java.util.SortedMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -8,12 +9,14 @@ import java.util.concurrent.atomic.AtomicInteger; /** - * The abstract base class for all polling reporters (i.e., reporters which process a registry's + * The abstract base class for all scheduled reporters (i.e., reporters which process a registry's * metrics periodically). * * @see ConsoleReporter + * @see CsvReporter + * @see Slf4jReporter */ -public abstract class AbstractPollingReporter { +public abstract class ScheduledReporter { /** * A simple named thread factory. */ @@ -43,19 +46,31 @@ public Thread newThread(Runnable r) { private final MetricRegistry registry; private final ScheduledExecutorService executor; private final MetricFilter filter; + private final double durationFactor; + private final String durationUnit; + private final double rateFactor; + private final String rateUnit; /** - * Creates a new {@link AbstractPollingReporter} instance. + * Creates a new {@link ScheduledReporter} instance. * * @param registry the {@link com.yammer.metrics.MetricRegistry} containing the metrics this * reporter will report * @param name the reporter's name * @param filter the filter for which metrics to report */ - protected AbstractPollingReporter(MetricRegistry registry, String name, MetricFilter filter) { + protected ScheduledReporter(MetricRegistry registry, + String name, + MetricFilter filter, + TimeUnit rateUnit, + TimeUnit durationUnit) { this.registry = registry; this.filter = filter; this.executor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory(name)); + this.rateFactor = rateUnit.toSeconds(1); + this.rateUnit = calculateRateUnit(rateUnit); + this.durationFactor = 1.0 / durationUnit.toNanos(1); + this.durationUnit = durationUnit.toString().toLowerCase(Locale.US); } /** @@ -103,4 +118,25 @@ public abstract void report(SortedMap gauges, SortedMap histograms, SortedMap meters, SortedMap timers); + + protected String getRateUnit() { + return rateUnit; + } + + protected String getDurationUnit() { + return durationUnit; + } + + protected double convertDuration(double duration) { + return duration * durationFactor; + } + + protected double convertRate(double rate) { + return rate * rateFactor; + } + + private String calculateRateUnit(TimeUnit unit) { + final String s = unit.toString().toLowerCase(Locale.US); + return s.substring(0, s.length() - 1); + } } diff --git a/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java b/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java index b5d5be7246..da3bf06a40 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java @@ -4,7 +4,6 @@ import org.slf4j.LoggerFactory; import org.slf4j.Marker; -import java.util.Locale; import java.util.Map.Entry; import java.util.SortedMap; import java.util.concurrent.TimeUnit; @@ -15,7 +14,7 @@ * supports specifying a {@link Marker} instance that can be used by custom appenders and filters * for the bound logging toolkit to further process metrics reports. */ -public class Slf4jReporter extends AbstractPollingReporter { +public class Slf4jReporter extends ScheduledReporter { /** * Returns a new {@link Builder} for {@link Slf4jReporter}. * @@ -115,10 +114,6 @@ public Slf4jReporter build() { private final Logger logger; private final Marker marker; - private final double durationFactor; - private final String durationUnit; - private final double rateFactor; - private final String rateUnit; private Slf4jReporter(MetricRegistry registry, Logger logger, @@ -126,13 +121,9 @@ private Slf4jReporter(MetricRegistry registry, TimeUnit rateUnit, TimeUnit durationUnit, MetricFilter filter) { - super(registry, "logger-reporter", filter); + super(registry, "logger-reporter", filter, rateUnit, durationUnit); this.logger = logger; this.marker = marker; - this.rateFactor = rateUnit.toSeconds(1); - this.rateUnit = "events/" + calculateRateUnit(rateUnit); - this.durationFactor = 1.0 / durationUnit.toNanos(1); - this.durationUnit = durationUnit.toString().toLowerCase(Locale.US); } @Override @@ -170,22 +161,22 @@ private void logTimer(String name, Timer timer) { "rate_unit={}, duration_unit={}", name, timer.getCount(), - snapshot.getMin() * durationFactor, - snapshot.getMax() * durationFactor, - snapshot.getMean() * durationFactor, - snapshot.getStdDev() * durationFactor, - snapshot.getMedian() * durationFactor, - snapshot.get75thPercentile() * durationFactor, - snapshot.get95thPercentile() * durationFactor, - snapshot.get98thPercentile() * durationFactor, - snapshot.get99thPercentile() * durationFactor, - snapshot.get999thPercentile() * durationFactor, - timer.getMeanRate() * rateFactor, - timer.getOneMinuteRate() * rateFactor, - timer.getFiveMinuteRate() * rateFactor, - timer.getFifteenMinuteRate() * rateFactor, - rateUnit, - durationUnit); + convertDuration(snapshot.getMin()), + convertDuration(snapshot.getMax()), + convertDuration(snapshot.getMean()), + convertDuration(snapshot.getStdDev()), + convertDuration(snapshot.getMedian()), + convertDuration(snapshot.get75thPercentile()), + convertDuration(snapshot.get95thPercentile()), + convertDuration(snapshot.get98thPercentile()), + convertDuration(snapshot.get99thPercentile()), + convertDuration(snapshot.get999thPercentile()), + convertRate(timer.getMeanRate()), + convertRate(timer.getOneMinuteRate()), + convertRate(timer.getFiveMinuteRate()), + convertRate(timer.getFifteenMinuteRate()), + getRateUnit(), + getDurationUnit()); } private void logMeter(String name, Meter meter) { @@ -193,11 +184,11 @@ private void logMeter(String name, Meter meter) { "type=METER, name={}, count={}, mean_rate={}, m1={}, m5={}, m15={}, rate_unit={}", name, meter.getCount(), - meter.getMeanRate() * rateFactor, - meter.getOneMinuteRate() * rateFactor, - meter.getFiveMinuteRate() * rateFactor, - meter.getFifteenMinuteRate() * rateFactor, - rateUnit); + convertRate(meter.getMeanRate()), + convertRate(meter.getOneMinuteRate()), + convertRate(meter.getFiveMinuteRate()), + convertRate(meter.getFifteenMinuteRate()), + getRateUnit()); } private void logHistogram(String name, Histogram histogram) { @@ -226,10 +217,8 @@ private void logGauge(String name, Gauge gauge) { logger.info(marker, "type=GAUGE, name={}, value={}", name, gauge.getValue()); } - private String calculateRateUnit(TimeUnit unit) { - final String s = unit.toString().toLowerCase(Locale.US); - return s.substring(0, s.length() - 1); + @Override + protected String getRateUnit() { + return "events/" + super.getRateUnit(); } - - } diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/AbstractPollingReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/ScheduledReporterTest.java similarity index 85% rename from metrics-core/src/test/java/com/yammer/metrics/tests/AbstractPollingReporterTest.java rename to metrics-core/src/test/java/com/yammer/metrics/tests/ScheduledReporterTest.java index 4d1bcee916..67cacb4509 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/AbstractPollingReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/ScheduledReporterTest.java @@ -11,7 +11,7 @@ import static org.mockito.Mockito.*; -public class AbstractPollingReporterTest { +public class ScheduledReporterTest { private final Gauge gauge = mock(Gauge.class); private final Counter counter = mock(Counter.class); private final Histogram histogram = mock(Histogram.class); @@ -19,8 +19,12 @@ public class AbstractPollingReporterTest { private final Timer timer = mock(Timer.class); private final MetricRegistry registry = new MetricRegistry("test"); - private final AbstractPollingReporter reporter = spy( - new AbstractPollingReporter(registry, "example", MetricFilter.ALL) { + private final ScheduledReporter reporter = spy( + new ScheduledReporter(registry, + "example", + MetricFilter.ALL, + TimeUnit.SECONDS, + TimeUnit.MILLISECONDS) { @Override public void report(SortedMap gauges, SortedMap counters, diff --git a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java index bc998db350..c81f8cc257 100644 --- a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java +++ b/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java @@ -8,7 +8,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Locale; import java.util.Map; import java.util.SortedMap; import java.util.concurrent.TimeUnit; @@ -20,7 +19,7 @@ * * @see Ganglia Monitoring System */ -public class GangliaReporter extends AbstractPollingReporter { +public class GangliaReporter extends ScheduledReporter { /** * Returns a new {@link Builder} for {@link GangliaReporter}. * @@ -124,10 +123,6 @@ public GangliaReporter build(GMetric ganglia) { private static final Logger LOGGER = LoggerFactory.getLogger(GangliaReporter.class); private final GMetric ganglia; - private final double durationFactor; - private final String durationUnit; - private final double rateFactor; - private final String rateUnit; private final int tMax; private final int dMax; @@ -138,19 +133,10 @@ private GangliaReporter(MetricRegistry registry, TimeUnit rateUnit, TimeUnit durationUnit, MetricFilter filter) { - super(registry, "ganglia-reporter", filter); + super(registry, "ganglia-reporter", filter, rateUnit, durationUnit); this.ganglia = ganglia; this.tMax = tMax; this.dMax = dMax; - this.rateFactor = rateUnit.toSeconds(1); - this.rateUnit = calculateRateUnit(rateUnit); - this.durationFactor = 1.0 / durationUnit.toNanos(1); - this.durationUnit = durationUnit.toString().toLowerCase(Locale.US); - } - - private String calculateRateUnit(TimeUnit unit) { - final String s = unit.toString().toLowerCase(Locale.US); - return s.substring(0, s.length() - 1); } @Override @@ -185,32 +171,32 @@ private void reportTimer(String name, Timer timer) { try { final Snapshot snapshot = timer.getSnapshot(); - announce(name(name, "max"), group, snapshot.getMax() * durationFactor, durationUnit); - announce(name(name, "mean"), group, snapshot.getMean() * durationFactor, durationUnit); - announce(name(name, "min"), group, snapshot.getMin() * durationFactor, durationUnit); - announce(name(name, "stddev"), group, snapshot.getStdDev() * durationFactor, durationUnit); + announce(name(name, "max"), group, convertDuration(snapshot.getMax()), getDurationUnit()); + announce(name(name, "mean"), group, convertDuration(snapshot.getMean()), getDurationUnit()); + announce(name(name, "min"), group, convertDuration(snapshot.getMin()), getDurationUnit()); + announce(name(name, "stddev"), group, convertDuration(snapshot.getStdDev()), getDurationUnit()); - announce(name(name, "p50"), group, snapshot.getMedian() * durationFactor, durationUnit); + announce(name(name, "p50"), group, convertDuration(snapshot.getMedian()), getDurationUnit()); announce(name(name, "p75"), group, - snapshot.get75thPercentile() * durationFactor, - durationUnit); + convertDuration(snapshot.get75thPercentile()), + getDurationUnit()); announce(name(name, "p95"), group, - snapshot.get95thPercentile() * durationFactor, - durationUnit); + convertDuration(snapshot.get95thPercentile()), + getDurationUnit()); announce(name(name, "p98"), group, - snapshot.get98thPercentile() * durationFactor, - durationUnit); + convertDuration(snapshot.get98thPercentile()), + getDurationUnit()); announce(name(name, "p99"), group, - snapshot.get99thPercentile() * durationFactor, - durationUnit); + convertDuration(snapshot.get99thPercentile()), + getDurationUnit()); announce(name(name, "p999"), group, - snapshot.get999thPercentile() * durationFactor, - durationUnit); + convertDuration(snapshot.get999thPercentile()), + getDurationUnit()); reportMetered(name, timer, group, "calls"); } catch (GangliaException e) { @@ -228,12 +214,12 @@ private void reportMeter(String name, Meter meter) { } private void reportMetered(String name, Metered meter, String group, String eventName) throws GangliaException { - final String unit = eventName + '/' + rateUnit; + final String unit = eventName + '/' + getRateUnit(); announce(name(name, "count"), group, meter.getCount(), eventName); - announce(name(name, "m1_rate"), group, meter.getOneMinuteRate() * rateFactor, unit); - announce(name(name, "m5_rate"), group, meter.getFiveMinuteRate() * rateFactor, unit); - announce(name(name, "m15_rate"), group, meter.getFifteenMinuteRate() * rateFactor, unit); - announce(name(name, "mean_rate"), group, meter.getMeanRate() * rateFactor, unit); + announce(name(name, "m1_rate"), group, convertRate(meter.getOneMinuteRate()), unit); + announce(name(name, "m5_rate"), group, convertRate(meter.getFiveMinuteRate()), unit); + announce(name(name, "m15_rate"), group, convertRate(meter.getFifteenMinuteRate()), unit); + announce(name(name, "mean_rate"), group, convertRate(meter.getMeanRate()), unit); } private void reportHistogram(String name, Histogram histogram) { diff --git a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java index 3a0db60018..72a40c3134 100644 --- a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java +++ b/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java @@ -15,7 +15,7 @@ * * @see Graphite - Scalable Realtime Graphing */ -public class GraphiteReporter extends AbstractPollingReporter { +public class GraphiteReporter extends ScheduledReporter { /** * Returns a new {@link Builder} for {@link GraphiteReporter}. * @@ -126,8 +126,6 @@ public GraphiteReporter build(Graphite graphite) { private final Graphite graphite; private final Clock clock; private final String prefix; - private final double rateFactor; - private final double durationFactor; private GraphiteReporter(MetricRegistry registry, Graphite graphite, @@ -136,12 +134,10 @@ private GraphiteReporter(MetricRegistry registry, TimeUnit rateUnit, TimeUnit durationUnit, MetricFilter filter) { - super(registry, "graphite-reporter", filter); + super(registry, "graphite-reporter", filter, rateUnit, durationUnit); this.graphite = graphite; this.clock = clock; this.prefix = prefix; - this.rateFactor = rateUnit.toSeconds(1); - this.durationFactor = 1.0 / durationUnit.toNanos(1); } @Override @@ -189,29 +185,29 @@ public void report(SortedMap gauges, private void reportTimer(String name, Timer timer, long timestamp) throws IOException { final Snapshot snapshot = timer.getSnapshot(); - graphite.send(prefix(name, "max"), format(snapshot.getMax() * durationFactor), timestamp); - graphite.send(prefix(name, "mean"), format(snapshot.getMean() * durationFactor), timestamp); - graphite.send(prefix(name, "min"), format(snapshot.getMin() * durationFactor), timestamp); + graphite.send(prefix(name, "max"), format(convertDuration(snapshot.getMax())), timestamp); + graphite.send(prefix(name, "mean"), format(convertDuration(snapshot.getMean())), timestamp); + graphite.send(prefix(name, "min"), format(convertDuration(snapshot.getMin())), timestamp); graphite.send(prefix(name, "stddev"), - format(snapshot.getStdDev() * durationFactor), + format(convertDuration(snapshot.getStdDev())), timestamp); graphite.send(prefix(name, "p50"), - format(snapshot.getMedian() * durationFactor), + format(convertDuration(snapshot.getMedian())), timestamp); graphite.send(prefix(name, "p75"), - format(snapshot.get75thPercentile() * durationFactor), + format(convertDuration(snapshot.get75thPercentile())), timestamp); graphite.send(prefix(name, "p95"), - format(snapshot.get95thPercentile() * durationFactor), + format(convertDuration(snapshot.get95thPercentile())), timestamp); graphite.send(prefix(name, "p98"), - format(snapshot.get98thPercentile() * durationFactor), + format(convertDuration(snapshot.get98thPercentile())), timestamp); graphite.send(prefix(name, "p99"), - format(snapshot.get99thPercentile() * durationFactor), + format(convertDuration(snapshot.get99thPercentile())), timestamp); graphite.send(prefix(name, "p999"), - format(snapshot.get999thPercentile() * durationFactor), + format(convertDuration(snapshot.get999thPercentile())), timestamp); reportMetered(name, timer, timestamp); @@ -220,16 +216,16 @@ private void reportTimer(String name, Timer timer, long timestamp) throws IOExce private void reportMetered(String name, Metered meter, long timestamp) throws IOException { graphite.send(prefix(name, "count"), format(meter.getCount()), timestamp); graphite.send(prefix(name, "m1_rate"), - format(meter.getOneMinuteRate() * rateFactor), + format(convertRate(meter.getOneMinuteRate())), timestamp); graphite.send(prefix(name, "m5_rate"), - format(meter.getFiveMinuteRate() * rateFactor), + format(convertRate(meter.getFiveMinuteRate())), timestamp); graphite.send(prefix(name, "m15_rate"), - format(meter.getFifteenMinuteRate() * rateFactor), + format(convertRate(meter.getFifteenMinuteRate())), timestamp); graphite.send(prefix(name, "mean_rate"), - format(meter.getMeanRate() * rateFactor), + format(convertRate(meter.getMeanRate())), timestamp); } From 7930a69bd2a1a9f1edfa2eecaad9fab76d4f66cf Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 12:52:22 -0700 Subject: [PATCH 0156/2558] Modernized the release notes for 3.0. --- docs/source/about/release-notes.rst | 31 ++++++++++++++++------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/docs/source/about/release-notes.rst b/docs/source/about/release-notes.rst index dc20835280..51fdfbd009 100644 --- a/docs/source/about/release-notes.rst +++ b/docs/source/about/release-notes.rst @@ -9,20 +9,23 @@ Release Notes v3.0.0-SNAPSHOT =============== -* Added ``AdminServlet#setServiceName()``. -* Switched all getters to the standard ``#getValue()``. -* Use the full metric name in ``CsvReporter``. -* Made ``DefaultWebappMetricsFilter``'s registry configurable. -* Switched to ``HttpServletRequest#getContextPath()`` in ``AdminServlet``. -* Upgraded to Logback 1.0.3. -* Upgraded to Log4j 1.2.17. -* Upgraded to JDBI 2.34. -* Upgraded to Ehcache 2.5.2. -* Upgraded to Jackson 2.0.2. -* Upgraded to Jetty 8.1.4. -* Upgraded to SLF4J 1.6.5. -* Changed package names in ``metrics-ganglia``, ``metrics-graphite``, and ``metrics-servlet``. -* Removed ``metrics-guice`` and ``metrics-spring``. +* Total overhaul of most of the core Metrics classes: + + * Metric names are now just dotted paths like ``com.example.Thing``, allowing for very flexible + scopes, etc. + * Meters and timers no longer have rate or duration units; those are properties of reporters. + * Reporter architecture has been radically simplified, fixing many bugs. + * Histograms and timers can take arbitrary reservoir implementations. + * Added sliding window reservoir implementations. + * Added ``MetricSet`` for sets of metrics. + +* Changed package names to be OSGi-compatible and added OSGi bundling. +* Extracted JVM instrumentation to ``metrics-jvm``. +* Extracted Jackson integration to ``metrics-json``. +* Removed ``metrics-guice``, ``metrics-scala``, and ``metrics-spring``. +* Renamed ``metrics-servlet`` to ``metrics-servlets``. +* Renamed ``metrics-web`` to ``metrics-servlet``. +* Renamed ``metrics-jetty`` to ``metrics-jetty8``. .. _rel-2.2.0: From 05789808e65fa3fe1d8e38ee69d4d9a849fe678f Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 12:53:35 -0700 Subject: [PATCH 0157/2558] Upgrade to SLF4J 1.7.5. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fec54c96f8..c355276e52 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ UTF-8 UTF-8 2.5 - 1.7.2 + 1.7.5 2.1.4 8.1.9.v20130131 From 5f087a660257442198ecb19ef783680b5e2c0fd3 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 13:40:39 -0700 Subject: [PATCH 0158/2558] [maven-release-plugin] prepare release v3.0.0-BETA1 --- metrics-annotation/pom.xml | 2 +- metrics-core/pom.xml | 6 ++---- metrics-ehcache/pom.xml | 2 +- metrics-ganglia/pom.xml | 6 ++---- metrics-graphite/pom.xml | 6 ++---- metrics-healthchecks/pom.xml | 6 ++---- metrics-httpclient/pom.xml | 2 +- metrics-jdbi/pom.xml | 2 +- metrics-jersey/pom.xml | 2 +- metrics-jetty8/pom.xml | 2 +- metrics-json/pom.xml | 6 ++---- metrics-jvm/pom.xml | 6 ++---- metrics-log4j/pom.xml | 2 +- metrics-logback/pom.xml | 2 +- metrics-servlet/pom.xml | 6 ++---- metrics-servlets/pom.xml | 2 +- pom.xml | 2 +- 17 files changed, 24 insertions(+), 38 deletions(-) diff --git a/metrics-annotation/pom.xml b/metrics-annotation/pom.xml index 3ed9d1c556..0b43471be1 100644 --- a/metrics-annotation/pom.xml +++ b/metrics-annotation/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-SNAPSHOT + 3.0.0-BETA1 metrics-annotation diff --git a/metrics-core/pom.xml b/metrics-core/pom.xml index 14b014623b..6a8710c5f3 100644 --- a/metrics-core/pom.xml +++ b/metrics-core/pom.xml @@ -1,13 +1,11 @@ - + 4.0.0 com.yammer.metrics metrics-parent - 3.0.0-SNAPSHOT + 3.0.0-BETA1 metrics-core diff --git a/metrics-ehcache/pom.xml b/metrics-ehcache/pom.xml index 87f1ec583b..7c2b2613ec 100644 --- a/metrics-ehcache/pom.xml +++ b/metrics-ehcache/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-SNAPSHOT + 3.0.0-BETA1 metrics-ehcache diff --git a/metrics-ganglia/pom.xml b/metrics-ganglia/pom.xml index 53171a3cb9..058e171733 100644 --- a/metrics-ganglia/pom.xml +++ b/metrics-ganglia/pom.xml @@ -1,13 +1,11 @@ - + 4.0.0 com.yammer.metrics metrics-parent - 3.0.0-SNAPSHOT + 3.0.0-BETA1 metrics-ganglia diff --git a/metrics-graphite/pom.xml b/metrics-graphite/pom.xml index 4a57f8d64f..28a36e38cd 100644 --- a/metrics-graphite/pom.xml +++ b/metrics-graphite/pom.xml @@ -1,13 +1,11 @@ - + 4.0.0 com.yammer.metrics metrics-parent - 3.0.0-SNAPSHOT + 3.0.0-BETA1 metrics-graphite diff --git a/metrics-healthchecks/pom.xml b/metrics-healthchecks/pom.xml index 86872f175f..f590486241 100644 --- a/metrics-healthchecks/pom.xml +++ b/metrics-healthchecks/pom.xml @@ -1,13 +1,11 @@ - + 4.0.0 com.yammer.metrics metrics-parent - 3.0.0-SNAPSHOT + 3.0.0-BETA1 metrics-healthchecks diff --git a/metrics-httpclient/pom.xml b/metrics-httpclient/pom.xml index 4320b872b7..2ae499e62d 100644 --- a/metrics-httpclient/pom.xml +++ b/metrics-httpclient/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-SNAPSHOT + 3.0.0-BETA1 metrics-httpclient diff --git a/metrics-jdbi/pom.xml b/metrics-jdbi/pom.xml index c324e192bf..e565abbf9a 100644 --- a/metrics-jdbi/pom.xml +++ b/metrics-jdbi/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-SNAPSHOT + 3.0.0-BETA1 metrics-jdbi diff --git a/metrics-jersey/pom.xml b/metrics-jersey/pom.xml index 144893d64a..b469f31036 100644 --- a/metrics-jersey/pom.xml +++ b/metrics-jersey/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-SNAPSHOT + 3.0.0-BETA1 metrics-jersey diff --git a/metrics-jetty8/pom.xml b/metrics-jetty8/pom.xml index 67e8f7289b..4d255d02d7 100644 --- a/metrics-jetty8/pom.xml +++ b/metrics-jetty8/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-SNAPSHOT + 3.0.0-BETA1 metrics-jetty8 diff --git a/metrics-json/pom.xml b/metrics-json/pom.xml index e250095f64..b04b0d1d29 100644 --- a/metrics-json/pom.xml +++ b/metrics-json/pom.xml @@ -1,13 +1,11 @@ - + 4.0.0 com.yammer.metrics metrics-parent - 3.0.0-SNAPSHOT + 3.0.0-BETA1 metrics-json diff --git a/metrics-jvm/pom.xml b/metrics-jvm/pom.xml index d3c851a02c..dc2fb78c94 100644 --- a/metrics-jvm/pom.xml +++ b/metrics-jvm/pom.xml @@ -1,13 +1,11 @@ - + 4.0.0 com.yammer.metrics metrics-parent - 3.0.0-SNAPSHOT + 3.0.0-BETA1 metrics-jvm diff --git a/metrics-log4j/pom.xml b/metrics-log4j/pom.xml index 078abcb5b2..a9b0e30918 100644 --- a/metrics-log4j/pom.xml +++ b/metrics-log4j/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-SNAPSHOT + 3.0.0-BETA1 metrics-log4j diff --git a/metrics-logback/pom.xml b/metrics-logback/pom.xml index 2ab9966659..c48465ffc1 100644 --- a/metrics-logback/pom.xml +++ b/metrics-logback/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-SNAPSHOT + 3.0.0-BETA1 metrics-logback diff --git a/metrics-servlet/pom.xml b/metrics-servlet/pom.xml index ba54882c42..5f998424ea 100644 --- a/metrics-servlet/pom.xml +++ b/metrics-servlet/pom.xml @@ -1,13 +1,11 @@ - + 4.0.0 com.yammer.metrics metrics-parent - 3.0.0-SNAPSHOT + 3.0.0-BETA1 metrics-servlet diff --git a/metrics-servlets/pom.xml b/metrics-servlets/pom.xml index adb9cfe3a3..5f2222f19e 100644 --- a/metrics-servlets/pom.xml +++ b/metrics-servlets/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-SNAPSHOT + 3.0.0-BETA1 metrics-servlets diff --git a/pom.xml b/pom.xml index c355276e52..c0af843410 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.yammer.metrics metrics-parent - 3.0.0-SNAPSHOT + 3.0.0-BETA1 pom Metrics Parent From 2f763b21ea70508756a4b2dd49c381dd10821d29 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 13:40:46 -0700 Subject: [PATCH 0159/2558] [maven-release-plugin] prepare for next development iteration --- metrics-annotation/pom.xml | 2 +- metrics-core/pom.xml | 2 +- metrics-ehcache/pom.xml | 2 +- metrics-ganglia/pom.xml | 2 +- metrics-graphite/pom.xml | 2 +- metrics-healthchecks/pom.xml | 2 +- metrics-httpclient/pom.xml | 2 +- metrics-jdbi/pom.xml | 2 +- metrics-jersey/pom.xml | 2 +- metrics-jetty8/pom.xml | 2 +- metrics-json/pom.xml | 2 +- metrics-jvm/pom.xml | 2 +- metrics-log4j/pom.xml | 2 +- metrics-logback/pom.xml | 2 +- metrics-servlet/pom.xml | 2 +- metrics-servlets/pom.xml | 2 +- pom.xml | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/metrics-annotation/pom.xml b/metrics-annotation/pom.xml index 0b43471be1..e95a72bbaa 100644 --- a/metrics-annotation/pom.xml +++ b/metrics-annotation/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-BETA1 + 3.0.0-BETA2-SNAPSHOT metrics-annotation diff --git a/metrics-core/pom.xml b/metrics-core/pom.xml index 6a8710c5f3..f2f24e2f04 100644 --- a/metrics-core/pom.xml +++ b/metrics-core/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-BETA1 + 3.0.0-BETA2-SNAPSHOT metrics-core diff --git a/metrics-ehcache/pom.xml b/metrics-ehcache/pom.xml index 7c2b2613ec..e753a5fc7f 100644 --- a/metrics-ehcache/pom.xml +++ b/metrics-ehcache/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-BETA1 + 3.0.0-BETA2-SNAPSHOT metrics-ehcache diff --git a/metrics-ganglia/pom.xml b/metrics-ganglia/pom.xml index 058e171733..a928382071 100644 --- a/metrics-ganglia/pom.xml +++ b/metrics-ganglia/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-BETA1 + 3.0.0-BETA2-SNAPSHOT metrics-ganglia diff --git a/metrics-graphite/pom.xml b/metrics-graphite/pom.xml index 28a36e38cd..b9bfa55194 100644 --- a/metrics-graphite/pom.xml +++ b/metrics-graphite/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-BETA1 + 3.0.0-BETA2-SNAPSHOT metrics-graphite diff --git a/metrics-healthchecks/pom.xml b/metrics-healthchecks/pom.xml index f590486241..ec39d5fee2 100644 --- a/metrics-healthchecks/pom.xml +++ b/metrics-healthchecks/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-BETA1 + 3.0.0-BETA2-SNAPSHOT metrics-healthchecks diff --git a/metrics-httpclient/pom.xml b/metrics-httpclient/pom.xml index 2ae499e62d..33094f3041 100644 --- a/metrics-httpclient/pom.xml +++ b/metrics-httpclient/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-BETA1 + 3.0.0-BETA2-SNAPSHOT metrics-httpclient diff --git a/metrics-jdbi/pom.xml b/metrics-jdbi/pom.xml index e565abbf9a..4c3e254a2c 100644 --- a/metrics-jdbi/pom.xml +++ b/metrics-jdbi/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-BETA1 + 3.0.0-BETA2-SNAPSHOT metrics-jdbi diff --git a/metrics-jersey/pom.xml b/metrics-jersey/pom.xml index b469f31036..169d30707c 100644 --- a/metrics-jersey/pom.xml +++ b/metrics-jersey/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-BETA1 + 3.0.0-BETA2-SNAPSHOT metrics-jersey diff --git a/metrics-jetty8/pom.xml b/metrics-jetty8/pom.xml index 4d255d02d7..0d4811ebe1 100644 --- a/metrics-jetty8/pom.xml +++ b/metrics-jetty8/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-BETA1 + 3.0.0-BETA2-SNAPSHOT metrics-jetty8 diff --git a/metrics-json/pom.xml b/metrics-json/pom.xml index b04b0d1d29..3a38c4683c 100644 --- a/metrics-json/pom.xml +++ b/metrics-json/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-BETA1 + 3.0.0-BETA2-SNAPSHOT metrics-json diff --git a/metrics-jvm/pom.xml b/metrics-jvm/pom.xml index dc2fb78c94..dfce2b5ea6 100644 --- a/metrics-jvm/pom.xml +++ b/metrics-jvm/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-BETA1 + 3.0.0-BETA2-SNAPSHOT metrics-jvm diff --git a/metrics-log4j/pom.xml b/metrics-log4j/pom.xml index a9b0e30918..c4d8e799a9 100644 --- a/metrics-log4j/pom.xml +++ b/metrics-log4j/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-BETA1 + 3.0.0-BETA2-SNAPSHOT metrics-log4j diff --git a/metrics-logback/pom.xml b/metrics-logback/pom.xml index c48465ffc1..82e52d1b18 100644 --- a/metrics-logback/pom.xml +++ b/metrics-logback/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-BETA1 + 3.0.0-BETA2-SNAPSHOT metrics-logback diff --git a/metrics-servlet/pom.xml b/metrics-servlet/pom.xml index 5f998424ea..b9b2dc5b23 100644 --- a/metrics-servlet/pom.xml +++ b/metrics-servlet/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-BETA1 + 3.0.0-BETA2-SNAPSHOT metrics-servlet diff --git a/metrics-servlets/pom.xml b/metrics-servlets/pom.xml index 5f2222f19e..9d88fa814d 100644 --- a/metrics-servlets/pom.xml +++ b/metrics-servlets/pom.xml @@ -5,7 +5,7 @@ com.yammer.metrics metrics-parent - 3.0.0-BETA1 + 3.0.0-BETA2-SNAPSHOT metrics-servlets diff --git a/pom.xml b/pom.xml index c0af843410..8d450ca054 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.yammer.metrics metrics-parent - 3.0.0-BETA1 + 3.0.0-BETA2-SNAPSHOT pom Metrics Parent From 0878ab9b7dc86a6b274b7fc6dccb416cd89fd708 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 13:57:29 -0700 Subject: [PATCH 0160/2558] Finalize relnotes. --- docs/source/about/release-notes.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/source/about/release-notes.rst b/docs/source/about/release-notes.rst index 51fdfbd009..d63db5ba21 100644 --- a/docs/source/about/release-notes.rst +++ b/docs/source/about/release-notes.rst @@ -4,10 +4,10 @@ Release Notes ############# -.. _rel-3.0.0: +.. _rel-3.0.0-BETA1: -v3.0.0-SNAPSHOT -=============== +v3.0.0-BETA1: Apr 1 2013 +======================== * Total overhaul of most of the core Metrics classes: @@ -26,6 +26,7 @@ v3.0.0-SNAPSHOT * Renamed ``metrics-servlet`` to ``metrics-servlets``. * Renamed ``metrics-web`` to ``metrics-servlet``. * Renamed ``metrics-jetty`` to ``metrics-jetty8``. +* Many more small changes! .. _rel-2.2.0: From 430cd9675cdca18a6d253cfaad8065be6917dac9 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 14:14:01 -0700 Subject: [PATCH 0161/2558] Added some more contributors. Please please send me a pull request if you should be on here but aren't. (Or, horrors, if I've mangled your name.) --- docs/source/about/contributors.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/source/about/contributors.rst b/docs/source/about/contributors.rst index e9b0df91b1..ee60a81669 100644 --- a/docs/source/about/contributors.rst +++ b/docs/source/about/contributors.rst @@ -8,7 +8,9 @@ Many, many thanks to: * `Alex Lambert `_ * `Basil James Whitehouse III `_ +* `Benjamin Gehrels `_ * `Bernardo Gomez Palacio `_ +* `Brian Ehmann `_ * `Brian Roberts `_ * `Bruce Mitchener `_ * `C. Scott Andreas `_ @@ -19,6 +21,7 @@ Many, many thanks to: * `Cliff Moon `_ * `Collin VanDyck `_ * `Dag Liodden `_ +* `Dan Brown `_ * `Dan Revel `_ * `Diwaker Gupta `_ * `Drew Stephens `_ @@ -41,6 +44,7 @@ Many, many thanks to: * `Matt Ryall `_ * `Matthew Gilliard `_ * `Matthew O'Connor `_ +* `Mathijs Vogelzang `_ * `Mårten Gustafson `_ * `Michał Minicki `_ * `Neil Prosser `_ @@ -58,3 +62,5 @@ Many, many thanks to: * `Steven Schlansker `_ * `Thomas Dudziak `_ * `Tobias Lidskog `_ +* `Yang Ye `_ +* `Wolfgang Schell `_ From b585c488f77a896ccfee4bff93e1d0a12db4bdba Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 14:20:19 -0700 Subject: [PATCH 0162/2558] More contributors! --- docs/source/about/contributors.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/source/about/contributors.rst b/docs/source/about/contributors.rst index ee60a81669..f83f3ea21f 100644 --- a/docs/source/about/contributors.rst +++ b/docs/source/about/contributors.rst @@ -21,6 +21,7 @@ Many, many thanks to: * `Cliff Moon `_ * `Collin VanDyck `_ * `Dag Liodden `_ +* `Dale Wijnand `_ * `Dan Brown `_ * `Dan Revel `_ * `Diwaker Gupta `_ @@ -52,6 +53,7 @@ Many, many thanks to: * `Niklas Konstenius `_ * `Paul Bloch `_ * `Paul Brown `_ +* `Paul Doran `_ * `Realbot `_ * `Robby Walker `_ * `Ryan Kennedy `_ @@ -60,6 +62,7 @@ Many, many thanks to: * `Sean Laurent `_ * `Shaneal Manek `_ * `Steven Schlansker `_ +* `Stewart Allen `_ * `Thomas Dudziak `_ * `Tobias Lidskog `_ * `Yang Ye `_ From ece24b3f1c914f087d2c48bde61053ac61a091b0 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 14:21:42 -0700 Subject: [PATCH 0163/2558] Some doc fixes. --- docs/source/getting-started.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/source/getting-started.rst b/docs/source/getting-started.rst index 251106ffeb..3b870f3779 100644 --- a/docs/source/getting-started.rst +++ b/docs/source/getting-started.rst @@ -210,7 +210,6 @@ Second, implement a ``HealthCheck`` subclass: private final Database database; public DatabaseHealthCheck(Database database) { - super("database"); this.database = database; } @@ -303,6 +302,6 @@ In addition to JMX and HTTP, Metrics also has reporters for the following output * ``STDOUT``, using :ref:`ConsoleReporter ` from ``metrics-core`` * ``CSV`` files, using :ref:`CsvReporter ` from ``metrics-core`` -* Slf4j loggers, using :ref:`Slf4jReporter ` from ``metrics-core`` +* SLF4J loggers, using :ref:`Slf4jReporter ` from ``metrics-core`` * Ganglia, using :ref:`GangliaReporter ` from ``metrics-ganglia`` * Graphite, using :ref:`GraphiteReporter ` from ``metrics-graphite`` From 2d3caf3490f80de223dca1969f61e40db0bc9118 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 15:56:26 -0700 Subject: [PATCH 0164/2558] Add default clauses to some switches. Now we can run with a fully-clean Findbugs config, which is lovely. --- findbugs-exclude.xml | 10 +--------- .../com/yammer/metrics/log4j/InstrumentedAppender.java | 2 ++ .../yammer/metrics/logback/InstrumentedAppender.java | 2 ++ 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/findbugs-exclude.xml b/findbugs-exclude.xml index 5e5881843a..b45c414747 100644 --- a/findbugs-exclude.xml +++ b/findbugs-exclude.xml @@ -1,12 +1,4 @@ - - - - - - - - - + diff --git a/metrics-log4j/src/main/java/com/yammer/metrics/log4j/InstrumentedAppender.java b/metrics-log4j/src/main/java/com/yammer/metrics/log4j/InstrumentedAppender.java index 3bf8bf5ab7..782e9e141c 100644 --- a/metrics-log4j/src/main/java/com/yammer/metrics/log4j/InstrumentedAppender.java +++ b/metrics-log4j/src/main/java/com/yammer/metrics/log4j/InstrumentedAppender.java @@ -58,6 +58,8 @@ protected void append(LoggingEvent event) { case Level.FATAL_INT: fatal.mark(); break; + default: + break; } } diff --git a/metrics-logback/src/main/java/com/yammer/metrics/logback/InstrumentedAppender.java b/metrics-logback/src/main/java/com/yammer/metrics/logback/InstrumentedAppender.java index faecb6a199..64a3269c0f 100644 --- a/metrics-logback/src/main/java/com/yammer/metrics/logback/InstrumentedAppender.java +++ b/metrics-logback/src/main/java/com/yammer/metrics/logback/InstrumentedAppender.java @@ -53,6 +53,8 @@ protected void append(ILoggingEvent event) { case Level.ERROR_INT: error.mark(); break; + default: + break; } } } From 515c03ff0776a3f09202a98e929b594a22b54930 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 16:06:15 -0700 Subject: [PATCH 0165/2558] Upgrade maven-findbugs to 2.5.2. Also, delete the exclusions file. We're going clean from here on out. Finally, drop Checkstyle. If it doesn't fail the build it doesn't exist. --- findbugs-exclude.xml | 4 ---- pom.xml | 14 ++------------ 2 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 findbugs-exclude.xml diff --git a/findbugs-exclude.xml b/findbugs-exclude.xml deleted file mode 100644 index b45c414747..0000000000 --- a/findbugs-exclude.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/pom.xml b/pom.xml index 8d450ca054..fb9e6fdb37 100644 --- a/pom.xml +++ b/pom.xml @@ -220,12 +220,11 @@ org.codehaus.mojo findbugs-maven-plugin - 2.4.0 + 2.5.2 Max Default true - ${basedir}/../findbugs-exclude.xml @@ -258,16 +257,7 @@ org.codehaus.mojo findbugs-maven-plugin - 2.4.0 - - - org.apache.maven.plugins - maven-checkstyle-plugin - 2.8 - - http://codahale.com/checkstyle.xml - UTF-8 - + 2.5.2 From 2e543e6c0c18d2febead69e9810b417756ed2b52 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 16:06:48 -0700 Subject: [PATCH 0166/2558] Upgrade maven-gpg to 1.4. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fb9e6fdb37..00a3a7aa2f 100644 --- a/pom.xml +++ b/pom.xml @@ -139,7 +139,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.2 + 1.4 sign-artifacts From 272c4f26a56e7e3b07b746cce2e7add1b84ad7e7 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 16:07:12 -0700 Subject: [PATCH 0167/2558] Upgrade maven-compiler to 3.0. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 00a3a7aa2f..536e3888b0 100644 --- a/pom.xml +++ b/pom.xml @@ -160,7 +160,7 @@ org.apache.maven.plugins maven-compiler-plugin - 2.3.2 + 3.0 1.6 1.6 From c148b1a6ec7b41c8f177edc8540f967b1efb0440 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 16:07:23 -0700 Subject: [PATCH 0168/2558] Upgrade maven-surefire to 2.14. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 536e3888b0..7dfdec6bc7 100644 --- a/pom.xml +++ b/pom.xml @@ -175,7 +175,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.8.1 + 2.14 classes From 086c6865fa03056d610bd56577a8ad1c73f2ff2f Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 16:07:37 -0700 Subject: [PATCH 0169/2558] Upgrade maven-source to 2.2.1. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7dfdec6bc7..112a229d18 100644 --- a/pom.xml +++ b/pom.xml @@ -183,7 +183,7 @@ org.apache.maven.plugins maven-source-plugin - 2.1.2 + 2.2.1 attach-sources From f590180f379cf77445d21144e7c5c03212b8422a Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 16:07:51 -0700 Subject: [PATCH 0170/2558] Upgrade maven-javadoc to 2.9. --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 112a229d18..5fe8e999ac 100644 --- a/pom.xml +++ b/pom.xml @@ -196,7 +196,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.8.1 + 2.9 attach-javadocs @@ -252,7 +252,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.8.1 + 2.9 org.codehaus.mojo From c664d8800fab197fa393fbec1bec4081bf15915c Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 16:08:07 -0700 Subject: [PATCH 0171/2558] Upgrade maven-release to 2.4.1. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5fe8e999ac..c1ff299536 100644 --- a/pom.xml +++ b/pom.xml @@ -209,7 +209,7 @@ org.apache.maven.plugins maven-release-plugin - 2.2.1 + 2.4.1 true forked-path From 9f39add9abebca673c9866242f9ee735af2da470 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 16:08:26 -0700 Subject: [PATCH 0172/2558] Upgrade maven-site to 3.2. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c1ff299536..55189936be 100644 --- a/pom.xml +++ b/pom.xml @@ -237,7 +237,7 @@ org.apache.maven.plugins maven-site-plugin - 3.0 + 3.2 From 318cebfddd52e6269606de32cfcc3a22f826712e Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 16:08:54 -0700 Subject: [PATCH 0173/2558] Upgrade maven-project-info-reports to 2.6. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 55189936be..14a970d497 100644 --- a/pom.xml +++ b/pom.xml @@ -243,7 +243,7 @@ org.apache.maven.plugins maven-project-info-reports-plugin - 2.4 + 2.6 false false From 105656c6b9d0e91960f18180fc999d8a848ff750 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 16:09:46 -0700 Subject: [PATCH 0174/2558] Upgrade wagon-ssh to 2.4. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 14a970d497..2b8a196920 100644 --- a/pom.xml +++ b/pom.xml @@ -267,7 +267,7 @@ org.apache.maven.wagon wagon-ssh - 2.2 + 2.4 From 9c8ee9130bdcf18121765d3a34f0e18477a82c4e Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 17:42:30 -0700 Subject: [PATCH 0175/2558] Remove names from MetricRegistry. --- docs/source/about/release-notes.rst | 8 ++++++++ docs/source/getting-started.rst | 8 ++++---- .../java/com/yammer/metrics/JmxReporter.java | 18 ++++++++++++++--- .../com/yammer/metrics/MetricRegistry.java | 20 ++----------------- .../yammer/metrics/tests/JmxReporterTest.java | 3 ++- .../metrics/tests/MetricRegistryTest.java | 8 +------- .../metrics/tests/ScheduledReporterTest.java | 2 +- .../tests/InstrumentedEhcacheTest.java | 2 +- .../tests/InstrumentedHttpClientTest.java | 2 +- .../InstrumentedTimingCollectorTest.java | 2 +- .../tests/SingletonMetricsJerseyTest.java | 2 +- .../yammer/metrics/json/MetricsModule.java | 1 - .../metrics/json/tests/MetricsModuleTest.java | 3 +-- .../metrics/servlet/WebappMetricsFilter.java | 2 +- .../servlets/experiments/ExampleServer.java | 2 +- .../servlets/tests/AdminServletTest.java | 2 +- .../servlets/tests/MetricsServletTest.java | 4 +--- 17 files changed, 42 insertions(+), 47 deletions(-) diff --git a/docs/source/about/release-notes.rst b/docs/source/about/release-notes.rst index d63db5ba21..338c9173c6 100644 --- a/docs/source/about/release-notes.rst +++ b/docs/source/about/release-notes.rst @@ -4,6 +4,14 @@ Release Notes ############# +.. _rel-3.0.0-BETA2: + +v3.0.0-BETA2-SNAPSHOT +===================== + +* ``MetricRegistry`` no longer has a name. +* ``JmxReporter`` takes an optional domain property to disambiguate multiple reporters. + .. _rel-3.0.0-BETA1: v3.0.0-BETA1: Apr 1 2013 diff --git a/docs/source/getting-started.rst b/docs/source/getting-started.rst index 3b870f3779..e0207f61b2 100644 --- a/docs/source/getting-started.rst +++ b/docs/source/getting-started.rst @@ -40,14 +40,14 @@ The Registry ============ The centerpiece of Metrics is the ``MetricRegistry`` class, which is the container for all your -application's metrics. Go ahead and create a new one and give it a name: +application's metrics. Go ahead and create a new one: .. code-block:: java - final MetricRegistry metrics = new MetricRegistry("example"); + final MetricRegistry metrics = new MetricRegistry(); You'll probably want to integrate this into your application's lifecycle (maybe using your -dependency injection framework), but for now a ``static`` field is fine. +dependency injection framework), but ``static`` field is fine. .. _gs-gauges: @@ -87,7 +87,7 @@ for constructing these names: This will return a string with something like ``"com.example.QueueManager.jobs.size"``. For most queue and queue-like structures, you won't want to simply return ``queue.size()``. Most of -``java.util`` and ``java.util.concurrent`` have implementations of ``#size()`` which are ``O(n)``, +``java.util`` and ``java.util.concurrent`` have implementations of ``#size()`` which are **O(n)**, which means your gauge will be slow (potentially while holding a lock). .. _gs-counters: diff --git a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java index cdb9ba4e9f..fab2bd7927 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java @@ -34,11 +34,13 @@ public static class Builder { private TimeUnit rateUnit; private TimeUnit durationUnit; private MetricFilter filter = MetricFilter.ALL; + private String domain; private Builder(MetricRegistry registry) { this.registry = registry; this.rateUnit = TimeUnit.SECONDS; this.durationUnit = TimeUnit.MILLISECONDS; + this.domain = "metrics"; } /** @@ -85,13 +87,18 @@ public Builder filter(MetricFilter filter) { return this; } + public Builder inDomain(String domain) { + this.domain = domain; + return this; + } + /** * Builds a {@link JmxReporter} with the given properties. * * @return a {@link JmxReporter} */ public JmxReporter build() { - return new JmxReporter(mBeanServer, registry, filter, rateUnit, durationUnit); + return new JmxReporter(mBeanServer, domain, registry, filter, rateUnit, durationUnit); } } @@ -624,9 +631,14 @@ void unregisterAll() { private final MetricRegistry registry; private final JmxListener listener; - private JmxReporter(MBeanServer mBeanServer, MetricRegistry registry, MetricFilter filter, TimeUnit rateUnit, TimeUnit durationUnit) { + private JmxReporter(MBeanServer mBeanServer, + String domain, + MetricRegistry registry, + MetricFilter filter, + TimeUnit rateUnit, + TimeUnit durationUnit) { this.registry = registry; - this.listener = new JmxListener(mBeanServer, registry.getName(), filter, rateUnit, durationUnit); + this.listener = new JmxListener(mBeanServer, domain, filter, rateUnit, durationUnit); } /** diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java index 1f123183fa..20ee6077fd 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -48,18 +48,11 @@ private static void append(StringBuilder builder, String part) { private final ConcurrentMap metrics; private final List listeners; - private final String name; /** - * Creates a new {@link MetricRegistry} with the given name. - * - * @param name the name of the registry + * Creates a new {@link MetricRegistry}. */ - public MetricRegistry(String name) { - if (name == null || name.isEmpty()) { - throw new IllegalArgumentException("A registry needs a name"); - } - this.name = name; + public MetricRegistry() { this.metrics = new ConcurrentHashMap(); this.listeners = new CopyOnWriteArrayList(); } @@ -178,15 +171,6 @@ public void removeListener(MetricRegistryListener listener) { listeners.remove(listener); } - /** - * Returns the registry's name. - * - * @return the registry's name - */ - public String getName() { - return name; - } - /** * Returns a set of the names of all the metrics in the registry. * diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java index cba02cb6fb..71028604b2 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java @@ -20,10 +20,11 @@ public class JmxReporterTest { private final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); private final String name = UUID.randomUUID().toString().replaceAll("[{\\-}]", ""); - private final MetricRegistry registry = spy(new MetricRegistry(name)); + private final MetricRegistry registry = spy(new MetricRegistry()); private final JmxReporter reporter = JmxReporter.forRegistry(registry) .registerWith(mBeanServer) + .inDomain(name) .convertDurationsTo(TimeUnit.MILLISECONDS) .convertRatesTo(TimeUnit.SECONDS) .filter(MetricFilter.ALL) diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java index 71f02b3caa..8cf749f985 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java @@ -14,7 +14,7 @@ public class MetricRegistryTest { private final MetricRegistryListener listener = mock(MetricRegistryListener.class); - private final MetricRegistry registry = new MetricRegistry("name"); + private final MetricRegistry registry = new MetricRegistry(); @SuppressWarnings("unchecked") private final Gauge gauge = mock(Gauge.class); private final Counter counter = mock(Counter.class); @@ -27,12 +27,6 @@ public void setUp() throws Exception { registry.addListener(listener); } - @Test - public void hasAName() throws Exception { - assertThat(registry.getName()) - .isEqualTo("name"); - } - @Test public void registeringAGaugeTriggersANotification() throws Exception { assertThat(registry.register("thing", gauge)) diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/ScheduledReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/ScheduledReporterTest.java index 67cacb4509..5035fec631 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/ScheduledReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/ScheduledReporterTest.java @@ -18,7 +18,7 @@ public class ScheduledReporterTest { private final Meter meter = mock(Meter.class); private final Timer timer = mock(Timer.class); - private final MetricRegistry registry = new MetricRegistry("test"); + private final MetricRegistry registry = new MetricRegistry(); private final ScheduledReporter reporter = spy( new ScheduledReporter(registry, "example", diff --git a/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/InstrumentedEhcacheTest.java b/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/InstrumentedEhcacheTest.java index 79925e1341..d1dc61fc5e 100644 --- a/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/InstrumentedEhcacheTest.java +++ b/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/InstrumentedEhcacheTest.java @@ -17,7 +17,7 @@ public class InstrumentedEhcacheTest { private static final CacheManager MANAGER = CacheManager.create(); - private final MetricRegistry registry = new MetricRegistry("cache"); + private final MetricRegistry registry = new MetricRegistry(); private Ehcache cache; @Before diff --git a/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/tests/InstrumentedHttpClientTest.java b/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/tests/InstrumentedHttpClientTest.java index 6a7b8564eb..fde4072a2b 100644 --- a/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/tests/InstrumentedHttpClientTest.java +++ b/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/tests/InstrumentedHttpClientTest.java @@ -9,7 +9,7 @@ import static org.fest.assertions.api.Assertions.assertThat; public class InstrumentedHttpClientTest { - private final MetricRegistry registry = new MetricRegistry("test"); + private final MetricRegistry registry = new MetricRegistry(); private final HttpClient client = new InstrumentedHttpClient(registry); @Test diff --git a/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java b/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java index 17a295ff54..965b26a1af 100644 --- a/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java +++ b/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java @@ -18,7 +18,7 @@ import static org.mockito.Mockito.mock; public class InstrumentedTimingCollectorTest { - private final MetricRegistry registry = new MetricRegistry("test"); + private final MetricRegistry registry = new MetricRegistry(); @Test public void updatesTimerForSqlObjects() throws Exception { diff --git a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/SingletonMetricsJerseyTest.java b/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/SingletonMetricsJerseyTest.java index 9a6fa356b0..b64347afd5 100644 --- a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/SingletonMetricsJerseyTest.java +++ b/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/SingletonMetricsJerseyTest.java @@ -33,7 +33,7 @@ public class SingletonMetricsJerseyTest extends JerseyTest { @Override protected AppDescriptor configure() { - this.registry = new MetricRegistry("test"); + this.registry = new MetricRegistry(); final DefaultResourceConfig config = new DefaultResourceConfig(); config.getSingletons().add(new InstrumentedResourceMethodDispatchAdapter(registry)); diff --git a/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java b/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java index 2f84b86121..a9db024514 100644 --- a/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java +++ b/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java @@ -178,7 +178,6 @@ public void serialize(MetricRegistry registry, JsonGenerator json, SerializerProvider provider) throws IOException { json.writeStartObject(); - json.writeStringField("name", registry.getName()); json.writeStringField("version", VERSION.toString()); json.writeObjectField("gauges", registry.getGauges()); json.writeObjectField("counters", registry.getCounters()); diff --git a/metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java b/metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java index 596cef5bf3..4863a53633 100644 --- a/metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java +++ b/metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java @@ -198,11 +198,10 @@ public void serializesTimers() throws Exception { @Test public void serializesMetricRegistries() throws Exception { - final MetricRegistry registry = new MetricRegistry("metrics"); + final MetricRegistry registry = new MetricRegistry(); assertThat(mapper.writeValueAsString(registry)) .isEqualTo("{" + - "\"name\":\"metrics\"," + "\"version\":\"3.0.0\"," + "\"gauges\":{}," + "\"counters\":{}," + diff --git a/metrics-servlet/src/main/java/com/yammer/metrics/servlet/WebappMetricsFilter.java b/metrics-servlet/src/main/java/com/yammer/metrics/servlet/WebappMetricsFilter.java index 5bcb9fe3db..290f069941 100644 --- a/metrics-servlet/src/main/java/com/yammer/metrics/servlet/WebappMetricsFilter.java +++ b/metrics-servlet/src/main/java/com/yammer/metrics/servlet/WebappMetricsFilter.java @@ -75,7 +75,7 @@ private MetricRegistry getMetricsFactory(FilterConfig filterConfig) { if (o instanceof MetricRegistry) { metricsRegistry = (MetricRegistry) o; } else { - metricsRegistry = new MetricRegistry("servlet"); + metricsRegistry = new MetricRegistry(); } return metricsRegistry; } diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/experiments/ExampleServer.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/experiments/ExampleServer.java index 2a61cbb189..000d5b3c32 100644 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/experiments/ExampleServer.java +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/experiments/ExampleServer.java @@ -20,7 +20,7 @@ import static com.yammer.metrics.MetricRegistry.name; public class ExampleServer { - private static final MetricRegistry REGISTRY = new MetricRegistry("example"); + private static final MetricRegistry REGISTRY = new MetricRegistry(); private static final Counter COUNTER_1 = REGISTRY.counter(name(ExampleServer.class, "wah", "doody")); diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AdminServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AdminServletTest.java index 5771324329..57ca489efd 100755 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AdminServletTest.java +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AdminServletTest.java @@ -10,7 +10,7 @@ import static org.fest.assertions.api.Assertions.assertThat; public class AdminServletTest extends AbstractServletTest { - private final MetricRegistry registry = new MetricRegistry("test"); + private final MetricRegistry registry = new MetricRegistry(); private final HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry(); @Override diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java index 843f553d7a..a6d202ffd1 100644 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java @@ -14,7 +14,7 @@ public class MetricsServletTest extends AbstractServletTest { private final Clock clock = mock(Clock.class); - private final MetricRegistry registry = new MetricRegistry("test"); + private final MetricRegistry registry = new MetricRegistry(); @Override protected void setUp(ServletTester tester) { @@ -51,7 +51,6 @@ public void returnsA200() throws Exception { .isEqualTo(200); assertThat(response.getContent()) .isEqualTo("{" + - "\"name\":\"test\"," + "\"version\":\"3.0.0\"," + "\"gauges\":{" + "\"g1\":{\"value\":100}" + @@ -80,7 +79,6 @@ public void optionallyPrettyPrintsTheJson() throws Exception { .isEqualTo(200); assertThat(response.getContent()) .isEqualTo(String.format("{%n" + - " \"name\" : \"test\",%n" + " \"version\" : \"3.0.0\",%n" + " \"gauges\" : {%n" + " \"g1\" : {%n" + From 27888261fc23f5d610019f7d739bfeb416e5475f Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 18:21:58 -0700 Subject: [PATCH 0176/2558] Update VisualVM screenshot. Closes #351. --- docs/source/metrics-visualvm.png | Bin 44423 -> 302732 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/source/metrics-visualvm.png b/docs/source/metrics-visualvm.png index ae4fda303600bd48e14b0b3d6cd588da949422d2..bd5297bbf009bcb50d5b5bd97689d6e6ae7ac0ca 100644 GIT binary patch literal 302732 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%0AD-5+%mP%kH@DIzps;h z^nhyhFf=U_U~TZrbvpi+wwX7!Y8H(t{SRhDKCw0ClP!!-TFZ{Ud0WFH_A65R0$8RUOFEA1cv4Q+Dz(4%56eky!w(anIz4o1JT9IxxQ$5Mf*AG;6kz*930E2WO!Ri%nq)u4VhB!H~h#MhH5&iyPoOPoof ztrWL&hkG_(n@|WZD(HNUmz0CTokR-R*a2&u!+{czkE9Q}UzM65H2ullEiq**;!Z8U zH`ks|Wg04~|3tWJUc`~F1r}2|?`_eyG~h*73LNF0S`8lv!KFdG<1m89p6Y&~jgjwr zWn%5+2{Wg=t`^`1B>PzG6U?V`l}|S|Jw3|6<3)o&13e#^9Up4qR1{uE`i3tgU&Oyu zCL#T?iqa%XVcDJ*3-D*7qN~v7#E)6vaGf7zFq+pML0o^q*{Twjz`~14?%CW&C$>Z+ z@T+Vt3R5#Qd8B^?z*@a}lXL&sS_l#=#(qCfPhFqAVG=9Q_2(-;z?#JqihVO=p7vTjC=%=8atz&(&YoI4$TD9~9Z zZIm;(Ph_Hl1=jMsb}6k5=xL|7iIVR`Ky>jZ>yB`kObLUo>IDt-)C?geo04fY*Kheb z>0zpo_XwZork^9!KI}C+KA(3ZNY0qcV{#qe_jb;^z1~Ydo#< zIGncGJ>=i)J}W22ommnL`WTX*!k9w;u719Z zGQ9=R&T#kp>|kb5nbX0rblD7vyxVEQ?#02fDuSq|<k zB;=ACYRLa%f`A-=4cgy#fP7ouiq%P*17#QYa}Tp-M{)e>o@vpZs1@rI0?M$9(~cLS z#hpZZ5F1*Kq9LTkx`j`CnJlsK_{z~5JnUkxKF zFh&kcg9TKsoXSf=-ga4U7q71~#=G)BPIF!x?)EzTp*4szU;dor+wlkTN{mw%lS~h` zE017pFGm)e5A#+gP_h)-6q7)O&}02N@P}H1t`XLTsa#L}_j(y$)l;-FpSz{EwQFDh zh}~})VR3V0N-;6GG)zCuh8sYQ0Bc}Ml6JDX#qx1aLay(riO)YBfN_wIBE8pkxkv4- zpM>EUhZX?lF?eX7+1aSUu0X4_9MyMa%BzOjW~A4j^OZJSD((Nddi*PLHW3vxCjN|D zK@TOQQCW);Uk8a^*6rrpj;$m0A;Bg_F5&ko7Dg><}-Eg~u%RL3^Lm zsIChXDU1qVy1!?^wq2w7rzJ(b0&>;NLkOR4B<1R&HX%mAU=^w1H?%0>LN|@QazgQc zKY%I_@O{Ydn3w0nFX0vr^FvvZtBu6aqbkiP?F`Y9^QKQHV5 z2+jY$Kj<+)OXx9#7}ByR6mb1McE5~9VzM2wbvV6T4JK0PR(9+UmaDWFR@0<7|1TpU z2Xq7eX$u2uGRL7e!TVg#pX1<@9X{Xt!Lfx>`5B+rLV^#4KW8^BzMsp|&}+2+*Wpb5 zPGq6~+x8er=r+Io^W^686b=G`_tjJv!=;zg&EcbUDNFcf_`!&-=N*{MHjj&^>qE*` zhnt?m5LzKDOYO|rLTM~MpXc-Cnp`IP)Z}EPUXQP%qr>BpuMTb-{WRt8=t`O?zTDwP#!tx_AC$Btk&3oZJ=Px2|i6hMNibCn!ywmHdewm+;44i0{^ z53$)H9kTq(H452ibvOV}n3kjdEF|6aC!=)1gxV8PNkFg(lQiq z#yvMvkt8caSjD+AAUP*Fos_wK;Zh@Rmm&qc6KG&ys5M=SK@bRdTrS5T548hIXqpzlhjVpOV=C-w z@3p4IXGu%i@qVHAXFUwEUcQ}Gg6fiL_o}L8dtRBJTYkLEID)LiLk_kc>Ep>}UZcwd z@)Cf$u(gHn{JIDI{|V8~#r#M52kP`9GZ65&N$p)1V_*7DkT^#)h`qRDEA$Zg_=A@s zbse`+02h1DLB$fqSvLa+K0DETFJEtqi-##wI6GXk9u_xt95z{PmLtm-)oXPQrrhm1 zPFF53zX|-#%y&f1u4nr|Vg)`2C1Vv=tfYgB^x z(sYA=U`SL)vPcWOGF+YhZf)m$a;mb0M>kYy0bpS*cpOZiM2eeN8 z7Q){bE^>_Uo*|mi%ZEdV!GV+YrX{CIujaj@V`2LKW|j} zyAQA9$8JlX`8XW5a~3`hJpRwJ1*2nE z&BAIUzaOtF>0K!lte0A>w~ijvo@y7Ao%W?JJzGsK%u}r#g5=^-P!=Uy2kg@k!0$;o3>f@te#w z@3?yAY0-1ZPUV%Y&TM^p?r;OZhIs6LzmINw002!6kE`KtZ#!hVC$Zi4*G_+4h7Tlq z>|gX~Cw&()Tl9WE8L^b=ZL)UMnhX@oWUCR|V-XXcT)YKIZsgZ;Y`cprUSagr^nA6~ zV-Qv@HQtzT&QUo6@+}~zh5@o!Vs0nEqdjKx3jmJ4BltZ>03{%bJtOev^QHTRFI|tJ zIic=}CRFkO-nJ&Sp)8>x2Q0!&pR(>_>&91&fx=0LW5>H8+b}4x4Y{Lb%Qds1>s1Iy z!-x++lT+Zm3zAk!H2>v6?ZGCmCs1j;-E*H@vuaOvM6;7$hdrc_^Y5mE?Wezy@?W5u z#E@Obo7l61Zk*zJCdgk&hSyB88oNi{#biw`-_v8{aA~b+udEO-AXHV&`}@_Gcf{pCHJ2Pyk>fca!tWy{ZKYifs>L!#{tt z{YwA~PhK*m!5~wyKP# z5ROr>v^Zh`6=two#=#vvJ;g|Uk0{RyQ$l~^L^@#GvxS3y%I{g0lo|F!r zgg}}Q=G6d;jpVf}21_XsWXN@eF0JQcom|Ro?g0LVURO{7n}R z@zZQrNw8d4Dv*iE;R~xD%;9@~SZxZUMHsNQ0i?tqe3oh6r}?3e0Pv~b#bp%SN*J{g zOl;VgN#d1?5MA>(wvbtH3TtcY1y{=G@XHFk1$9?l9*a}`|HTJeMFuX$> z=$#!Z#aY0lM3(z_^!OjrXTqGo!zj+1+i5m~4~c4} zCEz9|2nI?fXfQE2@Mq@ha>lmpD8&~kFsW0j6oT`8@ zE62TEQem<~4c$MO8an?ke2H9IE_~f&Qqh81I-CDKJ~pflZjWX>sdTOTwlLiju9sim zHN=J>LytsBvE;jIR&uc4`n*%}la?H+tR|2lnF3M*{Der)T3uzvY_en5aLWIg>^x*3 z@F^J(VkiSX>_DlLGDbI7f)68g0aF#_){-ut=%PS6gNIM`X`@0VCo?-(`<0zqp7i{L)>3CA zt&mdkdnW1D}Ac1x0-QZmB@ZS&wh_Zb1G&5{8gnsyq75 zt+LDMWTQ3LHqekb@mM~{KH_rE8u{~3wAhPMf!|baqx@FHms@hI1=a$;lDf7ytp0PB z(r-hoCGpCdthgi0DkWm*1K`={@dbFO$hr>DZvsE^143sb5nL!JU_kz9hb@y4sU`~# zO*Q#nn8ka5XQ`9p*WuoQ#CC=rXYEb#*un;THjj^cy_4xe$(qUnj08fL=jSR4N6*XS zTKaUjN;ZiEcj$8uFW=LBx>L5x2{%p@&lcAo)s$kcI1?1QFpnJXd#MxcL1At0{3Mgh zq|liJ;Rt6UKOKyKfQhZwBm_f}M@#I$<5kb_n#0Io*?wxJlu~hK?RKv)(zh}u6qUfB zQ6#IIH)Z=^xMuNFGH?|{uhzpDBqTNqa-m^I11KCr>eOL5e9Q5t1MEn!pv*ZbQG4Z6 z`rW%GI;0#2O8e+ug)|6VAzFScjr!D{JG`>tJvK^+rSSl(@fy&_Uu@^&Yh+b|vg#(9c{x^{sm^VK=&U3xwCSweZ|~r7CM) zX)7vsEDMYVNyS#O{wGAwAiTPUp9|xj{^u}F8!mWxR@s6V2joWO1Y+F^4O=oBj794V z-Y5|eZE|Z<^Q=dA1w%i)FjPYILHmVJe@8-*8961ar{$z>+NOpReN`Ey1uf~2^(aQr z!p@x90p5h=fd4Ay9)r>Z+r*x8In3at?nsyJtRQG4lJvdF%19O@*L=8pSY^LwZKyU# z_-?0Qs@7=i85^uAWW}RwI}{D+vVpW-BUQjpZ8l7pPe2Fn;lBWrv?RdI zjxnIA!`(Azd+=)=VEM{$n`k-yayudPXoWzzBkmh9R4aug>748INQ0H6XKf(+p#JD3HhX*F%5z^p_jYgBiCL2YrbrMvCNqS1bfk z^a72*=NCA%U;E*kD{=7(zEH zj}=zXsrW&o>8tSH>ldT&OAX&|~UrV>Z4;*qFEuDQ$C|%GM3b-!b;B!?L*% z@hn6!_2u_0mfHiI{0y`@Etd(GQF6zXW-HSf5Qm--!^PS~E!(Wme4)%6p!~`rf`A+W zSV!gXHp+mPKH&0_W5GSS9y8$<+NKVd#1cdm~)kR4a{;8efFk-t^MS)m7N% zxgj_Cd`RFAv_SJWcz`R3f1@h6gMH?Y}-y_+l_78X>2x)lg74f+qSV{^Q^w_-uvC(ch31gzxAwjXP%jR zW-g$9gdBDeUte+r%cI~0`KOEKSJTQ> zGa9F91*4sOL<6>C+H4H}9WX7k$Mb^Z&-FDXuj2|YZ#ko8LeWvylGes$FL4PD6!vabX z7Th$F6Kj*ibt;n85+{SL5~H^pgMH32l#`HL;OrHKXAzP6?(c-!8GrR@XE{|VQj;li zvT~p!fXP>_9C-t1v1%-L70)ysAl>Zfb3%8m(q>2T6Du;f@pAz6X>=a1+Se(FGBQC8 znV+fm%z}`6~;5Mhfbep#^2r+1R`hxRG zb(+>bVshlZrL_(L20aE(Lk|yLOps_P;9J1TKI7LuK!0j8={{A~DPey5z){1y!OoJL zX5{89#TI$=ZA(OIhl`76Rz@gPgAZPGwNZ+pWrOu{_9OCltenUQ)a*-h&mDnfM(Z^T zSb%*^HDXeEgDnx7r^gmUk2>O~2wtf1hCCOQ=wF%*yP4`poM2*o5Ob`ZO(jX~rM7>+ zN`Fh}8Vh&HT5Gy~$?6^7GPnEmGb-l~m|v>U8!Z(!QZ8G9cGMCA*wsWcC7u>eXjdPP zjcJ+MYzN7puutmP1MW}x>x~!5H@%(;Oz~3qZx?QqP8W4s0aNF=w&yEr%X=9J5xzdT zC&=_+Dt(xA9ULDUS90``C=Px8hc%k_BLcDpvRJY|b5fRw6|;H}IE8(96lZ<{ejw>f zCV$BlZ_g$$Lz)crmq)}6!w;m(DLz`=lN3v#uf+TXF~31t=a?)qNA7BVbgc+=J8y=r zpSYKVlQn0NmgU1bkf_gtFklNA1A`9|LqfwA$Zo)f6TkkOCikpdnRqH(m@y}T^ZGa(uQ47@zPI>@}j2p}e#t6q`ax3b!?wp}+L7tGdXJ&QEdl zT-Yun2{@$3l%H5@fd$4Lc}T};wF490v!qQ(C<%(c#F0>XOqgA8rLlrigP`wr*-i*^ zjg3Hlhd?E&${R+%3X8zvo7xfsu-i{e3p;qnL!PRT;dG22lUR#%Wpb~kGI5&&3?Nq? z7tO3%)&52+09RDpyPTw_rqppH#&JDwc4x{Ezvv0qm-8BoJi`pp6)~{kP%u;{OtST) z92Qm1@OB!ISJ^)XApne+tI;64R7(^9R)O*0b0~(8zJdWEJE`4E6!}nZu~1{~*_nmh z++RyjokaIa@1HEHKewR0_~W8{4ke(N0tF??q@=p}x#zXrtJ2jO-Lk6a4bQ!xCt+O) zgvYBmqNna(3IXx)5`?4wOyH}upoZM+l(lfaWPZj7T~+cz0M}IsRzjlft*5XCw#r&H zBpGNyU5K1bX#EfQ1|~Vo`d8&?Yj!3MhiiS=PmauoT=DcuX>7I$ue{za7SHo%{;?bZ?>XSL}y6uyWt#!ygThIRx|8`%>@QOA`;-FJl7kf_ARsDELCSDp-qUS7i7(pXrL|r94WZn;BYRFf}ex$J0Rmlu?bD;h!t@ETJ{y( zm?t*Jq=O~;hcxM$G>-HhqY5&=|KTUqU#jun4c4t%oaR0md^;MpD9#JHbOPsh%f-B$ zONv)g=v+$tKx+(v7aM$DF1AJB4Z!lI2!p6(+WBT!G|vKx09dBHyr)T5TOws?Fdh7x zwE*lj-h*??j0dkAzb?$Hr3FRJ=+IysQ>DFc-Ix6u@c#C;nVUzu;uFLU_A;Nw`vof z+z#;8HTg9xmJJGDJ7$MaQ|D^hO#U>tc1q1Q4(nrM&yGr~@MD`^Za$}bw+C>CF5?*- zUiV9<37R%-*C(g@TH1Ey%mNvEIj4kO6d~XA4`s{t3kz~s!ALXqi@{^ZAFIfL)7rUR z^c%F#-9NVIf{Kt%M|NBH>ifIiv6?<>Re?rCH{e9=f*1lTS^fam2visJ$HqWtYHOT8 zftrm3%f)hSp_4*UR>qOatPe{V1+uXVT2y$ofn0rRs8VOD=x#PIw@lf}K30_V!^q#9 z#S_+HCbTg|IfZLfe3)4yl*!bcP|U)f%9%p@}Ze z&LG)oHQh%aikY;>$dGe4UfNjjpzI*)H9JCEf2e!oEMwk1hvHJREmjBZZXS^kkTDTcek)pu~?g*DV&-pMMgDZ zn-Bf`2C9rof^Lv0tkKfMqJABo%{uGT)zqCisYig7MCdQhZ3R=4ukln8Tns0D>z@c_ z{CZVgRg2Y>(>{5>Z-dbYU{gRDx1(b}Su^Wvf5UQ}f50Z)E6=JVh`=h=CQOw?r3hn4 z%zT7KOg)%|FbtO|k{c|$TH*32SlQgID@)3??g)#6svu`fK7ff~qW^C`kE60x!aF;lg(4&-_GB6gK%c7&P z#S*2&{LYh6SIPf!@LCosC0!I1T6CyBE;$bSow-7N`t+u%kb48CQ6Ggdpm*qp)U13Z~2Lt3mo=0cXCa8Zbv_!?koYtj5qQEFyOF=oig9 z5xSW--JQ&;tWI15!zR!EX!FY-*fyy6`-j6LdqWUN^e*0;*zAM*>zyD@0$g z5VEJVYV?xqtCB9Z`(dhUSKCGqp{r*ZCsMtyB6M$EouqL3ds+<&skJ?#i#)GDmh{g- zKE|hHKCGJuPKLXn5`{I;`7z`s?eKq(m*0d2Wh7R#IIhgn@yx_$^zWjY*fB{DCGk=5;J!5@hZ0n6yF$DNU zF4c{O9f)!;uq>84tx$yD(vW zEl07a!XudX@4B#Hk(YC8R-%P$jl#9e3YgQCCL4F#ZD=*q{o1I0$21J-Z{L!E>S_Vs z$In=j-+EybA;u1>kXer(sAn06VCz$Z1?xAP6gCIibPOStCR+bBNczNVGr5BxEOoHx zVoSd(ShXEzPOGY&)(x;`-01B}hhXIn!W|*6J*c)kA_xQ1Wx(f5L^N)%fw76lwQmyvY zhVog#F_HNP!2v7U)iK77m_5n~71ckjh{DIIwgwS2)MD0ayyLL+wr_1j(zik}s%Pi+ zDC@wKh`N)$rdM%&j`i(NmXX`p8#_BoWY6MQci&d?BtO`ETN1NLU#^y65Ak)?aqj09 z+w|ODlylkez7IYwtZXY_=3Y<9JSj^%8p}Aw;Vqta*5rTGjggb-a68jHlr3*PoI@7V zDpx#RV``AkCvNBc#ccx;L9_HZ9c;Y~(>67aRanNw8Jub2Fda@wsPT#P>(eNZwkVA(~Hg#6$ci>b_ z?nzZl=1l=h3+b!m_jn|%>ohx6dM{_{9Z^Ln04!?pSXB>^Vh$X)t6M-o60V_+?9=AQ z+otbbscsx-Le}$K;ht~ylqjW(e>axDsougCS&cQsp7tX~7|B$2ATKnrf4=w>frDA7 z3DRssvxq#`>KV$fJa`zCD1#;g8sVutXEM-hXuTLs*4kO4rh>PBg&5?Mf3~Vp*_EX7 zOBD8<^_al-;_3j(!@o-h1aa3&Q;v+fvwj#wkdz)wLo)|p6r{>p;wEY> zV$=2X3eW3da^p`sa(%5Taz7pvL2vY%$L~v_fUPwhug3qICA@=An#RS=UwtlocvRgB zxtY$@;W~;I;v!9-dCAM%?AG%zRGm_&v$Du}*S&h)+HGPDHl`SiU`0a+_0&jnR+)#oT1%h(Y7!=t`=zDtwu2=A7t z+qwYyjbaML+|@DrDWIz z{!vTOuw3J2Nv`5=DQ-Om&<4R5Q0l(dn)}GK8n~WWz8F2;wmrk8ugxF=rgokaU+Dd{ zYu-dW3+6=!a;uagEShj!!n0qqx?cy^tA;TfW|zHTALf+gkOmXP(UEUPf*Z6O@MYPL zF}PmY{p@&ngvmi0NBF3_CF!`y7`1@_L7?7quD%FY+y5hzeZ%V_E!@7-fPa8$59+OH z$@G;H9zweeeMD}9s+`?3DE@gG+x_@7>5F$B9$6JA#|yq8H5>(dKzmVP0fa(na(k~< zG+i%npP$pDU&V(EANOyb(*DiT-H15--z?pIs3R3mGQL}}){lGs4-D;!PW#455VbNc z`{>5eFU&r`Ica}{y$Fl}L`5@7S(%^a=TN70X zKw}fAMs?`z@W434oV-B0ej+fnnP?)SWr+b1Ue7&Mb@c4$IXop}8dJ4;#`IPz#Dk~` z5Tw}o0(>=@gXzT@lQnVOosSLA3sRsaK$mHj_b=9;rYuAeVF_F0-`HZo7_aOjRKV|l zo}GhgRAI4r2Yn$j5d5|dgsw0x_K@WL-m7?}=Nc=m-eNdg+lCgXK3D6wMjO5j!Sgr* zGLFHy5FKSpq7)f)nI(B`WX96=57cK;F+Fdm#fqVq3H_|0J#lyX87O*x*dm1RrkuAzCJQWINQm{rPm9-grr&(-0p=8(O0?`Nx`C^B-h7Qy2+ zMsf0%EviRB&QAVaAc2rizEP=aW~wFLW;m<*^_4xOUvwj>q56uO*)0 zrt@+adlC50u4}dPUZS2Vha7xE}ht%WDyp~sAjsB}lMh5#qUDam2(I7~^ zQX{@kxOVgo#@c-wuNc>DIn4BY4gF==Er!f`6R~zzjH7!>iCSpMqFTFX@dt-l&&*n@ zKRaRR`>-&5RBNN}V9-0we7eEsavkZIf1`QM+imy!;AHEoA%deM@v7~`?HtFZWCRi+&Laj}XdsUuL^42ZN> z1TUgQ*F7PL%Jjx295u0OQm!DRF6}_LkT=?tMKHYhe$!n~_Af#!7Cejd+bEJ6sIRKg z;Mrk51e+-NsR~t8P-TPVg>>85ks|nG+%?K{_h_!(&gQc*nvF#`4Em%Ia2saJuTFF- zD~ps27%b9_hKpFqVnj{U#B#+R6S4`i#xL0-T%HZj}>!tLE6 zLJ+mBqVc*t-pO^(&S)J?AEwtA2oOf|w+}J>r1U!jUtMPL0sG#!d_-gJ802H4?=FUcdqwPoxu;INkaf;+TI1 zHvAt9G0<+80#u7~oB5-gK9+-d+?Ye`56JG5~lY@3gM%ViE z4wbHmK*DREfMGis!nTrJi2j26wS7?6{vaG@$%tS%3S|;@iF;v8mxI9iyagbknSIw?T5THZXAwl|(3&ZmudXS1wp0pG{O41bYZe4g0mp zu0rN=Iid7u^`{h0{a#ZXc@XxCIJ#h*n%xi|);i;i?^BX*_ZWM}6&%7~vQ%T6SJ!8j z>I62Uum}zgl88vWBUr!;rVNDIgc_lNEHY!U8MGK7{m{=9HuPy_c;FSRi?c6M5IV1l zQSaP>C6Feush$x*n<#=>o;1#VkZ?+xQ9}Q`r*Mu_wSKO^QdXZdE(%WlN9uF`Sr++g zD}tQ>8p0N9#q!lkERZq@s_B3~LitNUj@|9e-6h4{h5 z%E+os)2(rONKy$Y-rb8u=9BsdBNmRUW26k+sQ?w+6f>(TH0fCHZoF`zgXp_bt4Tq@ zg5^vzH$Majubcp~EJ;VW=Ht681BW2Q`cA$U7*W3bVC%wJ3oaC;d1+IfG7tB6H7*`NW5j(k^P_*q}CnbF3FaL+C0N3b7 z|2I|P1~e?eNDUO!mt&$Cxk*Etr;pp0-4=)#Ta@9@{a=h#NGemVRAE7^(iPG%#K@~@ z+FdAA_rq!q5u}}1Ht*B5`7#VW4yW0&FeZPx1p!1-8b;whD^V7f8=&UP&>^z!FsK+q z0SI&tEe*Ju1;Zp>^OSz}oZdI%+9*ls`@L{^>t>4qky*Q)Ne*gBumEV`Af26W!E)lm zk_NE+bfykrkxa#C1b+I`@#120Bl*`nMVfVA!X&Y^e;4Ls<>id-jKT4{LfnyoseM%g z$>LR*aT``}s_ZK#KL~;|Y(7e3;5$G~rf8iLs;oQXEQEjh1;GnrloyyCGELwVrjtT5 zn1lmTn+n8qWovA{w|!Fq#u?ddHY_f%8Y{A>DWPPUyj;0MS@1IAOB${+3D&ejq>cTZ z=|G^wjXmT|SFdlyxAe)4c`-><+sXNQ8^ma#uDO;}g!=HTAL4kd5vCi)#vJllso69r zSltROSI5Z6kNVDWxJ~3fWIV^uN{D0Tf1!>`ma$kGDh|sj&Crm8{(GCz74*%o(NOo` zq$!;?VJ+5Eq4#6k@R26q+VK&)LQx4i5moYq21s9c|IK2lIRQhy&bp}eV5<9$^$ZOJ z4XAeXHfG{eFI=O`P%?m2Ja@3tA_;TGD;*B2O(RrH;ddo!(!{ZaE-K-T-E7(Kd$pTR z;U@?P>k$_?5Dlc=<#~%_*M!K5ON>z(N_-xNHnb4@<(ET_ZG#q;BM_&My-wQHZs(}x zrmiUDj{-moAF>WkDx2m-gd*ZgI%6{VEYxQNe)!ph90IH1?OM{v2`oUuSS{)ic*%9_ z(`Rj1X8(I>LlgFin*8;WXNfJ3F>Mye8d(pqj`61(8YrxUHAy?WUZ6>m5iK-v)fv!s z_A2`&b-$kxm-ml|Mkdr!5-+Rk5mkemZb-kr7j$oQGC+;sk1D&Xq6xcgv-=Ic?BYUa zZ=-`AwU-^DibSC<^R4!HYYr;8CG1Ie911C~7)C&IYWs?@k>|a2V_D@_9=d=dmWnLq zz7&FEZL!$47(wZJ&j*{%@1$yh1)^t6sZHf^MeCM+kr<#q*i>0_VF$`(YISAC)!&{M z7So8ocof!V1cvKgz#sT7-OI@SQX`tvl6ht|)`l=sfD7`mmdBGce3>48{_Ev^ChX#S z;NDoR9jyW&X|R-`c{)vQix#mg)GX~`aj{wlBZCNF#D<)xV4b$o)L{7kH(D^X)o5N@dzQ;Jt3fRwa-?z zo%CDJ;uqb{m`l-~%uYW{hgd70ZX||)te3UJ*UWwxA|sz&k~u$x5Nm!vRJCm2h=mhj8X=nbZmrdb(IDDs>qhTq9Mocoeh70kbL*JYObqyJ>jg|DV@u9 z?2rYbA(=e2K*7v@vtxO-=8z+rSPv(N^)_^Z5$(tRJMzE%Gd%g%08UE_*gbgKU+KW* za%#Dcvo5;n`)7rWHUTj_nw(7i_*C{h%U3!QLcSmalV~dOL!Lfr@L9vddSGL}S-fAJ z2~HO8QvF>v^;a7y|NGAQU*o;M9%%^+?20R@zi30)PndeBH%4_|x%tA*!V~Kk zowjpFw6NH+|M6E2h+9fe%y(KeG*`v1#g23fBl`558ni&_k8Ayy@^#9;nIFJsbx8_; z`?iysmBsJ#`na~1K4j$K;o;)4U^+lfZQm1L_|MU(UqKB$bv$f%^4Xu5KLg#ny(`=* z61veszEO>br^Opl{8P@5f1pKw%kvN+7K_IDV_h0}0pzjldx4WHw)Y@&>Rt^WjHdx9 z9SuMy@qIKpg_@-Q{zCa8e!l&QJF2)mZkJQpLRw`?g|b&=Y*tHM*)7*VspOWE5?aj$ zx4uld1$uyVdCoL5pf#L~TH`N(&%ca)^+X5=Xs#qffb0*Rr@e$p-iI|He%))Q?~8=a z@&?D#Y3b+A#x;jgAlbtMh*2_LaTSr)WQzOeYfxkU;MfNGIBr1VxosU6e)!&>*9}MS zoS-}cv=uEu2m?vj!o0-{s*%#%+w`#^;bV4xsL3(t>UWH_rD8;(90?$le zBim!KLfdmzhW$_Psiy0FH#HT@i+^Cptys?)2=sxR1w`%+M*9GbaW&&^m|aFTsCR9K zBIui)p04+2(K7b`I}(!kJ>g>f2K_N8w>NVV0#Mq^st+#rsED&KH_^hW7aBJN}#T2BFgb*6)X!c_wfz~ z!x*FGJk9(v@c<+}Z_#TzAN;0DqTGy5=kKLu{ZAlFd;zmMy9D~$TQAq_@5b=o|9%E) zv>G)t6!8IrqA=5AWY?uq(f#+tIkH*uuQ5(WvV!%YJnur#ZP&pVz>CXdadq6j6bAm% zOaH_q|4RMMO-J|kcWa`t_oZp#RsJXO%nR9c9Gw=cx92;&A?f_o24mSw9`h^=8x`TQ z&`J$Vf%n?kisj;b70Ar>6;1QRW9A=z9rsjzFQ}aaHDmTJ7qiEw3AwIh0}>coK2<_=ov+tGLLATs%GsoO$B-E5gt|-hH1HF3zZ6n_PZlF%sJ;5cOX)ue{Q=iKit51y7NciE3#H^i9wjR{Ba)5eI83kqM>&U zREeu|Jd_nI8n@UH`(p@sJw>Zs0M4UTYyR(=(ADE};__ZMv%@Nfmdmnnn>b@9 zESj#vP16dgrd8N$A_zubu>DxXLVmm{640}})SuqpXmexTLDz0u1V|Fyd>L1JdGKOn z@G-Nq)4S5Ucq*muxGG4a)Ao&m74+_W-kHr6i#dCI>;-0#wug!G_If3;81F_SH**YP z-++!QwJ1=1H{o|x>oJ@Oc5Xmw#`o{tUM`ll`SWLa#NV3NiiG6ok$$(m1>u?-?>A88 zk_0AdloO|0yZpBKK6-n{nIvYbB+kr;!~u!4^(z_r5!*!;F{OuGV1h_7f@2dDC9Wa* zu{d-Qjkg*-pXdY~hQ=vqP!JSv`Gmj#Tx2v^;A}*Q&md_s0EUhPOxKF8AKWo;@25Wi z4T~81V5>VIR1Z>cxlc+iW?=tT$r&DvGmrZr2{fq0J^r5*9@eEH>}TJjZZJ?~(bf_O zK-&m8)W@wZSmKn%B10sWgm8gh9kx`Ic**RiOQTmI2WZw8+I7(Le*9cZyLd78_1&z= zk9fE!w*GicBaH}AE!XO}&q<63A={94(yB87O!{1%!l;&?6i1z{wjS0eW%?MNO0DvL zJYU19IWu0fuaPH!HV!)JL5Hp!zHBa=FP_cKl+aJu0Ltr9Gn~>dfDDaMz=IET!B(6Y zUh~sI8m%D=>x|^Lbb4HU(Xv4(8?e5!t+kS0$ES{TF&nByCLTm~6@-2acefMUeV=)@ z$)lIDve8hXRGGJd3H=Rd8J2cg38Z=IH}HIo0G(OLA#e&J-_@H8Tn(5Jf##+k(m2f2 zmU!5j;_Tp11itbx{b;B+)QHLIS3N*#RGTXa&~#hX1X(rpC6GW=8t4evI(XUez)DWP z1zx10Yvl3IJ!nh-Or15zyWgPu#=wUc6+tDx%8rEViYHBJfGEC(!T;kx&y|KGG5$WI zja^LE$MyE|$LGy+2PTd>^&g~C!Su~my^qClZ!h=vcaaG2w+Ocn54ZP$W&NKE>2!R~ z_Ke>)H|%fJTJLGU*SS5M+jgvUl-s@zxeod;yL-<&G|hwexJ~Q^AC86U6sy`)9HwTt zjLXuNSMjyhH_liT%sv6MoB|d`8|XV8iB=ZrVu2|%$xruG!?Q!Lz&VZc>3JnV{lWYa z)%1-oql548;3%URDLx+M$5ZIH*m6ALlzDez9z-26r1{k)A|-qwaAwQ*#&J+8^g~Ns zD1=@TgCDw{0M=u?_%HxzxR;WQT}KIpneO3CSbTSL_^SvnB+S`y>JIyw-Dmjk>eaT{ zQ12qtvyOn3db;I9HQ9)0I8vjCmX)IQrC90zh?fAN$$G(vdK(PiabU?8koXq6+rmCk zl8Z?0p#n~gf`3X>!+Zh0=HwSL=@<78x_4m>nrs{P?CqkXXE#O?wI0WCZIpur14@JQ`94W`QNBypz@P)T?uQk zK_-kf|4f-8H>Se`i@z_QO58Op6oW^&i>}aY_j2psA%=8W^ng@N_wBCP{(<&+@E$H2 z?(DeqE)sX`#{8rB^C)D@!`bFc?Tg%J!*-!kHaJ@CS}z6bs}mvJa!v*8+o%)h`HV1~ zFh~wTVt1N!Y9+t;{>2J+jv#`@7M}ZibUw;L&rB3je^KR#SCNJR6!88b3T(pOiDv6I z0;Mpi*Bs{ar9S+_a4DL)a707Of=t0uBGbSP;IfD;Qy}h)s=UpzI9$|tB~Q6hZ6cgD z{6sp=4=s3jvX4KNJ>N1qNxC0e0W%ps%x@lD-8|Y#QluAKQ+dH<;?aqG+xZ21 zTYIX3(nz6XihRHwq!m`?6u{!U%V9pB6I=Gg5;rLK`S-E*0tNFq8BU{O1^Tqb^w_{F zz}ak`yZpRB^l5#D{I=5P;)pLx&0#b-E|Nl1>Ff7EarP5f1?>wR<}ovM_8GxLRc1xA zcwgW(ajT4A>z>7ubIwMP;ZRso$#`u=mHoE#X^Q1MfQ0Rdk;b{K_R8^UeG<;BVF{w|G0hrX(`juz>-Ow z?^SZKf}%)ja~XvSNI;{YNC2y$65b=1@gk@6?NB$Zk8>NJb$#yM2X*gZ={n74d8yTC zzPA`De4N~KY>Do*HLsQed)Rx?q zy=jo8+qhQi(YV}gIY+&-Z3LO|l;5BtKYe{!aOY>N+Z!H=RH>%Vw32ONG|<_(lgL!@ zHk9ma)`#Mw7wov~fw9=OvF~bo9YWb9%jR>J7cSTB3ZnoQS9ZS z8;qu8s1=JKV6mw@!@L}<9<7)S3c*OgLTDcxzCCPC2C>O{C5>@ZGZJ;;(gf$Q8(p+s zbTCOeOD??SMFP2IShDvjkr(%m5;fgQ6Iw^aX-;3eHVRz1#_@?eB_jlD1oopHO{X`_ z%srCoThd?|rmYdFRgcWf3%Tk}a2r*=w(NKv+=fw=Y;tcs-(-D6(tT_r_?mf+?{p(r zWx4Hq$?ZAa5cTZaQEBrXMfegm>r*@HmI)Z={z~HBQ z_h){|4Ds$?aA2^?dpx%5`5hl6Gat;D3_~l4djJJGZ#Kl4bEctasQ3Hfb-!0fzH&Y~ z29MwFc55+r*VD!Ng1gyZ_&5Rb*}|~lnU|5n$86J7ET+w;-_H*#pU;I3v=g`R z@K}&k4Qhytdgc^C>d@b6)awNC!Kl@ZauNDhZe0UK9jecL3 z8Avv<5Yf(ZDG@5&FbBC8h9oS;zBoTVMPXI#baf(PB{)6~T`keG%5Rce_prg=?bdZw z6??EPZ7}_FQyF6c6{!7An$hGa6B*5Rfywy2j0`@!+LQ(#o;=$$ZpT!A1677X5t?uh zH7qWQr9mvC8|6CUI~F)P<^Q@viurMmH90VtQv{sYqsH_$C&cw28r<8-USY@Glv!p% zkJ$t35uhvp9S*Kc!}-0VJF0NW>x6m-6iJ061A5a3| z)569rrPbvTxQlkZZf2c`p&v*TRd@;*S~P8% zMIa&pj+4A0rp>_pVSo{%H;Xk1IUqktr{|$qK_k2Ak$VkiVJ6$64OW!tmBn4&r_SOI z&W1~gX0A879XL3F;seS4;|k!0M!zo1GV0g217k57ui@*4f7JaIZ`=ZXI>-*+>hsChKK*=z-F-2~s%Nb?e-C*re zIQz;dTT(|svMK-$<-rN@ZML;+e%*-ROB{sl)V_%AUjai2wvaKF&ZnSo=Ls1M(a@Vw zgc7w8i?}4(;&>yir}*|>Zs`D=P$2)%5bCP46^JqQ8G}g3+wa<)RAnmI@3EN;T{0%*lMwQvu!px9N^i2$6eRTc1C(TdrH@8 zaaB`~QPFC1+cmsS+$tLF_3{+=Wx{nnex)S8KKp$|-FRgYKx+rVN@j#s9cs|)z7Etu zd(ZKH5@dQj&myb21>aP@5<>e|K--Ax@Cw3NaqQPi zJxmJ>E?Z3t<|r`APCet1=vl#u0HTI;t7qKw+uW5@B7{d`D^ovv=*D(fqE(1mpx6RHx+cTv&vFc z$Gw^`L8N$Y>ze&wXI@3f5u}#fJ(f`=Ho*?%VjkhS()XrF}+X6XHo}GYC696H_~ak-b~lYU0QRnsWU)k3D=cf%3+_iJU#KByX1v+P6*)` zz{8n3N-xVBrjz0!sX>Pf#So4BoK|Nf)FP|+`JIEGJ4@5Ex;8btMbV@ui1N=rI4gw04p zoyt&5hs0Yjju}8DvRd&J)~1W1mcxD$gf%BuyhZxpSO1)`PZ6$mkwKwI+8Kv%Bda<0 zPM6!ct^H<`nhGD)9*|3a@B@^EJY!>BX|@o0#khPWD+E4cqZ43!Fc0ijk~zVo`Ba5a znlLQKE`K5WzSXHReKyE8rBx@RDLC*=O(xgAPBJFqS`u1tOSab5b6yG^hSf6$asqbN z9Q3|IOAXuzF1M}T?nKakXHl`XKB;qdB1@5;QJ0hsk|j0zpA8W_NCWg~C}*F>fpP8c zreQ4;avFRAsWO60t2X%20>Q;VTRums{Jo$bz_D<~ouOR0SeLEsW=?g-JTdQ!s2oB@ z1)=V95{>qcS6=rL-^<$d!WjDUC?D1-U%USJl^S>hO>&lQi7V^1B`>_j_6#BWn)BBJ zeedfu48-UiA$Q3o7D`YNPPR5IKFpX>bK%ING&@fY<#2-co@plg%K}8MwQ~6`6|w9@ zMHYcwBE|i1~HQ5xF-`^-Gk<}njAW2IyD?nXxn>417Q4yFH9T27>oO2-Nr}7C#pBm0U+m;LU zCy2o}v)n4F0YV{AJ?sgIvR*l+9|OCdr@5tS(;h0V`uzpHqeS3C*hMhI)PLbCA$gF ztiSVMu$lvM(eIElhZZ2G55Q`v&^2OKQoS2n5|&dQVv!L(^7`-T;GZVQ3HJxHv;=*` zP-!C$OPU;00;XWP*#fFiC|tnu1U&ofDTsP54%L=94IWcewZ|Xg_y2#DKV%Or^ zpE*qpA4zwCt<`dL+Y)C8DU175W4oM>q1EJa6XSDt*K)(}3Gkhd_E9+Hz%<@j_j0Z8 z{ZfFOJEA1m-!K!uxj*45Y!_v9?)A_~nLCc^SH3Hm%_@W6q=5FMDQ;FK#YRPV5xQ?v zEE`9}Z!y0{5_b_o`ZHys;Wn`bICD)(5k?AFzY0&SHb%H<+ z#tn~|^Mt~+^?>a$w|fXty;tQpp7W4$;K`1N(CDv9E&J{I+&9Ys%Qx>{OV4MFC@IItK;*`>XoU7EXCgN3_*|>&kv95lW-gxL-vN;@wYRogX!%&ttuI$ zWqD^P4rLghSn`2ghdN547$SIM?0PCiEjS3oZA+kN-Xzd-=@d@hCv`cTeJEF{kFYmS zOqn+A6-E8$6b10l?q1=*cN7&wpcFi{Xw|6X;%F_xP;adCa!|_Wd1u@|VN@)mdRx`aP%JXk;_FP~5&AHq@!yg(ECnoI!lbe;^Fi z57Yb|;!aKumsT}(BChjTSH%p&U@;mWXknuqiuFq);c1)0IK8+O&xDJfJ&P=e>3UGk zmvY&uPc&FCY5*7paeQ2k39Ox?LuW^SYQyx2!v*Si8FvbWlanBq8snUGSV_QFylbNkM33Af1I+kby zy_&jHq}x!rv2ARIxI{!R0$fC^3bshSl2HT6AN5y{!{zt*F*3-T&h)-i8?KV^Y6NFzpq2bJ?p3?aUIQ$ZGU+^hALbE+D|! zYYVtPMs)jldqCMxZak@<%FBJXP{CcF=;F_-?Rwe8|KI>^&Dosa2^^(uYE2CM#BZjn z(0jUAiBW3YPo#=uXHN5p)}%tTts!n!^1IZI2y289GlKb(c}8hvr}vBj$aF+u8M^gp1Hi->O0T87vS)g_G# z6?lMqBvp(4`5vWkGG>0Cr%~NhITBG>L8D*#y}@vi=*wC)^$KW~Bt{3D|08{1fuO^f zrc~cX=)U%(NnjG5fjI`z_OTei+mE@+nRksjiP4Ze0k54KX}s-k_1*iql+&Z>@eYO%;%+A+{bBK03` z8^>KR=-U0VdfcLQW)5{aE6-Yzjz$+H0}E0$lk6Wxq9jH*9M{?<-}a$01KkN5jvQ2w ze>YG?elb+TdNN)Bvo@cT?Dm~*Rn0|UDVZDXR^m?i4#|#v;Xx=USvSPT!Ki)BFnU8Q z19YK(K;;+6rY%qgBfU`m{8TMId<2C`PCu6p!|rSzJwSD2Tj&ibZDIMlW)H@7UVtjm z=2Hpu;4AD%nS@@zuN!25jHV4 zriMl*FF+N$svV+H0GKdDaF|=z{?t&T^8IgY34PjX0j7e@IN8FwtCFFzaV%&9EaG>!FPHN zZ>?fzDP!Z6!QC%9L*`eLoMXJ*fy`I6&kKeSJto7Z)pzw_+<+?JA$ zc4^OOJIH!P%r$jV^@N2=D%_WMRwMytb(i0lN}f&bp$r#0GEc13N@t3Rj)p`Zhjx%J z6kR}$q}%lNbi9Nr+(QN9UEII75Rjkfc4T!tf#~q#@Ith>*6TU- z^NuSEfnbo>2t+Q>8W^hw@f?^hhf*QvL=#y8yr2OetqH&K$LjkH?i$Nml4aLixA&VB z>F7#+WW<%lATq#qn>cme3rp|V0M#nUI{7wTGJ3j3YFmon-(PS+Zt#jEUqNr9@ zfZ}q;fzDHp6hF1K?Bw9ar96LaJ^(qh3YM}N@raz znb*E`Ok-EA+4`HX&!h|<#xTjzJa@;$HG-e zgxJRW-V6WkiLy7jLxcu00@_hdx$>v<3~6@+SZ>+Ri%M^fuW0wdApI*@9+G5@QuZ)6 zmV0CAg`?FC{ioemlf^&T8*Nqg#b@w&i^_xqBB|rZ;Os*ta+|2Tf0-34YSK`uR%wlQ z*$5R^7BH?uobni3w;3YFZcb#TRO%K?!mZTQl#P9lOng4X0})QMM%rw-ENOpzUOV;oDqmW$pS?bM9m#oBj1FbcJhe{sW#6LC zJ_gKP5s7bs@))>GhQ@3Q8P8~(5fWJ?>y5%gKs2lJ{_@R268|b{LVSfXWDXv7 zoaYku5dEv~C;lnjY8iJT>TgXGFvkl(d%TuUG3RDf@({Ttq$`&V0r6JYwQl4Gbno!A zSV{H8VSJ5P+gI(WS0E8-njTxv-9rlkE!0?nBqEqROWLcH7_4e{I-yt!QEuskItpov zXj(c6kr*CH#uI?!n6i>+vwv(;za-**Yi>SMO25G?gX&HCe7E5Xh--?+e>@Iya3a>! z=#q$KeT!Es{-xC4`c13+Lz!q*nR){@^%DGhGyYDS2LQH;cv7sCRd2;7$IC3a1@{=y zP9WGr&Z2--e0X$jcoZ7CnUfKf9iWYSvYaSYbBj>}6jD(%;8itz}*Qo@;sP z4@Wcame3XQN$Wcx?0yn7r8f)b`JYn1vNnn;slCJ^)F@I$({nOeM^m_r-ZPQW83V7M2% zMEUE~lR)s69IKeo=e≶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%0A5rJ9T`#|lRi(bCGF z5iB6|EtbOpoxh-Yl9mfiM@78PWt(MXkj;hZ0;yrM8DMKyX7>x$$%Z z<#j$zTVlmZTdJ4leY#9G&L9xRVY68U1Q^S;<``MU%k)dX$4%OJU+%_L#ZU8=ffyHG zpjNHl>q=X!{?w8uYW~le3`GLI=Vf$5c4R}*s=;ENCG>Hd6(4YVPTqEi?PdI(wbgr? zemD%o1WBD;g6dc90oq$|s-Tuxc_g{WN(|f0;etJ20mus&_Oq58ieLOu>x({k0lpYO zKy=%3von#*DaOb0)@v*_sjz3jI{t|Bq^0`rLien72ZqO8N>HWFhkE^8WvDm7gB)|EL!)faTF75;(RJfgXb;d^qwkmlKmY}2R0ubD*wUJ~-h0h_rPc-pE zRPa<|iJ${rGNLD#IY3yl_4I!hmSnla%&)j?gaeK!!%AyD~wJV{~0yk{iap|vz z=-FmWFkNJs9_u_PS<`n!kz7-iIq%AKE`!IExbmyMO9*3J1cx+!p`h>a?G83!pz8_X zBYf50vrwV?Mpx~WZF^Qoi`n=!10e%nK+YZ(nt;9}~I^%u|{eNv?j|E`)FS_=a zwgs*{=kZQn*Lr@KwT5gHeCA%(yg6~VcmVQgd0cP0l2I^(`)DJ=?i`J39Iw{b#I2u> zR(E@?xvwurCb*Zmx$#>chJOWrR{A_zs#%*GBX10~a)8$xotEa6d>j4PoaP-arR)ay z&%m%=FFL5V2WQZ*b1X-CKk8#maNjK1&uKb~afK}bdD7LEFGe8k^v{!m>pK7nfHNM6 zuB`pEuN;m6-^=~JxUfgKYj&MmTdxzKSDu`2X}fO4|KY)XOIhZR>ZML2Tug+|T97)k%m$?`@k7DA)YVK=K5ZJu5~rtEgLliplDH;W=3eSP-h7R>&gkr# zVr}OrMxkKLsiCC$0m0T!9Q;g=G;MEbcCzpAr)zTo;-@^zGlgXYqKldAJAxrSReCJ6&_%9`DOk6q zwRXiaR8lkIQg`Qi1)$6p6=D9}@HwVN7X|bBUR)u zQAk}iRY-e-n0heB?qByHR-;@XRxNx) zD_Onj_E!1`;wz8`6W&q@U)fzO%gfx7v|p`u`c`@Bis-*QKY8WSi8i3~-41_eYN>^> zS!!7o@4f1&{WV7tjcA6WFE#klafJz>WGhT7Kc(b&b%%6vISRsv%o~vefBj4%9%=Le zn<=m7l43l4w**Id&Ff(fCPq&eiN#G<7hrJH^z7CbtntSWpIJCzrss%6n)MXmO48KJ zZ08GV>nfHoYZpi^E8{Ip6sF+ri}E=Yj8d0=LZh7w{7kk`gTA*YWvpM6ga% literal 44423 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 From 811fc42820a3fdc3fd276f0db70bc511300ed7bd Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 1 Apr 2013 18:28:14 -0700 Subject: [PATCH 0177/2558] Fix ThreadDeadlockHealthCheck reference. --- docs/source/getting-started.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/getting-started.rst b/docs/source/getting-started.rst index e0207f61b2..30a261f86d 100644 --- a/docs/source/getting-started.rst +++ b/docs/source/getting-started.rst @@ -246,8 +246,8 @@ To run all of the registered health checks: } } -Metrics comes with a pre-built health check: ``DeadlockHealthCheck``, which uses Java's built-in -thread deadlock detection to determine if any threads are deadlocked. +Metrics comes with a pre-built health check: ``ThreadDeadlockHealthCheck``, which uses Java's +built-in thread deadlock detection to determine if any threads are deadlocked. .. _gs-jmx: From bee0993764cb0c80cc613bef5ef77a615723d26d Mon Sep 17 00:00:00 2001 From: Christopher Swenson Date: Wed, 3 Apr 2013 23:19:12 -0700 Subject: [PATCH 0178/2558] Catch Java 1.6 exceptions in FileDescriptorRatioGauge --- .../com/yammer/metrics/jvm/FileDescriptorRatioGauge.java | 9 +++++++-- .../metrics/jvm/tests/FileDescriptorRatioGaugeTest.java | 5 ----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/FileDescriptorRatioGauge.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/FileDescriptorRatioGauge.java index 89b6bf5d87..7e4944809d 100644 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/FileDescriptorRatioGauge.java +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/FileDescriptorRatioGauge.java @@ -4,6 +4,7 @@ import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** @@ -33,12 +34,16 @@ protected Ratio getRatio() { try { return Ratio.of(invoke("getOpenFileDescriptorCount"), invoke("getMaxFileDescriptorCount")); - } catch (ReflectiveOperationException e) { + } catch (NoSuchMethodException e) { + return Ratio.of(Double.NaN, Double.NaN); + } catch (IllegalAccessException e) { + return Ratio.of(Double.NaN, Double.NaN); + } catch (InvocationTargetException e) { return Ratio.of(Double.NaN, Double.NaN); } } - private long invoke(String name) throws ReflectiveOperationException { + private long invoke(String name) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { final Method method = os.getClass().getDeclaredMethod(name); method.setAccessible(true); return (Long) method.invoke(os); diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/FileDescriptorRatioGaugeTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/FileDescriptorRatioGaugeTest.java index 733bc4d5fd..b494dfcc5b 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/FileDescriptorRatioGaugeTest.java +++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/FileDescriptorRatioGaugeTest.java @@ -37,11 +37,6 @@ public double getSystemLoadAverage() { return 0; } - @Override - public ObjectName getObjectName() { - return null; - } - // these duplicate methods from UnixOperatingSystem private long getOpenFileDescriptorCount() { From 48bad4dfb5ff6fa954fbdbaff98d0ea5bdef0154 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 4 Apr 2013 09:29:33 -0700 Subject: [PATCH 0179/2558] Fix build for Java 1.7. --- .../metrics/jvm/tests/FileDescriptorRatioGaugeTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/FileDescriptorRatioGaugeTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/FileDescriptorRatioGaugeTest.java index b494dfcc5b..46713f1ef9 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/FileDescriptorRatioGaugeTest.java +++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/FileDescriptorRatioGaugeTest.java @@ -46,6 +46,11 @@ private long getOpenFileDescriptorCount() { private long getMaxFileDescriptorCount() { return 100; } + + // overridden on Java 1.7; random crap on Java 1.6 + public ObjectName getObjectName() { + return null; + } }; private final FileDescriptorRatioGauge gauge = new FileDescriptorRatioGauge(os); From ca79f9bf26bcc2a0dde8373c3a3fb81f6bf20818 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Fri, 5 Apr 2013 14:30:54 -0700 Subject: [PATCH 0180/2558] Add TravisCI integration. --- .travis.yml | 15 +++++++++++++++ README.md | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..58cc0d23cb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: java + +# don't just run the tests, also run Findbugs and friends +script: mvn verify + +jdk: + - oraclejdk7 + - openjdk7 + - openjdk6 + +notifications: + # Email notifications are disabled to not annoy anybody. + # See http://about.travis-ci.org/docs/user/build-configuration/ to learn more + # about configuring notification recipients and more. + email: false diff --git a/README.md b/README.md index 4dae141360..47a87496d5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Metrics +Metrics ![Build Status](https://api.travis-ci.org/codahale/metrics.png) ======= *Capturing JVM- and application-level metrics. So you know what's going on.* From b1867f8a08eb31e4c1f6ddfa6269fcabf6c523ab Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Fri, 5 Apr 2013 14:41:47 -0700 Subject: [PATCH 0181/2558] Added MetricRegistryListener.Base. --- .../metrics/MetricRegistryListener.java | 45 ++++++++++++++ .../tests/MetricRegistryListenerTest.java | 62 +++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryListenerTest.java diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistryListener.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistryListener.java index 29548a1968..002cdb4a69 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistryListener.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistryListener.java @@ -6,6 +6,51 @@ * Listeners for events from the registry. Listeners must be thread-safe. */ public interface MetricRegistryListener extends EventListener { + /** + * A no-op implementation of {@link MetricRegistryListener}. + */ + public static abstract class Base implements MetricRegistryListener { + @Override + public void onGaugeAdded(String name, Gauge gauge) { + } + + @Override + public void onGaugeRemoved(String name) { + } + + @Override + public void onCounterAdded(String name, Counter counter) { + } + + @Override + public void onCounterRemoved(String name) { + } + + @Override + public void onHistogramAdded(String name, Histogram histogram) { + } + + @Override + public void onHistogramRemoved(String name) { + } + + @Override + public void onMeterAdded(String name, Meter meter) { + } + + @Override + public void onMeterRemoved(String name) { + } + + @Override + public void onTimerAdded(String name, Timer timer) { + } + + @Override + public void onTimerRemoved(String name) { + } + } + /** * Called when a {@link Gauge} is added to the registry. * diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryListenerTest.java b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryListenerTest.java new file mode 100644 index 0000000000..e2a2c4ff6f --- /dev/null +++ b/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryListenerTest.java @@ -0,0 +1,62 @@ +package com.yammer.metrics.tests; + +import com.yammer.metrics.*; +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyZeroInteractions; + +public class MetricRegistryListenerTest { + private final Gauge gauge = mock(Gauge.class); + private final Counter counter = mock(Counter.class); + private final Histogram histogram = mock(Histogram.class); + private final Meter meter = mock(Meter.class); + private final Timer timer = mock(Timer.class); + private final MetricRegistryListener listener = new MetricRegistryListener.Base() { + + }; + + @Test + public void noOpsOnGaugeAdded() throws Exception { + listener.onGaugeAdded("blah", gauge); + + verifyZeroInteractions(gauge); + } + + @Test + public void noOpsOnCounterAdded() throws Exception { + listener.onCounterAdded("blah", counter); + + verifyZeroInteractions(counter); + } + + @Test + public void noOpsOnHistogramAdded() throws Exception { + listener.onHistogramAdded("blah", histogram); + + verifyZeroInteractions(histogram); + } + + @Test + public void noOpsOnMeterAdded() throws Exception { + listener.onMeterAdded("blah", meter); + + verifyZeroInteractions(meter); + } + + @Test + public void noOpsOnTimerAdded() throws Exception { + listener.onTimerAdded("blah", timer); + + verifyZeroInteractions(timer); + } + + @Test + public void doesNotExplodeWhenMetricsAreRemoved() throws Exception { + listener.onGaugeRemoved("blah"); + listener.onCounterRemoved("blah"); + listener.onHistogramRemoved("blah"); + listener.onMeterRemoved("blah"); + listener.onTimerRemoved("blah"); + } +} From ffeed35b063d9be1f3d69ff7d1272612f95b60db Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sun, 7 Apr 2013 10:10:21 -0700 Subject: [PATCH 0182/2558] Swap out some AtomicLongs for LongAdders. This allows us to trade a bit of memory for a huge improvement in the performance of contended counters and meters. Striped64 and LongAdder are from the JSR166e project led by Doug Lea and are in the public domain. They're intended to be included in JDK8, so Metrics should remove these copied implementations once it moves to JDK8. --- .../main/java/com/yammer/metrics/Counter.java | 12 +- .../main/java/com/yammer/metrics/EWMA.java | 7 +- .../java/com/yammer/metrics/Histogram.java | 10 +- .../java/com/yammer/metrics/LongAdder.java | 197 ++++++++++ .../main/java/com/yammer/metrics/Meter.java | 6 +- .../java/com/yammer/metrics/Striped64.java | 354 ++++++++++++++++++ .../com/yammer/metrics/ThreadLocalRandom.java | 2 +- 7 files changed, 567 insertions(+), 21 deletions(-) create mode 100644 metrics-core/src/main/java/com/yammer/metrics/LongAdder.java create mode 100644 metrics-core/src/main/java/com/yammer/metrics/Striped64.java diff --git a/metrics-core/src/main/java/com/yammer/metrics/Counter.java b/metrics-core/src/main/java/com/yammer/metrics/Counter.java index bb64dab710..ba4205b8d6 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Counter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Counter.java @@ -1,15 +1,13 @@ package com.yammer.metrics; -import java.util.concurrent.atomic.AtomicLong; - /** * An incrementing and decrementing counter metric. */ public class Counter implements Metric { - private final AtomicLong count; + private final LongAdder count; public Counter() { - this.count = new AtomicLong(0); + this.count = new LongAdder(); } /** @@ -25,7 +23,7 @@ public void inc() { * @param n the amount by which the counter will be increased */ public void inc(long n) { - count.addAndGet(n); + count.add(n); } /** @@ -41,7 +39,7 @@ public void dec() { * @param n the amount by which the counter will be increased */ public void dec(long n) { - count.addAndGet(0 - n); + count.add(-n); } /** @@ -50,6 +48,6 @@ public void dec(long n) { * @return the counter's current value */ public long getCount() { - return count.get(); + return count.sum(); } } diff --git a/metrics-core/src/main/java/com/yammer/metrics/EWMA.java b/metrics-core/src/main/java/com/yammer/metrics/EWMA.java index 4bef9f58da..4e33663904 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/EWMA.java +++ b/metrics-core/src/main/java/com/yammer/metrics/EWMA.java @@ -1,7 +1,6 @@ package com.yammer.metrics; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; import static java.lang.Math.exp; @@ -27,7 +26,7 @@ public class EWMA { private volatile boolean initialized = false; private volatile double rate = 0.0; - private final AtomicLong uncounted = new AtomicLong(); + private final LongAdder uncounted = new LongAdder(); private final double alpha, interval; /** @@ -78,14 +77,14 @@ public EWMA(double alpha, long interval, TimeUnit intervalUnit) { * @param n the new value */ public void update(long n) { - uncounted.addAndGet(n); + uncounted.add(n); } /** * Mark the passage of time and decay the current rate accordingly. */ public void tick() { - final long count = uncounted.getAndSet(0); + final long count = uncounted.sumThenReset(); final double instantRate = count / interval; if (initialized) { rate += (alpha * (instantRate - rate)); diff --git a/metrics-core/src/main/java/com/yammer/metrics/Histogram.java b/metrics-core/src/main/java/com/yammer/metrics/Histogram.java index b8329faa3b..5b6aa25daf 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Histogram.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Histogram.java @@ -1,7 +1,5 @@ package com.yammer.metrics; -import java.util.concurrent.atomic.AtomicLong; - /** * A metric which calculates the distribution of a value. * @@ -10,7 +8,7 @@ */ public class Histogram implements Metric, Sampling { private final Reservoir reservoir; - private final AtomicLong count; + private final LongAdder count; /** * Creates a new {@link Histogram} with the given reservoir. @@ -19,7 +17,7 @@ public class Histogram implements Metric, Sampling { */ public Histogram(Reservoir reservoir) { this.reservoir = reservoir; - this.count = new AtomicLong(0); + this.count = new LongAdder(); } /** @@ -37,7 +35,7 @@ public void update(int value) { * @param value the length of the value */ public void update(long value) { - count.incrementAndGet(); + count.increment(); reservoir.update(value); } @@ -47,7 +45,7 @@ public void update(long value) { * @return the number of values recorded */ public long getCount() { - return count.get(); + return count.sum(); } @Override diff --git a/metrics-core/src/main/java/com/yammer/metrics/LongAdder.java b/metrics-core/src/main/java/com/yammer/metrics/LongAdder.java new file mode 100644 index 0000000000..03df6b007a --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/LongAdder.java @@ -0,0 +1,197 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/LongAdder.java?revision=1.14&view=markup + */ + +package com.yammer.metrics; + +import java.util.concurrent.atomic.AtomicLong; +import java.io.Serializable; + +// CHECKSTYLE:OFF +/** + * One or more variables that together maintain an initially zero {@code long} sum. When updates + * (method {@link #add}) are contended across threads, the set of variables may grow dynamically to + * reduce contention. Method {@link #sum} (or, equivalently, {@link #longValue}) returns the current + * total combined across the variables maintaining the sum. + *

+ *

This class is usually preferable to {@link AtomicLong} when multiple threads update a common + * sum that is used for purposes such as collecting statistics, not for fine-grained synchronization + * control. Under low update contention, the two classes have similar characteristics. But under + * high contention, expected throughput of this class is significantly higher, at the expense of + * higher space consumption. + *

+ *

This class extends {@link Number}, but does not define methods such as {@code + * equals}, {@code hashCode} and {@code compareTo} because instances are expected to be mutated, and + * so are not useful as collection keys. + *

+ *

jsr166e note: This class is targeted to be placed in java.util.concurrent.atomic. + * + * @author Doug Lea + * @since 1.8 + */ +@SuppressWarnings("all") +class LongAdder extends Striped64 implements Serializable { + private static final long serialVersionUID = 7249069246863182397L; + + /** + * Version of plus for use in retryUpdate + */ + final long fn(long v, long x) { + return v + x; + } + + /** + * Creates a new adder with initial sum of zero. + */ + LongAdder() { + } + + /** + * Adds the given value. + * + * @param x the value to add + */ + public void add(long x) { + Cell[] as; + long b, v; + HashCode hc; + Cell a; + int n; + if ((as = cells) != null || !casBase(b = base, b + x)) { + boolean uncontended = true; + int h = (hc = threadHashCode.get()).code; + if (as == null || (n = as.length) < 1 || + (a = as[(n - 1) & h]) == null || + !(uncontended = a.cas(v = a.value, v + x))) + retryUpdate(x, hc, uncontended); + } + } + + /** + * Equivalent to {@code add(1)}. + */ + public void increment() { + add(1L); + } + + /** + * Equivalent to {@code add(-1)}. + */ + public void decrement() { + add(-1L); + } + + /** + * Returns the current sum. The returned value is NOT an atomic snapshot; invocation + * in the absence of concurrent updates returns an accurate result, but concurrent updates that + * occur while the sum is being calculated might not be incorporated. + * + * @return the sum + */ + public long sum() { + long sum = base; + Cell[] as = cells; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) + sum += a.value; + } + } + return sum; + } + + /** + * Resets variables maintaining the sum to zero. This method may be a useful alternative to + * creating a new adder, but is only effective if there are no concurrent updates. Because this + * method is intrinsically racy, it should only be used when it is known that no threads are + * concurrently updating. + */ + public void reset() { + internalReset(0L); + } + + /** + * Equivalent in effect to {@link #sum} followed by {@link #reset}. This method may apply for + * example during quiescent points between multithreaded computations. If there are updates + * concurrent with this method, the returned value is not guaranteed to be the final + * value occurring before the reset. + * + * @return the sum + */ + public long sumThenReset() { + long sum = base; + Cell[] as = cells; + base = 0L; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) { + sum += a.value; + a.value = 0L; + } + } + } + return sum; + } + + /** + * Returns the String representation of the {@link #sum}. + * + * @return the String representation of the {@link #sum} + */ + public String toString() { + return Long.toString(sum()); + } + + /** + * Equivalent to {@link #sum}. + * + * @return the sum + */ + public long longValue() { + return sum(); + } + + /** + * Returns the {@link #sum} as an {@code int} after a narrowing primitive conversion. + */ + public int intValue() { + return (int) sum(); + } + + /** + * Returns the {@link #sum} as a {@code float} after a widening primitive conversion. + */ + public float floatValue() { + return (float) sum(); + } + + /** + * Returns the {@link #sum} as a {@code double} after a widening primitive conversion. + */ + public double doubleValue() { + return (double) sum(); + } + + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + s.defaultWriteObject(); + s.writeLong(sum()); + } + + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + busy = 0; + cells = null; + base = s.readLong(); + } +} +// CHECKSTYLE:ON diff --git a/metrics-core/src/main/java/com/yammer/metrics/Meter.java b/metrics-core/src/main/java/com/yammer/metrics/Meter.java index f68cb5e752..b2df895918 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Meter.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Meter.java @@ -16,7 +16,7 @@ public class Meter implements Metered { private final EWMA m5Rate = EWMA.fiveMinuteEWMA(); private final EWMA m15Rate = EWMA.fifteenMinuteEWMA(); - private final AtomicLong count = new AtomicLong(); + private final LongAdder count = new LongAdder(); private final long startTime; private final AtomicLong lastTick; private final Clock clock; @@ -53,7 +53,7 @@ public void mark() { */ public void mark(long n) { tickIfNecessary(); - count.addAndGet(n); + count.add(n); m1Rate.update(n); m5Rate.update(n); m15Rate.update(n); @@ -75,7 +75,7 @@ private void tickIfNecessary() { @Override public long getCount() { - return count.get(); + return count.sum(); } @Override diff --git a/metrics-core/src/main/java/com/yammer/metrics/Striped64.java b/metrics-core/src/main/java/com/yammer/metrics/Striped64.java new file mode 100644 index 0000000000..ec768f0f21 --- /dev/null +++ b/metrics-core/src/main/java/com/yammer/metrics/Striped64.java @@ -0,0 +1,354 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/Striped64.java?revision=1.8&view=markup + */ + +package com.yammer.metrics; + +import java.util.Random; + +// CHECKSTYLE:OFF +/** + * A package-local class holding common representation and mechanics for classes supporting dynamic + * striping on 64bit values. The class extends Number so that concrete subclasses must publicly do + * so. + */ +@SuppressWarnings("all") +abstract class Striped64 extends Number { + /* + * This class maintains a lazily-initialized table of atomically + * updated variables, plus an extra "base" field. The table size + * is a power of two. Indexing uses masked per-thread hash codes. + * Nearly all declarations in this class are package-private, + * accessed directly by subclasses. + * + * Table entries are of class Cell; a variant of AtomicLong padded + * to reduce cache contention on most processors. Padding is + * overkill for most Atomics because they are usually irregularly + * scattered in memory and thus don't interfere much with each + * other. But Atomic objects residing in arrays will tend to be + * placed adjacent to each other, and so will most often share + * cache lines (with a huge negative performance impact) without + * this precaution. + * + * In part because Cells are relatively large, we avoid creating + * them until they are needed. When there is no contention, all + * updates are made to the base field. Upon first contention (a + * failed CAS on base update), the table is initialized to size 2. + * The table size is doubled upon further contention until + * reaching the nearest power of two greater than or equal to the + * number of CPUS. Table slots remain empty (null) until they are + * needed. + * + * A single spinlock ("busy") is used for initializing and + * resizing the table, as well as populating slots with new Cells. + * There is no need for a blocking lock; when the lock is not + * available, threads try other slots (or the base). During these + * retries, there is increased contention and reduced locality, + * which is still better than alternatives. + * + * Per-thread hash codes are initialized to random values. + * Contention and/or table collisions are indicated by failed + * CASes when performing an update operation (see method + * retryUpdate). Upon a collision, if the table size is less than + * the capacity, it is doubled in size unless some other thread + * holds the lock. If a hashed slot is empty, and lock is + * available, a new Cell is created. Otherwise, if the slot + * exists, a CAS is tried. Retries proceed by "double hashing", + * using a secondary hash (Marsaglia XorShift) to try to find a + * free slot. + * + * The table size is capped because, when there are more threads + * than CPUs, supposing that each thread were bound to a CPU, + * there would exist a perfect hash function mapping threads to + * slots that eliminates collisions. When we reach capacity, we + * search for this mapping by randomly varying the hash codes of + * colliding threads. Because search is random, and collisions + * only become known via CAS failures, convergence can be slow, + * and because threads are typically not bound to CPUS forever, + * may not occur at all. However, despite these limitations, + * observed contention rates are typically low in these cases. + * + * It is possible for a Cell to become unused when threads that + * once hashed to it terminate, as well as in the case where + * doubling the table causes no thread to hash to it under + * expanded mask. We do not try to detect or remove such cells, + * under the assumption that for long-running instances, observed + * contention levels will recur, so the cells will eventually be + * needed again; and for short-lived ones, it does not matter. + */ + + /** + * Padded variant of AtomicLong supporting only raw accesses plus CAS. The value field is placed + * between pads, hoping that the JVM doesn't reorder them. + *

+ * JVM intrinsics note: It would be possible to use a release-only form of CAS here, if it were + * provided. + */ + static final class Cell { + volatile long p0, p1, p2, p3, p4, p5, p6; + volatile long value; + volatile long q0, q1, q2, q3, q4, q5, q6; + + Cell(long x) { + value = x; + } + + final boolean cas(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long valueOffset; + + static { + try { + UNSAFE = getUnsafe(); + Class ak = Cell.class; + valueOffset = UNSAFE.objectFieldOffset + (ak.getDeclaredField("value")); + } catch (Exception e) { + throw new Error(e); + } + } + + } + + /** + * Holder for the thread-local hash code. The code is initially random, but may be set to a + * different value upon collisions. + */ + static final class HashCode { + static final Random rng = new Random(); + int code; + + HashCode() { + int h = rng.nextInt(); // Avoid zero to allow xorShift rehash + code = (h == 0) ? 1 : h; + } + } + + /** + * The corresponding ThreadLocal class + */ + static final class ThreadHashCode extends ThreadLocal { + public HashCode initialValue() { + return new HashCode(); + } + } + + /** + * Static per-thread hash codes. Shared across all instances to reduce ThreadLocal pollution and + * because adjustments due to collisions in one table are likely to be appropriate for others. + */ + static final ThreadHashCode threadHashCode = new ThreadHashCode(); + + /** + * Number of CPUS, to place bound on table size + */ + static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** + * Table of cells. When non-null, size is a power of 2. + */ + transient volatile Cell[] cells; + + /** + * Base value, used mainly when there is no contention, but also as a fallback during table + * initialization races. Updated via CAS. + */ + transient volatile long base; + + /** + * Spinlock (locked via CAS) used when resizing and/or creating Cells. + */ + transient volatile int busy; + + /** + * Package-private default constructor + */ + Striped64() { + } + + /** + * CASes the base field. + */ + final boolean casBase(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, baseOffset, cmp, val); + } + + /** + * CASes the busy field from 0 to 1 to acquire lock. + */ + final boolean casBusy() { + return UNSAFE.compareAndSwapInt(this, busyOffset, 0, 1); + } + + /** + * Computes the function of current and new value. Subclasses should open-code this update + * function for most uses, but the virtualized form is needed within retryUpdate. + * + * @param currentValue the current value (of either base or a cell) + * @param newValue the argument from a user update call + * @return result of the update function + */ + abstract long fn(long currentValue, long newValue); + + /** + * Handles cases of updates involving initialization, resizing, creating new Cells, and/or + * contention. See above for explanation. This method suffers the usual non-modularity problems + * of optimistic retry code, relying on rechecked sets of reads. + * + * @param x the value + * @param hc the hash code holder + * @param wasUncontended false if CAS failed before call + */ + final void retryUpdate(long x, HashCode hc, boolean wasUncontended) { + int h = hc.code; + boolean collide = false; // True if last slot nonempty + for (; ; ) { + Cell[] as; + Cell a; + int n; + long v; + if ((as = cells) != null && (n = as.length) > 0) { + if ((a = as[(n - 1) & h]) == null) { + if (busy == 0) { // Try to attach new Cell + Cell r = new Cell(x); // Optimistically create + if (busy == 0 && casBusy()) { + boolean created = false; + try { // Recheck under lock + Cell[] rs; + int m, j; + if ((rs = cells) != null && + (m = rs.length) > 0 && + rs[j = (m - 1) & h] == null) { + rs[j] = r; + created = true; + } + } finally { + busy = 0; + } + if (created) + break; + continue; // Slot is now non-empty + } + } + collide = false; + } else if (!wasUncontended) // CAS already known to fail + wasUncontended = true; // Continue after rehash + else if (a.cas(v = a.value, fn(v, x))) + break; + else if (n >= NCPU || cells != as) + collide = false; // At max size or stale + else if (!collide) + collide = true; + else if (busy == 0 && casBusy()) { + try { + if (cells == as) { // Expand table unless stale + Cell[] rs = new Cell[n << 1]; + for (int i = 0; i < n; ++i) + rs[i] = as[i]; + cells = rs; + } + } finally { + busy = 0; + } + collide = false; + continue; // Retry with expanded table + } + h ^= h << 13; // Rehash + h ^= h >>> 17; + h ^= h << 5; + } else if (busy == 0 && cells == as && casBusy()) { + boolean init = false; + try { // Initialize table + if (cells == as) { + Cell[] rs = new Cell[2]; + rs[h & 1] = new Cell(x); + cells = rs; + init = true; + } + } finally { + busy = 0; + } + if (init) + break; + } else if (casBase(v = base, fn(v, x))) + break; // Fall back on using base + } + hc.code = h; // Record index for next time + } + + + /** + * Sets base and all cells to the given value. + */ + final void internalReset(long initialValue) { + Cell[] as = cells; + base = initialValue; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) + a.value = initialValue; + } + } + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long baseOffset; + private static final long busyOffset; + + static { + try { + UNSAFE = getUnsafe(); + Class sk = Striped64.class; + baseOffset = UNSAFE.objectFieldOffset + (sk.getDeclaredField("base")); + busyOffset = UNSAFE.objectFieldOffset + (sk.getDeclaredField("busy")); + } catch (Exception e) { + throw new Error(e); + } + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. Replace with a simple + * call to Unsafe.getUnsafe when integrating into a jdk. + * + * @return a sun.misc.Unsafe + */ + private static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } catch (SecurityException ignored) { + + } + try { + return java.security.AccessController.doPrivileged + (new java.security.PrivilegedExceptionAction() { + public sun.misc.Unsafe run() throws Exception { + Class k = sun.misc.Unsafe.class; + for (java.lang.reflect.Field f : k.getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if (k.isInstance(x)) + return k.cast(x); + } + throw new NoSuchFieldError("the Unsafe"); + } + }); + } catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", + e.getCause()); + } + } +} +// CHECKSTYLE:ON diff --git a/metrics-core/src/main/java/com/yammer/metrics/ThreadLocalRandom.java b/metrics-core/src/main/java/com/yammer/metrics/ThreadLocalRandom.java index 876a6793e4..28c3165ce0 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/ThreadLocalRandom.java +++ b/metrics-core/src/main/java/com/yammer/metrics/ThreadLocalRandom.java @@ -14,7 +14,7 @@ /** * Copied directly from the JSR-166 project. */ -@SuppressWarnings("UnusedDeclaration") +@SuppressWarnings("all") class ThreadLocalRandom extends Random { // same constants as Random, but must be redeclared because private private static final long multiplier = 0x5DEECE66DL; From d6c5095b3145e2ee7cfd67067110bda25d663d5e Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 8 Apr 2013 11:13:48 -0700 Subject: [PATCH 0183/2558] Make map-building in MetricRegistry overrideable. Closes #373. --- .../java/com/yammer/metrics/MetricRegistry.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java index 20ee6077fd..e373d40b57 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -53,10 +53,21 @@ private static void append(StringBuilder builder, String part) { * Creates a new {@link MetricRegistry}. */ public MetricRegistry() { - this.metrics = new ConcurrentHashMap(); + this.metrics = buildMap(); this.listeners = new CopyOnWriteArrayList(); } + /** + * Creates a new {@link ConcurrentMap} implementation for use inside the registry. Override this + * to create a {@link MetricRegistry} with space- or time-bounded metric lifecycles, for + * example. + * + * @return a new {@link ConcurrentMap} + */ + protected ConcurrentMap buildMap() { + return new ConcurrentHashMap(); + } + /** * Given a {@link Metric}, registers it under the given name. * From 03a67d96a541cab131f2ab714dcf77b10a723fb7 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 8 Apr 2013 11:30:54 -0700 Subject: [PATCH 0184/2558] Move all the tests out of test packages. No one seems to want to do this, including my own IDE. --- .../src/main/java/com/yammer/metrics/LongAdder.java | 2 +- .../src/main/java/com/yammer/metrics/Snapshot.java | 7 +++++-- .../com/yammer/metrics/{tests => }/CachedGaugeTest.java | 4 +--- .../java/com/yammer/metrics/{tests => }/ClockTest.java | 3 +-- .../yammer/metrics/{tests => }/ConsoleReporterTest.java | 3 +-- .../java/com/yammer/metrics/{tests => }/CounterTest.java | 3 +-- .../com/yammer/metrics/{tests => }/CsvReporterTest.java | 3 +-- .../yammer/metrics/{tests => }/DerivativeGaugeTest.java | 4 +--- .../java/com/yammer/metrics/{tests => }/EWMATest.java | 3 +-- .../{tests => }/ExponentiallyDecayingReservoirTest.java | 5 +---- .../com/yammer/metrics/{tests => }/HistogramTest.java | 5 +---- .../yammer/metrics/{tests => }/JmxAttributeGaugeTest.java | 3 +-- .../com/yammer/metrics/{tests => }/JmxReporterTest.java | 7 ++----- .../metrics/{tests => }/JvmAttributeGaugeSetTest.java | 4 +--- .../java/com/yammer/metrics/{tests => }/MeterTest.java | 4 +--- .../com/yammer/metrics/{tests => }/MetricFilterTest.java | 4 +--- .../metrics/{tests => }/MetricRegistryListenerTest.java | 3 +-- .../yammer/metrics/{tests => }/MetricRegistryTest.java | 7 +++---- .../com/yammer/metrics/{tests => }/RatioGaugeTest.java | 3 +-- .../yammer/metrics/{tests => }/ScheduledReporterTest.java | 3 +-- .../com/yammer/metrics/{tests => }/Slf4jReporterTest.java | 7 ++----- .../{tests => }/SlidingTimeWindowReservoirTest.java | 4 +--- .../metrics/{tests => }/SlidingWindowReservoirTest.java | 3 +-- .../java/com/yammer/metrics/{tests => }/SnapshotTest.java | 3 +-- .../java/com/yammer/metrics/{tests => }/TimerTest.java | 6 +----- .../yammer/metrics/{tests => }/UniformReservoirTest.java | 4 +--- .../com/yammer/metrics/benchmarks/ReservoirBenchmark.java | 3 ++- .../ehcache/{tests => }/InstrumentedEhcacheTest.java | 3 +-- .../metrics/ganglia/{tests => }/GangliaReporterTest.java | 3 +-- .../graphite/{tests => }/GraphiteReporterTest.java | 4 +--- .../yammer/metrics/graphite/{tests => }/GraphiteTest.java | 3 +-- .../health/{tests => }/HealthCheckRegistryTest.java | 8 ++------ .../metrics/health/{tests => }/HealthCheckTest.java | 3 +-- .../jvm/{tests => }/ThreadDeadlockHealthCheckTest.java | 3 +-- .../{tests => }/InstrumentedHttpClientTest.java | 4 +--- .../jdbi/{tests => }/InstrumentedTimingCollectorTest.java | 3 +-- .../jersey/{tests => }/SingletonMetricsJerseyTest.java | 5 ++--- .../{tests => }/resources/InstrumentedResource.java | 2 +- .../metrics/json/{tests => }/HealthCheckModuleTest.java | 3 +-- .../metrics/json/{tests => }/MetricsModuleTest.java | 3 +-- .../com/yammer/metrics/jvm/ThreadDeadlockDetector.java | 3 +-- .../metrics/jvm/{tests => }/BufferPoolMetricSetTest.java | 3 +-- .../jvm/{tests => }/FileDescriptorRatioGaugeTest.java | 3 +-- .../jvm/{tests => }/GarbageCollectorMetricSetTest.java | 3 +-- .../metrics/jvm/{tests => }/MemoryUsageGaugeSetTest.java | 3 +-- .../jvm/{tests => }/ThreadDeadlockDetectorTest.java | 3 +-- .../yammer/metrics/jvm/{tests => }/ThreadDumpTest.java | 3 +-- .../metrics/jvm/{tests => }/ThreadStatesGaugeSetTest.java | 4 +--- .../log4j/{tests => }/InstrumentedAppenderTest.java | 3 +-- .../logback/{tests => }/InstrumentedAppenderTest.java | 3 +-- .../metrics/servlets/{tests => }/AbstractServletTest.java | 2 +- .../metrics/servlets/{tests => }/AdminServletTest.java | 3 +-- .../servlets/{tests => }/HealthCheckServletTest.java | 3 +-- .../metrics/servlets/{tests => }/MetricsServletTest.java | 3 +-- .../metrics/servlets/{tests => }/PingServletTest.java | 3 +-- .../servlets/{tests => }/ThreadDumpServletTest.java | 3 +-- 56 files changed, 67 insertions(+), 138 deletions(-) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/CachedGaugeTest.java (90%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/ClockTest.java (95%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/ConsoleReporterTest.java (99%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/CounterTest.java (93%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/CsvReporterTest.java (99%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/DerivativeGaugeTest.java (85%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/EWMATest.java (99%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/ExponentiallyDecayingReservoirTest.java (96%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/HistogramTest.java (86%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/JmxAttributeGaugeTest.java (93%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/JmxReporterTest.java (97%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/JvmAttributeGaugeSetTest.java (94%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/MeterTest.java (94%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/MetricFilterTest.java (75%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/MetricRegistryListenerTest.java (96%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/MetricRegistryTest.java (97%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/RatioGaugeTest.java (96%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/ScheduledReporterTest.java (97%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/Slf4jReporterTest.java (97%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/SlidingTimeWindowReservoirTest.java (91%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/SlidingWindowReservoirTest.java (89%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/SnapshotTest.java (98%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/TimerTest.java (93%) rename metrics-core/src/test/java/com/yammer/metrics/{tests => }/UniformReservoirTest.java (87%) rename metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/{tests => }/InstrumentedEhcacheTest.java (93%) rename metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/{tests => }/GangliaReporterTest.java (99%) rename metrics-graphite/src/test/java/com/yammer/metrics/graphite/{tests => }/GraphiteReporterTest.java (98%) rename metrics-graphite/src/test/java/com/yammer/metrics/graphite/{tests => }/GraphiteTest.java (96%) rename metrics-healthchecks/src/test/java/com/yammer/metrics/health/{tests => }/HealthCheckRegistryTest.java (89%) rename metrics-healthchecks/src/test/java/com/yammer/metrics/health/{tests => }/HealthCheckTest.java (97%) rename metrics-healthchecks/src/test/java/com/yammer/metrics/health/jvm/{tests => }/ThreadDeadlockHealthCheckTest.java (94%) rename metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/{tests => }/InstrumentedHttpClientTest.java (76%) rename metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/{tests => }/InstrumentedTimingCollectorTest.java (99%) rename metrics-jersey/src/test/java/com/yammer/metrics/jersey/{tests => }/SingletonMetricsJerseyTest.java (94%) rename metrics-jersey/src/test/java/com/yammer/metrics/jersey/{tests => }/resources/InstrumentedResource.java (94%) rename metrics-json/src/test/java/com/yammer/metrics/json/{tests => }/HealthCheckModuleTest.java (97%) rename metrics-json/src/test/java/com/yammer/metrics/json/{tests => }/MetricsModuleTest.java (99%) rename metrics-jvm/src/test/java/com/yammer/metrics/jvm/{tests => }/BufferPoolMetricSetTest.java (97%) rename metrics-jvm/src/test/java/com/yammer/metrics/jvm/{tests => }/FileDescriptorRatioGaugeTest.java (95%) rename metrics-jvm/src/test/java/com/yammer/metrics/jvm/{tests => }/GarbageCollectorMetricSetTest.java (94%) rename metrics-jvm/src/test/java/com/yammer/metrics/jvm/{tests => }/MemoryUsageGaugeSetTest.java (98%) rename metrics-jvm/src/test/java/com/yammer/metrics/jvm/{tests => }/ThreadDeadlockDetectorTest.java (96%) rename metrics-jvm/src/test/java/com/yammer/metrics/jvm/{tests => }/ThreadDumpTest.java (95%) rename metrics-jvm/src/test/java/com/yammer/metrics/jvm/{tests => }/ThreadStatesGaugeSetTest.java (96%) rename metrics-log4j/src/test/java/com/yammer/metrics/log4j/{tests => }/InstrumentedAppenderTest.java (96%) rename metrics-logback/src/test/java/com/yammer/metrics/logback/{tests => }/InstrumentedAppenderTest.java (96%) rename metrics-servlets/src/test/java/com/yammer/metrics/servlets/{tests => }/AbstractServletTest.java (94%) rename metrics-servlets/src/test/java/com/yammer/metrics/servlets/{tests => }/AdminServletTest.java (96%) rename metrics-servlets/src/test/java/com/yammer/metrics/servlets/{tests => }/HealthCheckServletTest.java (97%) rename metrics-servlets/src/test/java/com/yammer/metrics/servlets/{tests => }/MetricsServletTest.java (98%) rename metrics-servlets/src/test/java/com/yammer/metrics/servlets/{tests => }/PingServletTest.java (93%) rename metrics-servlets/src/test/java/com/yammer/metrics/servlets/{tests => }/ThreadDumpServletTest.java (92%) diff --git a/metrics-core/src/main/java/com/yammer/metrics/LongAdder.java b/metrics-core/src/main/java/com/yammer/metrics/LongAdder.java index 03df6b007a..00f1b44755 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/LongAdder.java +++ b/metrics-core/src/main/java/com/yammer/metrics/LongAdder.java @@ -8,8 +8,8 @@ package com.yammer.metrics; -import java.util.concurrent.atomic.AtomicLong; import java.io.Serializable; +import java.util.concurrent.atomic.AtomicLong; // CHECKSTYLE:OFF /** diff --git a/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java b/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java index 68fe12d449..9a4396e028 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java +++ b/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java @@ -1,8 +1,11 @@ package com.yammer.metrics; -import java.io.*; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; import java.nio.charset.Charset; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; import static java.lang.Math.floor; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/CachedGaugeTest.java b/metrics-core/src/test/java/com/yammer/metrics/CachedGaugeTest.java similarity index 90% rename from metrics-core/src/test/java/com/yammer/metrics/tests/CachedGaugeTest.java rename to metrics-core/src/test/java/com/yammer/metrics/CachedGaugeTest.java index e48664b57a..bef87eaa1b 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/CachedGaugeTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/CachedGaugeTest.java @@ -1,7 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.CachedGauge; -import com.yammer.metrics.Gauge; import org.junit.Test; import java.util.concurrent.TimeUnit; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/ClockTest.java b/metrics-core/src/test/java/com/yammer/metrics/ClockTest.java similarity index 95% rename from metrics-core/src/test/java/com/yammer/metrics/tests/ClockTest.java rename to metrics-core/src/test/java/com/yammer/metrics/ClockTest.java index d1b16bc94c..a61a16f8d8 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/ClockTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/ClockTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.Clock; import org.junit.Test; import java.lang.management.ManagementFactory; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/ConsoleReporterTest.java similarity index 99% rename from metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java rename to metrics-core/src/test/java/com/yammer/metrics/ConsoleReporterTest.java index 10575e890e..6382631aba 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/ConsoleReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/ConsoleReporterTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.*; import org.junit.Before; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/CounterTest.java b/metrics-core/src/test/java/com/yammer/metrics/CounterTest.java similarity index 93% rename from metrics-core/src/test/java/com/yammer/metrics/tests/CounterTest.java rename to metrics-core/src/test/java/com/yammer/metrics/CounterTest.java index 73a1180a61..1a56eaf7bd 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/CounterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/CounterTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.Counter; import org.junit.Test; import static org.fest.assertions.api.Assertions.assertThat; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/CsvReporterTest.java similarity index 99% rename from metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java rename to metrics-core/src/test/java/com/yammer/metrics/CsvReporterTest.java index 077026ce24..1d8b4309dd 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/CsvReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/CsvReporterTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.*; import org.junit.Before; import org.junit.Rule; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/DerivativeGaugeTest.java b/metrics-core/src/test/java/com/yammer/metrics/DerivativeGaugeTest.java similarity index 85% rename from metrics-core/src/test/java/com/yammer/metrics/tests/DerivativeGaugeTest.java rename to metrics-core/src/test/java/com/yammer/metrics/DerivativeGaugeTest.java index 6b129f2595..572154cf2d 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/DerivativeGaugeTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/DerivativeGaugeTest.java @@ -1,7 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.DerivativeGauge; -import com.yammer.metrics.Gauge; import org.junit.Test; import static org.fest.assertions.api.Assertions.assertThat; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/EWMATest.java b/metrics-core/src/test/java/com/yammer/metrics/EWMATest.java similarity index 99% rename from metrics-core/src/test/java/com/yammer/metrics/tests/EWMATest.java rename to metrics-core/src/test/java/com/yammer/metrics/EWMATest.java index 5a283277c4..f5ae41915e 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/EWMATest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/EWMATest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.EWMA; import org.junit.Test; import java.util.concurrent.TimeUnit; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/ExponentiallyDecayingReservoirTest.java b/metrics-core/src/test/java/com/yammer/metrics/ExponentiallyDecayingReservoirTest.java similarity index 96% rename from metrics-core/src/test/java/com/yammer/metrics/tests/ExponentiallyDecayingReservoirTest.java rename to metrics-core/src/test/java/com/yammer/metrics/ExponentiallyDecayingReservoirTest.java index c19b48e53c..4fde5fa6cd 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/ExponentiallyDecayingReservoirTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/ExponentiallyDecayingReservoirTest.java @@ -1,8 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.Clock; -import com.yammer.metrics.ExponentiallyDecayingReservoir; -import com.yammer.metrics.Snapshot; import org.junit.Test; import java.util.concurrent.TimeUnit; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java b/metrics-core/src/test/java/com/yammer/metrics/HistogramTest.java similarity index 86% rename from metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java rename to metrics-core/src/test/java/com/yammer/metrics/HistogramTest.java index 7e1f868455..5c07fa3c33 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/HistogramTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/HistogramTest.java @@ -1,8 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.Histogram; -import com.yammer.metrics.Reservoir; -import com.yammer.metrics.Snapshot; import org.junit.Test; import static org.fest.assertions.api.Assertions.assertThat; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/JmxAttributeGaugeTest.java b/metrics-core/src/test/java/com/yammer/metrics/JmxAttributeGaugeTest.java similarity index 93% rename from metrics-core/src/test/java/com/yammer/metrics/tests/JmxAttributeGaugeTest.java rename to metrics-core/src/test/java/com/yammer/metrics/JmxAttributeGaugeTest.java index b5918b248e..f8005c3cd5 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/JmxAttributeGaugeTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/JmxAttributeGaugeTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.JmxAttributeGauge; import org.junit.Test; import javax.management.AttributeNotFoundException; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/JmxReporterTest.java similarity index 97% rename from metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java rename to metrics-core/src/test/java/com/yammer/metrics/JmxReporterTest.java index 71028604b2..4dae099655 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/JmxReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/JmxReporterTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.*; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -12,9 +11,7 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; -import static org.fest.assertions.api.Assertions.assertThat; -import static org.fest.assertions.api.Assertions.entry; -import static org.fest.assertions.api.Assertions.failBecauseExceptionWasNotThrown; +import static org.fest.assertions.api.Assertions.*; import static org.mockito.Mockito.*; public class JmxReporterTest { diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/JvmAttributeGaugeSetTest.java b/metrics-core/src/test/java/com/yammer/metrics/JvmAttributeGaugeSetTest.java similarity index 94% rename from metrics-core/src/test/java/com/yammer/metrics/tests/JvmAttributeGaugeSetTest.java rename to metrics-core/src/test/java/com/yammer/metrics/JvmAttributeGaugeSetTest.java index 527c4eaaad..7113991d22 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/JvmAttributeGaugeSetTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/JvmAttributeGaugeSetTest.java @@ -1,7 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.Gauge; -import com.yammer.metrics.JvmAttributeGaugeSet; import org.junit.Before; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MeterTest.java b/metrics-core/src/test/java/com/yammer/metrics/MeterTest.java similarity index 94% rename from metrics-core/src/test/java/com/yammer/metrics/tests/MeterTest.java rename to metrics-core/src/test/java/com/yammer/metrics/MeterTest.java index 2389390f3f..3df4cf45d6 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/MeterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/MeterTest.java @@ -1,7 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.Clock; -import com.yammer.metrics.Meter; import org.junit.Before; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricFilterTest.java b/metrics-core/src/test/java/com/yammer/metrics/MetricFilterTest.java similarity index 75% rename from metrics-core/src/test/java/com/yammer/metrics/tests/MetricFilterTest.java rename to metrics-core/src/test/java/com/yammer/metrics/MetricFilterTest.java index 00ae25de7c..fd0ce6327a 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricFilterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/MetricFilterTest.java @@ -1,7 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.Metric; -import com.yammer.metrics.MetricFilter; import org.junit.Test; import static org.fest.assertions.api.Assertions.assertThat; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryListenerTest.java b/metrics-core/src/test/java/com/yammer/metrics/MetricRegistryListenerTest.java similarity index 96% rename from metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryListenerTest.java rename to metrics-core/src/test/java/com/yammer/metrics/MetricRegistryListenerTest.java index e2a2c4ff6f..cecad65f0b 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryListenerTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/MetricRegistryListenerTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.*; import org.junit.Test; import static org.mockito.Mockito.mock; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java b/metrics-core/src/test/java/com/yammer/metrics/MetricRegistryTest.java similarity index 97% rename from metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java rename to metrics-core/src/test/java/com/yammer/metrics/MetricRegistryTest.java index 8cf749f985..eeedf3b42a 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/MetricRegistryTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/MetricRegistryTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.*; import org.junit.Before; import org.junit.Test; @@ -324,7 +323,7 @@ public void elidesEmptyStringsFromNames() throws Exception { @Test public void concatenatesClassNamesWithStringsToFormADottedName() throws Exception { assertThat(name(MetricRegistryTest.class, "one", "two")) - .isEqualTo("com.yammer.metrics.tests.MetricRegistryTest.one.two"); + .isEqualTo("com.yammer.metrics.MetricRegistryTest.one.two"); } @Test @@ -337,6 +336,6 @@ public String getValue() { }; assertThat(name(g.getClass(), "one", "two")) - .isEqualTo("com.yammer.metrics.tests.MetricRegistryTest$5.one.two"); + .isEqualTo("com.yammer.metrics.MetricRegistryTest$5.one.two"); } } diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/RatioGaugeTest.java b/metrics-core/src/test/java/com/yammer/metrics/RatioGaugeTest.java similarity index 96% rename from metrics-core/src/test/java/com/yammer/metrics/tests/RatioGaugeTest.java rename to metrics-core/src/test/java/com/yammer/metrics/RatioGaugeTest.java index 4508328797..47d571ea4d 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/RatioGaugeTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/RatioGaugeTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.RatioGauge; import org.junit.Test; import static org.fest.assertions.api.Assertions.assertThat; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/ScheduledReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/ScheduledReporterTest.java similarity index 97% rename from metrics-core/src/test/java/com/yammer/metrics/tests/ScheduledReporterTest.java rename to metrics-core/src/test/java/com/yammer/metrics/ScheduledReporterTest.java index 5035fec631..7e529ea9c9 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/ScheduledReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/ScheduledReporterTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.*; import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java b/metrics-core/src/test/java/com/yammer/metrics/Slf4jReporterTest.java similarity index 97% rename from metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java rename to metrics-core/src/test/java/com/yammer/metrics/Slf4jReporterTest.java index bc63a3bc7f..0b4f321475 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/Slf4jReporterTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/Slf4jReporterTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.*; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.Marker; @@ -9,9 +8,7 @@ import java.util.TreeMap; import java.util.concurrent.TimeUnit; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; public class Slf4jReporterTest { private final Logger logger = mock(Logger.class); diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingTimeWindowReservoirTest.java b/metrics-core/src/test/java/com/yammer/metrics/SlidingTimeWindowReservoirTest.java similarity index 91% rename from metrics-core/src/test/java/com/yammer/metrics/tests/SlidingTimeWindowReservoirTest.java rename to metrics-core/src/test/java/com/yammer/metrics/SlidingTimeWindowReservoirTest.java index 03617f8eaa..5f897aebef 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingTimeWindowReservoirTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/SlidingTimeWindowReservoirTest.java @@ -1,7 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.Clock; -import com.yammer.metrics.SlidingTimeWindowReservoir; import org.junit.Test; import java.util.concurrent.TimeUnit; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingWindowReservoirTest.java b/metrics-core/src/test/java/com/yammer/metrics/SlidingWindowReservoirTest.java similarity index 89% rename from metrics-core/src/test/java/com/yammer/metrics/tests/SlidingWindowReservoirTest.java rename to metrics-core/src/test/java/com/yammer/metrics/SlidingWindowReservoirTest.java index 156954f2c3..23935ae3c5 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/SlidingWindowReservoirTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/SlidingWindowReservoirTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.SlidingWindowReservoir; import org.junit.Test; import static org.fest.assertions.api.Assertions.assertThat; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java b/metrics-core/src/test/java/com/yammer/metrics/SnapshotTest.java similarity index 98% rename from metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java rename to metrics-core/src/test/java/com/yammer/metrics/SnapshotTest.java index 80b06769cc..5dfd528d9e 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/SnapshotTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/SnapshotTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.Snapshot; import org.junit.Test; import java.io.ByteArrayOutputStream; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java b/metrics-core/src/test/java/com/yammer/metrics/TimerTest.java similarity index 93% rename from metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java rename to metrics-core/src/test/java/com/yammer/metrics/TimerTest.java index f16eae92f9..fbb4686278 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/TimerTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/TimerTest.java @@ -1,9 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.Clock; -import com.yammer.metrics.Reservoir; -import com.yammer.metrics.Snapshot; -import com.yammer.metrics.Timer; import org.junit.Test; import java.util.concurrent.Callable; diff --git a/metrics-core/src/test/java/com/yammer/metrics/tests/UniformReservoirTest.java b/metrics-core/src/test/java/com/yammer/metrics/UniformReservoirTest.java similarity index 87% rename from metrics-core/src/test/java/com/yammer/metrics/tests/UniformReservoirTest.java rename to metrics-core/src/test/java/com/yammer/metrics/UniformReservoirTest.java index 3d692cd678..43c5dd28b4 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/tests/UniformReservoirTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/UniformReservoirTest.java @@ -1,7 +1,5 @@ -package com.yammer.metrics.tests; +package com.yammer.metrics; -import com.yammer.metrics.Snapshot; -import com.yammer.metrics.UniformReservoir; import org.junit.Test; import static org.fest.assertions.api.Assertions.assertThat; diff --git a/metrics-core/src/test/java/com/yammer/metrics/benchmarks/ReservoirBenchmark.java b/metrics-core/src/test/java/com/yammer/metrics/benchmarks/ReservoirBenchmark.java index 00b085ae64..a2f0788eef 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/benchmarks/ReservoirBenchmark.java +++ b/metrics-core/src/test/java/com/yammer/metrics/benchmarks/ReservoirBenchmark.java @@ -2,9 +2,10 @@ import com.google.caliper.Runner; import com.google.caliper.SimpleBenchmark; -import com.yammer.metrics.*; import com.yammer.metrics.ExponentiallyDecayingReservoir; +import com.yammer.metrics.SlidingTimeWindowReservoir; import com.yammer.metrics.SlidingWindowReservoir; +import com.yammer.metrics.UniformReservoir; import java.util.concurrent.TimeUnit; diff --git a/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/InstrumentedEhcacheTest.java b/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/InstrumentedEhcacheTest.java similarity index 93% rename from metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/InstrumentedEhcacheTest.java rename to metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/InstrumentedEhcacheTest.java index d1dc61fc5e..083f150e47 100644 --- a/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/tests/InstrumentedEhcacheTest.java +++ b/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/InstrumentedEhcacheTest.java @@ -1,8 +1,7 @@ -package com.yammer.metrics.ehcache.tests; +package com.yammer.metrics.ehcache; import com.yammer.metrics.MetricRegistry; import com.yammer.metrics.Timer; -import com.yammer.metrics.ehcache.InstrumentedEhcache; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Ehcache; diff --git a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java b/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaReporterTest.java similarity index 99% rename from metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java rename to metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaReporterTest.java index 26dc34f802..15268890b2 100644 --- a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/tests/GangliaReporterTest.java +++ b/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaReporterTest.java @@ -1,7 +1,6 @@ -package com.yammer.metrics.ganglia.tests; +package com.yammer.metrics.ganglia; import com.yammer.metrics.*; -import com.yammer.metrics.ganglia.GangliaReporter; import info.ganglia.gmetric4j.gmetric.GMetric; import info.ganglia.gmetric4j.gmetric.GMetricSlope; import info.ganglia.gmetric4j.gmetric.GMetricType; diff --git a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/GraphiteReporterTest.java similarity index 98% rename from metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java rename to metrics-graphite/src/test/java/com/yammer/metrics/graphite/GraphiteReporterTest.java index ce3bd7aac0..22c70aa458 100644 --- a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteReporterTest.java +++ b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/GraphiteReporterTest.java @@ -1,8 +1,6 @@ -package com.yammer.metrics.graphite.tests; +package com.yammer.metrics.graphite; import com.yammer.metrics.*; -import com.yammer.metrics.graphite.Graphite; -import com.yammer.metrics.graphite.GraphiteReporter; import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; diff --git a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteTest.java b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/GraphiteTest.java similarity index 96% rename from metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteTest.java rename to metrics-graphite/src/test/java/com/yammer/metrics/graphite/GraphiteTest.java index 3be1dbf406..46fb76f289 100644 --- a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/tests/GraphiteTest.java +++ b/metrics-graphite/src/test/java/com/yammer/metrics/graphite/GraphiteTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.graphite.tests; +package com.yammer.metrics.graphite; -import com.yammer.metrics.graphite.Graphite; import org.junit.Before; import org.junit.Test; diff --git a/metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckRegistryTest.java b/metrics-healthchecks/src/test/java/com/yammer/metrics/health/HealthCheckRegistryTest.java similarity index 89% rename from metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckRegistryTest.java rename to metrics-healthchecks/src/test/java/com/yammer/metrics/health/HealthCheckRegistryTest.java index 9610527ef7..6789396b9d 100644 --- a/metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckRegistryTest.java +++ b/metrics-healthchecks/src/test/java/com/yammer/metrics/health/HealthCheckRegistryTest.java @@ -1,7 +1,5 @@ -package com.yammer.metrics.health.tests; +package com.yammer.metrics.health; -import com.yammer.metrics.health.HealthCheck; -import com.yammer.metrics.health.HealthCheckRegistry; import org.junit.Before; import org.junit.Test; @@ -11,9 +9,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import static org.fest.assertions.api.Assertions.assertThat; -import static org.fest.assertions.api.Assertions.entry; -import static org.fest.assertions.api.Assertions.failBecauseExceptionWasNotThrown; +import static org.fest.assertions.api.Assertions.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckTest.java b/metrics-healthchecks/src/test/java/com/yammer/metrics/health/HealthCheckTest.java similarity index 97% rename from metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckTest.java rename to metrics-healthchecks/src/test/java/com/yammer/metrics/health/HealthCheckTest.java index f56f9c7712..062a09ec8c 100644 --- a/metrics-healthchecks/src/test/java/com/yammer/metrics/health/tests/HealthCheckTest.java +++ b/metrics-healthchecks/src/test/java/com/yammer/metrics/health/HealthCheckTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.health.tests; +package com.yammer.metrics.health; -import com.yammer.metrics.health.HealthCheck; import org.junit.Test; import static org.fest.assertions.api.Assertions.assertThat; diff --git a/metrics-healthchecks/src/test/java/com/yammer/metrics/health/jvm/tests/ThreadDeadlockHealthCheckTest.java b/metrics-healthchecks/src/test/java/com/yammer/metrics/health/jvm/ThreadDeadlockHealthCheckTest.java similarity index 94% rename from metrics-healthchecks/src/test/java/com/yammer/metrics/health/jvm/tests/ThreadDeadlockHealthCheckTest.java rename to metrics-healthchecks/src/test/java/com/yammer/metrics/health/jvm/ThreadDeadlockHealthCheckTest.java index a8acee18f9..02909373cb 100644 --- a/metrics-healthchecks/src/test/java/com/yammer/metrics/health/jvm/tests/ThreadDeadlockHealthCheckTest.java +++ b/metrics-healthchecks/src/test/java/com/yammer/metrics/health/jvm/ThreadDeadlockHealthCheckTest.java @@ -1,7 +1,6 @@ -package com.yammer.metrics.health.jvm.tests; +package com.yammer.metrics.health.jvm; import com.yammer.metrics.health.HealthCheck; -import com.yammer.metrics.health.jvm.ThreadDeadlockHealthCheck; import com.yammer.metrics.jvm.ThreadDeadlockDetector; import org.junit.Test; diff --git a/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/tests/InstrumentedHttpClientTest.java b/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/InstrumentedHttpClientTest.java similarity index 76% rename from metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/tests/InstrumentedHttpClientTest.java rename to metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/InstrumentedHttpClientTest.java index fde4072a2b..2bf0293ee5 100644 --- a/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/tests/InstrumentedHttpClientTest.java +++ b/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/InstrumentedHttpClientTest.java @@ -1,8 +1,6 @@ -package com.yammer.metrics.httpclient.tests; +package com.yammer.metrics.httpclient; import com.yammer.metrics.MetricRegistry; -import com.yammer.metrics.httpclient.InstrumentedClientConnManager; -import com.yammer.metrics.httpclient.InstrumentedHttpClient; import org.apache.http.client.HttpClient; import org.junit.Test; diff --git a/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java b/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/InstrumentedTimingCollectorTest.java similarity index 99% rename from metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java rename to metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/InstrumentedTimingCollectorTest.java index 965b26a1af..b6ed37ee6d 100644 --- a/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/tests/InstrumentedTimingCollectorTest.java +++ b/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/InstrumentedTimingCollectorTest.java @@ -1,8 +1,7 @@ -package com.yammer.metrics.jdbi.tests; +package com.yammer.metrics.jdbi; import com.yammer.metrics.MetricRegistry; import com.yammer.metrics.Timer; -import com.yammer.metrics.jdbi.InstrumentedTimingCollector; import com.yammer.metrics.jdbi.strategies.NameStrategies; import com.yammer.metrics.jdbi.strategies.ShortNameStrategy; import com.yammer.metrics.jdbi.strategies.SmartNameStrategy; diff --git a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/SingletonMetricsJerseyTest.java b/metrics-jersey/src/test/java/com/yammer/metrics/jersey/SingletonMetricsJerseyTest.java similarity index 94% rename from metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/SingletonMetricsJerseyTest.java rename to metrics-jersey/src/test/java/com/yammer/metrics/jersey/SingletonMetricsJerseyTest.java index b64347afd5..3baad5465a 100644 --- a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/SingletonMetricsJerseyTest.java +++ b/metrics-jersey/src/test/java/com/yammer/metrics/jersey/SingletonMetricsJerseyTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.jersey.tests; +package com.yammer.metrics.jersey; import com.sun.jersey.api.container.MappableContainerException; import com.sun.jersey.api.core.DefaultResourceConfig; @@ -8,8 +8,7 @@ import com.yammer.metrics.Meter; import com.yammer.metrics.MetricRegistry; import com.yammer.metrics.Timer; -import com.yammer.metrics.jersey.InstrumentedResourceMethodDispatchAdapter; -import com.yammer.metrics.jersey.tests.resources.InstrumentedResource; +import com.yammer.metrics.jersey.resources.InstrumentedResource; import org.junit.Test; import java.io.IOException; diff --git a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/resources/InstrumentedResource.java b/metrics-jersey/src/test/java/com/yammer/metrics/jersey/resources/InstrumentedResource.java similarity index 94% rename from metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/resources/InstrumentedResource.java rename to metrics-jersey/src/test/java/com/yammer/metrics/jersey/resources/InstrumentedResource.java index fdf60b6288..c3046b7332 100644 --- a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/tests/resources/InstrumentedResource.java +++ b/metrics-jersey/src/test/java/com/yammer/metrics/jersey/resources/InstrumentedResource.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.jersey.tests.resources; +package com.yammer.metrics.jersey.resources; import com.yammer.metrics.annotation.ExceptionMetered; import com.yammer.metrics.annotation.Metered; diff --git a/metrics-json/src/test/java/com/yammer/metrics/json/tests/HealthCheckModuleTest.java b/metrics-json/src/test/java/com/yammer/metrics/json/HealthCheckModuleTest.java similarity index 97% rename from metrics-json/src/test/java/com/yammer/metrics/json/tests/HealthCheckModuleTest.java rename to metrics-json/src/test/java/com/yammer/metrics/json/HealthCheckModuleTest.java index 4490a67170..22a87a80a0 100644 --- a/metrics-json/src/test/java/com/yammer/metrics/json/tests/HealthCheckModuleTest.java +++ b/metrics-json/src/test/java/com/yammer/metrics/json/HealthCheckModuleTest.java @@ -1,8 +1,7 @@ -package com.yammer.metrics.json.tests; +package com.yammer.metrics.json; import com.fasterxml.jackson.databind.ObjectMapper; import com.yammer.metrics.health.HealthCheck; -import com.yammer.metrics.json.HealthCheckModule; import org.junit.Test; import static org.fest.assertions.api.Assertions.assertThat; diff --git a/metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java b/metrics-json/src/test/java/com/yammer/metrics/json/MetricsModuleTest.java similarity index 99% rename from metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java rename to metrics-json/src/test/java/com/yammer/metrics/json/MetricsModuleTest.java index 4863a53633..dce9e2b0d0 100644 --- a/metrics-json/src/test/java/com/yammer/metrics/json/tests/MetricsModuleTest.java +++ b/metrics-json/src/test/java/com/yammer/metrics/json/MetricsModuleTest.java @@ -1,8 +1,7 @@ -package com.yammer.metrics.json.tests; +package com.yammer.metrics.json; import com.fasterxml.jackson.databind.ObjectMapper; import com.yammer.metrics.*; -import com.yammer.metrics.json.MetricsModule; import org.junit.Test; import java.util.concurrent.TimeUnit; diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDeadlockDetector.java b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDeadlockDetector.java index 6da92f1c77..2f4d396db6 100644 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDeadlockDetector.java +++ b/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDeadlockDetector.java @@ -1,7 +1,6 @@ package com.yammer.metrics.jvm; -import - java.lang.management.ManagementFactory; +import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.Collections; diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/BufferPoolMetricSetTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/BufferPoolMetricSetTest.java similarity index 97% rename from metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/BufferPoolMetricSetTest.java rename to metrics-jvm/src/test/java/com/yammer/metrics/jvm/BufferPoolMetricSetTest.java index 15bd1c0dbd..c7a02c7cd3 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/BufferPoolMetricSetTest.java +++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/BufferPoolMetricSetTest.java @@ -1,7 +1,6 @@ -package com.yammer.metrics.jvm.tests; +package com.yammer.metrics.jvm; import com.yammer.metrics.Gauge; -import com.yammer.metrics.jvm.BufferPoolMetricSet; import org.junit.Before; import org.junit.Test; diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/FileDescriptorRatioGaugeTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/FileDescriptorRatioGaugeTest.java similarity index 95% rename from metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/FileDescriptorRatioGaugeTest.java rename to metrics-jvm/src/test/java/com/yammer/metrics/jvm/FileDescriptorRatioGaugeTest.java index 46713f1ef9..c0c038bc71 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/FileDescriptorRatioGaugeTest.java +++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/FileDescriptorRatioGaugeTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.jvm.tests; +package com.yammer.metrics.jvm; -import com.yammer.metrics.jvm.FileDescriptorRatioGauge; import org.junit.Test; import javax.management.ObjectName; diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/GarbageCollectorMetricSetTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/GarbageCollectorMetricSetTest.java similarity index 94% rename from metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/GarbageCollectorMetricSetTest.java rename to metrics-jvm/src/test/java/com/yammer/metrics/jvm/GarbageCollectorMetricSetTest.java index a190219df8..bd00a8f26c 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/GarbageCollectorMetricSetTest.java +++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/GarbageCollectorMetricSetTest.java @@ -1,7 +1,6 @@ -package com.yammer.metrics.jvm.tests; +package com.yammer.metrics.jvm; import com.yammer.metrics.Gauge; -import com.yammer.metrics.jvm.GarbageCollectorMetricSet; import org.junit.Before; import org.junit.Test; diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/MemoryUsageGaugeSetTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/MemoryUsageGaugeSetTest.java similarity index 98% rename from metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/MemoryUsageGaugeSetTest.java rename to metrics-jvm/src/test/java/com/yammer/metrics/jvm/MemoryUsageGaugeSetTest.java index 183bb9de98..bda0949940 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/MemoryUsageGaugeSetTest.java +++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/MemoryUsageGaugeSetTest.java @@ -1,7 +1,6 @@ -package com.yammer.metrics.jvm.tests; +package com.yammer.metrics.jvm; import com.yammer.metrics.Gauge; -import com.yammer.metrics.jvm.MemoryUsageGaugeSet; import org.junit.Before; import org.junit.Test; diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadDeadlockDetectorTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadDeadlockDetectorTest.java similarity index 96% rename from metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadDeadlockDetectorTest.java rename to metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadDeadlockDetectorTest.java index 337f62a40c..3299851b02 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadDeadlockDetectorTest.java +++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadDeadlockDetectorTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.jvm.tests; +package com.yammer.metrics.jvm; -import com.yammer.metrics.jvm.ThreadDeadlockDetector; import org.junit.Test; import java.lang.management.ThreadInfo; diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadDumpTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadDumpTest.java similarity index 95% rename from metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadDumpTest.java rename to metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadDumpTest.java index 57fdfb430d..37f1b1c1f1 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadDumpTest.java +++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadDumpTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.jvm.tests; +package com.yammer.metrics.jvm; -import com.yammer.metrics.jvm.ThreadDump; import org.junit.Before; import org.junit.Test; diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadStatesGaugeSetTest.java b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadStatesGaugeSetTest.java similarity index 96% rename from metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadStatesGaugeSetTest.java rename to metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadStatesGaugeSetTest.java index 46c3edd191..86c9f0f091 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/tests/ThreadStatesGaugeSetTest.java +++ b/metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadStatesGaugeSetTest.java @@ -1,8 +1,6 @@ -package com.yammer.metrics.jvm.tests; +package com.yammer.metrics.jvm; import com.yammer.metrics.Gauge; -import com.yammer.metrics.jvm.ThreadDeadlockDetector; -import com.yammer.metrics.jvm.ThreadStatesGaugeSet; import org.junit.Before; import org.junit.Test; diff --git a/metrics-log4j/src/test/java/com/yammer/metrics/log4j/tests/InstrumentedAppenderTest.java b/metrics-log4j/src/test/java/com/yammer/metrics/log4j/InstrumentedAppenderTest.java similarity index 96% rename from metrics-log4j/src/test/java/com/yammer/metrics/log4j/tests/InstrumentedAppenderTest.java rename to metrics-log4j/src/test/java/com/yammer/metrics/log4j/InstrumentedAppenderTest.java index 972796aa11..6b3b46a558 100644 --- a/metrics-log4j/src/test/java/com/yammer/metrics/log4j/tests/InstrumentedAppenderTest.java +++ b/metrics-log4j/src/test/java/com/yammer/metrics/log4j/InstrumentedAppenderTest.java @@ -1,8 +1,7 @@ -package com.yammer.metrics.log4j.tests; +package com.yammer.metrics.log4j; import com.yammer.metrics.Meter; import com.yammer.metrics.MetricRegistry; -import com.yammer.metrics.log4j.InstrumentedAppender; import org.apache.log4j.Appender; import org.apache.log4j.Level; import org.apache.log4j.spi.LoggingEvent; diff --git a/metrics-logback/src/test/java/com/yammer/metrics/logback/tests/InstrumentedAppenderTest.java b/metrics-logback/src/test/java/com/yammer/metrics/logback/InstrumentedAppenderTest.java similarity index 96% rename from metrics-logback/src/test/java/com/yammer/metrics/logback/tests/InstrumentedAppenderTest.java rename to metrics-logback/src/test/java/com/yammer/metrics/logback/InstrumentedAppenderTest.java index fcb230d87f..ed115598d6 100644 --- a/metrics-logback/src/test/java/com/yammer/metrics/logback/tests/InstrumentedAppenderTest.java +++ b/metrics-logback/src/test/java/com/yammer/metrics/logback/InstrumentedAppenderTest.java @@ -1,11 +1,10 @@ -package com.yammer.metrics.logback.tests; +package com.yammer.metrics.logback; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; import com.yammer.metrics.Meter; import com.yammer.metrics.MetricRegistry; -import com.yammer.metrics.logback.InstrumentedAppender; import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AbstractServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/AbstractServletTest.java similarity index 94% rename from metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AbstractServletTest.java rename to metrics-servlets/src/test/java/com/yammer/metrics/servlets/AbstractServletTest.java index 63bd6c8c40..92aab1be3e 100644 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AbstractServletTest.java +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/AbstractServletTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.servlets.tests; +package com.yammer.metrics.servlets; import org.eclipse.jetty.testing.HttpTester; import org.eclipse.jetty.testing.ServletTester; diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AdminServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/AdminServletTest.java similarity index 96% rename from metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AdminServletTest.java rename to metrics-servlets/src/test/java/com/yammer/metrics/servlets/AdminServletTest.java index 57ca489efd..883fa4fb20 100755 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/AdminServletTest.java +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/AdminServletTest.java @@ -1,8 +1,7 @@ -package com.yammer.metrics.servlets.tests; +package com.yammer.metrics.servlets; import com.yammer.metrics.MetricRegistry; import com.yammer.metrics.health.HealthCheckRegistry; -import com.yammer.metrics.servlets.AdminServlet; import org.eclipse.jetty.testing.ServletTester; import org.junit.Before; import org.junit.Test; diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/HealthCheckServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/HealthCheckServletTest.java similarity index 97% rename from metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/HealthCheckServletTest.java rename to metrics-servlets/src/test/java/com/yammer/metrics/servlets/HealthCheckServletTest.java index e02dcbbed4..35c463dd1e 100644 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/HealthCheckServletTest.java +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/HealthCheckServletTest.java @@ -1,8 +1,7 @@ -package com.yammer.metrics.servlets.tests; +package com.yammer.metrics.servlets; import com.yammer.metrics.health.HealthCheck; import com.yammer.metrics.health.HealthCheckRegistry; -import com.yammer.metrics.servlets.HealthCheckServlet; import org.eclipse.jetty.testing.ServletTester; import org.junit.After; import org.junit.Before; diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/MetricsServletTest.java similarity index 98% rename from metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java rename to metrics-servlets/src/test/java/com/yammer/metrics/servlets/MetricsServletTest.java index a6d202ffd1..33edb6e170 100644 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/MetricsServletTest.java +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/MetricsServletTest.java @@ -1,7 +1,6 @@ -package com.yammer.metrics.servlets.tests; +package com.yammer.metrics.servlets; import com.yammer.metrics.*; -import com.yammer.metrics.servlets.MetricsServlet; import org.eclipse.jetty.testing.ServletTester; import org.junit.Before; import org.junit.Test; diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/PingServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/PingServletTest.java similarity index 93% rename from metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/PingServletTest.java rename to metrics-servlets/src/test/java/com/yammer/metrics/servlets/PingServletTest.java index d45fdcda0a..31b5786785 100644 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/PingServletTest.java +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/PingServletTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.servlets.tests; +package com.yammer.metrics.servlets; -import com.yammer.metrics.servlets.PingServlet; import org.eclipse.jetty.testing.ServletTester; import org.junit.Before; import org.junit.Test; diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/ThreadDumpServletTest.java b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/ThreadDumpServletTest.java similarity index 92% rename from metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/ThreadDumpServletTest.java rename to metrics-servlets/src/test/java/com/yammer/metrics/servlets/ThreadDumpServletTest.java index 680e729005..252968f047 100644 --- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/tests/ThreadDumpServletTest.java +++ b/metrics-servlets/src/test/java/com/yammer/metrics/servlets/ThreadDumpServletTest.java @@ -1,6 +1,5 @@ -package com.yammer.metrics.servlets.tests; +package com.yammer.metrics.servlets; -import com.yammer.metrics.servlets.ThreadDumpServlet; import org.eclipse.jetty.testing.ServletTester; import org.junit.Before; import org.junit.Test; From 889badeedac36f6afc8f2b13d406747f37726fcd Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 8 Apr 2013 12:26:46 -0700 Subject: [PATCH 0185/2558] Update Maven module names and descriptions. --- metrics-annotation/pom.xml | 2 +- metrics-core/pom.xml | 8 +++++++- metrics-ehcache/pom.xml | 5 ++++- metrics-ganglia/pom.xml | 5 ++++- metrics-graphite/pom.xml | 5 ++++- metrics-healthchecks/pom.xml | 4 ++++ metrics-httpclient/pom.xml | 6 +++++- metrics-jdbi/pom.xml | 5 ++++- metrics-jersey/pom.xml | 2 +- metrics-jetty8/pom.xml | 6 +++++- metrics-json/pom.xml | 5 ++++- metrics-jvm/pom.xml | 6 +++++- metrics-log4j/pom.xml | 5 ++++- metrics-logback/pom.xml | 5 ++++- metrics-servlet/pom.xml | 5 ++++- metrics-servlets/pom.xml | 4 ++++ 16 files changed, 64 insertions(+), 14 deletions(-) diff --git a/metrics-annotation/pom.xml b/metrics-annotation/pom.xml index e95a72bbaa..39f48a32b0 100644 --- a/metrics-annotation/pom.xml +++ b/metrics-annotation/pom.xml @@ -9,7 +9,7 @@ metrics-annotation - Metrics Annotations + Annotations for Metrics bundle A dependency-less package of just the annotations used by other Metrics modules. diff --git a/metrics-core/pom.xml b/metrics-core/pom.xml index f2f24e2f04..720e5e4c32 100644 --- a/metrics-core/pom.xml +++ b/metrics-core/pom.xml @@ -9,6 +9,12 @@ metrics-core - Metrics Core Library + Metrics Core bundle + + Metrics is a Java library which gives you unparalleled insight into what your code does in + production. Developed by Yammer to instrument their JVM-based backend services, Metrics + provides a powerful toolkit of ways to measure the behavior of critical components in your + production environment. + diff --git a/metrics-ehcache/pom.xml b/metrics-ehcache/pom.xml index e753a5fc7f..dcf95952ea 100644 --- a/metrics-ehcache/pom.xml +++ b/metrics-ehcache/pom.xml @@ -9,8 +9,11 @@ metrics-ehcache - Metrics Ehcache Support + Metrics Integration for Ehcache bundle + + An Ehcache wrapper providing Metrics instrumentation of caches. + diff --git a/metrics-ganglia/pom.xml b/metrics-ganglia/pom.xml index a928382071..a957a1a791 100644 --- a/metrics-ganglia/pom.xml +++ b/metrics-ganglia/pom.xml @@ -9,8 +9,11 @@ metrics-ganglia - Metrics Ganglia Support + Ganglia Integration for Metrics bundle + + A reporter for Metrics which announces measurements to a Ganglia cluster. + diff --git a/metrics-graphite/pom.xml b/metrics-graphite/pom.xml index b9bfa55194..5480c40c1c 100644 --- a/metrics-graphite/pom.xml +++ b/metrics-graphite/pom.xml @@ -9,8 +9,11 @@ metrics-graphite - Metrics Graphite Support + Graphite Integration for Metrics bundle + + A reporter for Metrics which announces measurements to a Graphite server. + diff --git a/metrics-healthchecks/pom.xml b/metrics-healthchecks/pom.xml index ec39d5fee2..ae7eda0f79 100644 --- a/metrics-healthchecks/pom.xml +++ b/metrics-healthchecks/pom.xml @@ -11,6 +11,10 @@ metrics-healthchecks Metrics Health Checks bundle + + An addition to Metrics which provides the ability to run application-specific health checks, + allowing you to check your application's heath in production. + diff --git a/metrics-httpclient/pom.xml b/metrics-httpclient/pom.xml index 33094f3041..fda255a137 100644 --- a/metrics-httpclient/pom.xml +++ b/metrics-httpclient/pom.xml @@ -9,8 +9,12 @@ metrics-httpclient - Metrics HttpClient Support + Metrics Integration for Apache HttpClient bundle + + An Apache HttpClient wrapper providing Metrics instrumentation of connection pools, request + durations and rates, and other useful information. + diff --git a/metrics-jdbi/pom.xml b/metrics-jdbi/pom.xml index 4c3e254a2c..1cb2d73791 100644 --- a/metrics-jdbi/pom.xml +++ b/metrics-jdbi/pom.xml @@ -9,8 +9,11 @@ metrics-jdbi - Metrics JDBI Support + Metrics Integration for JDBI bundle + + A JDBI wrapper providing Metrics instrumentation of query durations and rates. + diff --git a/metrics-jersey/pom.xml b/metrics-jersey/pom.xml index 169d30707c..2758594138 100644 --- a/metrics-jersey/pom.xml +++ b/metrics-jersey/pom.xml @@ -9,7 +9,7 @@ metrics-jersey - Metrics Jersey Support + Metrics Integration for Jersey bundle A set of class providing Metrics integration for Jersey, the reference JAX-RS diff --git a/metrics-jetty8/pom.xml b/metrics-jetty8/pom.xml index 0d4811ebe1..7ccc9cd118 100644 --- a/metrics-jetty8/pom.xml +++ b/metrics-jetty8/pom.xml @@ -9,8 +9,12 @@ metrics-jetty8 - Metrics Support for Jetty 8 + Metrics Integration for Jetty 8 bundle + + A set of extensions for Jetty 8 which provide instrumentation of thread pools, connector + metrics, and application latency and utilization. + diff --git a/metrics-json/pom.xml b/metrics-json/pom.xml index 3a38c4683c..c61908f50d 100644 --- a/metrics-json/pom.xml +++ b/metrics-json/pom.xml @@ -9,8 +9,11 @@ metrics-json - JSON Support for Metrics + Jackson Integration for Metrics bundle + + A set of Jackson modules which provide serializers for most Metrics classes. + diff --git a/metrics-jvm/pom.xml b/metrics-jvm/pom.xml index dfce2b5ea6..251d9767d1 100644 --- a/metrics-jvm/pom.xml +++ b/metrics-jvm/pom.xml @@ -9,8 +9,12 @@ metrics-jvm - Metrics JVM Instrumentation + JVM Integration for Metrics bundle + + A set of classes which allow you to monitor critical aspects of your Java Virtual Machine + using Metrics. + diff --git a/metrics-log4j/pom.xml b/metrics-log4j/pom.xml index c4d8e799a9..162031ab91 100644 --- a/metrics-log4j/pom.xml +++ b/metrics-log4j/pom.xml @@ -9,8 +9,11 @@ metrics-log4j - Metrics Log4j Support + Metrics Integration for Log4j bundle + + An instrumented appender for Log4j. + diff --git a/metrics-logback/pom.xml b/metrics-logback/pom.xml index 82e52d1b18..4551813294 100644 --- a/metrics-logback/pom.xml +++ b/metrics-logback/pom.xml @@ -9,8 +9,11 @@ metrics-logback - Metrics Logback Support + Metrics Integration for Logback bundle + + An instrumented appender for Logback. + 1.0.9 diff --git a/metrics-servlet/pom.xml b/metrics-servlet/pom.xml index b9b2dc5b23..f37951c5bf 100644 --- a/metrics-servlet/pom.xml +++ b/metrics-servlet/pom.xml @@ -9,8 +9,11 @@ metrics-servlet - Metrics Servlet Support + Metrics Integration for Servlets bundle + + An instrumented filter for servlet environments. + diff --git a/metrics-servlets/pom.xml b/metrics-servlets/pom.xml index 9d88fa814d..c55f6d12b3 100644 --- a/metrics-servlets/pom.xml +++ b/metrics-servlets/pom.xml @@ -11,6 +11,10 @@ metrics-servlets Metrics Utility Servlets bundle + + A set of utility servlets for Metrics, allowing you to expose valuable information about + your production environment. + From e378c36a2b5fb9feabd572bc0423137298c16731 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 8 Apr 2013 12:26:59 -0700 Subject: [PATCH 0186/2558] Make metrics-json optionally depend on health checks. --- metrics-json/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/metrics-json/pom.xml b/metrics-json/pom.xml index c61908f50d..0dca02ecdb 100644 --- a/metrics-json/pom.xml +++ b/metrics-json/pom.xml @@ -25,6 +25,7 @@ com.yammer.metrics metrics-healthchecks ${project.version} + true com.fasterxml.jackson.core From 2a7fde3fe317344808f8ba2dc507b102b30f31bb Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 8 Apr 2013 14:27:43 -0700 Subject: [PATCH 0187/2558] Ported @romseygeek's removeMatching. Not only did I give bad advice on #377, but I also moved the tests around underneath it, which makes it unmergeable. So instead of making him rebase it, I'm doing it myself. Closes #377. --- .../com/yammer/metrics/MetricRegistry.java | 15 +++++++++++++++ .../yammer/metrics/MetricRegistryTest.java | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java index e373d40b57..3b9aa942ca 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -157,6 +157,21 @@ public boolean remove(String name) { return false; } + /** + * Removes all metrics which match the given filter. + * + * @param filter a filter + */ + public void removeMatching(MetricFilter filter) { + final Iterator> iterator = metrics.entrySet().iterator(); + while (iterator.hasNext()) { + final Map.Entry entry = iterator.next(); + if (filter.matches(entry.getKey(), entry.getValue())) { + iterator.remove(); + } + } + } + /** * Adds a {@link MetricRegistryListener} to a collection of listeners that will be notified on * metric creation. Listeners will be notified in the order in which they are added. diff --git a/metrics-core/src/test/java/com/yammer/metrics/MetricRegistryTest.java b/metrics-core/src/test/java/com/yammer/metrics/MetricRegistryTest.java index eeedf3b42a..42277d35ee 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/MetricRegistryTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/MetricRegistryTest.java @@ -338,4 +338,23 @@ public String getValue() { assertThat(name(g.getClass(), "one", "two")) .isEqualTo("com.yammer.metrics.MetricRegistryTest$5.one.two"); } + + @Test + public void removesMetricsMatchingAFilter() throws Exception { + registry.timer("timer-1"); + registry.timer("timer-2"); + registry.histogram("histogram-1"); + + assertThat(registry.getNames()).contains("timer-1", "timer-2", "histogram-1"); + + registry.removeMatching(new MetricFilter() { + @Override + public boolean matches(String name, Metric metric) { + return name.endsWith("1"); + } + }); + + assertThat(registry.getNames()).doesNotContain("timer-1", "histogram-1"); + assertThat(registry.getNames()).contains("timer-2"); + } } From 03b12bf1a0f3ffd2b022350f15cffb78dcd33f24 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Mon, 8 Apr 2013 14:35:05 -0700 Subject: [PATCH 0188/2558] Update release notes and contributors. --- docs/source/about/contributors.rst | 2 ++ docs/source/about/release-notes.rst | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/docs/source/about/contributors.rst b/docs/source/about/contributors.rst index f83f3ea21f..a14b19ba90 100644 --- a/docs/source/about/contributors.rst +++ b/docs/source/about/contributors.rst @@ -6,6 +6,7 @@ Contributors Many, many thanks to: +* `Alan Woodward `_ * `Alex Lambert `_ * `Basil James Whitehouse III `_ * `Benjamin Gehrels `_ @@ -17,6 +18,7 @@ Many, many thanks to: * `Charles Care `_ * `Chris Birchall `_ * `Chris Burroughs `_ +* `Christopher Swenson `_ * `Ciamac Moallemi `_ * `Cliff Moon `_ * `Collin VanDyck `_ diff --git a/docs/source/about/release-notes.rst b/docs/source/about/release-notes.rst index 338c9173c6..0273fd0873 100644 --- a/docs/source/about/release-notes.rst +++ b/docs/source/about/release-notes.rst @@ -11,6 +11,14 @@ v3.0.0-BETA2-SNAPSHOT * ``MetricRegistry`` no longer has a name. * ``JmxReporter`` takes an optional domain property to disambiguate multiple reporters. +* Fixed Java 6 compatibility problem. (Also added Java 6 as a CI environment.) +* Added ``MetricRegistryListener.Base``. +* Switched ``Counter``, ``Meter``, and ``EWMA`` to use JSR133's ``LongAdder`` instead of + ``AtomicLong``, improving contended concurrency. +* Added ``MetricRegistry#buildMap()``, allowing for custom map implementations in + ``MetricRegistry``. +* Added ``MetricRegistry#removeMatching(MetricFilter)``. +* Changed ``metrics-json`` to optionally depend on ``metrics-healthcheck``. .. _rel-3.0.0-BETA1: From 63f01ba1a033c9173dd76d3e1e40b03fde50f969 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 9 Apr 2013 07:30:43 -0700 Subject: [PATCH 0189/2558] Fix listener notification in MetricRegistry#removeMatching. --- .../main/java/com/yammer/metrics/MetricRegistry.java | 6 ++---- .../java/com/yammer/metrics/MetricRegistryTest.java | 12 +++++++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java index 3b9aa942ca..cd6f2e3038 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java @@ -163,11 +163,9 @@ public boolean remove(String name) { * @param filter a filter */ public void removeMatching(MetricFilter filter) { - final Iterator> iterator = metrics.entrySet().iterator(); - while (iterator.hasNext()) { - final Map.Entry entry = iterator.next(); + for (Map.Entry entry : metrics.entrySet()) { if (filter.matches(entry.getKey(), entry.getValue())) { - iterator.remove(); + remove(entry.getKey()); } } } diff --git a/metrics-core/src/test/java/com/yammer/metrics/MetricRegistryTest.java b/metrics-core/src/test/java/com/yammer/metrics/MetricRegistryTest.java index 42277d35ee..76800af968 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/MetricRegistryTest.java +++ b/metrics-core/src/test/java/com/yammer/metrics/MetricRegistryTest.java @@ -345,7 +345,8 @@ public void removesMetricsMatchingAFilter() throws Exception { registry.timer("timer-2"); registry.histogram("histogram-1"); - assertThat(registry.getNames()).contains("timer-1", "timer-2", "histogram-1"); + assertThat(registry.getNames()) + .contains("timer-1", "timer-2", "histogram-1"); registry.removeMatching(new MetricFilter() { @Override @@ -354,7 +355,12 @@ public boolean matches(String name, Metric metric) { } }); - assertThat(registry.getNames()).doesNotContain("timer-1", "histogram-1"); - assertThat(registry.getNames()).contains("timer-2"); + assertThat(registry.getNames()) + .doesNotContain("timer-1", "histogram-1"); + assertThat(registry.getNames()) + .contains("timer-2"); + + verify(listener).onTimerRemoved("timer-1"); + verify(listener).onHistogramRemoved("histogram-1"); } } From 324afde99ffcc7e6c21b8cba8a4f0f4965ebe4dc Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Tue, 9 Apr 2013 18:20:49 -0700 Subject: [PATCH 0190/2558] Mention the JMX reporter in Getting Started. --- docs/source/getting-started.rst | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/source/getting-started.rst b/docs/source/getting-started.rst index 30a261f86d..4bc8ed2858 100644 --- a/docs/source/getting-started.rst +++ b/docs/source/getting-started.rst @@ -254,7 +254,15 @@ built-in thread deadlock detection to determine if any threads are deadlocked. Reporting Via JMX ================= -All metrics are visible via **JConsole** or **VisualVM** (if you install the JConsole plugin): +To report metrics via JMX: + +.. code-block:: java + + final JmxReporter reporter = JmxReporter.forRegistry(registry).build(); + reporter.start(); + +Once the reporter is started, all of the metrics in the registry will become visible via +**JConsole** or **VisualVM** (if you install the JConsole plugin): .. image:: metrics-visualvm.png :alt: Metrics exposed as JMX MBeans being viewed in VisualVM's MBeans browser @@ -264,7 +272,6 @@ All metrics are visible via **JConsole** or **VisualVM** (if you install the JCo If you double-click any of the metric properties, VisualVM will start graphing the data for that property. Sweet, eh? - .. _gs-http: Reporting Via HTTP From 0da0e97b35e4f08b04910de740d759fb15bcfa7e Mon Sep 17 00:00:00 2001 From: Raman Gupta Date: Wed, 10 Apr 2013 19:01:27 -0400 Subject: [PATCH 0191/2558] Slf4j 1.6 is binary compatible with 1.7 - change OSGi import The OSGi import for SLF4J is defaulted to 1.7-2.0 by Maven. Since 1.7 is binary compatible with 1.6, users of the Metrics API should not be required to use 1.7+. Modify the import to indicate use of 1.6+ is acceptable. --- pom.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pom.xml b/pom.xml index 2b8a196920..75555f43bb 100644 --- a/pom.xml +++ b/pom.xml @@ -171,6 +171,15 @@ maven-bundle-plugin 2.3.7 true + + + + + + org.apache.maven.plugins From d8e9c63e83384634cecad4d0cc78609759f51315 Mon Sep 17 00:00:00 2001 From: Raman Gupta Date: Wed, 10 Apr 2013 20:51:53 -0400 Subject: [PATCH 0192/2558] Servlet API 2.5+ is acceptable at runtime The build uses the Servlet API 3+ due to its dependency on Jetty 8. However, there is no capability used by metrics that is dependent on Servlet 3. Modify the build to allow the OSGi bundles created to be used in a Servlet 2.5+ environment. --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 75555f43bb..d3b212cdd3 100644 --- a/pom.xml +++ b/pom.xml @@ -174,6 +174,7 @@ From caf49eda81290fc30ef54d38203efa5cb7528d6a Mon Sep 17 00:00:00 2001 From: Tom Akehurst Date: Thu, 11 Apr 2013 23:23:20 +0100 Subject: [PATCH 0193/2558] Added HttpClient metric naming strategies --- .../HttpClientMetricNameStrategies.java | 52 +++++++++++++++++ .../HttpClientMetricNameStrategy.java | 7 +++ .../httpclient/InstrumentedHttpClient.java | 23 +++++++- .../InstrumentedRequestDirector.java | 56 +++---------------- .../HttpClientMetricNameStrategiesTest.java | 45 +++++++++++++++ .../InstrumentedHttpClientTest.java | 43 ++++++++++++-- 6 files changed, 171 insertions(+), 55 deletions(-) create mode 100644 metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/HttpClientMetricNameStrategies.java create mode 100644 metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/HttpClientMetricNameStrategy.java create mode 100644 metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/HttpClientMetricNameStrategiesTest.java diff --git a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/HttpClientMetricNameStrategies.java b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/HttpClientMetricNameStrategies.java new file mode 100644 index 0000000000..a6361c735d --- /dev/null +++ b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/HttpClientMetricNameStrategies.java @@ -0,0 +1,52 @@ +package com.yammer.metrics.httpclient; + +import org.apache.http.HttpRequest; +import org.apache.http.RequestLine; +import org.apache.http.client.HttpClient; +import org.apache.http.client.utils.URIBuilder; + +import java.net.URI; +import java.net.URISyntaxException; + +import static com.yammer.metrics.MetricRegistry.name; + +public class HttpClientMetricNameStrategies { + + public static final HttpClientMetricNameStrategy METHOD_ONLY = new HttpClientMetricNameStrategy() { + @Override + public String getNameFor(String name, HttpRequest request) { + return name(HttpClient.class, + name, + methodNameString(request)); + } + }; + + public static final HttpClientMetricNameStrategy HOST_AND_METHOD = new HttpClientMetricNameStrategy() { + @Override + public String getNameFor(String name, HttpRequest request) { + RequestLine requestLine = request.getRequestLine(); + URI uri = URI.create(requestLine.getUri()); + return name(HttpClient.class, + name, + uri.getHost(), + methodNameString(request)); + } + }; + + public static final HttpClientMetricNameStrategy QUERYLESS_URL_AND_METHOD = new HttpClientMetricNameStrategy() { + @Override + public String getNameFor(String name, HttpRequest request) { + try { + RequestLine requestLine = request.getRequestLine(); + String querylessUrl = new URIBuilder(requestLine.getUri()).removeQuery().build().toString(); + return name(HttpClient.class, name, querylessUrl, methodNameString(request)); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + } + }; + + private static String methodNameString(HttpRequest request) { + return request.getRequestLine().getMethod().toLowerCase() + "-requests"; + } +} diff --git a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/HttpClientMetricNameStrategy.java b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/HttpClientMetricNameStrategy.java new file mode 100644 index 0000000000..69429affb7 --- /dev/null +++ b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/HttpClientMetricNameStrategy.java @@ -0,0 +1,7 @@ +package com.yammer.metrics.httpclient; + +import org.apache.http.HttpRequest; + +public interface HttpClientMetricNameStrategy { + String getNameFor(String name, HttpRequest request); +} diff --git a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedHttpClient.java b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedHttpClient.java index e69b3cfa3e..e2097d7c5c 100644 --- a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedHttpClient.java +++ b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedHttpClient.java @@ -13,19 +13,31 @@ import org.apache.http.protocol.HttpProcessor; import org.apache.http.protocol.HttpRequestExecutor; +import static com.yammer.metrics.httpclient.HttpClientMetricNameStrategies.METHOD_ONLY; + public class InstrumentedHttpClient extends DefaultHttpClient { private final Log log = LogFactory.getLog(getClass()); private final MetricRegistry registry; private final String name; + private final HttpClientMetricNameStrategy metricNameStrategy; public InstrumentedHttpClient(MetricRegistry registry, InstrumentedClientConnManager manager, HttpParams params, - String name) { + String name, + HttpClientMetricNameStrategy metricNameStrategy) { super(manager, params); this.registry = registry; this.name = name; + this.metricNameStrategy = metricNameStrategy; + } + + public InstrumentedHttpClient(MetricRegistry registry, + InstrumentedClientConnManager manager, + HttpParams params, + String name) { + this(registry, manager, params, name, METHOD_ONLY); } public InstrumentedHttpClient(MetricRegistry registry, @@ -37,6 +49,14 @@ public InstrumentedHttpClient(MetricRegistry registry) { this(registry, new InstrumentedClientConnManager(registry), null, null); } + public InstrumentedHttpClient(MetricRegistry registry, HttpClientMetricNameStrategy metricNameStrategy) { + this(registry, new InstrumentedClientConnManager(registry), null, null, metricNameStrategy); + } + + public InstrumentedHttpClient(MetricRegistry registry, String name, HttpClientMetricNameStrategy metricNameStrategy) { + this(registry, new InstrumentedClientConnManager(registry), null, name, metricNameStrategy); + } + @Override protected RequestDirector createClientRequestDirector(HttpRequestExecutor requestExec, ClientConnectionManager conman, @@ -53,6 +73,7 @@ protected RequestDirector createClientRequestDirector(HttpRequestExecutor reques return new InstrumentedRequestDirector( registry, name, + metricNameStrategy, log, requestExec, conman, diff --git a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedRequestDirector.java b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedRequestDirector.java index ee8fb9931c..fa8a6b36d8 100644 --- a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedRequestDirector.java +++ b/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedRequestDirector.java @@ -19,24 +19,14 @@ import static com.yammer.metrics.MetricRegistry.name; class InstrumentedRequestDirector extends DefaultRequestDirector { - private final static String GET = "GET", POST = "POST", HEAD = "HEAD", PUT = "PUT", - OPTIONS = "OPTIONS", DELETE = "DELETE", TRACE = "TRACE", - CONNECT = "CONNECT", MOVE = "MOVE", PATCH = "PATCH"; - private final Timer getTimer; - private final Timer postTimer; - private final Timer headTimer; - private final Timer putTimer; - private final Timer deleteTimer; - private final Timer optionsTimer; - private final Timer traceTimer; - private final Timer connectTimer; - private final Timer moveTimer; - private final Timer patchTimer; - private final Timer otherTimer; + private final MetricRegistry registry; + private final HttpClientMetricNameStrategy metricNameStrategy; + private final String name; InstrumentedRequestDirector(MetricRegistry registry, String name, + HttpClientMetricNameStrategy metricNameStrategy, Log log, HttpRequestExecutor requestExec, ClientConnectionManager conman, @@ -63,17 +53,9 @@ class InstrumentedRequestDirector extends DefaultRequestDirector { proxyAuthStrategy, userTokenHandler, params); - getTimer = registry.timer(name(HttpClient.class, name, "get-requests")); - postTimer = registry.timer(name(HttpClient.class, name, "post-requests")); - headTimer = registry.timer(name(HttpClient.class, name, "head-requests")); - putTimer = registry.timer(name(HttpClient.class, name, "put-requests")); - deleteTimer = registry.timer(name(HttpClient.class, name, "delete-requests")); - optionsTimer = registry.timer(name(HttpClient.class, name, "options-requests")); - traceTimer = registry.timer(name(HttpClient.class, name, "trace-requests")); - connectTimer = registry.timer(name(HttpClient.class, name, "connect-requests")); - moveTimer = registry.timer(name(HttpClient.class, name, "move-requests")); - patchTimer = registry.timer(name(HttpClient.class, name, "patch-requests")); - otherTimer = registry.timer(name(HttpClient.class, name, "other-requests")); + this.registry = registry; + this.name = name; + this.metricNameStrategy = metricNameStrategy; } @Override @@ -87,28 +69,6 @@ public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext co } private Timer timer(HttpRequest request) { - final String method = request.getRequestLine().getMethod(); - if (GET.equalsIgnoreCase(method)) { - return getTimer; - } else if (POST.equalsIgnoreCase(method)) { - return postTimer; - } else if (PUT.equalsIgnoreCase(method)) { - return putTimer; - } else if (HEAD.equalsIgnoreCase(method)) { - return headTimer; - } else if (DELETE.equalsIgnoreCase(method)) { - return deleteTimer; - } else if (OPTIONS.equalsIgnoreCase(method)) { - return optionsTimer; - } else if (TRACE.equalsIgnoreCase(method)) { - return traceTimer; - } else if (CONNECT.equalsIgnoreCase(method)) { - return connectTimer; - } else if (PATCH.equalsIgnoreCase(method)) { - return patchTimer; - } else if (MOVE.equalsIgnoreCase(method)) { - return moveTimer; - } - return otherTimer; + return registry.timer(metricNameStrategy.getNameFor(name, request)); } } diff --git a/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/HttpClientMetricNameStrategiesTest.java b/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/HttpClientMetricNameStrategiesTest.java new file mode 100644 index 0000000000..e16b9c584c --- /dev/null +++ b/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/HttpClientMetricNameStrategiesTest.java @@ -0,0 +1,45 @@ +package com.yammer.metrics.httpclient; + +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.junit.Test; + +import static com.yammer.metrics.httpclient.HttpClientMetricNameStrategies.*; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class HttpClientMetricNameStrategiesTest { + + @Test + public void methodOnlyWithName() { + assertThat(METHOD_ONLY.getNameFor("some-service", new HttpGet("/whatever")), + is("org.apache.http.client.HttpClient.some-service.get-requests")); + } + + @Test + public void methodOnlyWithoutName() { + assertThat(METHOD_ONLY.getNameFor(null, new HttpGet("/whatever")), + is("org.apache.http.client.HttpClient.get-requests")); + } + + @Test + public void hostAndMethodWithName() { + assertThat(HOST_AND_METHOD.getNameFor("some-service", new HttpPost("http://my.host.com/whatever")), + is("org.apache.http.client.HttpClient.some-service.my.host.com.post-requests")); + } + + @Test + public void hostAndMethodWithoutName() { + assertThat(HOST_AND_METHOD.getNameFor(null, new HttpPost("http://my.host.com/whatever")), + is("org.apache.http.client.HttpClient.my.host.com.post-requests")); + } + + @Test + public void querylessUrlAndMethodWithName() { + assertThat(QUERYLESS_URL_AND_METHOD.getNameFor( + "some-service", + new HttpPut("https://thing.com:8090/my/path?ignore=this&and=this")), + is("org.apache.http.client.HttpClient.some-service.https://thing.com:8090/my/path.put-requests")); + } +} diff --git a/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/InstrumentedHttpClientTest.java b/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/InstrumentedHttpClientTest.java index 2bf0293ee5..42e49c3623 100644 --- a/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/InstrumentedHttpClientTest.java +++ b/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/InstrumentedHttpClientTest.java @@ -1,19 +1,50 @@ package com.yammer.metrics.httpclient; import com.yammer.metrics.MetricRegistry; +import com.yammer.metrics.MetricRegistryListener; +import com.yammer.metrics.Timer; import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; -import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +@RunWith(MockitoJUnitRunner.class) public class InstrumentedHttpClientTest { - private final MetricRegistry registry = new MetricRegistry(); - private final HttpClient client = new InstrumentedHttpClient(registry); + + @Mock + public HttpClientMetricNameStrategy metricNameStrategy; + @Mock + public MetricRegistryListener registryListener; + + private MetricRegistry metricRegistry; + private HttpClient client; + + @Before + public void init() { + metricRegistry = new MetricRegistry(); + metricRegistry.addListener(registryListener); + client = new InstrumentedHttpClient(metricRegistry, metricNameStrategy); + } @Test - public void hasAnInstrumentedConnectionManager() throws Exception { + public void registersExpectedMetricsGivenNameStrategy() throws Exception { + HttpGet get = new HttpGet("http://google.com?q=anything"); + String metricName = "some.made.up.metric.name"; + + when(metricNameStrategy.getNameFor(anyString(), eq(get))).thenReturn(metricName); + + try { + client.execute(get); + } catch (Exception e) { + } - assertThat(client.getConnectionManager()) - .isInstanceOf(InstrumentedClientConnManager.class); + verify(registryListener).onTimerAdded(eq(metricName), any(Timer.class)); } } From 3bb71a79c9b685d55102425e6e3a793443df6c4e Mon Sep 17 00:00:00 2001 From: Tom Akehurst Date: Thu, 18 Apr 2013 21:25:05 +0100 Subject: [PATCH 0194/2558] Added doc note for http metric naming strategies --- docs/source/manual/httpclient.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/source/manual/httpclient.rst b/docs/source/manual/httpclient.rst index 40aa42b374..40f61f64a0 100644 --- a/docs/source/manual/httpclient.rst +++ b/docs/source/manual/httpclient.rst @@ -15,3 +15,16 @@ opened. ``InstrumentedHttpClient`` is a ``HttpClient`` implementation which has per-HTTP method timers for HTTP requests. + + +Metric naming strategies +======================== +The default per-method metric naming and scoping strategy can be overridden by passing an implementation of +``HttpClientMetricNameStrategy`` to the ``InstrumentedHttpClient`` constructor. + +A number of pre-rolled strategies +are available e.g.: + +.. code-block:: java + + HttpClient client = new InstrumentedHttpClient(registry, HttpClientMetricNameStrategies.HOST_AND_METHOD); \ No newline at end of file From afcf7fd6a12a0f13364107065785445aa7e56e69 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Sat, 20 Apr 2013 11:54:17 -0700 Subject: [PATCH 0195/2558] Move Metrics to com.codahale namespace. This should allow for an easier upgrade path by reducing Maven and classpath conflicts. Still keeping licensing and copyright info. --- NOTICE | 6 +- docs/Makefile | 2 +- .../{yammerdoc => metrics}/genindex.html | 0 .../{yammerdoc => metrics}/layout.html | 0 .../less/accordion.less | 0 .../{yammerdoc => metrics}/less/alerts.less | 0 .../less/bootstrap.less | 0 .../less/breadcrumbs.less | 0 .../less/button-groups.less | 0 .../{yammerdoc => metrics}/less/buttons.less | 0 .../{yammerdoc => metrics}/less/carousel.less | 0 .../{yammerdoc => metrics}/less/close.less | 0 .../{yammerdoc => metrics}/less/code.less | 0 .../less/component-animations.less | 0 .../less/dropdowns.less | 0 .../{yammerdoc => metrics}/less/forms.less | 0 .../{yammerdoc => metrics}/less/grid.less | 0 .../less/hero-unit.less | 0 .../{yammerdoc => metrics}/less/labels.less | 0 .../{yammerdoc => metrics}/less/layouts.less | 0 .../less/metrics.less} | 0 .../{yammerdoc => metrics}/less/mixins.less | 0 .../{yammerdoc => metrics}/less/modals.less | 0 .../{yammerdoc => metrics}/less/navbar.less | 0 .../{yammerdoc => metrics}/less/navs.less | 0 .../{yammerdoc => metrics}/less/pager.less | 0 .../less/pagination.less | 0 .../{yammerdoc => metrics}/less/patterns.less | 0 .../{yammerdoc => metrics}/less/popovers.less | 0 .../{yammerdoc => metrics}/less/print.less | 0 .../less/progress-bars.less | 0 .../{yammerdoc => metrics}/less/reset.less | 0 .../less/responsive.less | 0 .../less/scaffolding.less | 0 .../{yammerdoc => metrics}/less/sprites.less | 0 .../{yammerdoc => metrics}/less/tables.less | 0 .../less/thumbnails.less | 0 .../{yammerdoc => metrics}/less/tooltip.less | 0 .../{yammerdoc => metrics}/less/type.less | 0 .../less/utilities.less | 0 .../less/variables.less | 0 .../{yammerdoc => metrics}/less/wells.less | 0 .../_themes/{yammerdoc => metrics}/page.html | 0 .../{yammerdoc => metrics}/search.html | 0 .../static/metrics.css} | 0 .../_themes/metrics/static/yammerdoc.css | 305 ++++++++++++++++++ .../_themes/{yammerdoc => metrics}/theme.conf | 2 +- docs/source/about/release-notes.rst | 2 + docs/source/conf.py | 2 +- docs/source/getting-started.rst | 4 +- docs/source/index.rst | 5 +- docs/source/manual/servlet.rst | 2 +- docs/source/manual/servlets.rst | 4 +- metrics-annotation/pom.xml | 2 +- .../metrics/annotation/ExceptionMetered.java | 2 +- .../metrics/annotation/Gauge.java | 2 +- .../metrics/annotation/Metered.java | 2 +- .../metrics/annotation/Timed.java | 2 +- metrics-core/pom.xml | 7 +- .../metrics/CachedGauge.java | 2 +- .../{yammer => codahale}/metrics/Clock.java | 2 +- .../metrics/ConsoleReporter.java | 2 +- .../{yammer => codahale}/metrics/Counter.java | 2 +- .../metrics/CsvReporter.java | 2 +- .../metrics/DerivativeGauge.java | 2 +- .../{yammer => codahale}/metrics/EWMA.java | 2 +- .../ExponentiallyDecayingReservoir.java | 2 +- .../{yammer => codahale}/metrics/Gauge.java | 2 +- .../metrics/Histogram.java | 2 +- .../metrics/JmxAttributeGauge.java | 2 +- .../metrics/JmxReporter.java | 2 +- .../metrics/JvmAttributeGaugeSet.java | 2 +- .../metrics/LongAdder.java | 2 +- .../{yammer => codahale}/metrics/Meter.java | 2 +- .../{yammer => codahale}/metrics/Metered.java | 2 +- .../{yammer => codahale}/metrics/Metric.java | 2 +- .../metrics/MetricFilter.java | 2 +- .../metrics/MetricRegistry.java | 2 +- .../metrics/MetricRegistryListener.java | 2 +- .../metrics/MetricSet.java | 2 +- .../metrics/RatioGauge.java | 2 +- .../metrics/Reservoir.java | 2 +- .../metrics/Sampling.java | 2 +- .../metrics/ScheduledReporter.java | 4 +- .../metrics/Slf4jReporter.java | 2 +- .../metrics/SlidingTimeWindowReservoir.java | 2 +- .../metrics/SlidingWindowReservoir.java | 2 +- .../metrics/Snapshot.java | 2 +- .../metrics/Striped64.java | 2 +- .../metrics/ThreadLocalRandom.java | 2 +- .../{yammer => codahale}/metrics/Timer.java | 2 +- .../metrics/UniformReservoir.java | 2 +- .../metrics/CachedGaugeTest.java | 2 +- .../metrics/ClockTest.java | 2 +- .../metrics/ConsoleReporterTest.java | 2 +- .../metrics/CounterTest.java | 2 +- .../metrics/CsvReporterTest.java | 2 +- .../metrics/DerivativeGaugeTest.java | 2 +- .../metrics/EWMATest.java | 2 +- .../ExponentiallyDecayingReservoirTest.java | 2 +- .../metrics/HistogramTest.java | 2 +- .../metrics/JmxAttributeGaugeTest.java | 2 +- .../metrics/JmxReporterTest.java | 2 +- .../metrics/JvmAttributeGaugeSetTest.java | 2 +- .../metrics/MeterTest.java | 2 +- .../metrics/MetricFilterTest.java | 2 +- .../metrics/MetricRegistryListenerTest.java | 2 +- .../metrics/MetricRegistryTest.java | 8 +- .../metrics/RatioGaugeTest.java | 2 +- .../metrics/ScheduledReporterTest.java | 2 +- .../metrics/Slf4jReporterTest.java | 2 +- .../SlidingTimeWindowReservoirTest.java | 2 +- .../metrics/SlidingWindowReservoirTest.java | 2 +- .../metrics/SnapshotTest.java | 2 +- .../metrics/TimerTest.java | 2 +- .../metrics/UniformReservoirTest.java | 2 +- .../metrics/benchmarks/CounterBenchmark.java | 4 +- .../metrics/benchmarks/MeterBenchmark.java | 4 +- .../benchmarks/ReservoirBenchmark.java | 10 +- metrics-ehcache/pom.xml | 4 +- .../metrics/ehcache/InstrumentedEhcache.java | 10 +- .../ehcache/InstrumentedEhcacheTest.java | 8 +- .../src/test/resources/ehcache.xml | 2 +- metrics-ganglia/pom.xml | 4 +- .../metrics/ganglia/GangliaReporter.java | 6 +- .../metrics/ganglia/GangliaReporterTest.java | 4 +- metrics-graphite/pom.xml | 4 +- .../metrics/graphite/Graphite.java | 2 +- .../metrics/graphite/GraphiteReporter.java | 4 +- .../graphite/GraphiteReporterTest.java | 4 +- .../metrics/graphite/GraphiteTest.java | 2 +- metrics-healthchecks/pom.xml | 4 +- .../metrics/health/HealthCheck.java | 2 +- .../metrics/health/HealthCheckRegistry.java | 4 +- .../health/jvm/ThreadDeadlockHealthCheck.java | 6 +- .../health/HealthCheckRegistryTest.java | 2 +- .../metrics/health/HealthCheckTest.java | 2 +- .../jvm/ThreadDeadlockHealthCheckTest.java | 6 +- metrics-httpclient/pom.xml | 4 +- .../InstrumentedClientConnManager.java | 8 +- .../httpclient/InstrumentedHttpClient.java | 4 +- .../InstrumentedRequestDirector.java | 8 +- .../InstrumentedHttpClientTest.java | 4 +- metrics-jdbi/pom.xml | 4 +- .../jdbi/InstrumentedTimingCollector.java | 10 +- .../jdbi/strategies/BasicSqlNameStrategy.java | 2 +- .../jdbi/strategies/ContextNameStrategy.java | 2 +- .../DelegatingStatementNameStrategy.java | 2 +- .../jdbi/strategies/NaiveNameStrategy.java | 2 +- .../jdbi/strategies/NameStrategies.java | 4 +- .../jdbi/strategies/ShortNameStrategy.java | 4 +- .../jdbi/strategies/SmartNameStrategy.java | 2 +- .../strategies/StatementNameStrategy.java | 2 +- .../jdbi/InstrumentedTimingCollectorTest.java | 18 +- metrics-jersey/pom.xml | 6 +- ...rumentedResourceMethodDispatchAdapter.java | 4 +- ...umentedResourceMethodDispatchProvider.java | 18 +- .../jersey/SingletonMetricsJerseyTest.java | 12 +- .../resources/InstrumentedResource.java | 8 +- metrics-jetty8/pom.xml | 4 +- .../InstrumentedBlockingChannelConnector.java | 6 +- .../metrics/jetty8/InstrumentedHandler.java | 6 +- .../jetty8/InstrumentedQueuedThreadPool.java | 10 +- .../InstrumentedSelectChannelConnector.java | 6 +- .../jetty8/InstrumentedSocketConnector.java | 6 +- ...InstrumentedSslSelectChannelConnector.java | 6 +- .../InstrumentedSslSocketConnector.java | 6 +- metrics-json/pom.xml | 6 +- .../metrics/json/HealthCheckModule.java | 4 +- .../metrics/json/MetricsModule.java | 6 +- .../metrics/json/HealthCheckModuleTest.java | 4 +- .../metrics/json/MetricsModuleTest.java | 4 +- metrics-jvm/pom.xml | 4 +- .../metrics/jvm/BufferPoolMetricSet.java | 10 +- .../metrics/jvm/FileDescriptorRatioGauge.java | 4 +- .../jvm/GarbageCollectorMetricSet.java | 10 +- .../metrics/jvm/MemoryUsageGaugeSet.java | 12 +- .../metrics/jvm/ThreadDeadlockDetector.java | 2 +- .../metrics/jvm/ThreadDump.java | 2 +- .../metrics/jvm/ThreadStatesGaugeSet.java | 10 +- .../metrics/jvm/BufferPoolMetricSetTest.java | 4 +- .../jvm/FileDescriptorRatioGaugeTest.java | 2 +- .../jvm/GarbageCollectorMetricSetTest.java | 4 +- .../metrics/jvm/MemoryUsageGaugeSetTest.java | 4 +- .../jvm/ThreadDeadlockDetectorTest.java | 2 +- .../metrics/jvm/ThreadDumpTest.java | 2 +- .../metrics/jvm/ThreadStatesGaugeSetTest.java | 4 +- metrics-log4j/pom.xml | 4 +- .../metrics/log4j/InstrumentedAppender.java | 8 +- .../log4j/InstrumentedAppenderTest.java | 8 +- metrics-logback/pom.xml | 4 +- .../metrics/logback/InstrumentedAppender.java | 8 +- .../logback/InstrumentedAppenderTest.java | 8 +- metrics-servlet/pom.xml | 4 +- .../servlet/DefaultWebappMetricsFilter.java | 4 +- .../metrics/servlet/WebappMetricsFilter.java | 12 +- metrics-servlets/pom.xml | 12 +- .../metrics/servlets/AdminServlet.java | 2 +- .../metrics/servlets/HealthCheckServlet.java | 8 +- .../metrics/servlets/MetricsServlet.java | 6 +- .../metrics/servlets/PingServlet.java | 2 +- .../metrics/servlets/ThreadDumpServlet.java | 4 +- .../metrics/servlets/AbstractServletTest.java | 2 +- .../metrics/servlets/AdminServletTest.java | 10 +- .../servlets/HealthCheckServletTest.java | 10 +- .../metrics/servlets/MetricsServletTest.java | 6 +- .../metrics/servlets/PingServletTest.java | 2 +- .../servlets/ThreadDumpServletTest.java | 2 +- .../servlets/experiments/ExampleServer.java | 28 +- pom.xml | 4 +- 210 files changed, 655 insertions(+), 352 deletions(-) rename docs/source/_themes/{yammerdoc => metrics}/genindex.html (100%) rename docs/source/_themes/{yammerdoc => metrics}/layout.html (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/accordion.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/alerts.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/bootstrap.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/breadcrumbs.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/button-groups.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/buttons.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/carousel.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/close.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/code.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/component-animations.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/dropdowns.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/forms.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/grid.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/hero-unit.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/labels.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/layouts.less (100%) rename docs/source/_themes/{yammerdoc/less/yammerdoc.less => metrics/less/metrics.less} (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/mixins.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/modals.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/navbar.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/navs.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/pager.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/pagination.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/patterns.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/popovers.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/print.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/progress-bars.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/reset.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/responsive.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/scaffolding.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/sprites.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/tables.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/thumbnails.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/tooltip.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/type.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/utilities.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/variables.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/less/wells.less (100%) rename docs/source/_themes/{yammerdoc => metrics}/page.html (100%) rename docs/source/_themes/{yammerdoc => metrics}/search.html (100%) rename docs/source/_themes/{yammerdoc/static/yammerdoc.css => metrics/static/metrics.css} (100%) create mode 100644 docs/source/_themes/metrics/static/yammerdoc.css rename docs/source/_themes/{yammerdoc => metrics}/theme.conf (93%) rename metrics-annotation/src/main/java/com/{yammer => codahale}/metrics/annotation/ExceptionMetered.java (97%) rename metrics-annotation/src/main/java/com/{yammer => codahale}/metrics/annotation/Gauge.java (95%) rename metrics-annotation/src/main/java/com/{yammer => codahale}/metrics/annotation/Metered.java (96%) rename metrics-annotation/src/main/java/com/{yammer => codahale}/metrics/annotation/Timed.java (96%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/CachedGauge.java (98%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/Clock.java (97%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/ConsoleReporter.java (99%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/Counter.java (96%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/CsvReporter.java (99%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/DerivativeGauge.java (96%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/EWMA.java (99%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/ExponentiallyDecayingReservoir.java (99%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/Gauge.java (95%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/Histogram.java (97%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/JmxAttributeGauge.java (98%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/JmxReporter.java (99%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/JvmAttributeGaugeSet.java (98%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/LongAdder.java (99%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/Meter.java (98%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/Metered.java (98%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/Metric.java (76%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/MetricFilter.java (95%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/MetricRegistry.java (99%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/MetricRegistryListener.java (99%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/MetricSet.java (90%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/RatioGauge.java (98%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/Reservoir.java (94%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/Sampling.java (87%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/ScheduledReporter.java (97%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/Slf4jReporter.java (99%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/SlidingTimeWindowReservoir.java (98%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/SlidingWindowReservoir.java (97%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/Snapshot.java (99%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/Striped64.java (99%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/ThreadLocalRandom.java (99%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/Timer.java (99%) rename metrics-core/src/main/java/com/{yammer => codahale}/metrics/UniformReservoir.java (98%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/CachedGaugeTest.java (97%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/ClockTest.java (97%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/ConsoleReporterTest.java (99%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/CounterTest.java (97%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/CsvReporterTest.java (99%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/DerivativeGaugeTest.java (95%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/EWMATest.java (99%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/ExponentiallyDecayingReservoirTest.java (99%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/HistogramTest.java (96%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/JmxAttributeGaugeTest.java (97%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/JmxReporterTest.java (99%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/JvmAttributeGaugeSetTest.java (98%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/MeterTest.java (98%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/MetricFilterTest.java (91%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/MetricRegistryListenerTest.java (98%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/MetricRegistryTest.java (97%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/RatioGaugeTest.java (98%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/ScheduledReporterTest.java (98%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/Slf4jReporterTest.java (99%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/SlidingTimeWindowReservoirTest.java (97%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/SlidingWindowReservoirTest.java (96%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/SnapshotTest.java (99%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/TimerTest.java (98%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/UniformReservoirTest.java (96%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/benchmarks/CounterBenchmark.java (85%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/benchmarks/MeterBenchmark.java (85%) rename metrics-core/src/test/java/com/{yammer => codahale}/metrics/benchmarks/ReservoirBenchmark.java (84%) rename metrics-ehcache/src/main/java/com/{yammer => codahale}/metrics/ehcache/InstrumentedEhcache.java (98%) rename metrics-ehcache/src/test/java/com/{yammer => codahale}/metrics/ehcache/InstrumentedEhcacheTest.java (87%) rename metrics-ganglia/src/main/java/com/{yammer => codahale}/metrics/ganglia/GangliaReporter.java (98%) rename metrics-ganglia/src/test/java/com/{yammer => codahale}/metrics/ganglia/GangliaReporterTest.java (99%) rename metrics-graphite/src/main/java/com/{yammer => codahale}/metrics/graphite/Graphite.java (98%) rename metrics-graphite/src/main/java/com/{yammer => codahale}/metrics/graphite/GraphiteReporter.java (99%) rename metrics-graphite/src/test/java/com/{yammer => codahale}/metrics/graphite/GraphiteReporterTest.java (99%) rename metrics-graphite/src/test/java/com/{yammer => codahale}/metrics/graphite/GraphiteTest.java (98%) rename metrics-healthchecks/src/main/java/com/{yammer => codahale}/metrics/health/HealthCheck.java (99%) rename metrics-healthchecks/src/main/java/com/{yammer => codahale}/metrics/health/HealthCheckRegistry.java (97%) rename metrics-healthchecks/src/main/java/com/{yammer => codahale}/metrics/health/jvm/ThreadDeadlockHealthCheck.java (85%) rename metrics-healthchecks/src/test/java/com/{yammer => codahale}/metrics/health/HealthCheckRegistryTest.java (98%) rename metrics-healthchecks/src/test/java/com/{yammer => codahale}/metrics/health/HealthCheckTest.java (99%) rename metrics-healthchecks/src/test/java/com/{yammer => codahale}/metrics/health/jvm/ThreadDeadlockHealthCheckTest.java (91%) rename metrics-httpclient/src/main/java/com/{yammer => codahale}/metrics/httpclient/InstrumentedClientConnManager.java (95%) rename metrics-httpclient/src/main/java/com/{yammer => codahale}/metrics/httpclient/InstrumentedHttpClient.java (97%) rename metrics-httpclient/src/main/java/com/{yammer => codahale}/metrics/httpclient/InstrumentedRequestDirector.java (96%) rename metrics-httpclient/src/test/java/com/{yammer => codahale}/metrics/httpclient/InstrumentedHttpClientTest.java (86%) rename metrics-jdbi/src/main/java/com/{yammer => codahale}/metrics/jdbi/InstrumentedTimingCollector.java (82%) rename metrics-jdbi/src/main/java/com/{yammer => codahale}/metrics/jdbi/strategies/BasicSqlNameStrategy.java (81%) rename metrics-jdbi/src/main/java/com/{yammer => codahale}/metrics/jdbi/strategies/ContextNameStrategy.java (90%) rename metrics-jdbi/src/main/java/com/{yammer => codahale}/metrics/jdbi/strategies/DelegatingStatementNameStrategy.java (95%) rename metrics-jdbi/src/main/java/com/{yammer => codahale}/metrics/jdbi/strategies/NaiveNameStrategy.java (87%) rename metrics-jdbi/src/main/java/com/{yammer => codahale}/metrics/jdbi/strategies/NameStrategies.java (98%) rename metrics-jdbi/src/main/java/com/{yammer => codahale}/metrics/jdbi/strategies/ShortNameStrategy.java (97%) rename metrics-jdbi/src/main/java/com/{yammer => codahale}/metrics/jdbi/strategies/SmartNameStrategy.java (93%) rename metrics-jdbi/src/main/java/com/{yammer => codahale}/metrics/jdbi/strategies/StatementNameStrategy.java (83%) rename metrics-jdbi/src/test/java/com/{yammer => codahale}/metrics/jdbi/InstrumentedTimingCollectorTest.java (96%) rename metrics-jersey/src/main/java/com/{yammer => codahale}/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java (93%) rename metrics-jersey/src/main/java/com/{yammer => codahale}/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java (94%) rename metrics-jersey/src/test/java/com/{yammer => codahale}/metrics/jersey/SingletonMetricsJerseyTest.java (91%) rename metrics-jersey/src/test/java/com/{yammer => codahale}/metrics/jersey/resources/InstrumentedResource.java (77%) rename metrics-jetty8/src/main/java/com/{yammer => codahale}/metrics/jetty8/InstrumentedBlockingChannelConnector.java (95%) rename metrics-jetty8/src/main/java/com/{yammer => codahale}/metrics/jetty8/InstrumentedHandler.java (98%) rename metrics-jetty8/src/main/java/com/{yammer => codahale}/metrics/jetty8/InstrumentedQueuedThreadPool.java (81%) rename metrics-jetty8/src/main/java/com/{yammer => codahale}/metrics/jetty8/InstrumentedSelectChannelConnector.java (95%) rename metrics-jetty8/src/main/java/com/{yammer => codahale}/metrics/jetty8/InstrumentedSocketConnector.java (94%) rename metrics-jetty8/src/main/java/com/{yammer => codahale}/metrics/jetty8/InstrumentedSslSelectChannelConnector.java (95%) rename metrics-jetty8/src/main/java/com/{yammer => codahale}/metrics/jetty8/InstrumentedSslSocketConnector.java (95%) rename metrics-json/src/main/java/com/{yammer => codahale}/metrics/json/HealthCheckModule.java (96%) rename metrics-json/src/main/java/com/{yammer => codahale}/metrics/json/MetricsModule.java (98%) rename metrics-json/src/test/java/com/{yammer => codahale}/metrics/json/HealthCheckModuleTest.java (97%) rename metrics-json/src/test/java/com/{yammer => codahale}/metrics/json/MetricsModuleTest.java (99%) rename metrics-jvm/src/main/java/com/{yammer => codahale}/metrics/jvm/BufferPoolMetricSet.java (89%) rename metrics-jvm/src/main/java/com/{yammer => codahale}/metrics/jvm/FileDescriptorRatioGauge.java (95%) rename metrics-jvm/src/main/java/com/{yammer => codahale}/metrics/jvm/GarbageCollectorMetricSet.java (89%) rename metrics-jvm/src/main/java/com/{yammer => codahale}/metrics/jvm/MemoryUsageGaugeSet.java (95%) rename metrics-jvm/src/main/java/com/{yammer => codahale}/metrics/jvm/ThreadDeadlockDetector.java (98%) rename metrics-jvm/src/main/java/com/{yammer => codahale}/metrics/jvm/ThreadDump.java (99%) rename metrics-jvm/src/main/java/com/{yammer => codahale}/metrics/jvm/ThreadStatesGaugeSet.java (93%) rename metrics-jvm/src/test/java/com/{yammer => codahale}/metrics/jvm/BufferPoolMetricSetTest.java (98%) rename metrics-jvm/src/test/java/com/{yammer => codahale}/metrics/jvm/FileDescriptorRatioGaugeTest.java (98%) rename metrics-jvm/src/test/java/com/{yammer => codahale}/metrics/jvm/GarbageCollectorMetricSetTest.java (95%) rename metrics-jvm/src/test/java/com/{yammer => codahale}/metrics/jvm/MemoryUsageGaugeSetTest.java (99%) rename metrics-jvm/src/test/java/com/{yammer => codahale}/metrics/jvm/ThreadDeadlockDetectorTest.java (98%) rename metrics-jvm/src/test/java/com/{yammer => codahale}/metrics/jvm/ThreadDumpTest.java (98%) rename metrics-jvm/src/test/java/com/{yammer => codahale}/metrics/jvm/ThreadStatesGaugeSetTest.java (98%) rename metrics-log4j/src/main/java/com/{yammer => codahale}/metrics/log4j/InstrumentedAppender.java (92%) rename metrics-log4j/src/test/java/com/{yammer => codahale}/metrics/log4j/InstrumentedAppenderTest.java (94%) rename metrics-logback/src/main/java/com/{yammer => codahale}/metrics/logback/InstrumentedAppender.java (91%) rename metrics-logback/src/test/java/com/{yammer => codahale}/metrics/logback/InstrumentedAppenderTest.java (93%) rename metrics-servlet/src/main/java/com/{yammer => codahale}/metrics/servlet/DefaultWebappMetricsFilter.java (93%) rename metrics-servlet/src/main/java/com/{yammer => codahale}/metrics/servlet/WebappMetricsFilter.java (95%) rename metrics-servlets/src/main/java/com/{yammer => codahale}/metrics/servlets/AdminServlet.java (99%) rename metrics-servlets/src/main/java/com/{yammer => codahale}/metrics/servlets/HealthCheckServlet.java (94%) rename metrics-servlets/src/main/java/com/{yammer => codahale}/metrics/servlets/MetricsServlet.java (96%) rename metrics-servlets/src/main/java/com/{yammer => codahale}/metrics/servlets/PingServlet.java (96%) rename metrics-servlets/src/main/java/com/{yammer => codahale}/metrics/servlets/ThreadDumpServlet.java (94%) rename metrics-servlets/src/test/java/com/{yammer => codahale}/metrics/servlets/AbstractServletTest.java (95%) rename metrics-servlets/src/test/java/com/{yammer => codahale}/metrics/servlets/AdminServletTest.java (86%) rename metrics-servlets/src/test/java/com/{yammer => codahale}/metrics/servlets/HealthCheckServletTest.java (91%) rename metrics-servlets/src/test/java/com/{yammer => codahale}/metrics/servlets/MetricsServletTest.java (97%) rename metrics-servlets/src/test/java/com/{yammer => codahale}/metrics/servlets/PingServletTest.java (96%) rename metrics-servlets/src/test/java/com/{yammer => codahale}/metrics/servlets/ThreadDumpServletTest.java (96%) rename metrics-servlets/src/test/java/com/{yammer => codahale}/metrics/servlets/experiments/ExampleServer.java (74%) diff --git a/NOTICE b/NOTICE index 9c7699405b..4fe83de38a 100644 --- a/NOTICE +++ b/NOTICE @@ -1,10 +1,10 @@ Metrics -Copyright 2010-2012 Coda Hale and Yammer, Inc. +Copyright 2010-2013 Coda Hale and Yammer, Inc. This product includes software developed by Coda Hale and Yammer, Inc. -This product includes code derived from the JSR-166 project (ThreadLocalRandom), which was released -with the following comments: +This product includes code derived from the JSR-166 project (ThreadLocalRandom, Striped64, +LongAdder), which was released with the following comments: Written by Doug Lea with assistance from members of JCP JSR-166 Expert Group and released to the public domain, as explained at diff --git a/docs/Makefile b/docs/Makefile index de2174ca66..3432bd422b 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -153,7 +153,7 @@ doctest: "results in $(BUILDDIR)/doctest/output.txt." less: - lessc --compress source/_themes/yammerdoc/less/yammerdoc.less > source/_themes/yammerdoc/static/yammerdoc.css + lessc --compress source/_themes/metrics/less/metrics.less > source/_themes/metrics/static/metrics.css upload: clean dirhtml rsync -avz --delete --exclude=maven $(BUILDDIR)/dirhtml/ codahale.com:/home/codahale/metrics.codahale.com/ diff --git a/docs/source/_themes/yammerdoc/genindex.html b/docs/source/_themes/metrics/genindex.html similarity index 100% rename from docs/source/_themes/yammerdoc/genindex.html rename to docs/source/_themes/metrics/genindex.html diff --git a/docs/source/_themes/yammerdoc/layout.html b/docs/source/_themes/metrics/layout.html similarity index 100% rename from docs/source/_themes/yammerdoc/layout.html rename to docs/source/_themes/metrics/layout.html diff --git a/docs/source/_themes/yammerdoc/less/accordion.less b/docs/source/_themes/metrics/less/accordion.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/accordion.less rename to docs/source/_themes/metrics/less/accordion.less diff --git a/docs/source/_themes/yammerdoc/less/alerts.less b/docs/source/_themes/metrics/less/alerts.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/alerts.less rename to docs/source/_themes/metrics/less/alerts.less diff --git a/docs/source/_themes/yammerdoc/less/bootstrap.less b/docs/source/_themes/metrics/less/bootstrap.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/bootstrap.less rename to docs/source/_themes/metrics/less/bootstrap.less diff --git a/docs/source/_themes/yammerdoc/less/breadcrumbs.less b/docs/source/_themes/metrics/less/breadcrumbs.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/breadcrumbs.less rename to docs/source/_themes/metrics/less/breadcrumbs.less diff --git a/docs/source/_themes/yammerdoc/less/button-groups.less b/docs/source/_themes/metrics/less/button-groups.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/button-groups.less rename to docs/source/_themes/metrics/less/button-groups.less diff --git a/docs/source/_themes/yammerdoc/less/buttons.less b/docs/source/_themes/metrics/less/buttons.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/buttons.less rename to docs/source/_themes/metrics/less/buttons.less diff --git a/docs/source/_themes/yammerdoc/less/carousel.less b/docs/source/_themes/metrics/less/carousel.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/carousel.less rename to docs/source/_themes/metrics/less/carousel.less diff --git a/docs/source/_themes/yammerdoc/less/close.less b/docs/source/_themes/metrics/less/close.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/close.less rename to docs/source/_themes/metrics/less/close.less diff --git a/docs/source/_themes/yammerdoc/less/code.less b/docs/source/_themes/metrics/less/code.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/code.less rename to docs/source/_themes/metrics/less/code.less diff --git a/docs/source/_themes/yammerdoc/less/component-animations.less b/docs/source/_themes/metrics/less/component-animations.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/component-animations.less rename to docs/source/_themes/metrics/less/component-animations.less diff --git a/docs/source/_themes/yammerdoc/less/dropdowns.less b/docs/source/_themes/metrics/less/dropdowns.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/dropdowns.less rename to docs/source/_themes/metrics/less/dropdowns.less diff --git a/docs/source/_themes/yammerdoc/less/forms.less b/docs/source/_themes/metrics/less/forms.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/forms.less rename to docs/source/_themes/metrics/less/forms.less diff --git a/docs/source/_themes/yammerdoc/less/grid.less b/docs/source/_themes/metrics/less/grid.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/grid.less rename to docs/source/_themes/metrics/less/grid.less diff --git a/docs/source/_themes/yammerdoc/less/hero-unit.less b/docs/source/_themes/metrics/less/hero-unit.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/hero-unit.less rename to docs/source/_themes/metrics/less/hero-unit.less diff --git a/docs/source/_themes/yammerdoc/less/labels.less b/docs/source/_themes/metrics/less/labels.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/labels.less rename to docs/source/_themes/metrics/less/labels.less diff --git a/docs/source/_themes/yammerdoc/less/layouts.less b/docs/source/_themes/metrics/less/layouts.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/layouts.less rename to docs/source/_themes/metrics/less/layouts.less diff --git a/docs/source/_themes/yammerdoc/less/yammerdoc.less b/docs/source/_themes/metrics/less/metrics.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/yammerdoc.less rename to docs/source/_themes/metrics/less/metrics.less diff --git a/docs/source/_themes/yammerdoc/less/mixins.less b/docs/source/_themes/metrics/less/mixins.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/mixins.less rename to docs/source/_themes/metrics/less/mixins.less diff --git a/docs/source/_themes/yammerdoc/less/modals.less b/docs/source/_themes/metrics/less/modals.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/modals.less rename to docs/source/_themes/metrics/less/modals.less diff --git a/docs/source/_themes/yammerdoc/less/navbar.less b/docs/source/_themes/metrics/less/navbar.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/navbar.less rename to docs/source/_themes/metrics/less/navbar.less diff --git a/docs/source/_themes/yammerdoc/less/navs.less b/docs/source/_themes/metrics/less/navs.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/navs.less rename to docs/source/_themes/metrics/less/navs.less diff --git a/docs/source/_themes/yammerdoc/less/pager.less b/docs/source/_themes/metrics/less/pager.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/pager.less rename to docs/source/_themes/metrics/less/pager.less diff --git a/docs/source/_themes/yammerdoc/less/pagination.less b/docs/source/_themes/metrics/less/pagination.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/pagination.less rename to docs/source/_themes/metrics/less/pagination.less diff --git a/docs/source/_themes/yammerdoc/less/patterns.less b/docs/source/_themes/metrics/less/patterns.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/patterns.less rename to docs/source/_themes/metrics/less/patterns.less diff --git a/docs/source/_themes/yammerdoc/less/popovers.less b/docs/source/_themes/metrics/less/popovers.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/popovers.less rename to docs/source/_themes/metrics/less/popovers.less diff --git a/docs/source/_themes/yammerdoc/less/print.less b/docs/source/_themes/metrics/less/print.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/print.less rename to docs/source/_themes/metrics/less/print.less diff --git a/docs/source/_themes/yammerdoc/less/progress-bars.less b/docs/source/_themes/metrics/less/progress-bars.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/progress-bars.less rename to docs/source/_themes/metrics/less/progress-bars.less diff --git a/docs/source/_themes/yammerdoc/less/reset.less b/docs/source/_themes/metrics/less/reset.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/reset.less rename to docs/source/_themes/metrics/less/reset.less diff --git a/docs/source/_themes/yammerdoc/less/responsive.less b/docs/source/_themes/metrics/less/responsive.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/responsive.less rename to docs/source/_themes/metrics/less/responsive.less diff --git a/docs/source/_themes/yammerdoc/less/scaffolding.less b/docs/source/_themes/metrics/less/scaffolding.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/scaffolding.less rename to docs/source/_themes/metrics/less/scaffolding.less diff --git a/docs/source/_themes/yammerdoc/less/sprites.less b/docs/source/_themes/metrics/less/sprites.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/sprites.less rename to docs/source/_themes/metrics/less/sprites.less diff --git a/docs/source/_themes/yammerdoc/less/tables.less b/docs/source/_themes/metrics/less/tables.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/tables.less rename to docs/source/_themes/metrics/less/tables.less diff --git a/docs/source/_themes/yammerdoc/less/thumbnails.less b/docs/source/_themes/metrics/less/thumbnails.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/thumbnails.less rename to docs/source/_themes/metrics/less/thumbnails.less diff --git a/docs/source/_themes/yammerdoc/less/tooltip.less b/docs/source/_themes/metrics/less/tooltip.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/tooltip.less rename to docs/source/_themes/metrics/less/tooltip.less diff --git a/docs/source/_themes/yammerdoc/less/type.less b/docs/source/_themes/metrics/less/type.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/type.less rename to docs/source/_themes/metrics/less/type.less diff --git a/docs/source/_themes/yammerdoc/less/utilities.less b/docs/source/_themes/metrics/less/utilities.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/utilities.less rename to docs/source/_themes/metrics/less/utilities.less diff --git a/docs/source/_themes/yammerdoc/less/variables.less b/docs/source/_themes/metrics/less/variables.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/variables.less rename to docs/source/_themes/metrics/less/variables.less diff --git a/docs/source/_themes/yammerdoc/less/wells.less b/docs/source/_themes/metrics/less/wells.less similarity index 100% rename from docs/source/_themes/yammerdoc/less/wells.less rename to docs/source/_themes/metrics/less/wells.less diff --git a/docs/source/_themes/yammerdoc/page.html b/docs/source/_themes/metrics/page.html similarity index 100% rename from docs/source/_themes/yammerdoc/page.html rename to docs/source/_themes/metrics/page.html diff --git a/docs/source/_themes/yammerdoc/search.html b/docs/source/_themes/metrics/search.html similarity index 100% rename from docs/source/_themes/yammerdoc/search.html rename to docs/source/_themes/metrics/search.html diff --git a/docs/source/_themes/yammerdoc/static/yammerdoc.css b/docs/source/_themes/metrics/static/metrics.css similarity index 100% rename from docs/source/_themes/yammerdoc/static/yammerdoc.css rename to docs/source/_themes/metrics/static/metrics.css diff --git a/docs/source/_themes/metrics/static/yammerdoc.css b/docs/source/_themes/metrics/static/yammerdoc.css new file mode 100644 index 0000000000..0b3d58e6e2 --- /dev/null +++ b/docs/source/_themes/metrics/static/yammerdoc.css @@ -0,0 +1,305 @@ +article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;} +audio,canvas,video{display:inline-block;*display:inline;*zoom:1;} +audio:not([controls]){display:none;} +html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;} +a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} +a:hover,a:active{outline:0;} +sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;} +sup{top:-0.5em;} +sub{bottom:-0.25em;} +img{max-width:100%;height:auto;border:0;-ms-interpolation-mode:bicubic;} +button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;} +button,input{*overflow:visible;line-height:normal;} +button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;} +button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;} +input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;} +input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;} +textarea{overflow:auto;vertical-align:top;} +body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-color:#ffffff;} +a{color:#0088cc;text-decoration:none;} +a:hover{color:#005580;text-decoration:underline;} +.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} +.row:after{clear:both;} +[class*="span"]{float:left;margin-left:20px;} +.span1{width:60px;} +.span2{width:140px;} +.span3{width:220px;} +.span4{width:300px;} +.span5{width:380px;} +.span6{width:460px;} +.span7{width:540px;} +.span8{width:620px;} +.span9{width:700px;} +.span10{width:780px;} +.span11{width:860px;} +.span12,.container{width:940px;} +.offset1{margin-left:100px;} +.offset2{margin-left:180px;} +.offset3{margin-left:260px;} +.offset4{margin-left:340px;} +.offset5{margin-left:420px;} +.offset6{margin-left:500px;} +.offset7{margin-left:580px;} +.offset8{margin-left:660px;} +.offset9{margin-left:740px;} +.offset10{margin-left:820px;} +.offset11{margin-left:900px;} +.row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} +.row-fluid:after{clear:both;} +.row-fluid>[class*="span"]{float:left;margin-left:2.127659574%;} +.row-fluid>[class*="span"]:first-child{margin-left:0;} +.row-fluid .span1{width:6.382978723%;} +.row-fluid .span2{width:14.89361702%;} +.row-fluid .span3{width:23.404255317%;} +.row-fluid .span4{width:31.914893614%;} +.row-fluid .span5{width:40.425531911%;} +.row-fluid .span6{width:48.93617020799999%;} +.row-fluid .span7{width:57.446808505%;} +.row-fluid .span8{width:65.95744680199999%;} +.row-fluid .span9{width:74.468085099%;} +.row-fluid .span10{width:82.97872339599999%;} +.row-fluid .span11{width:91.489361693%;} +.row-fluid .span12{width:99.99999998999999%;} +.container{width:940px;margin-left:auto;margin-right:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";} +.container:after{clear:both;} +.container-fluid{padding-left:20px;padding-right:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";} +.container-fluid:after{clear:both;} +p{margin:0 0 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;}p small{font-size:11px;color:#999999;} +.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;} +h1,h2,h3,h4,h5,h6{margin:0;font-weight:bold;color:#333333;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999999;} +h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;} +h2{font-size:24px;line-height:36px;}h2 small{font-size:18px;} +h3{line-height:27px;font-size:18px;}h3 small{font-size:14px;} +h4,h5,h6{line-height:18px;} +h4{font-size:14px;}h4 small{font-size:12px;} +h5{font-size:12px;} +h6{font-size:11px;color:#999999;text-transform:uppercase;} +.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;} +.page-header h1{line-height:1;} +ul,ol{padding:0;margin:0 0 9px 25px;} +ul ul,ul ol,ol ol,ol ul{margin-bottom:0;} +ul{list-style:disc;} +ol{list-style:decimal;} +li{line-height:18px;} +ul.unstyled{margin-left:0;list-style:none;} +dl{margin-bottom:18px;} +dt,dd{line-height:18px;} +dt{font-weight:bold;} +dd{margin-left:9px;} +hr{margin:18px 0;border:0;border-top:1px solid #e5e5e5;border-bottom:1px solid #ffffff;} +strong{font-weight:bold;} +em{font-style:italic;} +.muted{color:#999999;} +abbr{font-size:90%;text-transform:uppercase;border-bottom:1px dotted #ddd;cursor:help;} +blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;} +blockquote small{display:block;line-height:18px;color:#999999;}blockquote small:before{content:'\2014 \00A0';} +blockquote.pull-right{float:right;padding-left:0;padding-right:15px;border-left:0;border-right:5px solid #eeeeee;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;} +q:before,q:after,blockquote:before,blockquote:after{content:"";} +address{display:block;margin-bottom:18px;line-height:18px;font-style:normal;} +small{font-size:100%;} +cite{font-style:normal;} +.code-and-pre,pre{padding:0 3px 2px;font-family:"Panic Sans",Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +.code,code{padding:0 3px 2px;font-family:"Panic Sans",Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;} +pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12px;line-height:18px;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;white-space:pre;white-space:pre-wrap;word-break:break-all;}pre.prettyprint{margin-bottom:18px;} +pre code{padding:0;background-color:transparent;} +table{max-width:100%;border-collapse:collapse;border-spacing:0;} +.table{width:100%;margin-bottom:18px;}.table th,.table td{padding:16px;line-height:18px;text-align:left;border-top:1px solid #ddd;} +.table th{font-weight:bold;vertical-align:bottom;} +.table td{vertical-align:top;} +.table thead:first-child tr th,.table thead:first-child tr td{border-top:0;} +.table tbody+tbody{border-top:2px solid #ddd;} +.table-condensed th,.table-condensed td{padding:4px 5px;} +.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapsed;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th+th,.table-bordered td+td,.table-bordered th+td,.table-bordered td+th{border-left:1px solid #ddd;} +.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;} +.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;} +.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;} +.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;} +.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;} +.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;} +table .span1{float:none;width:44px;margin-left:0;} +table .span2{float:none;width:124px;margin-left:0;} +table .span3{float:none;width:204px;margin-left:0;} +table .span4{float:none;width:284px;margin-left:0;} +table .span5{float:none;width:364px;margin-left:0;} +table .span6{float:none;width:444px;margin-left:0;} +table .span7{float:none;width:524px;margin-left:0;} +table .span8{float:none;width:604px;margin-left:0;} +table .span9{float:none;width:684px;margin-left:0;} +table .span10{float:none;width:764px;margin-left:0;} +table .span11{float:none;width:844px;margin-left:0;} +table .span12{float:none;width:924px;margin-left:0;} +.btn{display:inline-block;padding:4px 10px 4px;font-size:13px;line-height:18px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);background-color:#fafafa;background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);background-image:-ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);border:1px solid #ccc;border-bottom-color:#bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);cursor:pointer;*margin-left:.3em;}.btn:first-child{*margin-left:0;} +.btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;} +.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} +.btn.active,.btn:active{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);background-color:#e6e6e6;background-color:#d9d9d9 \9;color:rgba(0, 0, 0, 0.5);outline:0;} +.btn.disabled,.btn[disabled]{cursor:default;background-image:none;background-color:#e6e6e6;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} +.btn-large .icon{margin-top:1px;} +.btn-small{padding:5px 9px;font-size:11px;line-height:16px;} +.btn-small .icon{margin-top:-1px;} +.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover{text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;} +.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active{color:rgba(255, 255, 255, 0.75);} +.btn-primary{background-color:#006dcc;background-image:-moz-linear-gradient(top, #0088cc, #0044cc);background-image:-ms-linear-gradient(top, #0088cc, #0044cc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));background-image:-webkit-linear-gradient(top, #0088cc, #0044cc);background-image:-o-linear-gradient(top, #0088cc, #0044cc);background-image:linear-gradient(top, #0088cc, #0044cc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);border-color:#0044cc #0044cc #002a80;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#0044cc;} +.btn-primary:active,.btn-primary.active{background-color:#003399 \9;} +.btn-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;} +.btn-warning:active,.btn-warning.active{background-color:#c67605 \9;} +.btn-danger{background-color:#da4f49;background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:-ms-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(top, #ee5f5b, #bd362f);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;} +.btn-danger:active,.btn-danger.active{background-color:#942a25 \9;} +.btn-success{background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;} +.btn-success:active,.btn-success.active{background-color:#408140 \9;} +.btn-info{background-color:#49afcd;background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:-ms-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(top, #5bc0de, #2f96b4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;} +.btn-info:active,.btn-info.active{background-color:#24748c \9;} +button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;} +button.btn.large,input[type="submit"].btn.large{*padding-top:7px;*padding-bottom:7px;} +button.btn.small,input[type="submit"].btn.small{*padding-top:3px;*padding-bottom:3px;} +.nav{margin-left:0;margin-bottom:18px;list-style:none;} +.nav>li>a{display:block;} +.nav>li>a:hover{text-decoration:none;background-color:#eeeeee;} +.nav-list{padding-left:14px;padding-right:14px;margin-bottom:0;} +.nav-list>li>a,.nav-list .nav-header{display:block;padding:3px 15px;margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);} +.nav-list .nav-header{font-size:11px;font-weight:bold;line-height:18px;color:#999999;text-transform:uppercase;} +.nav-list>li+.nav-header{margin-top:9px;} +.nav-list .active>a{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;} +.nav-list .icon{margin-right:2px;} +.nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";} +.nav-tabs:after,.nav-pills:after{clear:both;} +.nav-tabs>li,.nav-pills>li{float:left;} +.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;} +.nav-tabs{border-bottom:1px solid #ddd;} +.nav-tabs>li{margin-bottom:-1px;} +.nav-tabs>li>a{padding-top:9px;padding-bottom:9px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;} +.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;} +.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} +.nav-pills .active>a,.nav-pills .active>a:hover{color:#ffffff;background-color:#0088cc;} +.nav-stacked>li{float:none;} +.nav-stacked>li>a{margin-right:0;} +.nav-tabs.nav-stacked{border-bottom:0;} +.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;} +.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;} +.nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;} +.nav-pills.nav-stacked>li>a{margin-bottom:3px;} +.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;} +.nav-tabs .dropdown-menu,.nav-pills .dropdown-menu{margin-top:1px;border-width:1px;} +.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{border-top-color:#0088cc;margin-top:6px;} +.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;} +.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333333;} +.nav>.dropdown.active>a:hover{color:#000000;cursor:pointer;} +.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;} +.nav .open .caret,.nav .open.active .caret,.nav .open a:hover .caret{border-top-color:#ffffff;opacity:1;filter:alpha(opacity=100);} +.tabs-stacked .open>a:hover{border-color:#999999;} +.tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";} +.tabbable:after{clear:both;} +.tabs-below .nav-tabs,.tabs-right .nav-tabs,.tabs-left .nav-tabs{border-bottom:0;} +.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;} +.tab-content>.active,.pill-content>.active{display:block;} +.tabs-below .nav-tabs{border-top:1px solid #ddd;} +.tabs-below .nav-tabs>li{margin-top:-1px;margin-bottom:0;} +.tabs-below .nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below .nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;} +.tabs-below .nav-tabs .active>a,.tabs-below .nav-tabs .active>a:hover{border-color:transparent #ddd #ddd #ddd;} +.tabs-left .nav-tabs>li,.tabs-right .nav-tabs>li{float:none;} +.tabs-left .nav-tabs>li>a,.tabs-right .nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;} +.tabs-left .nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;} +.tabs-left .nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;} +.tabs-left .nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;} +.tabs-left .nav-tabs .active>a,.tabs-left .nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;} +.tabs-right .nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;} +.tabs-right .nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;} +.tabs-right .nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;} +.tabs-right .nav-tabs .active>a,.tabs-right .nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;} +.navbar{overflow:visible;margin-bottom:18px;} +.navbar-inner{padding-left:20px;padding-right:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);} +.btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);}.btn-navbar:hover,.btn-navbar:active,.btn-navbar.active,.btn-navbar.disabled,.btn-navbar[disabled]{background-color:#222222;} +.btn-navbar:active,.btn-navbar.active{background-color:#080808 \9;} +.btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);} +.btn-navbar .icon-bar+.icon-bar{margin-top:3px;} +.nav-collapse.collapse{height:auto;} +.navbar .brand:hover{text-decoration:none;} +.navbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#ffffff;} +.navbar .navbar-text{margin-bottom:0;line-height:40px;color:#999999;}.navbar .navbar-text a:hover{color:#ffffff;background-color:transparent;} +.navbar .btn,.navbar .btn-group{margin-top:5px;} +.navbar .btn-group .btn{margin-top:0;} +.navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";} +.navbar-form:after{clear:both;} +.navbar-form input,.navbar-form select{display:inline-block;margin-top:5px;margin-bottom:0;} +.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;} +.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;} +.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0;}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#ffffff;color:rgba(255, 255, 255, 0.75);background:#666;background:rgba(255, 255, 255, 0.3);border:1px solid #111;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.navbar-search .search-query :-moz-placeholder{color:#eeeeee;} +.navbar-search .search-query ::-webkit-input-placeholder{color:#eeeeee;} +.navbar-search .search-query:hover{color:#ffffff;background-color:#999999;background-color:rgba(255, 255, 255, 0.5);} +.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;} +.navbar-fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030;} +.navbar-fixed-top .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;} +.navbar .nav.pull-right{float:right;} +.navbar .nav>li{display:block;float:left;} +.navbar .nav>li>a{float:none;padding:10px 10px 11px;line-height:19px;color:#999999;text-decoration:none;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);} +.navbar .nav>li>a:hover{background-color:transparent;color:#ffffff;text-decoration:none;} +.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#ffffff;text-decoration:none;background-color:#222222;background-color:rgba(0, 0, 0, 0.5);} +.navbar .divider-vertical{height:40px;width:1px;margin:0 9px;overflow:hidden;background-color:#222222;border-right:1px solid #333333;} +.navbar .nav.pull-right{margin-left:10px;margin-right:0;} +.navbar .dropdown-menu{margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.navbar .dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;} +.navbar .dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;} +.navbar .nav .dropdown-toggle .caret,.navbar .nav .open.dropdown .caret{border-top-color:#ffffff;} +.navbar .nav .active .caret{opacity:1;filter:alpha(opacity=100);} +.navbar .nav .open>.dropdown-toggle,.navbar .nav .active>.dropdown-toggle,.navbar .nav .open.active>.dropdown-toggle{background-color:transparent;} +.navbar .nav .active>.dropdown-toggle:hover{color:#ffffff;} +.navbar .nav.pull-right .dropdown-menu{left:auto;right:0;}.navbar .nav.pull-right .dropdown-menu:before{left:auto;right:12px;} +.navbar .nav.pull-right .dropdown-menu:after{left:auto;right:13px;} +.hero-unit{padding:60px;margin-bottom:30px;background-color:#f5f5f5;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;} +.hero-unit p{font-size:18px;font-weight:200;line-height:27px;} +.pull-right{float:right;} +.pull-left{float:left;} +.hide{display:none;} +.show{display:block;} +.invisible{visibility:hidden;} +#call-to-action{text-align:right;} +a.headerlink{display:none;} +#title{color:#ffffff;} +.hero-unit h1{padding-bottom:20px ! important;} +#top-bar small{color:#f8f8ff;text-shadow:0px -1px 0px #5f0c17;} +.admonition{padding:14px 35px 14px 14px;margin-bottom:18px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.admonition .admonition-title{font-size:14pt;font-weight:bold;} +.admonition.note .admonition-title,.admonition-todo .admonition-title{color:#c09853;} +.admonition.tip,.admonition.hint{background-color:#dff0d8;border-color:#d6e9c6;} +.admonition.tip .admonition-title,.admonition.hint .admonition-title{color:#468847;} +.admonition.error,.admonition.warning,.admonition.caution,.admonition.danger,.admonition.attention{background-color:#f2dede;border-color:#eed3d7;} +.admonition.error .admonition-title,.admonition.warning .admonition-title,.admonition.caution .admonition-title,.admonition.danger .admonition-title,.admonition.attention .admonition-title{color:#b94a48;} +.admonition.important{background-color:#d9edf7;border-color:#bce8f1;} +.admonition.important .admonition-title{color:#3a87ad;} +.admonition>p,.admonition>ul{margin-bottom:0;} +.admonition p+p{margin-top:5px;} +a.internal.reference>em{font-style:normal ! important;text-decoration:none ! important;} +tt{padding:0 3px 2px;font-family:"Panic Sans",Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;} +.section>p,.section ul li,.admonition p,.section dt,.section dl{font-size:13pt;line-height:18pt;} +.section tt{font-size:11pt;line-height:11pt;} +.section>*{margin-bottom:20px;} +pre{font-family:'Panic Sans',Menlo,Monaco,Consolas,Andale Mono,Courier New,monospace !important;font-size:12pt !important;line-height:22px !important;display:block !important;width:auto !important;height:auto !important;overflow:auto !important;white-space:pre !important;word-wrap:normal !important;} +#body h1,h1 tt{font-size:28pt;} +h1 tt{background-color:transparent;font-size:26pt !important;} +#body h2{font-size:24pt;} +h2 tt{background-color:transparent;font-size:22pt !important;} +#body h3{font-size:20pt;} +h3 tt{background-color:transparent;font-size:18pt !important;} +#body h4{font-size:16pt;} +h4 tt{background-color:transparent;font-size:14pt !important;} +#sidebar tt{color:#08c;background-color:transparent;} +.hero-unit .toctree-wrapper{text-align:center;} +.hero-unit li{display:inline;list-style-type:none;padding-right:20px;} +.hero-unit li a{display:inline-block;padding:4px 10px 4px;font-size:13px;line-height:18px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);background-color:#fafafa;background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);background-image:-ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);border:1px solid #ccc;border-bottom-color:#bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);cursor:pointer;*margin-left:.3em;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);padding:10px 10px 10px;font-size:16pt;}.hero-unit li a:first-child{*margin-left:0;} +.hero-unit li a:hover,.hero-unit li a:active,.hero-unit li a.active,.hero-unit li a.disabled,.hero-unit li a[disabled]{background-color:#51a351;} +.hero-unit li a:active,.hero-unit li a.active{background-color:#408140 \9;} +.hero-unit li a:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.hero-unit li a:hover:hover,.hero-unit li a:hover:active,.hero-unit li a:hover.active,.hero-unit li a:hover.disabled,.hero-unit li a:hover[disabled]{background-color:#51a351;} +.hero-unit li a:hover:active,.hero-unit li a:hover.active{background-color:#408140 \9;} +.hero-unit li a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.hero-unit li a:focus:hover,.hero-unit li a:focus:active,.hero-unit li a:focus.active,.hero-unit li a:focus.disabled,.hero-unit li a:focus[disabled]{background-color:#51a351;} +.hero-unit li a:focus:active,.hero-unit li a:focus.active{background-color:#408140 \9;} +.hero-unit li a:active{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);background-color:#e6e6e6;background-color:#d9d9d9 \9;color:rgba(0, 0, 0, 0.5);outline:0;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.hero-unit li a:active:hover,.hero-unit li a:active:active,.hero-unit li a:active.active,.hero-unit li a:active.disabled,.hero-unit li a:active[disabled]{background-color:#51a351;} +.hero-unit li a:active:active,.hero-unit li a:active.active{background-color:#408140 \9;} +.hero-unit li a:after{content:" »";} +table.docutils{border:1px solid #DDD;width:100%;margin-bottom:18px;}table.docutils th,table.docutils td{padding:16px;line-height:18px;text-align:left;border-top:1px solid #ddd;} +table.docutils th{font-weight:bold;vertical-align:bottom;} +table.docutils td{vertical-align:top;} +table.docutils thead:first-child tr th,table.docutils thead:first-child tr td{border-top:0;} +table.docutils tbody+tbody{border-top:2px solid #ddd;} +table.docutils tbody tr:nth-child(odd) td,table.docutils tbody tr:nth-child(odd) th{background-color:#f9f9f9;} diff --git a/docs/source/_themes/yammerdoc/theme.conf b/docs/source/_themes/metrics/theme.conf similarity index 93% rename from docs/source/_themes/yammerdoc/theme.conf rename to docs/source/_themes/metrics/theme.conf index 23993a1000..175793bef8 100644 --- a/docs/source/_themes/yammerdoc/theme.conf +++ b/docs/source/_themes/metrics/theme.conf @@ -1,6 +1,6 @@ [theme] inherit = none -stylesheet = yammerdoc.css +stylesheet = metrics.css pygments_style = trac [options] diff --git a/docs/source/about/release-notes.rst b/docs/source/about/release-notes.rst index 0273fd0873..14eed496fb 100644 --- a/docs/source/about/release-notes.rst +++ b/docs/source/about/release-notes.rst @@ -9,6 +9,8 @@ Release Notes v3.0.0-BETA2-SNAPSHOT ===================== +* Metrics is now under the ``com.codahale.metrics`` package, with the corresponding changes in Maven + artifact groups. This should allow for an easier upgrade path without classpath conflicts. * ``MetricRegistry`` no longer has a name. * ``JmxReporter`` takes an optional domain property to disambiguate multiple reporters. * Fixed Java 6 compatibility problem. (Also added Java 6 as a CI environment.) diff --git a/docs/source/conf.py b/docs/source/conf.py index f4c46037de..3eac1838e4 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -91,7 +91,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'yammerdoc' +html_theme = 'metrics' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/docs/source/getting-started.rst b/docs/source/getting-started.rst index 4bc8ed2858..912f210076 100644 --- a/docs/source/getting-started.rst +++ b/docs/source/getting-started.rst @@ -21,7 +21,7 @@ Just add the ``metrics-core`` library as a dependency: - com.yammer.metrics + com.codahale.metrics metrics-core ${metrics.version} @@ -288,7 +288,7 @@ To use this servlet, include the ``metrics-servlets`` module as a dependency: .. code-block:: xml - com.yammer.metrics + com.codahale.metrics metrics-servlets ${metrics.version} diff --git a/docs/source/index.rst b/docs/source/index.rst index 82d41218df..a253f714a3 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -8,12 +8,9 @@ Metrics is a Java library which gives you unparalleled insight into what your code does in production. ###################################################################################################### -Developed by Yammer__ to instrument their JVM-based backend services, **Metrics** provides a -powerful toolkit of ways to measure the behavior of critical components +**Metrics** provides a powerful toolkit of ways to measure the behavior of critical components **in your production environment.** -.. __: https://www.yammer.com - With modules for common libraries like **Jetty**, **Logback**, **Log4j**, **Apache HttpClient**, **Ehcache**, **JDBI**, **Jersey** and reporting backends like **Ganglia** and **Graphite**, Metrics provides you with full-stack visibility. diff --git a/docs/source/manual/servlet.rst b/docs/source/manual/servlet.rst index 7f0e45197f..9f8505ea51 100644 --- a/docs/source/manual/servlet.rst +++ b/docs/source/manual/servlet.rst @@ -12,7 +12,7 @@ for the number of active requests, and a timer for request duration. You can use webappMetricsFilter - com.yammer.metrics.servlet.DefaultWebappMetricsFilter + com.codahale.metrics.servlet.DefaultWebappMetricsFilter webappMetricsFilter diff --git a/docs/source/manual/servlets.rst b/docs/source/manual/servlets.rst index 99e71a392d..23674d7f57 100644 --- a/docs/source/manual/servlets.rst +++ b/docs/source/manual/servlets.rst @@ -17,7 +17,7 @@ and returning ``501 Not Implemented`` if no health checks are registered, ``200 ``text/plain`` entity. If the servlet context has an attributed named -``com.yammer.metrics.servlet.HealthCheckServlet.registry`` which is a ``HealthCheckRegistry``, +``com.codahale.metrics.servlet.HealthCheckServlet.registry`` which is a ``HealthCheckRegistry``, ``HealthCheckServlet`` will use that instead of the default ``HealthCheckRegistry``. .. _man-servlet-threaddump: @@ -37,7 +37,7 @@ MetricsServlet ``MetricsServlet`` exposes the state of the metrics in a particular registry as a JSON object. If the servlet context has an attributed named -``com.yammer.metrics.servlet.MetricsServlet.registry`` which is a ``MetricsRegistry``, +``com.codahale.metrics.servlet.MetricsServlet.registry`` which is a ``MetricsRegistry``, ``MetricsServlet`` will use that instead of the default ``MetricsRegistry``. ``MetricsServlet`` also takes an initialization parameter, ``show-jvm-metrics``, which if ``"false"`` will diff --git a/metrics-annotation/pom.xml b/metrics-annotation/pom.xml index 39f48a32b0..9c28a546dc 100644 --- a/metrics-annotation/pom.xml +++ b/metrics-annotation/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - com.yammer.metrics + com.codahale.metrics metrics-parent 3.0.0-BETA2-SNAPSHOT diff --git a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/ExceptionMetered.java b/metrics-annotation/src/main/java/com/codahale/metrics/annotation/ExceptionMetered.java similarity index 97% rename from metrics-annotation/src/main/java/com/yammer/metrics/annotation/ExceptionMetered.java rename to metrics-annotation/src/main/java/com/codahale/metrics/annotation/ExceptionMetered.java index 976010ae2c..39deb5ed80 100644 --- a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/ExceptionMetered.java +++ b/metrics-annotation/src/main/java/com/codahale/metrics/annotation/ExceptionMetered.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.annotation; +package com.codahale.metrics.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Gauge.java b/metrics-annotation/src/main/java/com/codahale/metrics/annotation/Gauge.java similarity index 95% rename from metrics-annotation/src/main/java/com/yammer/metrics/annotation/Gauge.java rename to metrics-annotation/src/main/java/com/codahale/metrics/annotation/Gauge.java index f4ded680bd..0282b7c215 100644 --- a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Gauge.java +++ b/metrics-annotation/src/main/java/com/codahale/metrics/annotation/Gauge.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.annotation; +package com.codahale.metrics.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Metered.java b/metrics-annotation/src/main/java/com/codahale/metrics/annotation/Metered.java similarity index 96% rename from metrics-annotation/src/main/java/com/yammer/metrics/annotation/Metered.java rename to metrics-annotation/src/main/java/com/codahale/metrics/annotation/Metered.java index 5243cbc40b..31c2dcd189 100644 --- a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Metered.java +++ b/metrics-annotation/src/main/java/com/codahale/metrics/annotation/Metered.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.annotation; +package com.codahale.metrics.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Timed.java b/metrics-annotation/src/main/java/com/codahale/metrics/annotation/Timed.java similarity index 96% rename from metrics-annotation/src/main/java/com/yammer/metrics/annotation/Timed.java rename to metrics-annotation/src/main/java/com/codahale/metrics/annotation/Timed.java index aa1f36cac3..f2f729f366 100644 --- a/metrics-annotation/src/main/java/com/yammer/metrics/annotation/Timed.java +++ b/metrics-annotation/src/main/java/com/codahale/metrics/annotation/Timed.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.annotation; +package com.codahale.metrics.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/metrics-core/pom.xml b/metrics-core/pom.xml index 720e5e4c32..07a03d94b8 100644 --- a/metrics-core/pom.xml +++ b/metrics-core/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - com.yammer.metrics + com.codahale.metrics metrics-parent 3.0.0-BETA2-SNAPSHOT @@ -13,8 +13,7 @@ bundle Metrics is a Java library which gives you unparalleled insight into what your code does in - production. Developed by Yammer to instrument their JVM-based backend services, Metrics - provides a powerful toolkit of ways to measure the behavior of critical components in your - production environment. + production. Metrics provides a powerful toolkit of ways to measure the behavior of critical + components in your production environment. diff --git a/metrics-core/src/main/java/com/yammer/metrics/CachedGauge.java b/metrics-core/src/main/java/com/codahale/metrics/CachedGauge.java similarity index 98% rename from metrics-core/src/main/java/com/yammer/metrics/CachedGauge.java rename to metrics-core/src/main/java/com/codahale/metrics/CachedGauge.java index f7a4eacd8f..565e34ee33 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/CachedGauge.java +++ b/metrics-core/src/main/java/com/codahale/metrics/CachedGauge.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; diff --git a/metrics-core/src/main/java/com/yammer/metrics/Clock.java b/metrics-core/src/main/java/com/codahale/metrics/Clock.java similarity index 97% rename from metrics-core/src/main/java/com/yammer/metrics/Clock.java rename to metrics-core/src/main/java/com/codahale/metrics/Clock.java index e61c785cd4..2fb7b1e39c 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Clock.java +++ b/metrics-core/src/main/java/com/codahale/metrics/Clock.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import java.lang.management.ManagementFactory; import java.lang.management.ThreadMXBean; diff --git a/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java b/metrics-core/src/main/java/com/codahale/metrics/ConsoleReporter.java similarity index 99% rename from metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java rename to metrics-core/src/main/java/com/codahale/metrics/ConsoleReporter.java index d04c3a5fbe..52745692c3 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/ConsoleReporter.java +++ b/metrics-core/src/main/java/com/codahale/metrics/ConsoleReporter.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import java.io.PrintStream; import java.text.DateFormat; diff --git a/metrics-core/src/main/java/com/yammer/metrics/Counter.java b/metrics-core/src/main/java/com/codahale/metrics/Counter.java similarity index 96% rename from metrics-core/src/main/java/com/yammer/metrics/Counter.java rename to metrics-core/src/main/java/com/codahale/metrics/Counter.java index ba4205b8d6..762a81d684 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Counter.java +++ b/metrics-core/src/main/java/com/codahale/metrics/Counter.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; /** * An incrementing and decrementing counter metric. diff --git a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java b/metrics-core/src/main/java/com/codahale/metrics/CsvReporter.java similarity index 99% rename from metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java rename to metrics-core/src/main/java/com/codahale/metrics/CsvReporter.java index d4c71ab897..4319399520 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/CsvReporter.java +++ b/metrics-core/src/main/java/com/codahale/metrics/CsvReporter.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/metrics-core/src/main/java/com/yammer/metrics/DerivativeGauge.java b/metrics-core/src/main/java/com/codahale/metrics/DerivativeGauge.java similarity index 96% rename from metrics-core/src/main/java/com/yammer/metrics/DerivativeGauge.java rename to metrics-core/src/main/java/com/codahale/metrics/DerivativeGauge.java index a617facb87..c7d75dd3d6 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/DerivativeGauge.java +++ b/metrics-core/src/main/java/com/codahale/metrics/DerivativeGauge.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; /** * A gauge whose value is derived from the value of another gauge. diff --git a/metrics-core/src/main/java/com/yammer/metrics/EWMA.java b/metrics-core/src/main/java/com/codahale/metrics/EWMA.java similarity index 99% rename from metrics-core/src/main/java/com/yammer/metrics/EWMA.java rename to metrics-core/src/main/java/com/codahale/metrics/EWMA.java index 4e33663904..0e82fe44c5 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/EWMA.java +++ b/metrics-core/src/main/java/com/codahale/metrics/EWMA.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import java.util.concurrent.TimeUnit; diff --git a/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingReservoir.java b/metrics-core/src/main/java/com/codahale/metrics/ExponentiallyDecayingReservoir.java similarity index 99% rename from metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingReservoir.java rename to metrics-core/src/main/java/com/codahale/metrics/ExponentiallyDecayingReservoir.java index 8b31da979c..89ab188339 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/ExponentiallyDecayingReservoir.java +++ b/metrics-core/src/main/java/com/codahale/metrics/ExponentiallyDecayingReservoir.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import java.util.ArrayList; import java.util.concurrent.ConcurrentSkipListMap; diff --git a/metrics-core/src/main/java/com/yammer/metrics/Gauge.java b/metrics-core/src/main/java/com/codahale/metrics/Gauge.java similarity index 95% rename from metrics-core/src/main/java/com/yammer/metrics/Gauge.java rename to metrics-core/src/main/java/com/codahale/metrics/Gauge.java index dbd23ed83c..b6b7b3d8b7 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Gauge.java +++ b/metrics-core/src/main/java/com/codahale/metrics/Gauge.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; /** diff --git a/metrics-core/src/main/java/com/yammer/metrics/Histogram.java b/metrics-core/src/main/java/com/codahale/metrics/Histogram.java similarity index 97% rename from metrics-core/src/main/java/com/yammer/metrics/Histogram.java rename to metrics-core/src/main/java/com/codahale/metrics/Histogram.java index 5b6aa25daf..8e8a9c56a6 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Histogram.java +++ b/metrics-core/src/main/java/com/codahale/metrics/Histogram.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; /** * A metric which calculates the distribution of a value. diff --git a/metrics-core/src/main/java/com/yammer/metrics/JmxAttributeGauge.java b/metrics-core/src/main/java/com/codahale/metrics/JmxAttributeGauge.java similarity index 98% rename from metrics-core/src/main/java/com/yammer/metrics/JmxAttributeGauge.java rename to metrics-core/src/main/java/com/codahale/metrics/JmxAttributeGauge.java index 70830566e5..2894048057 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/JmxAttributeGauge.java +++ b/metrics-core/src/main/java/com/codahale/metrics/JmxAttributeGauge.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import javax.management.JMException; import javax.management.MBeanServer; diff --git a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java b/metrics-core/src/main/java/com/codahale/metrics/JmxReporter.java similarity index 99% rename from metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java rename to metrics-core/src/main/java/com/codahale/metrics/JmxReporter.java index fab2bd7927..272d265f76 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/JmxReporter.java +++ b/metrics-core/src/main/java/com/codahale/metrics/JmxReporter.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/metrics-core/src/main/java/com/yammer/metrics/JvmAttributeGaugeSet.java b/metrics-core/src/main/java/com/codahale/metrics/JvmAttributeGaugeSet.java similarity index 98% rename from metrics-core/src/main/java/com/yammer/metrics/JvmAttributeGaugeSet.java rename to metrics-core/src/main/java/com/codahale/metrics/JvmAttributeGaugeSet.java index 035711add6..20c6299fbb 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/JvmAttributeGaugeSet.java +++ b/metrics-core/src/main/java/com/codahale/metrics/JvmAttributeGaugeSet.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; diff --git a/metrics-core/src/main/java/com/yammer/metrics/LongAdder.java b/metrics-core/src/main/java/com/codahale/metrics/LongAdder.java similarity index 99% rename from metrics-core/src/main/java/com/yammer/metrics/LongAdder.java rename to metrics-core/src/main/java/com/codahale/metrics/LongAdder.java index 00f1b44755..5299ee9929 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/LongAdder.java +++ b/metrics-core/src/main/java/com/codahale/metrics/LongAdder.java @@ -6,7 +6,7 @@ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/LongAdder.java?revision=1.14&view=markup */ -package com.yammer.metrics; +package com.codahale.metrics; import java.io.Serializable; import java.util.concurrent.atomic.AtomicLong; diff --git a/metrics-core/src/main/java/com/yammer/metrics/Meter.java b/metrics-core/src/main/java/com/codahale/metrics/Meter.java similarity index 98% rename from metrics-core/src/main/java/com/yammer/metrics/Meter.java rename to metrics-core/src/main/java/com/codahale/metrics/Meter.java index b2df895918..af944f268a 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Meter.java +++ b/metrics-core/src/main/java/com/codahale/metrics/Meter.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; diff --git a/metrics-core/src/main/java/com/yammer/metrics/Metered.java b/metrics-core/src/main/java/com/codahale/metrics/Metered.java similarity index 98% rename from metrics-core/src/main/java/com/yammer/metrics/Metered.java rename to metrics-core/src/main/java/com/codahale/metrics/Metered.java index 99c84e8dd4..afb8c7c7c3 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Metered.java +++ b/metrics-core/src/main/java/com/codahale/metrics/Metered.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; /** * An object which maintains mean and exponentially-weighted rate. diff --git a/metrics-core/src/main/java/com/yammer/metrics/Metric.java b/metrics-core/src/main/java/com/codahale/metrics/Metric.java similarity index 76% rename from metrics-core/src/main/java/com/yammer/metrics/Metric.java rename to metrics-core/src/main/java/com/codahale/metrics/Metric.java index 370110d857..173c6f45b1 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Metric.java +++ b/metrics-core/src/main/java/com/codahale/metrics/Metric.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; /** * A tag interface to indicate that a class is a metric. diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricFilter.java b/metrics-core/src/main/java/com/codahale/metrics/MetricFilter.java similarity index 95% rename from metrics-core/src/main/java/com/yammer/metrics/MetricFilter.java rename to metrics-core/src/main/java/com/codahale/metrics/MetricFilter.java index 4e5a42741c..12390466f7 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricFilter.java +++ b/metrics-core/src/main/java/com/codahale/metrics/MetricFilter.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; /** * A filter used to determine whether or not a metric should be reported, among other things. diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java b/metrics-core/src/main/java/com/codahale/metrics/MetricRegistry.java similarity index 99% rename from metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java rename to metrics-core/src/main/java/com/codahale/metrics/MetricRegistry.java index cd6f2e3038..2af12639f7 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistry.java +++ b/metrics-core/src/main/java/com/codahale/metrics/MetricRegistry.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import java.util.*; import java.util.concurrent.ConcurrentHashMap; diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistryListener.java b/metrics-core/src/main/java/com/codahale/metrics/MetricRegistryListener.java similarity index 99% rename from metrics-core/src/main/java/com/yammer/metrics/MetricRegistryListener.java rename to metrics-core/src/main/java/com/codahale/metrics/MetricRegistryListener.java index 002cdb4a69..0625db7aa4 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricRegistryListener.java +++ b/metrics-core/src/main/java/com/codahale/metrics/MetricRegistryListener.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import java.util.EventListener; diff --git a/metrics-core/src/main/java/com/yammer/metrics/MetricSet.java b/metrics-core/src/main/java/com/codahale/metrics/MetricSet.java similarity index 90% rename from metrics-core/src/main/java/com/yammer/metrics/MetricSet.java rename to metrics-core/src/main/java/com/codahale/metrics/MetricSet.java index 4dba8af8fc..6c99301900 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/MetricSet.java +++ b/metrics-core/src/main/java/com/codahale/metrics/MetricSet.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import java.util.Map; diff --git a/metrics-core/src/main/java/com/yammer/metrics/RatioGauge.java b/metrics-core/src/main/java/com/codahale/metrics/RatioGauge.java similarity index 98% rename from metrics-core/src/main/java/com/yammer/metrics/RatioGauge.java rename to metrics-core/src/main/java/com/codahale/metrics/RatioGauge.java index a94724cc64..182155f89f 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/RatioGauge.java +++ b/metrics-core/src/main/java/com/codahale/metrics/RatioGauge.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import static java.lang.Double.isInfinite; import static java.lang.Double.isNaN; diff --git a/metrics-core/src/main/java/com/yammer/metrics/Reservoir.java b/metrics-core/src/main/java/com/codahale/metrics/Reservoir.java similarity index 94% rename from metrics-core/src/main/java/com/yammer/metrics/Reservoir.java rename to metrics-core/src/main/java/com/codahale/metrics/Reservoir.java index 368a1e3009..bc7c4a057e 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Reservoir.java +++ b/metrics-core/src/main/java/com/codahale/metrics/Reservoir.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; /** * A statistically representative reservoir of a data stream. diff --git a/metrics-core/src/main/java/com/yammer/metrics/Sampling.java b/metrics-core/src/main/java/com/codahale/metrics/Sampling.java similarity index 87% rename from metrics-core/src/main/java/com/yammer/metrics/Sampling.java rename to metrics-core/src/main/java/com/codahale/metrics/Sampling.java index 8cb9effed2..983ff26b6c 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Sampling.java +++ b/metrics-core/src/main/java/com/codahale/metrics/Sampling.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; /** * An object which samples values. diff --git a/metrics-core/src/main/java/com/yammer/metrics/ScheduledReporter.java b/metrics-core/src/main/java/com/codahale/metrics/ScheduledReporter.java similarity index 97% rename from metrics-core/src/main/java/com/yammer/metrics/ScheduledReporter.java rename to metrics-core/src/main/java/com/codahale/metrics/ScheduledReporter.java index 88c207e337..86b0af0958 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/ScheduledReporter.java +++ b/metrics-core/src/main/java/com/codahale/metrics/ScheduledReporter.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import java.util.Locale; import java.util.SortedMap; @@ -54,7 +54,7 @@ public Thread newThread(Runnable r) { /** * Creates a new {@link ScheduledReporter} instance. * - * @param registry the {@link com.yammer.metrics.MetricRegistry} containing the metrics this + * @param registry the {@link com.codahale.metrics.MetricRegistry} containing the metrics this * reporter will report * @param name the reporter's name * @param filter the filter for which metrics to report diff --git a/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java b/metrics-core/src/main/java/com/codahale/metrics/Slf4jReporter.java similarity index 99% rename from metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java rename to metrics-core/src/main/java/com/codahale/metrics/Slf4jReporter.java index da3bf06a40..d1bb8164d7 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Slf4jReporter.java +++ b/metrics-core/src/main/java/com/codahale/metrics/Slf4jReporter.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowReservoir.java b/metrics-core/src/main/java/com/codahale/metrics/SlidingTimeWindowReservoir.java similarity index 98% rename from metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowReservoir.java rename to metrics-core/src/main/java/com/codahale/metrics/SlidingTimeWindowReservoir.java index df6ffbdc84..8d3ad109b8 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/SlidingTimeWindowReservoir.java +++ b/metrics-core/src/main/java/com/codahale/metrics/SlidingTimeWindowReservoir.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.TimeUnit; diff --git a/metrics-core/src/main/java/com/yammer/metrics/SlidingWindowReservoir.java b/metrics-core/src/main/java/com/codahale/metrics/SlidingWindowReservoir.java similarity index 97% rename from metrics-core/src/main/java/com/yammer/metrics/SlidingWindowReservoir.java rename to metrics-core/src/main/java/com/codahale/metrics/SlidingWindowReservoir.java index 43cd153518..3cd5f1aded 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/SlidingWindowReservoir.java +++ b/metrics-core/src/main/java/com/codahale/metrics/SlidingWindowReservoir.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLongArray; diff --git a/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java b/metrics-core/src/main/java/com/codahale/metrics/Snapshot.java similarity index 99% rename from metrics-core/src/main/java/com/yammer/metrics/Snapshot.java rename to metrics-core/src/main/java/com/codahale/metrics/Snapshot.java index 9a4396e028..aa20b3ebcc 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Snapshot.java +++ b/metrics-core/src/main/java/com/codahale/metrics/Snapshot.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import java.io.OutputStream; import java.io.OutputStreamWriter; diff --git a/metrics-core/src/main/java/com/yammer/metrics/Striped64.java b/metrics-core/src/main/java/com/codahale/metrics/Striped64.java similarity index 99% rename from metrics-core/src/main/java/com/yammer/metrics/Striped64.java rename to metrics-core/src/main/java/com/codahale/metrics/Striped64.java index ec768f0f21..73a5d675a5 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Striped64.java +++ b/metrics-core/src/main/java/com/codahale/metrics/Striped64.java @@ -6,7 +6,7 @@ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/Striped64.java?revision=1.8&view=markup */ -package com.yammer.metrics; +package com.codahale.metrics; import java.util.Random; diff --git a/metrics-core/src/main/java/com/yammer/metrics/ThreadLocalRandom.java b/metrics-core/src/main/java/com/codahale/metrics/ThreadLocalRandom.java similarity index 99% rename from metrics-core/src/main/java/com/yammer/metrics/ThreadLocalRandom.java rename to metrics-core/src/main/java/com/codahale/metrics/ThreadLocalRandom.java index 28c3165ce0..14dd2642f9 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/ThreadLocalRandom.java +++ b/metrics-core/src/main/java/com/codahale/metrics/ThreadLocalRandom.java @@ -6,7 +6,7 @@ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/ThreadLocalRandom.java?view=markup */ -package com.yammer.metrics; +package com.codahale.metrics; import java.util.Random; diff --git a/metrics-core/src/main/java/com/yammer/metrics/Timer.java b/metrics-core/src/main/java/com/codahale/metrics/Timer.java similarity index 99% rename from metrics-core/src/main/java/com/yammer/metrics/Timer.java rename to metrics-core/src/main/java/com/codahale/metrics/Timer.java index d1992453ae..03ea7326b7 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/Timer.java +++ b/metrics-core/src/main/java/com/codahale/metrics/Timer.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import java.io.Closeable; import java.util.concurrent.Callable; diff --git a/metrics-core/src/main/java/com/yammer/metrics/UniformReservoir.java b/metrics-core/src/main/java/com/codahale/metrics/UniformReservoir.java similarity index 98% rename from metrics-core/src/main/java/com/yammer/metrics/UniformReservoir.java rename to metrics-core/src/main/java/com/codahale/metrics/UniformReservoir.java index cd37029b3b..d18a22fa12 100644 --- a/metrics-core/src/main/java/com/yammer/metrics/UniformReservoir.java +++ b/metrics-core/src/main/java/com/codahale/metrics/UniformReservoir.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import java.util.ArrayList; import java.util.List; diff --git a/metrics-core/src/test/java/com/yammer/metrics/CachedGaugeTest.java b/metrics-core/src/test/java/com/codahale/metrics/CachedGaugeTest.java similarity index 97% rename from metrics-core/src/test/java/com/yammer/metrics/CachedGaugeTest.java rename to metrics-core/src/test/java/com/codahale/metrics/CachedGaugeTest.java index bef87eaa1b..7e36681065 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/CachedGaugeTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/CachedGaugeTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/ClockTest.java b/metrics-core/src/test/java/com/codahale/metrics/ClockTest.java similarity index 97% rename from metrics-core/src/test/java/com/yammer/metrics/ClockTest.java rename to metrics-core/src/test/java/com/codahale/metrics/ClockTest.java index a61a16f8d8..61e61aaa46 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/ClockTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/ClockTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/ConsoleReporterTest.java b/metrics-core/src/test/java/com/codahale/metrics/ConsoleReporterTest.java similarity index 99% rename from metrics-core/src/test/java/com/yammer/metrics/ConsoleReporterTest.java rename to metrics-core/src/test/java/com/codahale/metrics/ConsoleReporterTest.java index 6382631aba..3e27f5bb2a 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/ConsoleReporterTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/ConsoleReporterTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Before; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/CounterTest.java b/metrics-core/src/test/java/com/codahale/metrics/CounterTest.java similarity index 97% rename from metrics-core/src/test/java/com/yammer/metrics/CounterTest.java rename to metrics-core/src/test/java/com/codahale/metrics/CounterTest.java index 1a56eaf7bd..0627c47871 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/CounterTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/CounterTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/CsvReporterTest.java b/metrics-core/src/test/java/com/codahale/metrics/CsvReporterTest.java similarity index 99% rename from metrics-core/src/test/java/com/yammer/metrics/CsvReporterTest.java rename to metrics-core/src/test/java/com/codahale/metrics/CsvReporterTest.java index 1d8b4309dd..c6398bece8 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/CsvReporterTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/CsvReporterTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Before; import org.junit.Rule; diff --git a/metrics-core/src/test/java/com/yammer/metrics/DerivativeGaugeTest.java b/metrics-core/src/test/java/com/codahale/metrics/DerivativeGaugeTest.java similarity index 95% rename from metrics-core/src/test/java/com/yammer/metrics/DerivativeGaugeTest.java rename to metrics-core/src/test/java/com/codahale/metrics/DerivativeGaugeTest.java index 572154cf2d..0927a61506 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/DerivativeGaugeTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/DerivativeGaugeTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/EWMATest.java b/metrics-core/src/test/java/com/codahale/metrics/EWMATest.java similarity index 99% rename from metrics-core/src/test/java/com/yammer/metrics/EWMATest.java rename to metrics-core/src/test/java/com/codahale/metrics/EWMATest.java index f5ae41915e..dec587ae9a 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/EWMATest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/EWMATest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/ExponentiallyDecayingReservoirTest.java b/metrics-core/src/test/java/com/codahale/metrics/ExponentiallyDecayingReservoirTest.java similarity index 99% rename from metrics-core/src/test/java/com/yammer/metrics/ExponentiallyDecayingReservoirTest.java rename to metrics-core/src/test/java/com/codahale/metrics/ExponentiallyDecayingReservoirTest.java index 4fde5fa6cd..8ea6e65e2e 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/ExponentiallyDecayingReservoirTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/ExponentiallyDecayingReservoirTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/HistogramTest.java b/metrics-core/src/test/java/com/codahale/metrics/HistogramTest.java similarity index 96% rename from metrics-core/src/test/java/com/yammer/metrics/HistogramTest.java rename to metrics-core/src/test/java/com/codahale/metrics/HistogramTest.java index 5c07fa3c33..ea6e87eac1 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/HistogramTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/HistogramTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/JmxAttributeGaugeTest.java b/metrics-core/src/test/java/com/codahale/metrics/JmxAttributeGaugeTest.java similarity index 97% rename from metrics-core/src/test/java/com/yammer/metrics/JmxAttributeGaugeTest.java rename to metrics-core/src/test/java/com/codahale/metrics/JmxAttributeGaugeTest.java index f8005c3cd5..d289660619 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/JmxAttributeGaugeTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/JmxAttributeGaugeTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/JmxReporterTest.java b/metrics-core/src/test/java/com/codahale/metrics/JmxReporterTest.java similarity index 99% rename from metrics-core/src/test/java/com/yammer/metrics/JmxReporterTest.java rename to metrics-core/src/test/java/com/codahale/metrics/JmxReporterTest.java index 4dae099655..f83681abc5 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/JmxReporterTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/JmxReporterTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.After; import org.junit.Before; diff --git a/metrics-core/src/test/java/com/yammer/metrics/JvmAttributeGaugeSetTest.java b/metrics-core/src/test/java/com/codahale/metrics/JvmAttributeGaugeSetTest.java similarity index 98% rename from metrics-core/src/test/java/com/yammer/metrics/JvmAttributeGaugeSetTest.java rename to metrics-core/src/test/java/com/codahale/metrics/JvmAttributeGaugeSetTest.java index 7113991d22..4aee7f85cb 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/JvmAttributeGaugeSetTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/JvmAttributeGaugeSetTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Before; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/MeterTest.java b/metrics-core/src/test/java/com/codahale/metrics/MeterTest.java similarity index 98% rename from metrics-core/src/test/java/com/yammer/metrics/MeterTest.java rename to metrics-core/src/test/java/com/codahale/metrics/MeterTest.java index 3df4cf45d6..676ac8e68e 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/MeterTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/MeterTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Before; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/MetricFilterTest.java b/metrics-core/src/test/java/com/codahale/metrics/MetricFilterTest.java similarity index 91% rename from metrics-core/src/test/java/com/yammer/metrics/MetricFilterTest.java rename to metrics-core/src/test/java/com/codahale/metrics/MetricFilterTest.java index fd0ce6327a..0ed73f763d 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/MetricFilterTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/MetricFilterTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/MetricRegistryListenerTest.java b/metrics-core/src/test/java/com/codahale/metrics/MetricRegistryListenerTest.java similarity index 98% rename from metrics-core/src/test/java/com/yammer/metrics/MetricRegistryListenerTest.java rename to metrics-core/src/test/java/com/codahale/metrics/MetricRegistryListenerTest.java index cecad65f0b..2d579ef5f8 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/MetricRegistryListenerTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/MetricRegistryListenerTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/MetricRegistryTest.java b/metrics-core/src/test/java/com/codahale/metrics/MetricRegistryTest.java similarity index 97% rename from metrics-core/src/test/java/com/yammer/metrics/MetricRegistryTest.java rename to metrics-core/src/test/java/com/codahale/metrics/MetricRegistryTest.java index 76800af968..49835efb7c 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/MetricRegistryTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/MetricRegistryTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Before; import org.junit.Test; @@ -6,7 +6,7 @@ import java.util.HashMap; import java.util.Map; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.data.MapEntry.entry; import static org.mockito.Mockito.*; @@ -323,7 +323,7 @@ public void elidesEmptyStringsFromNames() throws Exception { @Test public void concatenatesClassNamesWithStringsToFormADottedName() throws Exception { assertThat(name(MetricRegistryTest.class, "one", "two")) - .isEqualTo("com.yammer.metrics.MetricRegistryTest.one.two"); + .isEqualTo("com.codahale.metrics.MetricRegistryTest.one.two"); } @Test @@ -336,7 +336,7 @@ public String getValue() { }; assertThat(name(g.getClass(), "one", "two")) - .isEqualTo("com.yammer.metrics.MetricRegistryTest$5.one.two"); + .isEqualTo("com.codahale.metrics.MetricRegistryTest$5.one.two"); } @Test diff --git a/metrics-core/src/test/java/com/yammer/metrics/RatioGaugeTest.java b/metrics-core/src/test/java/com/codahale/metrics/RatioGaugeTest.java similarity index 98% rename from metrics-core/src/test/java/com/yammer/metrics/RatioGaugeTest.java rename to metrics-core/src/test/java/com/codahale/metrics/RatioGaugeTest.java index 47d571ea4d..e7c943d2ca 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/RatioGaugeTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/RatioGaugeTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/ScheduledReporterTest.java b/metrics-core/src/test/java/com/codahale/metrics/ScheduledReporterTest.java similarity index 98% rename from metrics-core/src/test/java/com/yammer/metrics/ScheduledReporterTest.java rename to metrics-core/src/test/java/com/codahale/metrics/ScheduledReporterTest.java index 7e529ea9c9..536f386126 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/ScheduledReporterTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/ScheduledReporterTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.After; import org.junit.Before; diff --git a/metrics-core/src/test/java/com/yammer/metrics/Slf4jReporterTest.java b/metrics-core/src/test/java/com/codahale/metrics/Slf4jReporterTest.java similarity index 99% rename from metrics-core/src/test/java/com/yammer/metrics/Slf4jReporterTest.java rename to metrics-core/src/test/java/com/codahale/metrics/Slf4jReporterTest.java index 0b4f321475..eb99b0aad7 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/Slf4jReporterTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/Slf4jReporterTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Test; import org.slf4j.Logger; diff --git a/metrics-core/src/test/java/com/yammer/metrics/SlidingTimeWindowReservoirTest.java b/metrics-core/src/test/java/com/codahale/metrics/SlidingTimeWindowReservoirTest.java similarity index 97% rename from metrics-core/src/test/java/com/yammer/metrics/SlidingTimeWindowReservoirTest.java rename to metrics-core/src/test/java/com/codahale/metrics/SlidingTimeWindowReservoirTest.java index 5f897aebef..cfdc74337b 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/SlidingTimeWindowReservoirTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/SlidingTimeWindowReservoirTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/SlidingWindowReservoirTest.java b/metrics-core/src/test/java/com/codahale/metrics/SlidingWindowReservoirTest.java similarity index 96% rename from metrics-core/src/test/java/com/yammer/metrics/SlidingWindowReservoirTest.java rename to metrics-core/src/test/java/com/codahale/metrics/SlidingWindowReservoirTest.java index 23935ae3c5..624611da65 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/SlidingWindowReservoirTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/SlidingWindowReservoirTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/SnapshotTest.java b/metrics-core/src/test/java/com/codahale/metrics/SnapshotTest.java similarity index 99% rename from metrics-core/src/test/java/com/yammer/metrics/SnapshotTest.java rename to metrics-core/src/test/java/com/codahale/metrics/SnapshotTest.java index 5dfd528d9e..5a1a409fe5 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/SnapshotTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/SnapshotTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/TimerTest.java b/metrics-core/src/test/java/com/codahale/metrics/TimerTest.java similarity index 98% rename from metrics-core/src/test/java/com/yammer/metrics/TimerTest.java rename to metrics-core/src/test/java/com/codahale/metrics/TimerTest.java index fbb4686278..e7ee5885da 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/TimerTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/TimerTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/UniformReservoirTest.java b/metrics-core/src/test/java/com/codahale/metrics/UniformReservoirTest.java similarity index 96% rename from metrics-core/src/test/java/com/yammer/metrics/UniformReservoirTest.java rename to metrics-core/src/test/java/com/codahale/metrics/UniformReservoirTest.java index 43c5dd28b4..51293b4039 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/UniformReservoirTest.java +++ b/metrics-core/src/test/java/com/codahale/metrics/UniformReservoirTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics; +package com.codahale.metrics; import org.junit.Test; diff --git a/metrics-core/src/test/java/com/yammer/metrics/benchmarks/CounterBenchmark.java b/metrics-core/src/test/java/com/codahale/metrics/benchmarks/CounterBenchmark.java similarity index 85% rename from metrics-core/src/test/java/com/yammer/metrics/benchmarks/CounterBenchmark.java rename to metrics-core/src/test/java/com/codahale/metrics/benchmarks/CounterBenchmark.java index f9994797b9..89de470a52 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/benchmarks/CounterBenchmark.java +++ b/metrics-core/src/test/java/com/codahale/metrics/benchmarks/CounterBenchmark.java @@ -1,8 +1,8 @@ -package com.yammer.metrics.benchmarks; +package com.codahale.metrics.benchmarks; import com.google.caliper.Runner; import com.google.caliper.SimpleBenchmark; -import com.yammer.metrics.Counter; +import com.codahale.metrics.Counter; public class CounterBenchmark extends SimpleBenchmark { public static void main(String[] args) throws Exception { diff --git a/metrics-core/src/test/java/com/yammer/metrics/benchmarks/MeterBenchmark.java b/metrics-core/src/test/java/com/codahale/metrics/benchmarks/MeterBenchmark.java similarity index 85% rename from metrics-core/src/test/java/com/yammer/metrics/benchmarks/MeterBenchmark.java rename to metrics-core/src/test/java/com/codahale/metrics/benchmarks/MeterBenchmark.java index f2fdb2f4cc..05dd7b2016 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/benchmarks/MeterBenchmark.java +++ b/metrics-core/src/test/java/com/codahale/metrics/benchmarks/MeterBenchmark.java @@ -1,8 +1,8 @@ -package com.yammer.metrics.benchmarks; +package com.codahale.metrics.benchmarks; import com.google.caliper.Runner; import com.google.caliper.SimpleBenchmark; -import com.yammer.metrics.Meter; +import com.codahale.metrics.Meter; public class MeterBenchmark extends SimpleBenchmark { public static void main(String[] args) throws Exception { diff --git a/metrics-core/src/test/java/com/yammer/metrics/benchmarks/ReservoirBenchmark.java b/metrics-core/src/test/java/com/codahale/metrics/benchmarks/ReservoirBenchmark.java similarity index 84% rename from metrics-core/src/test/java/com/yammer/metrics/benchmarks/ReservoirBenchmark.java rename to metrics-core/src/test/java/com/codahale/metrics/benchmarks/ReservoirBenchmark.java index a2f0788eef..69ea383579 100644 --- a/metrics-core/src/test/java/com/yammer/metrics/benchmarks/ReservoirBenchmark.java +++ b/metrics-core/src/test/java/com/codahale/metrics/benchmarks/ReservoirBenchmark.java @@ -1,11 +1,11 @@ -package com.yammer.metrics.benchmarks; +package com.codahale.metrics.benchmarks; import com.google.caliper.Runner; import com.google.caliper.SimpleBenchmark; -import com.yammer.metrics.ExponentiallyDecayingReservoir; -import com.yammer.metrics.SlidingTimeWindowReservoir; -import com.yammer.metrics.SlidingWindowReservoir; -import com.yammer.metrics.UniformReservoir; +import com.codahale.metrics.ExponentiallyDecayingReservoir; +import com.codahale.metrics.SlidingTimeWindowReservoir; +import com.codahale.metrics.SlidingWindowReservoir; +import com.codahale.metrics.UniformReservoir; import java.util.concurrent.TimeUnit; diff --git a/metrics-ehcache/pom.xml b/metrics-ehcache/pom.xml index dcf95952ea..abc91d45e1 100644 --- a/metrics-ehcache/pom.xml +++ b/metrics-ehcache/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - com.yammer.metrics + com.codahale.metrics metrics-parent 3.0.0-BETA2-SNAPSHOT @@ -17,7 +17,7 @@ - com.yammer.metrics + com.codahale.metrics metrics-core ${project.version} diff --git a/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcache.java b/metrics-ehcache/src/main/java/com/codahale/metrics/ehcache/InstrumentedEhcache.java similarity index 98% rename from metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcache.java rename to metrics-ehcache/src/main/java/com/codahale/metrics/ehcache/InstrumentedEhcache.java index 4f78a4f9ec..408109bebf 100644 --- a/metrics-ehcache/src/main/java/com/yammer/metrics/ehcache/InstrumentedEhcache.java +++ b/metrics-ehcache/src/main/java/com/codahale/metrics/ehcache/InstrumentedEhcache.java @@ -1,8 +1,8 @@ -package com.yammer.metrics.ehcache; +package com.codahale.metrics.ehcache; -import com.yammer.metrics.Gauge; -import com.yammer.metrics.MetricRegistry; -import com.yammer.metrics.Timer; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; import net.sf.ehcache.CacheException; import net.sf.ehcache.Ehcache; import net.sf.ehcache.Element; @@ -11,7 +11,7 @@ import java.io.Serializable; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; /** * An instrumented {@link Ehcache} instance. diff --git a/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/InstrumentedEhcacheTest.java b/metrics-ehcache/src/test/java/com/codahale/metrics/ehcache/InstrumentedEhcacheTest.java similarity index 87% rename from metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/InstrumentedEhcacheTest.java rename to metrics-ehcache/src/test/java/com/codahale/metrics/ehcache/InstrumentedEhcacheTest.java index 083f150e47..acee389a32 100644 --- a/metrics-ehcache/src/test/java/com/yammer/metrics/ehcache/InstrumentedEhcacheTest.java +++ b/metrics-ehcache/src/test/java/com/codahale/metrics/ehcache/InstrumentedEhcacheTest.java @@ -1,7 +1,7 @@ -package com.yammer.metrics.ehcache; +package com.codahale.metrics.ehcache; -import com.yammer.metrics.MetricRegistry; -import com.yammer.metrics.Timer; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Ehcache; @@ -10,7 +10,7 @@ import org.junit.Before; import org.junit.Test; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; import static org.fest.assertions.api.Assertions.assertThat; public class InstrumentedEhcacheTest { diff --git a/metrics-ehcache/src/test/resources/ehcache.xml b/metrics-ehcache/src/test/resources/ehcache.xml index f09d0b7800..3f594d7439 100644 --- a/metrics-ehcache/src/test/resources/ehcache.xml +++ b/metrics-ehcache/src/test/resources/ehcache.xml @@ -12,7 +12,7 @@ - + diff --git a/metrics-ganglia/pom.xml b/metrics-ganglia/pom.xml index a957a1a791..bb1114a844 100644 --- a/metrics-ganglia/pom.xml +++ b/metrics-ganglia/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - com.yammer.metrics + com.codahale.metrics metrics-parent 3.0.0-BETA2-SNAPSHOT @@ -17,7 +17,7 @@ - com.yammer.metrics + com.codahale.metrics metrics-core ${project.version} diff --git a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java b/metrics-ganglia/src/main/java/com/codahale/metrics/ganglia/GangliaReporter.java similarity index 98% rename from metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java rename to metrics-ganglia/src/main/java/com/codahale/metrics/ganglia/GangliaReporter.java index c81f8cc257..34ac5373b8 100644 --- a/metrics-ganglia/src/main/java/com/yammer/metrics/ganglia/GangliaReporter.java +++ b/metrics-ganglia/src/main/java/com/codahale/metrics/ganglia/GangliaReporter.java @@ -1,6 +1,6 @@ -package com.yammer.metrics.ganglia; +package com.codahale.metrics.ganglia; -import com.yammer.metrics.*; +import com.codahale.metrics.*; import info.ganglia.gmetric4j.gmetric.GMetric; import info.ganglia.gmetric4j.gmetric.GMetricSlope; import info.ganglia.gmetric4j.gmetric.GMetricType; @@ -12,7 +12,7 @@ import java.util.SortedMap; import java.util.concurrent.TimeUnit; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; /** * A reporter which announces metric values to a Ganglia cluster. diff --git a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaReporterTest.java b/metrics-ganglia/src/test/java/com/codahale/metrics/ganglia/GangliaReporterTest.java similarity index 99% rename from metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaReporterTest.java rename to metrics-ganglia/src/test/java/com/codahale/metrics/ganglia/GangliaReporterTest.java index 15268890b2..d8d07a5c07 100644 --- a/metrics-ganglia/src/test/java/com/yammer/metrics/ganglia/GangliaReporterTest.java +++ b/metrics-ganglia/src/test/java/com/codahale/metrics/ganglia/GangliaReporterTest.java @@ -1,6 +1,6 @@ -package com.yammer.metrics.ganglia; +package com.codahale.metrics.ganglia; -import com.yammer.metrics.*; +import com.codahale.metrics.*; import info.ganglia.gmetric4j.gmetric.GMetric; import info.ganglia.gmetric4j.gmetric.GMetricSlope; import info.ganglia.gmetric4j.gmetric.GMetricType; diff --git a/metrics-graphite/pom.xml b/metrics-graphite/pom.xml index 5480c40c1c..b0fbff7eb3 100644 --- a/metrics-graphite/pom.xml +++ b/metrics-graphite/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - com.yammer.metrics + com.codahale.metrics metrics-parent 3.0.0-BETA2-SNAPSHOT @@ -17,7 +17,7 @@ - com.yammer.metrics + com.codahale.metrics metrics-core ${project.version} diff --git a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/Graphite.java b/metrics-graphite/src/main/java/com/codahale/metrics/graphite/Graphite.java similarity index 98% rename from metrics-graphite/src/main/java/com/yammer/metrics/graphite/Graphite.java rename to metrics-graphite/src/main/java/com/codahale/metrics/graphite/Graphite.java index 9d717a6bd3..61c6c31f59 100644 --- a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/Graphite.java +++ b/metrics-graphite/src/main/java/com/codahale/metrics/graphite/Graphite.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.graphite; +package com.codahale.metrics.graphite; import javax.net.SocketFactory; import java.io.*; diff --git a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java b/metrics-graphite/src/main/java/com/codahale/metrics/graphite/GraphiteReporter.java similarity index 99% rename from metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java rename to metrics-graphite/src/main/java/com/codahale/metrics/graphite/GraphiteReporter.java index 72a40c3134..4c69c9e7af 100644 --- a/metrics-graphite/src/main/java/com/yammer/metrics/graphite/GraphiteReporter.java +++ b/metrics-graphite/src/main/java/com/codahale/metrics/graphite/GraphiteReporter.java @@ -1,6 +1,6 @@ -package com.yammer.metrics.graphite; +package com.codahale.metrics.graphite; -import com.yammer.metrics.*; +import com.codahale.metrics.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/GraphiteReporterTest.java b/metrics-graphite/src/test/java/com/codahale/metrics/graphite/GraphiteReporterTest.java similarity index 99% rename from metrics-graphite/src/test/java/com/yammer/metrics/graphite/GraphiteReporterTest.java rename to metrics-graphite/src/test/java/com/codahale/metrics/graphite/GraphiteReporterTest.java index 22c70aa458..4a3ee98979 100644 --- a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/GraphiteReporterTest.java +++ b/metrics-graphite/src/test/java/com/codahale/metrics/graphite/GraphiteReporterTest.java @@ -1,6 +1,6 @@ -package com.yammer.metrics.graphite; +package com.codahale.metrics.graphite; -import com.yammer.metrics.*; +import com.codahale.metrics.*; import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; diff --git a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/GraphiteTest.java b/metrics-graphite/src/test/java/com/codahale/metrics/graphite/GraphiteTest.java similarity index 98% rename from metrics-graphite/src/test/java/com/yammer/metrics/graphite/GraphiteTest.java rename to metrics-graphite/src/test/java/com/codahale/metrics/graphite/GraphiteTest.java index 46fb76f289..8042912e1f 100644 --- a/metrics-graphite/src/test/java/com/yammer/metrics/graphite/GraphiteTest.java +++ b/metrics-graphite/src/test/java/com/codahale/metrics/graphite/GraphiteTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.graphite; +package com.codahale.metrics.graphite; import org.junit.Before; import org.junit.Test; diff --git a/metrics-healthchecks/pom.xml b/metrics-healthchecks/pom.xml index ae7eda0f79..9cb84abb0d 100644 --- a/metrics-healthchecks/pom.xml +++ b/metrics-healthchecks/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - com.yammer.metrics + com.codahale.metrics metrics-parent 3.0.0-BETA2-SNAPSHOT @@ -18,7 +18,7 @@ - com.yammer.metrics + com.codahale.metrics metrics-jvm ${project.version} true diff --git a/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheck.java b/metrics-healthchecks/src/main/java/com/codahale/metrics/health/HealthCheck.java similarity index 99% rename from metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheck.java rename to metrics-healthchecks/src/main/java/com/codahale/metrics/health/HealthCheck.java index 9845e0bb96..ab115ceb06 100644 --- a/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheck.java +++ b/metrics-healthchecks/src/main/java/com/codahale/metrics/health/HealthCheck.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.health; +package com.codahale.metrics.health; /** * A health check for a component of your application. diff --git a/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java b/metrics-healthchecks/src/main/java/com/codahale/metrics/health/HealthCheckRegistry.java similarity index 97% rename from metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java rename to metrics-healthchecks/src/main/java/com/codahale/metrics/health/HealthCheckRegistry.java index b15f219757..87e8c223ef 100644 --- a/metrics-healthchecks/src/main/java/com/yammer/metrics/health/HealthCheckRegistry.java +++ b/metrics-healthchecks/src/main/java/com/codahale/metrics/health/HealthCheckRegistry.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.health; +package com.codahale.metrics.health; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -6,7 +6,7 @@ import java.util.*; import java.util.concurrent.*; -import static com.yammer.metrics.health.HealthCheck.Result; +import static com.codahale.metrics.health.HealthCheck.Result; /** * A registry for health checks. diff --git a/metrics-healthchecks/src/main/java/com/yammer/metrics/health/jvm/ThreadDeadlockHealthCheck.java b/metrics-healthchecks/src/main/java/com/codahale/metrics/health/jvm/ThreadDeadlockHealthCheck.java similarity index 85% rename from metrics-healthchecks/src/main/java/com/yammer/metrics/health/jvm/ThreadDeadlockHealthCheck.java rename to metrics-healthchecks/src/main/java/com/codahale/metrics/health/jvm/ThreadDeadlockHealthCheck.java index d8489186ff..0f7a44b3ba 100644 --- a/metrics-healthchecks/src/main/java/com/yammer/metrics/health/jvm/ThreadDeadlockHealthCheck.java +++ b/metrics-healthchecks/src/main/java/com/codahale/metrics/health/jvm/ThreadDeadlockHealthCheck.java @@ -1,7 +1,7 @@ -package com.yammer.metrics.health.jvm; +package com.codahale.metrics.health.jvm; -import com.yammer.metrics.health.HealthCheck; -import com.yammer.metrics.jvm.ThreadDeadlockDetector; +import com.codahale.metrics.health.HealthCheck; +import com.codahale.metrics.jvm.ThreadDeadlockDetector; import java.util.Set; diff --git a/metrics-healthchecks/src/test/java/com/yammer/metrics/health/HealthCheckRegistryTest.java b/metrics-healthchecks/src/test/java/com/codahale/metrics/health/HealthCheckRegistryTest.java similarity index 98% rename from metrics-healthchecks/src/test/java/com/yammer/metrics/health/HealthCheckRegistryTest.java rename to metrics-healthchecks/src/test/java/com/codahale/metrics/health/HealthCheckRegistryTest.java index 6789396b9d..73f4ecf7aa 100644 --- a/metrics-healthchecks/src/test/java/com/yammer/metrics/health/HealthCheckRegistryTest.java +++ b/metrics-healthchecks/src/test/java/com/codahale/metrics/health/HealthCheckRegistryTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.health; +package com.codahale.metrics.health; import org.junit.Before; import org.junit.Test; diff --git a/metrics-healthchecks/src/test/java/com/yammer/metrics/health/HealthCheckTest.java b/metrics-healthchecks/src/test/java/com/codahale/metrics/health/HealthCheckTest.java similarity index 99% rename from metrics-healthchecks/src/test/java/com/yammer/metrics/health/HealthCheckTest.java rename to metrics-healthchecks/src/test/java/com/codahale/metrics/health/HealthCheckTest.java index 062a09ec8c..9c71d0e819 100644 --- a/metrics-healthchecks/src/test/java/com/yammer/metrics/health/HealthCheckTest.java +++ b/metrics-healthchecks/src/test/java/com/codahale/metrics/health/HealthCheckTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.health; +package com.codahale.metrics.health; import org.junit.Test; diff --git a/metrics-healthchecks/src/test/java/com/yammer/metrics/health/jvm/ThreadDeadlockHealthCheckTest.java b/metrics-healthchecks/src/test/java/com/codahale/metrics/health/jvm/ThreadDeadlockHealthCheckTest.java similarity index 91% rename from metrics-healthchecks/src/test/java/com/yammer/metrics/health/jvm/ThreadDeadlockHealthCheckTest.java rename to metrics-healthchecks/src/test/java/com/codahale/metrics/health/jvm/ThreadDeadlockHealthCheckTest.java index 02909373cb..aec3647fab 100644 --- a/metrics-healthchecks/src/test/java/com/yammer/metrics/health/jvm/ThreadDeadlockHealthCheckTest.java +++ b/metrics-healthchecks/src/test/java/com/codahale/metrics/health/jvm/ThreadDeadlockHealthCheckTest.java @@ -1,7 +1,7 @@ -package com.yammer.metrics.health.jvm; +package com.codahale.metrics.health.jvm; -import com.yammer.metrics.health.HealthCheck; -import com.yammer.metrics.jvm.ThreadDeadlockDetector; +import com.codahale.metrics.health.HealthCheck; +import com.codahale.metrics.jvm.ThreadDeadlockDetector; import org.junit.Test; import java.util.Collections; diff --git a/metrics-httpclient/pom.xml b/metrics-httpclient/pom.xml index fda255a137..a4e01ffe10 100644 --- a/metrics-httpclient/pom.xml +++ b/metrics-httpclient/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - com.yammer.metrics + com.codahale.metrics metrics-parent 3.0.0-BETA2-SNAPSHOT @@ -18,7 +18,7 @@ - com.yammer.metrics + com.codahale.metrics metrics-core ${project.version} diff --git a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedClientConnManager.java b/metrics-httpclient/src/main/java/com/codahale/metrics/httpclient/InstrumentedClientConnManager.java similarity index 95% rename from metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedClientConnManager.java rename to metrics-httpclient/src/main/java/com/codahale/metrics/httpclient/InstrumentedClientConnManager.java index f289bb2c6d..f1d4fedcb9 100644 --- a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedClientConnManager.java +++ b/metrics-httpclient/src/main/java/com/codahale/metrics/httpclient/InstrumentedClientConnManager.java @@ -1,7 +1,7 @@ -package com.yammer.metrics.httpclient; +package com.codahale.metrics.httpclient; -import com.yammer.metrics.Gauge; -import com.yammer.metrics.MetricRegistry; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.MetricRegistry; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.DnsResolver; import org.apache.http.conn.scheme.SchemeRegistry; @@ -11,7 +11,7 @@ import java.util.concurrent.TimeUnit; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; /** * A {@link ClientConnectionManager} which monitors the number of open connections. diff --git a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedHttpClient.java b/metrics-httpclient/src/main/java/com/codahale/metrics/httpclient/InstrumentedHttpClient.java similarity index 97% rename from metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedHttpClient.java rename to metrics-httpclient/src/main/java/com/codahale/metrics/httpclient/InstrumentedHttpClient.java index e69b3cfa3e..2fd5e90609 100644 --- a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedHttpClient.java +++ b/metrics-httpclient/src/main/java/com/codahale/metrics/httpclient/InstrumentedHttpClient.java @@ -1,6 +1,6 @@ -package com.yammer.metrics.httpclient; +package com.codahale.metrics.httpclient; -import com.yammer.metrics.MetricRegistry; +import com.codahale.metrics.MetricRegistry; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.ConnectionReuseStrategy; diff --git a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedRequestDirector.java b/metrics-httpclient/src/main/java/com/codahale/metrics/httpclient/InstrumentedRequestDirector.java similarity index 96% rename from metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedRequestDirector.java rename to metrics-httpclient/src/main/java/com/codahale/metrics/httpclient/InstrumentedRequestDirector.java index ee8fb9931c..761bb06427 100644 --- a/metrics-httpclient/src/main/java/com/yammer/metrics/httpclient/InstrumentedRequestDirector.java +++ b/metrics-httpclient/src/main/java/com/codahale/metrics/httpclient/InstrumentedRequestDirector.java @@ -1,7 +1,7 @@ -package com.yammer.metrics.httpclient; +package com.codahale.metrics.httpclient; -import com.yammer.metrics.MetricRegistry; -import com.yammer.metrics.Timer; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; import org.apache.commons.logging.Log; import org.apache.http.*; import org.apache.http.client.*; @@ -16,7 +16,7 @@ import java.io.IOException; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; class InstrumentedRequestDirector extends DefaultRequestDirector { private final static String GET = "GET", POST = "POST", HEAD = "HEAD", PUT = "PUT", diff --git a/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/InstrumentedHttpClientTest.java b/metrics-httpclient/src/test/java/com/codahale/metrics/httpclient/InstrumentedHttpClientTest.java similarity index 86% rename from metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/InstrumentedHttpClientTest.java rename to metrics-httpclient/src/test/java/com/codahale/metrics/httpclient/InstrumentedHttpClientTest.java index 2bf0293ee5..ccf40063f1 100644 --- a/metrics-httpclient/src/test/java/com/yammer/metrics/httpclient/InstrumentedHttpClientTest.java +++ b/metrics-httpclient/src/test/java/com/codahale/metrics/httpclient/InstrumentedHttpClientTest.java @@ -1,6 +1,6 @@ -package com.yammer.metrics.httpclient; +package com.codahale.metrics.httpclient; -import com.yammer.metrics.MetricRegistry; +import com.codahale.metrics.MetricRegistry; import org.apache.http.client.HttpClient; import org.junit.Test; diff --git a/metrics-jdbi/pom.xml b/metrics-jdbi/pom.xml index 1cb2d73791..a7e844d69b 100644 --- a/metrics-jdbi/pom.xml +++ b/metrics-jdbi/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - com.yammer.metrics + com.codahale.metrics metrics-parent 3.0.0-BETA2-SNAPSHOT @@ -17,7 +17,7 @@ - com.yammer.metrics + com.codahale.metrics metrics-core ${project.version} diff --git a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/InstrumentedTimingCollector.java b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/InstrumentedTimingCollector.java similarity index 82% rename from metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/InstrumentedTimingCollector.java rename to metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/InstrumentedTimingCollector.java index 230f10d5ea..7682397433 100644 --- a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/InstrumentedTimingCollector.java +++ b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/InstrumentedTimingCollector.java @@ -1,9 +1,9 @@ -package com.yammer.metrics.jdbi; +package com.codahale.metrics.jdbi; -import com.yammer.metrics.MetricRegistry; -import com.yammer.metrics.Timer; -import com.yammer.metrics.jdbi.strategies.SmartNameStrategy; -import com.yammer.metrics.jdbi.strategies.StatementNameStrategy; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; +import com.codahale.metrics.jdbi.strategies.SmartNameStrategy; +import com.codahale.metrics.jdbi.strategies.StatementNameStrategy; import org.skife.jdbi.v2.StatementContext; import org.skife.jdbi.v2.TimingCollector; diff --git a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/BasicSqlNameStrategy.java b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/BasicSqlNameStrategy.java similarity index 81% rename from metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/BasicSqlNameStrategy.java rename to metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/BasicSqlNameStrategy.java index 697dba4c97..5f60bad932 100644 --- a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/BasicSqlNameStrategy.java +++ b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/BasicSqlNameStrategy.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.jdbi.strategies; +package com.codahale.metrics.jdbi.strategies; public class BasicSqlNameStrategy extends DelegatingStatementNameStrategy { public BasicSqlNameStrategy() { diff --git a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/ContextNameStrategy.java b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/ContextNameStrategy.java similarity index 90% rename from metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/ContextNameStrategy.java rename to metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/ContextNameStrategy.java index 41451e3fe6..849f6fc6d1 100644 --- a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/ContextNameStrategy.java +++ b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/ContextNameStrategy.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.jdbi.strategies; +package com.codahale.metrics.jdbi.strategies; /** diff --git a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/DelegatingStatementNameStrategy.java b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/DelegatingStatementNameStrategy.java similarity index 95% rename from metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/DelegatingStatementNameStrategy.java rename to metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/DelegatingStatementNameStrategy.java index c6b3985e35..38c565fce8 100644 --- a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/DelegatingStatementNameStrategy.java +++ b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/DelegatingStatementNameStrategy.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.jdbi.strategies; +package com.codahale.metrics.jdbi.strategies; import org.skife.jdbi.v2.StatementContext; diff --git a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/NaiveNameStrategy.java b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/NaiveNameStrategy.java similarity index 87% rename from metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/NaiveNameStrategy.java rename to metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/NaiveNameStrategy.java index 2ff47e07fe..6c2f811b78 100644 --- a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/NaiveNameStrategy.java +++ b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/NaiveNameStrategy.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.jdbi.strategies; +package com.codahale.metrics.jdbi.strategies; /** * Very simple strategy, can be used with any JDBI loader to build basic statistics. diff --git a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/NameStrategies.java b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/NameStrategies.java similarity index 98% rename from metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/NameStrategies.java rename to metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/NameStrategies.java index 4ce9ac6030..0885e01737 100644 --- a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/NameStrategies.java +++ b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/NameStrategies.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.jdbi.strategies; +package com.codahale.metrics.jdbi.strategies; import org.skife.jdbi.v2.ClasspathStatementLocator; import org.skife.jdbi.v2.StatementContext; @@ -7,7 +7,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; public final class NameStrategies { public static final StatementNameStrategy CHECK_EMPTY = new CheckEmptyStrategy(); diff --git a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/ShortNameStrategy.java b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/ShortNameStrategy.java similarity index 97% rename from metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/ShortNameStrategy.java rename to metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/ShortNameStrategy.java index db0f74034e..9859d0a6d0 100644 --- a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/ShortNameStrategy.java +++ b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/ShortNameStrategy.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.jdbi.strategies; +package com.codahale.metrics.jdbi.strategies; import org.skife.jdbi.v2.StatementContext; @@ -6,7 +6,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; /** * Assembles all JDBI stats under a common prefix (passed in at constructor time). Stats are grouped diff --git a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/SmartNameStrategy.java b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/SmartNameStrategy.java similarity index 93% rename from metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/SmartNameStrategy.java rename to metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/SmartNameStrategy.java index e17aaba71a..9802497ad7 100644 --- a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/SmartNameStrategy.java +++ b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/SmartNameStrategy.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.jdbi.strategies; +package com.codahale.metrics.jdbi.strategies; /** diff --git a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/StatementNameStrategy.java b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/StatementNameStrategy.java similarity index 83% rename from metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/StatementNameStrategy.java rename to metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/StatementNameStrategy.java index 4a83168b74..c5c3f6dc88 100644 --- a/metrics-jdbi/src/main/java/com/yammer/metrics/jdbi/strategies/StatementNameStrategy.java +++ b/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/StatementNameStrategy.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.jdbi.strategies; +package com.codahale.metrics.jdbi.strategies; import org.skife.jdbi.v2.StatementContext; diff --git a/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/InstrumentedTimingCollectorTest.java b/metrics-jdbi/src/test/java/com/codahale/metrics/jdbi/InstrumentedTimingCollectorTest.java similarity index 96% rename from metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/InstrumentedTimingCollectorTest.java rename to metrics-jdbi/src/test/java/com/codahale/metrics/jdbi/InstrumentedTimingCollectorTest.java index b6ed37ee6d..b41558c573 100644 --- a/metrics-jdbi/src/test/java/com/yammer/metrics/jdbi/InstrumentedTimingCollectorTest.java +++ b/metrics-jdbi/src/test/java/com/codahale/metrics/jdbi/InstrumentedTimingCollectorTest.java @@ -1,17 +1,17 @@ -package com.yammer.metrics.jdbi; - -import com.yammer.metrics.MetricRegistry; -import com.yammer.metrics.Timer; -import com.yammer.metrics.jdbi.strategies.NameStrategies; -import com.yammer.metrics.jdbi.strategies.ShortNameStrategy; -import com.yammer.metrics.jdbi.strategies.SmartNameStrategy; -import com.yammer.metrics.jdbi.strategies.StatementNameStrategy; +package com.codahale.metrics.jdbi; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; +import com.codahale.metrics.jdbi.strategies.NameStrategies; +import com.codahale.metrics.jdbi.strategies.ShortNameStrategy; +import com.codahale.metrics.jdbi.strategies.SmartNameStrategy; +import com.codahale.metrics.jdbi.strategies.StatementNameStrategy; import org.junit.Test; import org.skife.jdbi.v2.StatementContext; import java.util.concurrent.TimeUnit; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; import static org.fest.assertions.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; diff --git a/metrics-jersey/pom.xml b/metrics-jersey/pom.xml index 2758594138..5d62dd9223 100644 --- a/metrics-jersey/pom.xml +++ b/metrics-jersey/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - com.yammer.metrics + com.codahale.metrics metrics-parent 3.0.0-BETA2-SNAPSHOT @@ -22,12 +22,12 @@ - com.yammer.metrics + com.codahale.metrics metrics-core ${project.version} - com.yammer.metrics + com.codahale.metrics metrics-annotation ${project.version} diff --git a/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java b/metrics-jersey/src/main/java/com/codahale/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java similarity index 93% rename from metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java rename to metrics-jersey/src/main/java/com/codahale/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java index 5309fb04bb..fc01f31ca4 100644 --- a/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java +++ b/metrics-jersey/src/main/java/com/codahale/metrics/jersey/InstrumentedResourceMethodDispatchAdapter.java @@ -1,8 +1,8 @@ -package com.yammer.metrics.jersey; +package com.codahale.metrics.jersey; import com.sun.jersey.spi.container.ResourceMethodDispatchAdapter; import com.sun.jersey.spi.container.ResourceMethodDispatchProvider; -import com.yammer.metrics.MetricRegistry; +import com.codahale.metrics.MetricRegistry; import javax.ws.rs.ext.Provider; diff --git a/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java b/metrics-jersey/src/main/java/com/codahale/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java similarity index 94% rename from metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java rename to metrics-jersey/src/main/java/com/codahale/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java index 6648fb5814..99c6848765 100644 --- a/metrics-jersey/src/main/java/com/yammer/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java +++ b/metrics-jersey/src/main/java/com/codahale/metrics/jersey/InstrumentedResourceMethodDispatchProvider.java @@ -1,17 +1,17 @@ -package com.yammer.metrics.jersey; +package com.codahale.metrics.jersey; import com.sun.jersey.api.core.HttpContext; import com.sun.jersey.api.model.AbstractResourceMethod; import com.sun.jersey.spi.container.ResourceMethodDispatchProvider; import com.sun.jersey.spi.dispatch.RequestDispatcher; -import com.yammer.metrics.Meter; -import com.yammer.metrics.MetricRegistry; -import com.yammer.metrics.Timer; -import com.yammer.metrics.annotation.ExceptionMetered; -import com.yammer.metrics.annotation.Metered; -import com.yammer.metrics.annotation.Timed; - -import static com.yammer.metrics.MetricRegistry.name; +import com.codahale.metrics.Meter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; +import com.codahale.metrics.annotation.ExceptionMetered; +import com.codahale.metrics.annotation.Metered; +import com.codahale.metrics.annotation.Timed; + +import static com.codahale.metrics.MetricRegistry.name; class InstrumentedResourceMethodDispatchProvider implements ResourceMethodDispatchProvider { private static class TimedRequestDispatcher implements RequestDispatcher { diff --git a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/SingletonMetricsJerseyTest.java b/metrics-jersey/src/test/java/com/codahale/metrics/jersey/SingletonMetricsJerseyTest.java similarity index 91% rename from metrics-jersey/src/test/java/com/yammer/metrics/jersey/SingletonMetricsJerseyTest.java rename to metrics-jersey/src/test/java/com/codahale/metrics/jersey/SingletonMetricsJerseyTest.java index 3baad5465a..4a96507b1b 100644 --- a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/SingletonMetricsJerseyTest.java +++ b/metrics-jersey/src/test/java/com/codahale/metrics/jersey/SingletonMetricsJerseyTest.java @@ -1,21 +1,21 @@ -package com.yammer.metrics.jersey; +package com.codahale.metrics.jersey; import com.sun.jersey.api.container.MappableContainerException; import com.sun.jersey.api.core.DefaultResourceConfig; import com.sun.jersey.test.framework.AppDescriptor; import com.sun.jersey.test.framework.JerseyTest; import com.sun.jersey.test.framework.LowLevelAppDescriptor; -import com.yammer.metrics.Meter; -import com.yammer.metrics.MetricRegistry; -import com.yammer.metrics.Timer; -import com.yammer.metrics.jersey.resources.InstrumentedResource; +import com.codahale.metrics.Meter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; +import com.codahale.metrics.jersey.resources.InstrumentedResource; import org.junit.Test; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.api.Assertions.failBecauseExceptionWasNotThrown; diff --git a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/resources/InstrumentedResource.java b/metrics-jersey/src/test/java/com/codahale/metrics/jersey/resources/InstrumentedResource.java similarity index 77% rename from metrics-jersey/src/test/java/com/yammer/metrics/jersey/resources/InstrumentedResource.java rename to metrics-jersey/src/test/java/com/codahale/metrics/jersey/resources/InstrumentedResource.java index c3046b7332..d2cf34607b 100644 --- a/metrics-jersey/src/test/java/com/yammer/metrics/jersey/resources/InstrumentedResource.java +++ b/metrics-jersey/src/test/java/com/codahale/metrics/jersey/resources/InstrumentedResource.java @@ -1,8 +1,8 @@ -package com.yammer.metrics.jersey.resources; +package com.codahale.metrics.jersey.resources; -import com.yammer.metrics.annotation.ExceptionMetered; -import com.yammer.metrics.annotation.Metered; -import com.yammer.metrics.annotation.Timed; +import com.codahale.metrics.annotation.ExceptionMetered; +import com.codahale.metrics.annotation.Metered; +import com.codahale.metrics.annotation.Timed; import javax.ws.rs.*; import javax.ws.rs.core.MediaType; diff --git a/metrics-jetty8/pom.xml b/metrics-jetty8/pom.xml index 7ccc9cd118..22b6c10735 100644 --- a/metrics-jetty8/pom.xml +++ b/metrics-jetty8/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - com.yammer.metrics + com.codahale.metrics metrics-parent 3.0.0-BETA2-SNAPSHOT @@ -18,7 +18,7 @@ - com.yammer.metrics + com.codahale.metrics metrics-core ${project.version} diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedBlockingChannelConnector.java b/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedBlockingChannelConnector.java similarity index 95% rename from metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedBlockingChannelConnector.java rename to metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedBlockingChannelConnector.java index 45ed085f9a..d554e16640 100644 --- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedBlockingChannelConnector.java +++ b/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedBlockingChannelConnector.java @@ -1,13 +1,13 @@ -package com.yammer.metrics.jetty8; +package com.codahale.metrics.jetty8; -import com.yammer.metrics.*; +import com.codahale.metrics.*; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.nio.BlockingChannelConnector; import java.io.IOException; import java.util.concurrent.TimeUnit; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; public class InstrumentedBlockingChannelConnector extends BlockingChannelConnector { private final Timer duration; diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java b/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedHandler.java similarity index 98% rename from metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java rename to metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedHandler.java index e05ddaf020..fed77da159 100644 --- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedHandler.java +++ b/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedHandler.java @@ -1,6 +1,6 @@ -package com.yammer.metrics.jetty8; +package com.codahale.metrics.jetty8; -import com.yammer.metrics.*; +import com.codahale.metrics.*; import org.eclipse.jetty.continuation.Continuation; import org.eclipse.jetty.continuation.ContinuationListener; import org.eclipse.jetty.server.AsyncContinuation; @@ -14,7 +14,7 @@ import java.io.IOException; import java.util.concurrent.TimeUnit; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; import static org.eclipse.jetty.http.HttpMethods.*; /** diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedQueuedThreadPool.java b/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedQueuedThreadPool.java similarity index 81% rename from metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedQueuedThreadPool.java rename to metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedQueuedThreadPool.java index 698cd005c4..9653ff7de0 100644 --- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedQueuedThreadPool.java +++ b/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedQueuedThreadPool.java @@ -1,11 +1,11 @@ -package com.yammer.metrics.jetty8; +package com.codahale.metrics.jetty8; -import com.yammer.metrics.Gauge; -import com.yammer.metrics.MetricRegistry; -import com.yammer.metrics.RatioGauge; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.RatioGauge; import org.eclipse.jetty.util.thread.QueuedThreadPool; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; public class InstrumentedQueuedThreadPool extends QueuedThreadPool { public InstrumentedQueuedThreadPool(MetricRegistry registry) { diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSelectChannelConnector.java b/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedSelectChannelConnector.java similarity index 95% rename from metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSelectChannelConnector.java rename to metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedSelectChannelConnector.java index 5af1028860..ab494beca8 100644 --- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSelectChannelConnector.java +++ b/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedSelectChannelConnector.java @@ -1,13 +1,13 @@ -package com.yammer.metrics.jetty8; +package com.codahale.metrics.jetty8; -import com.yammer.metrics.*; +import com.codahale.metrics.*; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.nio.SelectChannelConnector; import java.io.IOException; import java.util.concurrent.TimeUnit; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; public class InstrumentedSelectChannelConnector extends SelectChannelConnector { private final Timer duration; diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSocketConnector.java b/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedSocketConnector.java similarity index 94% rename from metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSocketConnector.java rename to metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedSocketConnector.java index 63df4d791a..233f049c0d 100644 --- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSocketConnector.java +++ b/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedSocketConnector.java @@ -1,13 +1,13 @@ -package com.yammer.metrics.jetty8; +package com.codahale.metrics.jetty8; -import com.yammer.metrics.*; +import com.codahale.metrics.*; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.bio.SocketConnector; import java.io.IOException; import java.util.concurrent.TimeUnit; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; public class InstrumentedSocketConnector extends SocketConnector { private final Timer duration; diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSslSelectChannelConnector.java b/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedSslSelectChannelConnector.java similarity index 95% rename from metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSslSelectChannelConnector.java rename to metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedSslSelectChannelConnector.java index 0055efd795..21c2f15ff3 100644 --- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSslSelectChannelConnector.java +++ b/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedSslSelectChannelConnector.java @@ -1,6 +1,6 @@ -package com.yammer.metrics.jetty8; +package com.codahale.metrics.jetty8; -import com.yammer.metrics.*; +import com.codahale.metrics.*; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; import org.eclipse.jetty.util.ssl.SslContextFactory; @@ -8,7 +8,7 @@ import java.io.IOException; import java.util.concurrent.TimeUnit; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; public class InstrumentedSslSelectChannelConnector extends SslSelectChannelConnector { private final Timer duration; diff --git a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSslSocketConnector.java b/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedSslSocketConnector.java similarity index 95% rename from metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSslSocketConnector.java rename to metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedSslSocketConnector.java index 8473ba1fc5..5182121862 100644 --- a/metrics-jetty8/src/main/java/com/yammer/metrics/jetty8/InstrumentedSslSocketConnector.java +++ b/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedSslSocketConnector.java @@ -1,6 +1,6 @@ -package com.yammer.metrics.jetty8; +package com.codahale.metrics.jetty8; -import com.yammer.metrics.*; +import com.codahale.metrics.*; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.ssl.SslSocketConnector; import org.eclipse.jetty.util.ssl.SslContextFactory; @@ -8,7 +8,7 @@ import java.io.IOException; import java.util.concurrent.TimeUnit; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; public class InstrumentedSslSocketConnector extends SslSocketConnector { private final Timer duration; diff --git a/metrics-json/pom.xml b/metrics-json/pom.xml index 0dca02ecdb..d70f9c9efb 100644 --- a/metrics-json/pom.xml +++ b/metrics-json/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - com.yammer.metrics + com.codahale.metrics metrics-parent 3.0.0-BETA2-SNAPSHOT @@ -17,12 +17,12 @@ - com.yammer.metrics + com.codahale.metrics metrics-core ${project.version} - com.yammer.metrics + com.codahale.metrics metrics-healthchecks ${project.version} true diff --git a/metrics-json/src/main/java/com/yammer/metrics/json/HealthCheckModule.java b/metrics-json/src/main/java/com/codahale/metrics/json/HealthCheckModule.java similarity index 96% rename from metrics-json/src/main/java/com/yammer/metrics/json/HealthCheckModule.java rename to metrics-json/src/main/java/com/codahale/metrics/json/HealthCheckModule.java index 53d061ca61..42746fce26 100644 --- a/metrics-json/src/main/java/com/yammer/metrics/json/HealthCheckModule.java +++ b/metrics-json/src/main/java/com/codahale/metrics/json/HealthCheckModule.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.json; +package com.codahale.metrics.json; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.Version; @@ -7,7 +7,7 @@ import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleSerializers; import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import com.yammer.metrics.health.HealthCheck; +import com.codahale.metrics.health.HealthCheck; import java.io.IOException; import java.util.Arrays; diff --git a/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java b/metrics-json/src/main/java/com/codahale/metrics/json/MetricsModule.java similarity index 98% rename from metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java rename to metrics-json/src/main/java/com/codahale/metrics/json/MetricsModule.java index a9db024514..8e94645f9f 100644 --- a/metrics-json/src/main/java/com/yammer/metrics/json/MetricsModule.java +++ b/metrics-json/src/main/java/com/codahale/metrics/json/MetricsModule.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.json; +package com.codahale.metrics.json; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.Version; @@ -7,7 +7,7 @@ import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleSerializers; import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import com.yammer.metrics.*; +import com.codahale.metrics.*; import java.io.IOException; import java.util.Arrays; @@ -15,7 +15,7 @@ import java.util.concurrent.TimeUnit; public class MetricsModule extends Module { - static final Version VERSION = new Version(3, 0, 0, "", "com.yammer.metrics", "metrics-json"); + static final Version VERSION = new Version(3, 0, 0, "", "com.codahale.metrics", "metrics-json"); private static class GaugeSerializer extends StdSerializer { private GaugeSerializer() { diff --git a/metrics-json/src/test/java/com/yammer/metrics/json/HealthCheckModuleTest.java b/metrics-json/src/test/java/com/codahale/metrics/json/HealthCheckModuleTest.java similarity index 97% rename from metrics-json/src/test/java/com/yammer/metrics/json/HealthCheckModuleTest.java rename to metrics-json/src/test/java/com/codahale/metrics/json/HealthCheckModuleTest.java index 22a87a80a0..c1eb27f547 100644 --- a/metrics-json/src/test/java/com/yammer/metrics/json/HealthCheckModuleTest.java +++ b/metrics-json/src/test/java/com/codahale/metrics/json/HealthCheckModuleTest.java @@ -1,7 +1,7 @@ -package com.yammer.metrics.json; +package com.codahale.metrics.json; import com.fasterxml.jackson.databind.ObjectMapper; -import com.yammer.metrics.health.HealthCheck; +import com.codahale.metrics.health.HealthCheck; import org.junit.Test; import static org.fest.assertions.api.Assertions.assertThat; diff --git a/metrics-json/src/test/java/com/yammer/metrics/json/MetricsModuleTest.java b/metrics-json/src/test/java/com/codahale/metrics/json/MetricsModuleTest.java similarity index 99% rename from metrics-json/src/test/java/com/yammer/metrics/json/MetricsModuleTest.java rename to metrics-json/src/test/java/com/codahale/metrics/json/MetricsModuleTest.java index dce9e2b0d0..0476e25cbd 100644 --- a/metrics-json/src/test/java/com/yammer/metrics/json/MetricsModuleTest.java +++ b/metrics-json/src/test/java/com/codahale/metrics/json/MetricsModuleTest.java @@ -1,7 +1,7 @@ -package com.yammer.metrics.json; +package com.codahale.metrics.json; import com.fasterxml.jackson.databind.ObjectMapper; -import com.yammer.metrics.*; +import com.codahale.metrics.*; import org.junit.Test; import java.util.concurrent.TimeUnit; diff --git a/metrics-jvm/pom.xml b/metrics-jvm/pom.xml index 251d9767d1..7e1841f66d 100644 --- a/metrics-jvm/pom.xml +++ b/metrics-jvm/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - com.yammer.metrics + com.codahale.metrics metrics-parent 3.0.0-BETA2-SNAPSHOT @@ -18,7 +18,7 @@ - com.yammer.metrics + com.codahale.metrics metrics-core ${project.version} diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolMetricSet.java b/metrics-jvm/src/main/java/com/codahale/metrics/jvm/BufferPoolMetricSet.java similarity index 89% rename from metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolMetricSet.java rename to metrics-jvm/src/main/java/com/codahale/metrics/jvm/BufferPoolMetricSet.java index 7259f6b3b0..db823911b0 100644 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/BufferPoolMetricSet.java +++ b/metrics-jvm/src/main/java/com/codahale/metrics/jvm/BufferPoolMetricSet.java @@ -1,8 +1,8 @@ -package com.yammer.metrics.jvm; +package com.codahale.metrics.jvm; -import com.yammer.metrics.JmxAttributeGauge; -import com.yammer.metrics.Metric; -import com.yammer.metrics.MetricSet; +import com.codahale.metrics.JmxAttributeGauge; +import com.codahale.metrics.Metric; +import com.codahale.metrics.MetricSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,7 +13,7 @@ import java.util.HashMap; import java.util.Map; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; /** * A set of gauges for the count, usage, and capacity of the JVM's direct and mapped buffer pools. diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/FileDescriptorRatioGauge.java b/metrics-jvm/src/main/java/com/codahale/metrics/jvm/FileDescriptorRatioGauge.java similarity index 95% rename from metrics-jvm/src/main/java/com/yammer/metrics/jvm/FileDescriptorRatioGauge.java rename to metrics-jvm/src/main/java/com/codahale/metrics/jvm/FileDescriptorRatioGauge.java index 7e4944809d..d6f2bb303d 100644 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/FileDescriptorRatioGauge.java +++ b/metrics-jvm/src/main/java/com/codahale/metrics/jvm/FileDescriptorRatioGauge.java @@ -1,6 +1,6 @@ -package com.yammer.metrics.jvm; +package com.codahale.metrics.jvm; -import com.yammer.metrics.RatioGauge; +import com.codahale.metrics.RatioGauge; import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/GarbageCollectorMetricSet.java b/metrics-jvm/src/main/java/com/codahale/metrics/jvm/GarbageCollectorMetricSet.java similarity index 89% rename from metrics-jvm/src/main/java/com/yammer/metrics/jvm/GarbageCollectorMetricSet.java rename to metrics-jvm/src/main/java/com/codahale/metrics/jvm/GarbageCollectorMetricSet.java index 3091ed5830..90741fcb72 100644 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/GarbageCollectorMetricSet.java +++ b/metrics-jvm/src/main/java/com/codahale/metrics/jvm/GarbageCollectorMetricSet.java @@ -1,15 +1,15 @@ -package com.yammer.metrics.jvm; +package com.codahale.metrics.jvm; -import com.yammer.metrics.Gauge; -import com.yammer.metrics.Metric; -import com.yammer.metrics.MetricSet; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Metric; +import com.codahale.metrics.MetricSet; import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.util.*; import java.util.regex.Pattern; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; /** * A set of gauges for the counts and elapsed times of garbage collections. diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/MemoryUsageGaugeSet.java b/metrics-jvm/src/main/java/com/codahale/metrics/jvm/MemoryUsageGaugeSet.java similarity index 95% rename from metrics-jvm/src/main/java/com/yammer/metrics/jvm/MemoryUsageGaugeSet.java rename to metrics-jvm/src/main/java/com/codahale/metrics/jvm/MemoryUsageGaugeSet.java index 0b9f42e179..bdce63515a 100644 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/MemoryUsageGaugeSet.java +++ b/metrics-jvm/src/main/java/com/codahale/metrics/jvm/MemoryUsageGaugeSet.java @@ -1,9 +1,9 @@ -package com.yammer.metrics.jvm; +package com.codahale.metrics.jvm; -import com.yammer.metrics.Gauge; -import com.yammer.metrics.Metric; -import com.yammer.metrics.MetricSet; -import com.yammer.metrics.RatioGauge; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Metric; +import com.codahale.metrics.MetricSet; +import com.codahale.metrics.RatioGauge; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; @@ -12,7 +12,7 @@ import java.util.*; import java.util.regex.Pattern; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; /** * A set of gauges for JVM memory usage, including stats on heap vs. non-heap memory, plus diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDeadlockDetector.java b/metrics-jvm/src/main/java/com/codahale/metrics/jvm/ThreadDeadlockDetector.java similarity index 98% rename from metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDeadlockDetector.java rename to metrics-jvm/src/main/java/com/codahale/metrics/jvm/ThreadDeadlockDetector.java index 2f4d396db6..e6dd697d4c 100644 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDeadlockDetector.java +++ b/metrics-jvm/src/main/java/com/codahale/metrics/jvm/ThreadDeadlockDetector.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.jvm; +package com.codahale.metrics.jvm; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDump.java b/metrics-jvm/src/main/java/com/codahale/metrics/jvm/ThreadDump.java similarity index 99% rename from metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDump.java rename to metrics-jvm/src/main/java/com/codahale/metrics/jvm/ThreadDump.java index a608b38eff..34da284e3b 100644 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadDump.java +++ b/metrics-jvm/src/main/java/com/codahale/metrics/jvm/ThreadDump.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.jvm; +package com.codahale.metrics.jvm; import java.io.OutputStream; import java.io.OutputStreamWriter; diff --git a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeSet.java b/metrics-jvm/src/main/java/com/codahale/metrics/jvm/ThreadStatesGaugeSet.java similarity index 93% rename from metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeSet.java rename to metrics-jvm/src/main/java/com/codahale/metrics/jvm/ThreadStatesGaugeSet.java index 5e55029bda..3334594585 100644 --- a/metrics-jvm/src/main/java/com/yammer/metrics/jvm/ThreadStatesGaugeSet.java +++ b/metrics-jvm/src/main/java/com/codahale/metrics/jvm/ThreadStatesGaugeSet.java @@ -1,8 +1,8 @@ -package com.yammer.metrics.jvm; +package com.codahale.metrics.jvm; -import com.yammer.metrics.Gauge; -import com.yammer.metrics.Metric; -import com.yammer.metrics.MetricSet; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Metric; +import com.codahale.metrics.MetricSet; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; @@ -12,7 +12,7 @@ import java.util.Map; import java.util.Set; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; /** * A set of gauges for the number of threads in their various states and deadlock detection. diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/BufferPoolMetricSetTest.java b/metrics-jvm/src/test/java/com/codahale/metrics/jvm/BufferPoolMetricSetTest.java similarity index 98% rename from metrics-jvm/src/test/java/com/yammer/metrics/jvm/BufferPoolMetricSetTest.java rename to metrics-jvm/src/test/java/com/codahale/metrics/jvm/BufferPoolMetricSetTest.java index c7a02c7cd3..68ce714e35 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/BufferPoolMetricSetTest.java +++ b/metrics-jvm/src/test/java/com/codahale/metrics/jvm/BufferPoolMetricSetTest.java @@ -1,6 +1,6 @@ -package com.yammer.metrics.jvm; +package com.codahale.metrics.jvm; -import com.yammer.metrics.Gauge; +import com.codahale.metrics.Gauge; import org.junit.Before; import org.junit.Test; diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/FileDescriptorRatioGaugeTest.java b/metrics-jvm/src/test/java/com/codahale/metrics/jvm/FileDescriptorRatioGaugeTest.java similarity index 98% rename from metrics-jvm/src/test/java/com/yammer/metrics/jvm/FileDescriptorRatioGaugeTest.java rename to metrics-jvm/src/test/java/com/codahale/metrics/jvm/FileDescriptorRatioGaugeTest.java index c0c038bc71..91296dff3a 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/FileDescriptorRatioGaugeTest.java +++ b/metrics-jvm/src/test/java/com/codahale/metrics/jvm/FileDescriptorRatioGaugeTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.jvm; +package com.codahale.metrics.jvm; import org.junit.Test; diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/GarbageCollectorMetricSetTest.java b/metrics-jvm/src/test/java/com/codahale/metrics/jvm/GarbageCollectorMetricSetTest.java similarity index 95% rename from metrics-jvm/src/test/java/com/yammer/metrics/jvm/GarbageCollectorMetricSetTest.java rename to metrics-jvm/src/test/java/com/codahale/metrics/jvm/GarbageCollectorMetricSetTest.java index bd00a8f26c..e1e44ae56d 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/GarbageCollectorMetricSetTest.java +++ b/metrics-jvm/src/test/java/com/codahale/metrics/jvm/GarbageCollectorMetricSetTest.java @@ -1,6 +1,6 @@ -package com.yammer.metrics.jvm; +package com.codahale.metrics.jvm; -import com.yammer.metrics.Gauge; +import com.codahale.metrics.Gauge; import org.junit.Before; import org.junit.Test; diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/MemoryUsageGaugeSetTest.java b/metrics-jvm/src/test/java/com/codahale/metrics/jvm/MemoryUsageGaugeSetTest.java similarity index 99% rename from metrics-jvm/src/test/java/com/yammer/metrics/jvm/MemoryUsageGaugeSetTest.java rename to metrics-jvm/src/test/java/com/codahale/metrics/jvm/MemoryUsageGaugeSetTest.java index bda0949940..f5a58c8bf4 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/MemoryUsageGaugeSetTest.java +++ b/metrics-jvm/src/test/java/com/codahale/metrics/jvm/MemoryUsageGaugeSetTest.java @@ -1,6 +1,6 @@ -package com.yammer.metrics.jvm; +package com.codahale.metrics.jvm; -import com.yammer.metrics.Gauge; +import com.codahale.metrics.Gauge; import org.junit.Before; import org.junit.Test; diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadDeadlockDetectorTest.java b/metrics-jvm/src/test/java/com/codahale/metrics/jvm/ThreadDeadlockDetectorTest.java similarity index 98% rename from metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadDeadlockDetectorTest.java rename to metrics-jvm/src/test/java/com/codahale/metrics/jvm/ThreadDeadlockDetectorTest.java index 3299851b02..378056796c 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadDeadlockDetectorTest.java +++ b/metrics-jvm/src/test/java/com/codahale/metrics/jvm/ThreadDeadlockDetectorTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.jvm; +package com.codahale.metrics.jvm; import org.junit.Test; diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadDumpTest.java b/metrics-jvm/src/test/java/com/codahale/metrics/jvm/ThreadDumpTest.java similarity index 98% rename from metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadDumpTest.java rename to metrics-jvm/src/test/java/com/codahale/metrics/jvm/ThreadDumpTest.java index 37f1b1c1f1..6fbfdd6490 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadDumpTest.java +++ b/metrics-jvm/src/test/java/com/codahale/metrics/jvm/ThreadDumpTest.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.jvm; +package com.codahale.metrics.jvm; import org.junit.Before; import org.junit.Test; diff --git a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadStatesGaugeSetTest.java b/metrics-jvm/src/test/java/com/codahale/metrics/jvm/ThreadStatesGaugeSetTest.java similarity index 98% rename from metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadStatesGaugeSetTest.java rename to metrics-jvm/src/test/java/com/codahale/metrics/jvm/ThreadStatesGaugeSetTest.java index 86c9f0f091..6560e94776 100644 --- a/metrics-jvm/src/test/java/com/yammer/metrics/jvm/ThreadStatesGaugeSetTest.java +++ b/metrics-jvm/src/test/java/com/codahale/metrics/jvm/ThreadStatesGaugeSetTest.java @@ -1,6 +1,6 @@ -package com.yammer.metrics.jvm; +package com.codahale.metrics.jvm; -import com.yammer.metrics.Gauge; +import com.codahale.metrics.Gauge; import org.junit.Before; import org.junit.Test; diff --git a/metrics-log4j/pom.xml b/metrics-log4j/pom.xml index 162031ab91..49fac6e92a 100644 --- a/metrics-log4j/pom.xml +++ b/metrics-log4j/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - com.yammer.metrics + com.codahale.metrics metrics-parent 3.0.0-BETA2-SNAPSHOT @@ -17,7 +17,7 @@ - com.yammer.metrics + com.codahale.metrics metrics-core ${project.version} diff --git a/metrics-log4j/src/main/java/com/yammer/metrics/log4j/InstrumentedAppender.java b/metrics-log4j/src/main/java/com/codahale/metrics/log4j/InstrumentedAppender.java similarity index 92% rename from metrics-log4j/src/main/java/com/yammer/metrics/log4j/InstrumentedAppender.java rename to metrics-log4j/src/main/java/com/codahale/metrics/log4j/InstrumentedAppender.java index 782e9e141c..0819441887 100644 --- a/metrics-log4j/src/main/java/com/yammer/metrics/log4j/InstrumentedAppender.java +++ b/metrics-log4j/src/main/java/com/codahale/metrics/log4j/InstrumentedAppender.java @@ -1,13 +1,13 @@ -package com.yammer.metrics.log4j; +package com.codahale.metrics.log4j; -import com.yammer.metrics.Meter; -import com.yammer.metrics.MetricRegistry; +import com.codahale.metrics.Meter; +import com.codahale.metrics.MetricRegistry; import org.apache.log4j.Appender; import org.apache.log4j.AppenderSkeleton; import org.apache.log4j.Level; import org.apache.log4j.spi.LoggingEvent; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; /** * A Log4J {@link Appender} delegate which has seven meters, one for each logging level and one for diff --git a/metrics-log4j/src/test/java/com/yammer/metrics/log4j/InstrumentedAppenderTest.java b/metrics-log4j/src/test/java/com/codahale/metrics/log4j/InstrumentedAppenderTest.java similarity index 94% rename from metrics-log4j/src/test/java/com/yammer/metrics/log4j/InstrumentedAppenderTest.java rename to metrics-log4j/src/test/java/com/codahale/metrics/log4j/InstrumentedAppenderTest.java index 6b3b46a558..233be795c0 100644 --- a/metrics-log4j/src/test/java/com/yammer/metrics/log4j/InstrumentedAppenderTest.java +++ b/metrics-log4j/src/test/java/com/codahale/metrics/log4j/InstrumentedAppenderTest.java @@ -1,14 +1,14 @@ -package com.yammer.metrics.log4j; +package com.codahale.metrics.log4j; -import com.yammer.metrics.Meter; -import com.yammer.metrics.MetricRegistry; +import com.codahale.metrics.Meter; +import com.codahale.metrics.MetricRegistry; import org.apache.log4j.Appender; import org.apache.log4j.Level; import org.apache.log4j.spi.LoggingEvent; import org.junit.Before; import org.junit.Test; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; import static org.mockito.Mockito.*; public class InstrumentedAppenderTest { diff --git a/metrics-logback/pom.xml b/metrics-logback/pom.xml index 4551813294..1aea734525 100644 --- a/metrics-logback/pom.xml +++ b/metrics-logback/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - com.yammer.metrics + com.codahale.metrics metrics-parent 3.0.0-BETA2-SNAPSHOT @@ -21,7 +21,7 @@ - com.yammer.metrics + com.codahale.metrics metrics-core ${project.version} diff --git a/metrics-logback/src/main/java/com/yammer/metrics/logback/InstrumentedAppender.java b/metrics-logback/src/main/java/com/codahale/metrics/logback/InstrumentedAppender.java similarity index 91% rename from metrics-logback/src/main/java/com/yammer/metrics/logback/InstrumentedAppender.java rename to metrics-logback/src/main/java/com/codahale/metrics/logback/InstrumentedAppender.java index 64a3269c0f..48f1f193c7 100644 --- a/metrics-logback/src/main/java/com/yammer/metrics/logback/InstrumentedAppender.java +++ b/metrics-logback/src/main/java/com/codahale/metrics/logback/InstrumentedAppender.java @@ -1,13 +1,13 @@ -package com.yammer.metrics.logback; +package com.codahale.metrics.logback; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; import ch.qos.logback.core.AppenderBase; -import com.yammer.metrics.Meter; -import com.yammer.metrics.MetricRegistry; +import com.codahale.metrics.Meter; +import com.codahale.metrics.MetricRegistry; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; /** * A Logback {@link AppenderBase} which has six meters, one for each logging level and one for the diff --git a/metrics-logback/src/test/java/com/yammer/metrics/logback/InstrumentedAppenderTest.java b/metrics-logback/src/test/java/com/codahale/metrics/logback/InstrumentedAppenderTest.java similarity index 93% rename from metrics-logback/src/test/java/com/yammer/metrics/logback/InstrumentedAppenderTest.java rename to metrics-logback/src/test/java/com/codahale/metrics/logback/InstrumentedAppenderTest.java index ed115598d6..f237fa90e6 100644 --- a/metrics-logback/src/test/java/com/yammer/metrics/logback/InstrumentedAppenderTest.java +++ b/metrics-logback/src/test/java/com/codahale/metrics/logback/InstrumentedAppenderTest.java @@ -1,15 +1,15 @@ -package com.yammer.metrics.logback; +package com.codahale.metrics.logback; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; -import com.yammer.metrics.Meter; -import com.yammer.metrics.MetricRegistry; +import com.codahale.metrics.Meter; +import com.codahale.metrics.MetricRegistry; import org.junit.After; import org.junit.Before; import org.junit.Test; -import static com.yammer.metrics.MetricRegistry.name; +import static com.codahale.metrics.MetricRegistry.name; import static org.mockito.Mockito.*; public class InstrumentedAppenderTest { diff --git a/metrics-servlet/pom.xml b/metrics-servlet/pom.xml index f37951c5bf..f6f2af87ca 100644 --- a/metrics-servlet/pom.xml +++ b/metrics-servlet/pom.xml @@ -3,7 +3,7 @@ 4.0.0 - com.yammer.metrics + com.codahale.metrics metrics-parent 3.0.0-BETA2-SNAPSHOT @@ -17,7 +17,7 @@ - com.yammer.metrics + com.codahale.metrics metrics-core ${project.version} diff --git a/metrics-servlet/src/main/java/com/yammer/metrics/servlet/DefaultWebappMetricsFilter.java b/metrics-servlet/src/main/java/com/codahale/metrics/servlet/DefaultWebappMetricsFilter.java similarity index 93% rename from metrics-servlet/src/main/java/com/yammer/metrics/servlet/DefaultWebappMetricsFilter.java rename to metrics-servlet/src/main/java/com/codahale/metrics/servlet/DefaultWebappMetricsFilter.java index d598361990..97ce076836 100644 --- a/metrics-servlet/src/main/java/com/yammer/metrics/servlet/DefaultWebappMetricsFilter.java +++ b/metrics-servlet/src/main/java/com/codahale/metrics/servlet/DefaultWebappMetricsFilter.java @@ -1,4 +1,4 @@ -package com.yammer.metrics.servlet; +package com.codahale.metrics.servlet; import java.util.HashMap; import java.util.Map; @@ -9,7 +9,7 @@ *

{@code
  * 
  *     webappMetricsFilter
- *     com.yammer.metrics.servlet.DefaultWebappMetricsFilter
+ *     com.codahale.metrics.servlet.DefaultWebappMetricsFilter
  * 
  * 
  *     webappMetricsFilter
diff --git a/metrics-servlet/src/main/java/com/yammer/metrics/servlet/WebappMetricsFilter.java b/metrics-servlet/src/main/java/com/codahale/metrics/servlet/WebappMetricsFilter.java
similarity index 95%
rename from metrics-servlet/src/main/java/com/yammer/metrics/servlet/WebappMetricsFilter.java
rename to metrics-servlet/src/main/java/com/codahale/metrics/servlet/WebappMetricsFilter.java
index 290f069941..2961b1a1d8 100644
--- a/metrics-servlet/src/main/java/com/yammer/metrics/servlet/WebappMetricsFilter.java
+++ b/metrics-servlet/src/main/java/com/codahale/metrics/servlet/WebappMetricsFilter.java
@@ -1,9 +1,9 @@
-package com.yammer.metrics.servlet;
+package com.codahale.metrics.servlet;
 
-import com.yammer.metrics.Counter;
-import com.yammer.metrics.Meter;
-import com.yammer.metrics.MetricRegistry;
-import com.yammer.metrics.Timer;
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Timer;
 
 import javax.servlet.*;
 import javax.servlet.http.HttpServletResponse;
@@ -14,7 +14,7 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
-import static com.yammer.metrics.MetricRegistry.name;
+import static com.codahale.metrics.MetricRegistry.name;
 
 /**
  * {@link Filter} implementation which captures request information and a breakdown of the response
diff --git a/metrics-servlets/pom.xml b/metrics-servlets/pom.xml
index c55f6d12b3..de8edd12af 100644
--- a/metrics-servlets/pom.xml
+++ b/metrics-servlets/pom.xml
@@ -3,7 +3,7 @@
     4.0.0
 
     
-        com.yammer.metrics
+        com.codahale.metrics
         metrics-parent
         3.0.0-BETA2-SNAPSHOT
     
@@ -18,22 +18,22 @@
 
     
         
-            com.yammer.metrics
+            com.codahale.metrics
             metrics-core
             ${project.version}
         
         
-            com.yammer.metrics
+            com.codahale.metrics
             metrics-healthchecks
             ${project.version}
         
         
-            com.yammer.metrics
+            com.codahale.metrics
             metrics-json
             ${project.version}
         
         
-            com.yammer.metrics
+            com.codahale.metrics
             metrics-jvm
             ${project.version}
         
@@ -61,7 +61,7 @@
             test
         
         
-            com.yammer.metrics
+            com.codahale.metrics
             metrics-jetty8
             ${project.version}
             test
diff --git a/metrics-servlets/src/main/java/com/yammer/metrics/servlets/AdminServlet.java b/metrics-servlets/src/main/java/com/codahale/metrics/servlets/AdminServlet.java
similarity index 99%
rename from metrics-servlets/src/main/java/com/yammer/metrics/servlets/AdminServlet.java
rename to metrics-servlets/src/main/java/com/codahale/metrics/servlets/AdminServlet.java
index 44eed9da48..8f542bb123 100755
--- a/metrics-servlets/src/main/java/com/yammer/metrics/servlets/AdminServlet.java
+++ b/metrics-servlets/src/main/java/com/codahale/metrics/servlets/AdminServlet.java
@@ -1,4 +1,4 @@
-package com.yammer.metrics.servlets;
+package com.codahale.metrics.servlets;
 
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
diff --git a/metrics-servlets/src/main/java/com/yammer/metrics/servlets/HealthCheckServlet.java b/metrics-servlets/src/main/java/com/codahale/metrics/servlets/HealthCheckServlet.java
similarity index 94%
rename from metrics-servlets/src/main/java/com/yammer/metrics/servlets/HealthCheckServlet.java
rename to metrics-servlets/src/main/java/com/codahale/metrics/servlets/HealthCheckServlet.java
index 32b151ac61..94487b6515 100644
--- a/metrics-servlets/src/main/java/com/yammer/metrics/servlets/HealthCheckServlet.java
+++ b/metrics-servlets/src/main/java/com/codahale/metrics/servlets/HealthCheckServlet.java
@@ -1,10 +1,10 @@
-package com.yammer.metrics.servlets;
+package com.codahale.metrics.servlets;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.ObjectWriter;
-import com.yammer.metrics.health.HealthCheck;
-import com.yammer.metrics.health.HealthCheckRegistry;
-import com.yammer.metrics.json.HealthCheckModule;
+import com.codahale.metrics.health.HealthCheck;
+import com.codahale.metrics.health.HealthCheckRegistry;
+import com.codahale.metrics.json.HealthCheckModule;
 
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
diff --git a/metrics-servlets/src/main/java/com/yammer/metrics/servlets/MetricsServlet.java b/metrics-servlets/src/main/java/com/codahale/metrics/servlets/MetricsServlet.java
similarity index 96%
rename from metrics-servlets/src/main/java/com/yammer/metrics/servlets/MetricsServlet.java
rename to metrics-servlets/src/main/java/com/codahale/metrics/servlets/MetricsServlet.java
index 38fa9ffc35..cdb065399d 100644
--- a/metrics-servlets/src/main/java/com/yammer/metrics/servlets/MetricsServlet.java
+++ b/metrics-servlets/src/main/java/com/codahale/metrics/servlets/MetricsServlet.java
@@ -1,9 +1,9 @@
-package com.yammer.metrics.servlets;
+package com.codahale.metrics.servlets;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.ObjectWriter;
-import com.yammer.metrics.MetricRegistry;
-import com.yammer.metrics.json.MetricsModule;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.json.MetricsModule;
 
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
diff --git a/metrics-servlets/src/main/java/com/yammer/metrics/servlets/PingServlet.java b/metrics-servlets/src/main/java/com/codahale/metrics/servlets/PingServlet.java
similarity index 96%
rename from metrics-servlets/src/main/java/com/yammer/metrics/servlets/PingServlet.java
rename to metrics-servlets/src/main/java/com/codahale/metrics/servlets/PingServlet.java
index 70251a6203..12387ec89f 100644
--- a/metrics-servlets/src/main/java/com/yammer/metrics/servlets/PingServlet.java
+++ b/metrics-servlets/src/main/java/com/codahale/metrics/servlets/PingServlet.java
@@ -1,4 +1,4 @@
-package com.yammer.metrics.servlets;
+package com.codahale.metrics.servlets;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
diff --git a/metrics-servlets/src/main/java/com/yammer/metrics/servlets/ThreadDumpServlet.java b/metrics-servlets/src/main/java/com/codahale/metrics/servlets/ThreadDumpServlet.java
similarity index 94%
rename from metrics-servlets/src/main/java/com/yammer/metrics/servlets/ThreadDumpServlet.java
rename to metrics-servlets/src/main/java/com/codahale/metrics/servlets/ThreadDumpServlet.java
index 045ae2142f..fabda4b2ff 100644
--- a/metrics-servlets/src/main/java/com/yammer/metrics/servlets/ThreadDumpServlet.java
+++ b/metrics-servlets/src/main/java/com/codahale/metrics/servlets/ThreadDumpServlet.java
@@ -1,6 +1,6 @@
-package com.yammer.metrics.servlets;
+package com.codahale.metrics.servlets;
 
-import com.yammer.metrics.jvm.ThreadDump;
+import com.codahale.metrics.jvm.ThreadDump;
 
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/AbstractServletTest.java b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/AbstractServletTest.java
similarity index 95%
rename from metrics-servlets/src/test/java/com/yammer/metrics/servlets/AbstractServletTest.java
rename to metrics-servlets/src/test/java/com/codahale/metrics/servlets/AbstractServletTest.java
index 92aab1be3e..45803a917a 100644
--- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/AbstractServletTest.java
+++ b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/AbstractServletTest.java
@@ -1,4 +1,4 @@
-package com.yammer.metrics.servlets;
+package com.codahale.metrics.servlets;
 
 import org.eclipse.jetty.testing.HttpTester;
 import org.eclipse.jetty.testing.ServletTester;
diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/AdminServletTest.java b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/AdminServletTest.java
similarity index 86%
rename from metrics-servlets/src/test/java/com/yammer/metrics/servlets/AdminServletTest.java
rename to metrics-servlets/src/test/java/com/codahale/metrics/servlets/AdminServletTest.java
index 883fa4fb20..f72791f134 100755
--- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/AdminServletTest.java
+++ b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/AdminServletTest.java
@@ -1,7 +1,7 @@
-package com.yammer.metrics.servlets;
+package com.codahale.metrics.servlets;
 
-import com.yammer.metrics.MetricRegistry;
-import com.yammer.metrics.health.HealthCheckRegistry;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.health.HealthCheckRegistry;
 import org.eclipse.jetty.testing.ServletTester;
 import org.junit.Before;
 import org.junit.Test;
@@ -16,8 +16,8 @@ public class AdminServletTest extends AbstractServletTest {
     protected void setUp(ServletTester tester) {
         tester.setContextPath("/context");
 
-        tester.setAttribute("com.yammer.metrics.servlets.MetricsServlet.registry", registry);
-        tester.setAttribute("com.yammer.metrics.servlets.HealthCheckServlet.registry", healthCheckRegistry);
+        tester.setAttribute("com.codahale.metrics.servlets.MetricsServlet.registry", registry);
+        tester.setAttribute("com.codahale.metrics.servlets.HealthCheckServlet.registry", healthCheckRegistry);
         tester.addServlet(AdminServlet.class, "/admin");
     }
 
diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/HealthCheckServletTest.java b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/HealthCheckServletTest.java
similarity index 91%
rename from metrics-servlets/src/test/java/com/yammer/metrics/servlets/HealthCheckServletTest.java
rename to metrics-servlets/src/test/java/com/codahale/metrics/servlets/HealthCheckServletTest.java
index 35c463dd1e..74228b3c38 100644
--- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/HealthCheckServletTest.java
+++ b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/HealthCheckServletTest.java
@@ -1,7 +1,7 @@
-package com.yammer.metrics.servlets;
+package com.codahale.metrics.servlets;
 
-import com.yammer.metrics.health.HealthCheck;
-import com.yammer.metrics.health.HealthCheckRegistry;
+import com.codahale.metrics.health.HealthCheck;
+import com.codahale.metrics.health.HealthCheckRegistry;
 import org.eclipse.jetty.testing.ServletTester;
 import org.junit.After;
 import org.junit.Before;
@@ -19,8 +19,8 @@ public class HealthCheckServletTest extends AbstractServletTest {
     @Override
     protected void setUp(ServletTester tester) {
         tester.addServlet(HealthCheckServlet.class, "/healthchecks");
-        tester.setAttribute("com.yammer.metrics.servlets.HealthCheckServlet.registry", registry);
-        tester.setAttribute("com.yammer.metrics.servlets.HealthCheckServlet.executor", threadPool);
+        tester.setAttribute("com.codahale.metrics.servlets.HealthCheckServlet.registry", registry);
+        tester.setAttribute("com.codahale.metrics.servlets.HealthCheckServlet.executor", threadPool);
     }
 
     @Before
diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/MetricsServletTest.java b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/MetricsServletTest.java
similarity index 97%
rename from metrics-servlets/src/test/java/com/yammer/metrics/servlets/MetricsServletTest.java
rename to metrics-servlets/src/test/java/com/codahale/metrics/servlets/MetricsServletTest.java
index 33edb6e170..c940b95952 100644
--- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/MetricsServletTest.java
+++ b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/MetricsServletTest.java
@@ -1,6 +1,6 @@
-package com.yammer.metrics.servlets;
+package com.codahale.metrics.servlets;
 
-import com.yammer.metrics.*;
+import com.codahale.metrics.*;
 import org.eclipse.jetty.testing.ServletTester;
 import org.junit.Before;
 import org.junit.Test;
@@ -17,7 +17,7 @@ public class MetricsServletTest extends AbstractServletTest {
 
     @Override
     protected void setUp(ServletTester tester) {
-        tester.setAttribute("com.yammer.metrics.servlets.MetricsServlet.registry", registry);
+        tester.setAttribute("com.codahale.metrics.servlets.MetricsServlet.registry", registry);
         tester.addServlet(MetricsServlet.class, "/metrics");
     }
 
diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/PingServletTest.java b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/PingServletTest.java
similarity index 96%
rename from metrics-servlets/src/test/java/com/yammer/metrics/servlets/PingServletTest.java
rename to metrics-servlets/src/test/java/com/codahale/metrics/servlets/PingServletTest.java
index 31b5786785..cf1a53c04b 100644
--- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/PingServletTest.java
+++ b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/PingServletTest.java
@@ -1,4 +1,4 @@
-package com.yammer.metrics.servlets;
+package com.codahale.metrics.servlets;
 
 import org.eclipse.jetty.testing.ServletTester;
 import org.junit.Before;
diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/ThreadDumpServletTest.java b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/ThreadDumpServletTest.java
similarity index 96%
rename from metrics-servlets/src/test/java/com/yammer/metrics/servlets/ThreadDumpServletTest.java
rename to metrics-servlets/src/test/java/com/codahale/metrics/servlets/ThreadDumpServletTest.java
index 252968f047..fa8ad8df25 100644
--- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/ThreadDumpServletTest.java
+++ b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/ThreadDumpServletTest.java
@@ -1,4 +1,4 @@
-package com.yammer.metrics.servlets;
+package com.codahale.metrics.servlets;
 
 import org.eclipse.jetty.testing.ServletTester;
 import org.junit.Before;
diff --git a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/experiments/ExampleServer.java b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/experiments/ExampleServer.java
similarity index 74%
rename from metrics-servlets/src/test/java/com/yammer/metrics/servlets/experiments/ExampleServer.java
rename to metrics-servlets/src/test/java/com/codahale/metrics/servlets/experiments/ExampleServer.java
index 000d5b3c32..2ab4389e35 100644
--- a/metrics-servlets/src/test/java/com/yammer/metrics/servlets/experiments/ExampleServer.java
+++ b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/experiments/ExampleServer.java
@@ -1,23 +1,23 @@
-package com.yammer.metrics.servlets.experiments;
-
-import com.yammer.metrics.Clock;
-import com.yammer.metrics.Counter;
-import com.yammer.metrics.Gauge;
-import com.yammer.metrics.MetricRegistry;
-import com.yammer.metrics.health.HealthCheckRegistry;
-import com.yammer.metrics.jetty8.InstrumentedHandler;
-import com.yammer.metrics.jetty8.InstrumentedQueuedThreadPool;
-import com.yammer.metrics.jetty8.InstrumentedSelectChannelConnector;
-import com.yammer.metrics.servlets.AdminServlet;
-import com.yammer.metrics.servlets.HealthCheckServlet;
-import com.yammer.metrics.servlets.MetricsServlet;
+package com.codahale.metrics.servlets.experiments;
+
+import com.codahale.metrics.Clock;
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.health.HealthCheckRegistry;
+import com.codahale.metrics.jetty8.InstrumentedHandler;
+import com.codahale.metrics.jetty8.InstrumentedQueuedThreadPool;
+import com.codahale.metrics.jetty8.InstrumentedSelectChannelConnector;
+import com.codahale.metrics.servlets.AdminServlet;
+import com.codahale.metrics.servlets.HealthCheckServlet;
+import com.codahale.metrics.servlets.MetricsServlet;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.eclipse.jetty.util.thread.ThreadPool;
 
-import static com.yammer.metrics.MetricRegistry.name;
+import static com.codahale.metrics.MetricRegistry.name;
 
 public class ExampleServer {
     private static final MetricRegistry REGISTRY = new MetricRegistry();
diff --git a/pom.xml b/pom.xml
index 2b8a196920..03c24f01a1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,13 +5,13 @@
         3.0.0
     
 
-    com.yammer.metrics
+    com.codahale.metrics
     metrics-parent
     3.0.0-BETA2-SNAPSHOT
     pom
     Metrics Parent
     
-        Yammer's Metrics library.
+        The Metrics library.
     
     http://metrics.codahale.com/
 

From 351d871ca558b158deb7543650d7ea9893f0b024 Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Sat, 20 Apr 2013 14:57:17 -0700
Subject: [PATCH 0196/2558] Add gauge for pending jobs in
 InstrumentedQueuedThreadPool.

---
 .../metrics/jetty8/InstrumentedQueuedThreadPool.java      | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedQueuedThreadPool.java b/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedQueuedThreadPool.java
index 9653ff7de0..2913c6d5a4 100644
--- a/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedQueuedThreadPool.java
+++ b/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedQueuedThreadPool.java
@@ -29,5 +29,13 @@ public Integer getValue() {
                 return getIdleThreads();
             }
         });
+        registry.register(name(QueuedThreadPool.class, "jobs"), new Gauge() {
+            @Override
+            public Integer getValue() {
+                // This assumes the QueuedThreadPool is using a BlockingArrayQueue or
+                // ArrayBlockingQueue for its queue, and is therefore a constant-time operation.
+                return getQueue().size();
+            }
+        });
     }
 }

From 382b2ad277a0f697a7c7cb84f5b7be29728f716f Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Sat, 20 Apr 2013 18:36:52 -0700
Subject: [PATCH 0197/2558] Added metrics-jetty9 w/ InstrumentedConnector.

---
 metrics-jetty9/pom.xml                        | 33 ++++++++
 .../jetty9/InstrumentedConnectionFactory.java | 41 ++++++++++
 .../metrics/jetty9/InstrumentedConnector.java | 71 ++++++++++++++++++
 .../jetty9/InstrumentedConnectorTest.java     | 75 +++++++++++++++++++
 pom.xml                                       |  2 +
 5 files changed, 222 insertions(+)
 create mode 100644 metrics-jetty9/pom.xml
 create mode 100644 metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/InstrumentedConnectionFactory.java
 create mode 100644 metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/InstrumentedConnector.java
 create mode 100644 metrics-jetty9/src/test/java/com/codahale/metrics/jetty9/InstrumentedConnectorTest.java

diff --git a/metrics-jetty9/pom.xml b/metrics-jetty9/pom.xml
new file mode 100644
index 0000000000..eb90f6d5d0
--- /dev/null
+++ b/metrics-jetty9/pom.xml
@@ -0,0 +1,33 @@
+
+
+    4.0.0
+
+    
+        com.codahale.metrics
+        metrics-parent
+        3.0.0-BETA2-SNAPSHOT
+    
+
+    metrics-jetty9
+
+    
+        
+            com.codahale.metrics
+            metrics-core
+            ${project.version}
+        
+        
+            org.eclipse.jetty
+            jetty-server
+            ${jetty9.version}
+        
+        
+            org.eclipse.jetty
+            jetty-client
+            ${jetty9.version}
+            test
+        
+    
+
diff --git a/metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/InstrumentedConnectionFactory.java b/metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/InstrumentedConnectionFactory.java
new file mode 100644
index 0000000000..80f46ee1ce
--- /dev/null
+++ b/metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/InstrumentedConnectionFactory.java
@@ -0,0 +1,41 @@
+package com.codahale.metrics.jetty9;
+
+import com.codahale.metrics.Timer;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.ConnectionFactory;
+import org.eclipse.jetty.server.Connector;
+
+public class InstrumentedConnectionFactory implements ConnectionFactory {
+    private final ConnectionFactory connectionFactory;
+    private final Timer timer;
+
+    public InstrumentedConnectionFactory(ConnectionFactory connectionFactory, Timer timer) {
+        this.connectionFactory = connectionFactory;
+        this.timer = timer;
+    }
+
+    @Override
+    public String getProtocol() {
+        return connectionFactory.getProtocol();
+    }
+
+    @Override
+    public Connection newConnection(Connector connector, EndPoint endPoint) {
+        final Connection connection = connectionFactory.newConnection(connector, endPoint);
+        connection.addListener(new Connection.Listener() {
+            private Timer.Context context;
+
+            @Override
+            public void onOpened(Connection connection) {
+                this.context = timer.time();
+            }
+
+            @Override
+            public void onClosed(Connection connection) {
+                context.stop();
+            }
+        });
+        return connection;
+    }
+}
diff --git a/metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/InstrumentedConnector.java b/metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/InstrumentedConnector.java
new file mode 100644
index 0000000000..6e67a8ce25
--- /dev/null
+++ b/metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/InstrumentedConnector.java
@@ -0,0 +1,71 @@
+package com.codahale.metrics.jetty9;
+
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Timer;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.server.*;
+import org.eclipse.jetty.util.annotation.Name;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.Scheduler;
+
+import java.util.concurrent.Executor;
+
+import static com.codahale.metrics.MetricRegistry.name;
+import static org.eclipse.jetty.server.AbstractConnectionFactory.getFactories;
+
+public class InstrumentedConnector extends ServerConnector {
+    public InstrumentedConnector(@Name("registry") MetricRegistry registry,
+                                 @Name("name") String name,
+                                 @Name("server") Server server) {
+        this(registry, name, server, null, null, null, 0, 0, new HttpConnectionFactory());
+    }
+
+    public InstrumentedConnector(@Name("registry") MetricRegistry registry,
+                                 @Name("name") String name,
+                                 @Name("server") Server server,
+                                 @Name("factories") ConnectionFactory... factories) {
+        this(registry, name, server, null, null, null, 0, 0, factories);
+    }
+
+    public InstrumentedConnector(@Name("registry") MetricRegistry registry,
+                                 @Name("name") String name,
+                                 @Name("server") Server server,
+                                 @Name("sslContextFactory") SslContextFactory sslContextFactory) {
+        this(registry, name, server, null, null, null, 0, 0,
+             getFactories(sslContextFactory, new HttpConnectionFactory()));
+    }
+
+    public InstrumentedConnector(@Name("registry") MetricRegistry registry,
+                                 @Name("name") String name,
+                                 @Name("server") Server server,
+                                 @Name("sslContextFactory") SslContextFactory sslContextFactory,
+                                 @Name("factories") ConnectionFactory... factories) {
+        this(registry, name, server, null, null, null, 0, 0, getFactories(sslContextFactory, factories));
+
+    }
+
+    public InstrumentedConnector(@Name("registry") MetricRegistry registry,
+                                 @Name("name") String name,
+                                 @Name("server") Server server,
+                                 @Name("executor") Executor executor,
+                                 @Name("scheduler") Scheduler scheduler,
+                                 @Name("bufferPool") ByteBufferPool bufferPool,
+                                 @Name("acceptors") int acceptors,
+                                 @Name("selectors") int selectors,
+                                 @Name("factories") ConnectionFactory... factories) {
+        super(server, executor, scheduler, bufferPool, acceptors, selectors,
+              instrument(factories, registry, name));
+        setName(name);
+    }
+
+    private static ConnectionFactory[] instrument(ConnectionFactory[] factories,
+                                                  MetricRegistry registry,
+                                                  String name) {
+        final ConnectionFactory[] instrumented = new ConnectionFactory[factories.length];
+        for (int i = 0; i < factories.length; i++) {
+            final Timer timer = registry.timer(name(ServerConnector.class, name, "connections"));
+            instrumented[i] = new InstrumentedConnectionFactory(factories[i], timer);
+        }
+        return instrumented;
+    }
+}
diff --git a/metrics-jetty9/src/test/java/com/codahale/metrics/jetty9/InstrumentedConnectorTest.java b/metrics-jetty9/src/test/java/com/codahale/metrics/jetty9/InstrumentedConnectorTest.java
new file mode 100644
index 0000000000..83c2e8944d
--- /dev/null
+++ b/metrics-jetty9/src/test/java/com/codahale/metrics/jetty9/InstrumentedConnectorTest.java
@@ -0,0 +1,75 @@
+package com.codahale.metrics.jetty9;
+
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Timer;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import static com.codahale.metrics.MetricRegistry.name;
+import static org.fest.assertions.api.Assertions.assertThat;
+
+public class InstrumentedConnectorTest {
+    private final MetricRegistry registry = new MetricRegistry();
+    private final Server server = new Server();
+    private final InstrumentedConnector connector = new InstrumentedConnector(registry,
+                                                                              "one",
+                                                                              server);
+    private final HttpClient client = new HttpClient();
+
+    @Before
+    public void setUp() throws Exception {
+        server.setHandler(new AbstractHandler() {
+            @Override
+            public void handle(String target,
+                               Request baseRequest,
+                               HttpServletRequest request,
+                               HttpServletResponse response) throws IOException, ServletException {
+                final PrintWriter writer = response.getWriter();
+                try {
+                    writer.println("OK");
+                } finally {
+                    writer.close();
+                }
+            }
+        });
+
+        server.addConnector(connector);
+        server.start();
+
+        client.start();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        server.stop();
+        client.stop();
+    }
+
+    @Test
+    public void instrumentsConnectionTimes() throws Exception {
+        final ContentResponse response = client.GET("http://localhost:" + connector.getLocalPort() + "/hello");
+        assertThat(response.getStatus())
+                .isEqualTo(200);
+
+        client.stop(); // close the connection
+
+        Thread.sleep(100); // make sure the connection is closed
+
+        final Timer timer = registry.timer(name(ServerConnector.class, "one", "connections"));
+        assertThat(timer.getCount())
+                .isEqualTo(1);
+    }
+}
diff --git a/pom.xml b/pom.xml
index 03c24f01a1..37fbeddcf0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,6 +26,7 @@
         metrics-jdbi
         metrics-jersey
         metrics-jetty8
+        metrics-jetty9
         metrics-json
         metrics-jvm
         metrics-log4j
@@ -41,6 +42,7 @@
         1.7.5
         2.1.4
         8.1.9.v20130131
+        9.0.2.v20130417
     
 
     

From ed0f6b14a4f0ea2ed6c9db4e5373e2b086327cd3 Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Sat, 20 Apr 2013 21:30:43 -0700
Subject: [PATCH 0198/2558] Added InstrumentedQueuedThreadPool.

---
 .../jetty9/InstrumentedQueuedThreadPool.java  | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/InstrumentedQueuedThreadPool.java

diff --git a/metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/InstrumentedQueuedThreadPool.java b/metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/InstrumentedQueuedThreadPool.java
new file mode 100644
index 0000000000..19b93f4fdb
--- /dev/null
+++ b/metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/InstrumentedQueuedThreadPool.java
@@ -0,0 +1,71 @@
+package com.codahale.metrics.jetty9;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.RatioGauge;
+import org.eclipse.jetty.util.annotation.Name;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+
+import java.util.concurrent.BlockingQueue;
+
+import static com.codahale.metrics.MetricRegistry.name;
+
+public class InstrumentedQueuedThreadPool extends QueuedThreadPool {
+    public InstrumentedQueuedThreadPool(@Name("registry") MetricRegistry registry,
+                                        @Name("name") String name) {
+        this(registry, name, 200);
+    }
+
+    public InstrumentedQueuedThreadPool(@Name("registry") MetricRegistry registry,
+                                        @Name("name") String name,
+                                        @Name("maxThreads") int maxThreads) {
+        this(registry, name, maxThreads, 8);
+    }
+
+    public InstrumentedQueuedThreadPool(@Name("registry") MetricRegistry registry,
+                                        @Name("name") String name,
+                                        @Name("maxThreads") int maxThreads,
+                                        @Name("minThreads") int minThreads) {
+        this(registry, name, maxThreads, minThreads, 60000);
+    }
+
+    public InstrumentedQueuedThreadPool(@Name("registry") MetricRegistry registry,
+                                        @Name("name") String name,
+                                        @Name("maxThreads") int maxThreads,
+                                        @Name("minThreads") int minThreads,
+                                        @Name("idleTimeout") int idleTimeout) {
+        this(registry, name, maxThreads, minThreads, idleTimeout, null);
+    }
+
+    public InstrumentedQueuedThreadPool(@Name("registry") MetricRegistry registry,
+                                        @Name("name") String name,
+                                        @Name("maxThreads") int maxThreads,
+                                        @Name("minThreads") int minThreads,
+                                        @Name("idleTimeout") int idleTimeout,
+                                        @Name("queue") BlockingQueue queue) {
+        super(maxThreads, minThreads, idleTimeout, queue);
+
+        registry.register(name(QueuedThreadPool.class, name, "utilization"), new RatioGauge() {
+            @Override
+            protected Ratio getRatio() {
+                return Ratio.of(getThreads() - getIdleThreads(), getThreads());
+            }
+        });
+        registry.register(name(QueuedThreadPool.class, name, "size"), new Gauge() {
+            @Override
+            public Integer getValue() {
+                return getThreads();
+            }
+        });
+        registry.register(name(QueuedThreadPool.class, name, "jobs"), new Gauge() {
+            @Override
+            public Integer getValue() {
+                // This assumes the QueuedThreadPool is using a BlockingArrayQueue or
+                // ArrayBlockingQueue for its queue, and is therefore a constant-time operation.
+                return getQueue().size();
+            }
+        });
+
+        setName(name);
+    }
+}

From d8b82c9bf90f925e9f36a43cd1b1212011caadf0 Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Sat, 20 Apr 2013 21:38:11 -0700
Subject: [PATCH 0199/2558] Added InstrumentedHandler.

---
 .../metrics/jetty9/InstrumentedHandler.java   | 222 ++++++++++++++++++
 .../jetty9/InstrumentedHandlerTest.java       |  69 ++++++
 2 files changed, 291 insertions(+)
 create mode 100644 metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/InstrumentedHandler.java
 create mode 100644 metrics-jetty9/src/test/java/com/codahale/metrics/jetty9/InstrumentedHandlerTest.java

diff --git a/metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/InstrumentedHandler.java b/metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/InstrumentedHandler.java
new file mode 100644
index 0000000000..701aab4a45
--- /dev/null
+++ b/metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/InstrumentedHandler.java
@@ -0,0 +1,222 @@
+package com.codahale.metrics.jetty9;
+
+import com.codahale.metrics.*;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpChannelState;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.HandlerWrapper;
+
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import static com.codahale.metrics.MetricRegistry.name;
+
+/**
+ * A Jetty {@link Handler} which records various metrics about an underlying {@link Handler}
+ * instance.
+ */
+public class InstrumentedHandler extends HandlerWrapper {
+    // the requests handled by this handler, excluding active
+    private final Timer requests;
+
+    // the number of dispatches seen by this handler, excluding active
+    private final Timer dispatches;
+
+    // the number of active requests
+    private final Counter activeRequests;
+
+    // the number of active dispatches
+    private final Counter activeDispatches;
+
+    // the number of requests currently suspended.
+    private final Counter activeSuspended;
+
+    // the number of requests that have been asynchronously dispatched
+    private final Meter asyncDispatches;
+
+    // the number of requests that expired while suspended
+    private final Meter asyncTimeouts;
+
+    private final Meter[] responses;
+
+    private final Timer getRequests;
+    private final Timer postRequests;
+    private final Timer headRequests;
+    private final Timer putRequests;
+    private final Timer deleteRequests;
+    private final Timer optionsRequests;
+    private final Timer traceRequests;
+    private final Timer connectRequests;
+    private final Timer otherRequests;
+
+    private final AsyncListener listener;
+
+    /**
+     * Create a new instrumented handler using a given metrics registry. The name of the metric will
+     * be derived from the class of the Handler.
+     *
+     * @param registry   the registry for the metrics
+     * @param underlying the handler about which metrics will be collected
+     */
+    public InstrumentedHandler(MetricRegistry registry, Handler underlying) {
+        this(registry, underlying, name(underlying.getClass()));
+    }
+
+    /**
+     * Create a new instrumented handler using a given metrics registry and a custom prefix.
+     *
+     * @param registry   the registry for the metrics
+     * @param underlying the handler about which metrics will be collected
+     * @param prefix     the prefix to use for the metrics names
+     */
+    public InstrumentedHandler(MetricRegistry registry, Handler underlying, String prefix) {
+        super();
+        this.requests = registry.timer(name(prefix, "requests"));
+        this.dispatches = registry.timer(name(prefix, "dispatches"));
+
+        this.activeRequests = registry.counter(name(prefix, "active-requests"));
+        this.activeDispatches = registry.counter(name(prefix, "active-dispatches"));
+        this.activeSuspended = registry.counter(name(prefix, "active-suspended"));
+
+        this.asyncDispatches = registry.meter(name(prefix, "async-dispatches"));
+        this.asyncTimeouts = registry.meter(name(prefix, "async-timeouts"));
+
+        this.responses = new Meter[]{
+                registry.meter(name(prefix, "1xx-responses")), // 1xx
+                registry.meter(name(prefix, "2xx-responses")), // 2xx
+                registry.meter(name(prefix, "3xx-responses")), // 3xx
+                registry.meter(name(prefix, "4xx-responses")), // 4xx
+                registry.meter(name(prefix, "5xx-responses"))  // 5xx
+        };
+
+        this.getRequests = registry.timer(name(prefix, "get-requests"));
+        this.postRequests = registry.timer(name(prefix, "post-requests"));
+        this.headRequests = registry.timer(name(prefix, "head-requests"));
+        this.putRequests = registry.timer(name(prefix, "put-requests"));
+        this.deleteRequests = registry.timer(name(prefix, "delete-requests"));
+        this.optionsRequests = registry.timer(name(prefix, "options-requests"));
+        this.traceRequests = registry.timer(name(prefix, "trace-requests"));
+        this.connectRequests = registry.timer(name(prefix, "connect-requests"));
+        this.otherRequests = registry.timer(name(prefix, "other-requests"));
+
+        this.listener = new AsyncListener() {
+            @Override
+            public void onTimeout(AsyncEvent event) throws IOException {
+                asyncTimeouts.mark();
+            }
+
+            @Override
+            public void onStartAsync(AsyncEvent event) throws IOException {
+                event.getAsyncContext().addListener(this);
+            }
+
+            @Override
+            public void onError(AsyncEvent event) throws IOException {
+            }
+
+            @Override
+            public void onComplete(AsyncEvent event) throws IOException {
+                final HttpChannelState state = (HttpChannelState) event.getAsyncContext();
+                final Request request = state.getBaseRequest();
+                activeRequests.dec();
+                updateResponses(request);
+                if (!state.isDispatched()) {
+                    activeSuspended.dec();
+                }
+            }
+        };
+
+        setHandler(underlying);
+    }
+
+    @Override
+    public void handle(String path,
+                       Request request,
+                       HttpServletRequest httpRequest,
+                       HttpServletResponse httpResponse) throws IOException, ServletException {
+
+        activeDispatches.inc();
+
+        final long start;
+        final HttpChannelState state = request.getHttpChannelState();
+        if (state.isInitial()) {
+            // new request
+            activeRequests.inc();
+            start = request.getTimeStamp();
+        } else {
+            // resumed request
+            start = System.currentTimeMillis();
+            activeSuspended.dec();
+            if (state.isDispatched()) {
+                asyncDispatches.mark();
+            }
+        }
+
+        try {
+            super.handle(path, request, httpRequest, httpResponse);
+        } finally {
+            final long now = System.currentTimeMillis();
+            final long dispatched = now - start;
+
+            activeDispatches.dec();
+            dispatches.update(dispatched, TimeUnit.MILLISECONDS);
+
+            if (state.isSuspended()) {
+                if (state.isInitial()) {
+                    state.addListener(listener);
+                }
+                activeSuspended.inc();
+            } else if (state.isInitial()) {
+                activeRequests.dec();
+                requests.update(dispatched, TimeUnit.MILLISECONDS);
+                updateResponses(request);
+            }
+            // else onCompletion will handle it.
+        }
+    }
+
+    private Timer requestTimer(String method) {
+        final HttpMethod m = HttpMethod.fromString(method);
+        if (m == null) {
+            return otherRequests;
+        } else {
+            switch (m) {
+                case GET:
+                    return getRequests;
+                case POST:
+                    return postRequests;
+                case PUT:
+                    return putRequests;
+                case HEAD:
+                    return headRequests;
+                case DELETE:
+                    return deleteRequests;
+                case OPTIONS:
+                    return optionsRequests;
+                case TRACE:
+                    return traceRequests;
+                case CONNECT:
+                    return connectRequests;
+                default:
+                    return otherRequests;
+            }
+        }
+    }
+
+    private void updateResponses(Request request) {
+        final int response = request.getResponse().getStatus() / 100;
+        if (response >= 1 && response <= 5) {
+            responses[response - 1].mark();
+        }
+        activeRequests.dec();
+        final long elapsedTime = System.currentTimeMillis() - request.getTimeStamp();
+        requests.update(elapsedTime, TimeUnit.MILLISECONDS);
+        requestTimer(request.getMethod()).update(elapsedTime, TimeUnit.MILLISECONDS);
+    }
+}
diff --git a/metrics-jetty9/src/test/java/com/codahale/metrics/jetty9/InstrumentedHandlerTest.java b/metrics-jetty9/src/test/java/com/codahale/metrics/jetty9/InstrumentedHandlerTest.java
new file mode 100644
index 0000000000..37f5159323
--- /dev/null
+++ b/metrics-jetty9/src/test/java/com/codahale/metrics/jetty9/InstrumentedHandlerTest.java
@@ -0,0 +1,69 @@
+package com.codahale.metrics.jetty9;
+
+import com.codahale.metrics.MetricRegistry;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.DefaultHandler;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.fest.assertions.api.Assertions.assertThat;
+
+public class InstrumentedHandlerTest {
+    private final HttpClient client = new HttpClient();
+    private final MetricRegistry registry = new MetricRegistry();
+    private final Server server = new Server();
+    private final ServerConnector connector = new ServerConnector(server);
+    private final InstrumentedHandler handler = new InstrumentedHandler(registry,
+                                                                        new DefaultHandler());
+
+    @Before
+    public void setUp() throws Exception {
+        server.addConnector(connector);
+        server.setHandler(handler);
+        server.start();
+        client.start();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        server.stop();
+        client.stop();
+    }
+
+    @Test
+    public void createsMetricsForTheHandler() throws Exception {
+        final ContentResponse response = client.GET("http://localhost:" + connector.getLocalPort() + "/hello");
+
+        assertThat(response.getStatus())
+                .isEqualTo(404);
+
+        assertThat(registry.getNames())
+                .containsOnly(
+                        "org.eclipse.jetty.server.handler.DefaultHandler.1xx-responses",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.2xx-responses",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.3xx-responses",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.4xx-responses",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.5xx-responses",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.requests",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.active-suspended",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.async-dispatches",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.async-timeouts",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.get-requests",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.put-requests",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.active-dispatches",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.trace-requests",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.other-requests",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.connect-requests",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.dispatches",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.head-requests",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.post-requests",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.options-requests",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.active-requests",
+                        "org.eclipse.jetty.server.handler.DefaultHandler.delete-requests"
+                );
+    }
+}

From 0e0fb32830631045beed52b82da9c2ccdbed3e53 Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Sat, 20 Apr 2013 21:40:42 -0700
Subject: [PATCH 0200/2558] Upgraded to Jetty 8.1.10.

---
 docs/source/about/release-notes.rst | 1 +
 pom.xml                             | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/docs/source/about/release-notes.rst b/docs/source/about/release-notes.rst
index 14eed496fb..71243779fe 100644
--- a/docs/source/about/release-notes.rst
+++ b/docs/source/about/release-notes.rst
@@ -21,6 +21,7 @@ v3.0.0-BETA2-SNAPSHOT
   ``MetricRegistry``.
 * Added ``MetricRegistry#removeMatching(MetricFilter)``.
 * Changed ``metrics-json`` to optionally depend on ``metrics-healthcheck``.
+* Upgraded to Jetty 8.1.10 for ``metrics-jetty8``.
 
 .. _rel-3.0.0-BETA1:
 
diff --git a/pom.xml b/pom.xml
index 37fbeddcf0..cb40d4cebd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,7 +41,7 @@
         2.5
         1.7.5
         2.1.4
-        8.1.9.v20130131
+        8.1.10.v20130312
         9.0.2.v20130417
     
 

From 21816980c530e9735620fa431e9d1ed326fc6b23 Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Mon, 22 Apr 2013 08:20:16 -0700
Subject: [PATCH 0201/2558] Fix Maven packaging for metrics-jetty9.

---
 metrics-jetty9/pom.xml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/metrics-jetty9/pom.xml b/metrics-jetty9/pom.xml
index eb90f6d5d0..895b4db34b 100644
--- a/metrics-jetty9/pom.xml
+++ b/metrics-jetty9/pom.xml
@@ -11,6 +11,12 @@
     
 
     metrics-jetty9
+    Metrics Integration for Jetty 9
+    bundle
+    
+        A set of extensions for Jetty 9 which provide instrumentation of thread pools, connector
+        metrics, and application latency and utilization.
+    
 
     
         

From 068a2af694249c8f63c9ec85d904e5a23286fa44 Mon Sep 17 00:00:00 2001
From: Coda Hale 
Date: Mon, 22 Apr 2013 08:45:36 -0700
Subject: [PATCH 0202/2558] Add YourKit links.

---
 docs/source/_themes/metrics/layout.html | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/docs/source/_themes/metrics/layout.html b/docs/source/_themes/metrics/layout.html
index 443a25e35b..539fa14797 100644
--- a/docs/source/_themes/metrics/layout.html
+++ b/docs/source/_themes/metrics/layout.html
@@ -102,6 +102,16 @@ 

{% block body %} {% endblock %}
+ {%- if title == "Home" -%} +
+ {%- endif -%}