View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.metrics2.lib;
20  
21  import java.util.concurrent.atomic.AtomicLong;
22  
23  import org.apache.commons.lang.StringUtils;
24  import org.apache.hadoop.classification.InterfaceAudience;
25  import org.apache.hadoop.metrics2.MetricHistogram;
26  import org.apache.hadoop.metrics2.MetricsInfo;
27  import org.apache.hadoop.metrics2.MetricsRecordBuilder;
28  
29  import com.yammer.metrics.stats.ExponentiallyDecayingSample;
30  import com.yammer.metrics.stats.Sample;
31  import com.yammer.metrics.stats.Snapshot;
32  
33  /**
34   * A histogram implementation that runs in constant space, and exports to hadoop2's metrics2 system.
35   */
36  @InterfaceAudience.Private
37  public class MutableHistogram extends MutableMetric implements MetricHistogram {
38  
39    private static final int DEFAULT_SAMPLE_SIZE = 2046;
40    // the bias towards sampling from more recent data.
41    // Per Cormode et al. an alpha of 0.015 strongly biases to the last 5 minutes
42    private static final double DEFAULT_ALPHA = 0.015;
43  
44    private final String name;
45    private final String desc;
46    private final Sample sample;
47    private final AtomicLong min;
48    private final AtomicLong max;
49    private final AtomicLong sum;
50    private final AtomicLong count;
51  
52    public MutableHistogram(MetricsInfo info) {
53      this(info.name(), info.description());
54    }
55  
56    public MutableHistogram(String name, String description) {
57      this.name = StringUtils.capitalize(name);
58      this.desc = StringUtils.uncapitalize(description);
59      sample = new ExponentiallyDecayingSample(DEFAULT_SAMPLE_SIZE, DEFAULT_ALPHA);
60      count = new AtomicLong();
61      min = new AtomicLong(Long.MAX_VALUE);
62      max = new AtomicLong(Long.MIN_VALUE);
63      sum = new AtomicLong();
64    }
65  
66    public void add(final long val) {
67      setChanged();
68      count.incrementAndGet();
69      sample.update(val);
70      setMax(val);
71      setMin(val);
72      sum.getAndAdd(val);
73    }
74  
75    private void setMax(final long potentialMax) {
76      boolean done = false;
77      while (!done) {
78        final long currentMax = max.get();
79        done = currentMax >= potentialMax
80            || max.compareAndSet(currentMax, potentialMax);
81      }
82    }
83  
84    private void setMin(long potentialMin) {
85      boolean done = false;
86      while (!done) {
87        final long currentMin = min.get();
88        done = currentMin <= potentialMin
89            || min.compareAndSet(currentMin, potentialMin);
90      }
91    }
92  
93    public long getMax() {
94      if (count.get() > 0) {
95        return max.get();
96      }
97      return 0L;
98    }
99  
100   public long getMin() {
101     if (count.get() > 0) {
102       return min.get();
103     }
104     return 0L;
105   }
106 
107   public double getMean() {
108     long cCount = count.get();
109     if (cCount > 0) {
110       return sum.get() / (double) cCount;
111     }
112     return 0.0;
113   }
114 
115   @Override
116   public void snapshot(MetricsRecordBuilder metricsRecordBuilder, boolean all) {
117     if (all || changed()) {
118       clearChanged();
119       final Snapshot s = sample.getSnapshot();
120       metricsRecordBuilder.addCounter(Interns.info(name + NUM_OPS_METRIC_NAME, desc), count.get());
121 
122       metricsRecordBuilder.addGauge(Interns.info(name + MIN_METRIC_NAME, desc), getMin());
123       metricsRecordBuilder.addGauge(Interns.info(name + MAX_METRIC_NAME, desc), getMax());
124       metricsRecordBuilder.addGauge(Interns.info(name + MEAN_METRIC_NAME, desc), getMean());
125 
126       metricsRecordBuilder.addGauge(Interns.info(name + MEDIAN_METRIC_NAME, desc), s.getMedian());
127       metricsRecordBuilder.addGauge(Interns.info(name + SEVENTY_FIFTH_PERCENTILE_METRIC_NAME, desc),
128           s.get75thPercentile());
129       metricsRecordBuilder.addGauge(Interns.info(name + NINETY_FIFTH_PERCENTILE_METRIC_NAME, desc),
130           s.get95thPercentile());
131       metricsRecordBuilder.addGauge(Interns.info(name + NINETY_NINETH_PERCENTILE_METRIC_NAME, desc),
132           s.get99thPercentile());
133     }
134   }
135 }