All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.wavefront.agent.logsharvesting.FlushProcessor Maven / Gradle / Ivy

There is a newer version: 9999.0
Show newest version
package com.wavefront.agent.logsharvesting;

import com.tdunning.math.stats.AVLTreeDigest;
import com.tdunning.math.stats.Centroid;
import com.tdunning.math.stats.TDigest;
import com.wavefront.common.MetricsToTimeseries;
import com.yammer.metrics.Metrics;
import com.yammer.metrics.core.Counter;
import com.yammer.metrics.core.DeltaCounter;
import com.yammer.metrics.core.Gauge;
import com.yammer.metrics.core.Histogram;
import com.yammer.metrics.core.Metered;
import com.yammer.metrics.core.MetricName;
import com.yammer.metrics.core.MetricProcessor;
import com.yammer.metrics.core.Sampling;
import com.yammer.metrics.core.Summarizable;
import com.yammer.metrics.core.Timer;
import com.yammer.metrics.core.WavefrontHistogram;
import com.yammer.metrics.stats.Snapshot;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

import wavefront.report.HistogramType;

/**
 * Wrapper for {@link com.yammer.metrics.core.MetricProcessor}. It provides additional support
 * for Delta Counters and WavefrontHistogram.
 *
 * @author Mori Bellamy ([email protected])
 */
public class FlushProcessor implements MetricProcessor {

  private final Counter sentCounter = Metrics.newCounter(new MetricName("logsharvesting", "", "sent"));
  private final Counter histogramCounter = Metrics.newCounter(new MetricName("logsharvesting", "", "histograms-sent"));
  private final Supplier currentMillis;
  private final boolean useWavefrontHistograms;
  private final boolean reportEmptyHistogramStats;

  /**
   * Create new FlushProcessor instance
   *
   * @param currentMillis             {@link Supplier} of time (in milliseconds)
   * @param useWavefrontHistograms    export data in {@link com.yammer.metrics.core.WavefrontHistogram} format
   * @param reportEmptyHistogramStats enable legacy {@link com.yammer.metrics.core.Histogram} behavior and send zero
   *                                  values for every stat
   */
  FlushProcessor(Supplier currentMillis, boolean useWavefrontHistograms,
                 boolean reportEmptyHistogramStats) {
    this.currentMillis = currentMillis;
    this.useWavefrontHistograms = useWavefrontHistograms;
    this.reportEmptyHistogramStats = reportEmptyHistogramStats;
  }

  @Override
  public void processMeter(MetricName name, Metered meter, FlushProcessorContext context) {
    throw new UnsupportedOperationException();
  }

  @Override
  public void processCounter(MetricName name, Counter counter, FlushProcessorContext context) {
    long count;
    // handle delta counter
    if (counter instanceof DeltaCounter) {
      count = DeltaCounter.processDeltaCounter((DeltaCounter) counter);
      if (count == 0) return; // do not report 0-value delta counters
    } else {
      count = counter.count();
    }
    context.report(count);
    sentCounter.inc();
  }

