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 com.yammer.metrics.stats.ExponentiallyDecayingSample;
22  import com.yammer.metrics.stats.Sample;
23  import com.yammer.metrics.stats.Snapshot;
24  import org.apache.commons.lang.StringUtils;
25  import org.apache.hadoop.metrics2.MetricHistogram;
26  import org.apache.hadoop.metrics2.MetricsInfo;
27  import org.apache.hadoop.metrics2.MetricsRecordBuilder;
28  
29  import java.util.concurrent.atomic.AtomicLong;
30  
31  /**
32   * A histogram implementation that runs in constant space, and exports to hadoop2's metrics2 system.
33   */
34  public class MutableHistogram extends MutableMetric implements MetricHistogram {
35  
36    private static final int DEFAULT_SAMPLE_SIZE = 2046;
37    // the bias towards sampling from more recent data.
38    // Per Cormode et al. an alpha of 0.015 strongly biases to the last 5 minutes
39    private static final double DEFAULT_ALPHA = 0.015;
40  
41    private final String name;
42    private final String desc;
43    private final Sample sample;
44    private final AtomicLong min;
45    private final AtomicLong max;
46    private final AtomicLong sum;
47    private final AtomicLong count;
48  
49    public MutableHistogram(MetricsInfo info) {
50      this(info.name(), info.description());
51    }
52  
53    public MutableHistogram(String name, String description) {
54      this.name = StringUtils.capitalize(name);
55      this.desc = StringUtils.uncapitalize(description);
56      sample = new ExponentiallyDecayingSample(DEFAULT_SAMPLE_SIZE, DEFAULT_ALPHA);
57      count = new AtomicLong();
58      min = new AtomicLong(Long.MAX_VALUE);
59      max = new AtomicLong(Long.MIN_VALUE);
60      sum = new AtomicLong();
61    }
62  
63    public void add(final long val) {
64      setChanged();
65      count.incrementAndGet();
66      sample.update(val);
67      setMax(val);
68      setMin(val);
69      sum.getAndAdd(val);
70    }
71  
72    private void setMax(final long potentialMax) {
73      boolean done = false;
74      while (!done) {
75        final long currentMax = max.get();
76        done = currentMax >= potentialMax
77            || max.compareAndSet(currentMax, potentialMax);
78      }
79    }
80  
81    private void setMin(long potentialMin) {
82      boolean done = false;
83      while (!done) {
84        final long currentMin = min.get();
85        done = currentMin <= potentialMin
86            || min.compareAndSet(currentMin, potentialMin);
87      }
88    }
89  
90    public long getMax() {
91      if (count.get() > 0) {
92        return max.get();
93      }
94      return 0L;
95    }
96  
97    public long getMin() {
98      if (count.get() > 0) {
99        return min.get();
100     }
101     return 0L;
102   }
103 
104   public double getMean() {
105     long cCount = count.get();
106     if (cCount > 0) {
107       return sum.get() / (double) cCount;
108     }
109     return 0.0;
110   }
111 
112   @Override
113   public void snapshot(MetricsRecordBuilder metricsRecordBuilder, boolean all) {
114     if (all || changed()) {
115       clearChanged();
116       final Snapshot s = sample.getSnapshot();
117       metricsRecordBuilder.addCounter(Interns.info(name + NUM_OPS_METRIC_NAME, desc), count.get());
118 
119       metricsRecordBuilder.addGauge(Interns.info(name + MIN_METRIC_NAME, desc), getMin());
120       metricsRecordBuilder.addGauge(Interns.info(name + MAX_METRIC_NAME, desc), getMax());
121       metricsRecordBuilder.addGauge(Interns.info(name + MEAN_METRIC_NAME, desc), getMean());
122 
123       metricsRecordBuilder.addGauge(Interns.info(name + MEDIAN_METRIC_NAME, desc), s.getMedian());
124       metricsRecordBuilder.addGauge(Interns.info(name + SEVENTY_FIFTH_PERCENTILE_METRIC_NAME, desc),
125           s.get75thPercentile());
126       metricsRecordBuilder.addGauge(Interns.info(name + NINETY_FIFTH_PERCENTILE_METRIC_NAME, desc),
127           s.get95thPercentile());
128       metricsRecordBuilder.addGauge(Interns.info(name + NINETY_NINETH_PERCENTILE_METRIC_NAME, desc),
129           s.get99thPercentile());
130     }
131   }
132 }