/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xd.analytics.metrics.core;
import org.springframework.util.Assert;
/**
* A gauge which stores the maximum, minimum and average in addition to the current value.
* <p>
* The value of the average will depend on whether a weight ('alpha') is set for the gauge. If it is unset, the average
* will contain a simple arithmetic mean. If a weight is set, an exponential moving average will be calculated as
* defined in this <a href="http://www.itl.nist.gov/div898/handbook/pmc/section4/pmc431.htm">NIST document</a>.
*
* @author Luke Taylor
*/
public final class RichGauge implements Metric {
private final String name;
private double value;
private double average;
private double max;
private double min;
private long count;
private double alpha;
/**
* Creates an "empty" gauge.
*
* The average, max and min will be zero, but this initial value will not be included after the first value has been
* set on the gauge.
*
* @param name the name under which the gauge will be stored.
*/
public RichGauge(String name) {
this(name, 0.0);
count = 0;
}
public RichGauge(String name, double value) {
Assert.notNull(name, "The gauge name cannot be null or empty");
this.name = name;
this.value = value;
average = this.value;
min = this.value;
max = this.value;
alpha = -1.0;
count = 1;
}
public RichGauge(String name, double value, double alpha, double mean, double max, double min, long count) {
this.name = name;
this.value = value;
this.alpha = alpha;
this.average = mean;
this.max = max;
this.min = min;
this.count = count;
}
/**
* @return the name of the gauge
*/
@Override
public String getName() {
return name;
}
/**
* @return the current value
*/
public double getValue() {
return value;
}
/**
* Either an exponential weighted moving average or a simple mean, respectively, depending on whether the weight
* 'alpha' has been set for this gauge.
*
* @return The average over all the accumulated values
*/
public double getAverage() {
return average;
}
/**
* @return the maximum value
*/
public double getMax() {
return max;
}
/**
* @return the minimum value
*/
public double getMin() {
return min;
}
/**
* @return Number of times the value has been set.
*/
public long getCount() {
return count;
}
/**
* @return the smoothing constant value.
*/
public double getAlpha() {
return alpha;
}
RichGauge set(double value, double alpha) {
Assert.isTrue(alpha == -1 || (alpha > 0.0 && alpha < 1.0),
"Smoothing constant must be between 0 and 1, or -1 to use arithmetic mean");
this.alpha = alpha;
if (count == 0) {
max = value;
min = value;
}
else if (value > max) {
max = value;
}
else if (value < min) {
min = value;
}
if (alpha > 0.0 && count > 0) {
average = alpha * this.value + (1 - alpha) * average;
}
else {
double sum = average * count;
sum += value;
average = sum / (count + 1);
}
count++;
this.value = value;
return this;
}
RichGauge reset() {
this.value = 0.0;
max = 0.0;
min = 0.0;
average = 0.0;
count = 0;
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RichGauge richGauge = (RichGauge) o;
if (!name.equals(richGauge.name)) {
return false;
}
return true;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public String toString() {
return "Gauge [name = " + name + ", value = " + value + ", alpha = " + alpha + ", average = " + average
+ ", max = " + max + ", min = " + min + ", count = " + count + "]";
}
}