  @Override
  public void processHistogram(MetricName name, Histogram histogram, FlushProcessorContext context) {
    if (histogram instanceof WavefrontHistogram) {
      WavefrontHistogram wavefrontHistogram = (WavefrontHistogram) histogram;
      if (useWavefrontHistograms) {
        // export Wavefront histograms in its native format
        if (wavefrontHistogram.count() == 0) return;
        for (WavefrontHistogram.MinuteBin bin : wavefrontHistogram.bins(true)) {
          if (bin.getDist().size() == 0) continue;
          int size = bin.getDist().centroids().size();
          List centroids = new ArrayList<>(size);
          List counts = new ArrayList<>(size);
          for (Centroid centroid : bin.getDist().centroids()) {
            centroids.add(centroid.mean());
            counts.add(centroid.count());
          }
          context.report(wavefront.report.Histogram.newBuilder().
              setDuration(60_000). // minute bins
              setType(HistogramType.TDIGEST).
              setBins(centroids).
              setCounts(counts).
              build(), bin.getMinMillis());
          histogramCounter.inc();
        }
      } else {
        // convert Wavefront histogram to Yammer-style histogram
        TDigest tDigest = new AVLTreeDigest(100);
        List bins = wavefrontHistogram.bins(true);
        bins.stream().map(WavefrontHistogram.MinuteBin::getDist).forEach(tDigest::add);
        context.reportSubMetric(tDigest.centroids().stream().mapToLong(Centroid::count).sum(), "count");
        Summarizable summarizable = new Summarizable() {
          @Override
          public double max() {
            return tDigest.centroids().stream().map(Centroid::mean).max(Comparator.naturalOrder()).orElse(Double.NaN);
          }

          @Override
          public double min() {
            return tDigest.centroids().stream().map(Centroid::mean).min(Comparator.naturalOrder()).orElse(Double.NaN);
          }

          @Override
          public double mean() {
            Centroid mean = tDigest.centroids().stream().
                reduce((x, y) -> new Centroid(x.mean() + (y.mean() * y.count()), x.count() + y.count())).orElse(null);
            return mean == null || tDigest.centroids().size() == 0 ? Double.NaN : mean.mean() / mean.count();
          }

          @Override
          public double stdDev() {
            return Double.NaN;
          }

          @Override
          public double sum() {
            return Double.NaN;
          }
        };
        for (Map.Entry entry : MetricsToTimeseries.explodeSummarizable(summarizable,
            reportEmptyHistogramStats).entrySet()) {
          if (!entry.getValue().isNaN()) {
            context.reportSubMetric(entry.getValue(), entry.getKey());
          }
        }
        Sampling sampling = () -> new Snapshot(new double[0]) {
          @Override
          public double get75thPercentile() {
            return tDigest.quantile(.75);
          }

          @Override
          public double get95thPercentile() {
            return tDigest.quantile(.95);
          }

          @Override
          public double get98thPercentile() {
            return tDigest.quantile(.98);
          }

          @Override
          public double get999thPercentile() {
            return tDigest.quantile(.999);
          }

          @Override
          public double get99thPercentile() {
            return tDigest.quantile(.99);
          }

          @Override
          public double getMedian() {
            return tDigest.quantile(.50);
          }

          @Override
          public double getValue(double quantile) {
            return tDigest.quantile(quantile);
          }

          @Override
          public double[] getValues() {
            return new double[0];
          }

          @Override
          public int size() {
            return (int) tDigest.size();
          }
        };
        for (Map.Entry entry : MetricsToTimeseries.explodeSampling(sampling,
            reportEmptyHistogramStats).entrySet()) {
          if (!entry.getValue().isNaN()) {
            context.reportSubMetric(entry.getValue(), entry.getKey());
          }
        }
        sentCounter.inc();
      }
    } else {
      context.reportSubMetric(histogram.count(), "count");
      for (Map.Entry entry : MetricsToTimeseries.explodeSummarizable(histogram, reportEmptyHistogramStats).entrySet()) {
        if (!entry.getValue().isNaN()) {
          context.reportSubMetric(entry.getValue(), entry.getKey());
        }
      }
      for (Map.Entry entry : MetricsToTimeseries.explodeSampling(histogram, reportEmptyHistogramStats).entrySet()) {
        if (!entry.getValue().isNaN()) {
          context.reportSubMetric(entry.getValue(), entry.getKey());
        }
      }
      sentCounter.inc();
      histogram.clear();
    }
  }

  @Override
  public void processTimer(MetricName name, Timer timer, FlushProcessorContext context) {
    throw new UnsupportedOperationException();
  }

  @Override
  public void processGauge(MetricName name, Gauge gauge, FlushProcessorContext context) {
    @SuppressWarnings("unchecked")
    ChangeableGauge changeableGauge = (ChangeableGauge) gauge;
    Double value = changeableGauge.value();
    if (value == null || value.isInfinite() || value.isNaN()) return;
    context.report(value);
    sentCounter.inc();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy