content: /**
   * 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
   * <p/>
   * http://www.apache.org/licenses/LICENSE-2.0
   * <p/>
   * 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 com.soundcloud.prometheus.hystrix;
  
  import com.netflix.hystrix.HystrixCollapserKey;
  import com.netflix.hystrix.HystrixCollapserMetrics;
  import com.netflix.hystrix.HystrixCollapserProperties;
  import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisherCollapser;
  import com.netflix.hystrix.util.HystrixRollingNumberEvent;
  
  import java.util.SortedMap;
  import java.util.TreeMap;
  import java.util.concurrent.Callable;
  
  /**
   * Implementation of a {@link HystrixMetricsPublisherCollapser} for Prometheus Metrics.
   * See <a href="https://github.com/Netflix/Hystrix/wiki/Metrics-and-Monitoring">Hystrix Metrics and Monitoring</a>.
   */
  public class HystrixPrometheusMetricsPublisherCollapser implements HystrixMetricsPublisherCollapser {
  
      private final SortedMap<String, String> labels;
      private HystrixMetricsPublisherCollapser delegate;
      private final boolean exportProperties;
  
      private final HystrixCollapserMetrics metrics;
      private final HystrixCollapserProperties properties;
      private final HystrixMetricsCollector collector;
  
      public HystrixPrometheusMetricsPublisherCollapser(
              HystrixMetricsCollector collector, HystrixCollapserKey key, HystrixCollapserMetrics metrics,
              HystrixCollapserProperties properties, boolean exportProperties,
              HystrixMetricsPublisherCollapser delegate) {
  
          this.metrics = metrics;
          this.collector = collector;
          this.properties = properties;
          this.exportProperties = exportProperties;
          this.labels = new TreeMap<String, String>();
          labels.put("collapser_name", key.name());
          this.delegate = delegate;
      }
  
      @Override
      public void initialize() {
          delegate.initialize();
  
          createCumulativeCountForEvent("count_batches", HystrixRollingNumberEvent.COLLAPSER_BATCH);
          createCumulativeCountForEvent("count_requests_batched", HystrixRollingNumberEvent.COLLAPSER_REQUEST_BATCHED);
          createCumulativeCountForEvent("count_responses_from_cache", HystrixRollingNumberEvent.RESPONSE_FROM_CACHE);
  
          createRollingCountForEvent("rolling_batches", HystrixRollingNumberEvent.COLLAPSER_BATCH);
          createRollingCountForEvent("rolling_requests_batched", HystrixRollingNumberEvent.COLLAPSER_REQUEST_BATCHED);
          createRollingCountForEvent("rolling_count_responses_from_cache", HystrixRollingNumberEvent.RESPONSE_FROM_CACHE);
  
          String batchDoc = "Collapser the batch size metric.";
          addGauge("batch_size_mean", batchDoc, new Callable<Number>() {
              @Override
              public Number call() throws Exception {
                  return metrics.getBatchSizeMean();
              }
          });
  
          createBatchSizePercentile("batch_size_percentile_25", 25, batchDoc);
          createBatchSizePercentile("batch_size_percentile_50", 50, batchDoc);
          createBatchSizePercentile("batch_size_percentile_75", 75, batchDoc);
          createBatchSizePercentile("batch_size_percentile_90", 90, batchDoc);
          createBatchSizePercentile("batch_size_percentile_99", 99, batchDoc);
          createBatchSizePercentile("batch_size_percentile_995", 99.5, batchDoc);
  
          String shardDoc = "Collapser shard size metric.";
          addGauge("shard_size_mean", shardDoc, new Callable<Number>() {
              @Override
              public Number call() throws Exception {
                  return metrics.getShardSizeMean();
              }
          });
  
          createShardSizePercentile("shard_size_percentile_25", 25, shardDoc);
          createShardSizePercentile("shard_size_percentile_50", 50, shardDoc);
          createShardSizePercentile("shard_size_percentile_75", 75, shardDoc);
          createShardSizePercentile("shard_size_percentile_90", 90, shardDoc);
          createShardSizePercentile("shard_size_percentile_99", 99, shardDoc);
          createShardSizePercentile("shard_size_percentile_995", 99.5, shardDoc);
  
          if (exportProperties) {
              String doc = "Configuration property partitioned by collapser_name.";
  
              addGauge("property_value_max_requests_in_batch", doc,
                      new Callable<Number>() {
                          @Override
                          public Number call() throws Exception {
                              return properties.maxRequestsInBatch().get();
                          }
                      });
  
              addGauge("property_value_request_cache_enabled", doc,
                      new Callable<Number>() {
                          @Override
                          public Number call() throws Exception {
                              return properties.requestCacheEnabled().get() ? 1 : 0;
                          }
                      });
  
              addGauge("property_value_timer_delay_in_milliseconds", doc,
                      new Callable<Number>() {
                          @Override
                          public Number call() throws Exception {
                              return properties.timerDelayInMilliseconds().get();
                          }
                      });
  
              addGauge("property_value_rolling_statistical_window_in_milliseconds", doc,
                      new Callable<Number>() {
                          @Override
                          public Number call() throws Exception {
                              return properties.metricsRollingStatisticalWindowInMilliseconds().get();
                          }
                      });
          }
      }
  
      private void createCumulativeCountForEvent(String metric, final HystrixRollingNumberEvent event) {
          String doc = "These are cumulative counts since the start of the application.";
          addGauge(metric, doc, new Callable<Number>() {
              @Override
              public Number call() throws Exception {
                  return metrics.getCumulativeCount(event);
              }
          });
      }
  
      private void createRollingCountForEvent(String metric, final HystrixRollingNumberEvent event) {
          String doc = "These are \"point in time\" counts representing the last X seconds.";
          addGauge(metric, doc, new Callable<Number>() {
              @Override
              public Number call() throws Exception {
                  return metrics.getRollingCount(event);
              }
          });
      }
  
      private void createBatchSizePercentile(String metric, final double percentile, String documentation) {
          addGauge(metric, documentation, new Callable<Number>() {
              @Override
              public Number call() throws Exception {
                  return metrics.getBatchSizePercentile(percentile);
              }
          });
      }
  
      private void createShardSizePercentile(String metric, final double percentile, String documentation) {
          addGauge(metric, documentation, new Callable<Number>() {
              @Override
              public Number call() throws Exception {
                  return metrics.getShardSizePercentile(percentile);
              }
          });
      }
  
      private void addGauge(String metric, String helpDoc, Callable<Number> value) {
          collector.addGauge("hystrix_collapser", metric, helpDoc, labels, value);
      }
  }
  