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