Package com.netflix.servo.monitor

Source Code of com.netflix.servo.monitor.StatsMonitor$VarianceGaugeWrapper

/**
* Copyright 2013 Netflix, Inc.
*
* 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
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* 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.netflix.servo.monitor;

import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.netflix.servo.stats.StatsBuffer;
import com.netflix.servo.stats.StatsConfig;
import com.netflix.servo.tag.BasicTagList;
import com.netflix.servo.tag.Tag;
import com.netflix.servo.tag.TagList;
import com.netflix.servo.tag.Tags;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

/**
* A {@link Timer} that provides statistics.
* <p/>
* The statistics are collected periodically and are published according to the configuration
* specified by the user using a {@link com.netflix.servo.stats.StatsConfig} object.
*/
public class StatsMonitor extends AbstractMonitor<Long> implements
        CompositeMonitor<Long>, NumericMonitor<Long> {
    private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder()
            .setDaemon(true)
            .setNameFormat("StatsMonitor-%d")
            .build();
    //CHECKSTYLE.OFF: ConstantName
    protected static final ScheduledExecutorService defaultExecutor =
            Executors.newScheduledThreadPool(1, THREAD_FACTORY);
    //CHECKSTYLE.ON: ConstantName
    private static final Logger LOGGER = LoggerFactory.getLogger(StatsMonitor.class);

    private final MonitorConfig baseConfig;
    protected final Counter count;
    protected final Counter totalMeasurement;
    private final List<Monitor<?>> monitors;

    private final List<GaugeWrapper> gaugeWrappers;
    private final Runnable startComputingAction;

    private final Object updateLock = new Object();
    private StatsBuffer cur;
    private StatsBuffer prev;

    private static final String STATISTIC = "statistic";
    private static final String PERCENTILE_FMT = "percentile_%.2f";
    private static final Tag STAT_COUNT = Tags.newTag(STATISTIC, "count");
    private static final Tag STAT_MIN = Tags.newTag(STATISTIC, "min");
    private static final Tag STAT_MAX = Tags.newTag(STATISTIC, "max");
    private static final Tag STAT_MEAN = Tags.newTag(STATISTIC, "avg");
    private static final Tag STAT_VARIANCE = Tags.newTag(STATISTIC, "variance");
    private static final Tag STAT_STDDEV = Tags.newTag(STATISTIC, "stdDev");

    private interface GaugeWrapper {
        void update(StatsBuffer buffer);

        Monitor<?> getMonitor();
    }

    private abstract static class LongGaugeWrapper implements GaugeWrapper {
        protected final LongGauge gauge;

        protected LongGaugeWrapper(MonitorConfig config) {
            gauge = new LongGauge(config);
        }

        @Override
        public Monitor<?> getMonitor() {
            return gauge;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (!(o instanceof LongGaugeWrapper)) {
                return false;
            }
            final LongGaugeWrapper that = (LongGaugeWrapper) o;
            return gauge.equals(that.gauge);
        }

        @Override
        public int hashCode() {
            return gauge.hashCode();
        }

        @Override
        public String toString() {
            return Objects.toStringHelper(this).add("gauge", gauge).toString();
        }
    }

    private abstract static class DoubleGaugeWrapper implements GaugeWrapper {
        protected final DoubleGauge gauge;

        protected DoubleGaugeWrapper(MonitorConfig config) {
            gauge = new DoubleGauge(config);
        }

        @Override
        public Monitor<?> getMonitor() {
            return gauge;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (!(o instanceof DoubleGaugeWrapper)) {
                return false;
            }
            final DoubleGaugeWrapper that = (DoubleGaugeWrapper) o;
            return gauge.equals(that.gauge);
        }

        @Override
        public int hashCode() {
            return gauge.hashCode();
        }

        @Override
        public String toString() {
            return Objects.toStringHelper(this).add("gauge", gauge).toString();
        }
    }

    private static class MinStatGaugeWrapper extends LongGaugeWrapper {
        MinStatGaugeWrapper(MonitorConfig baseConfig) {
            super(baseConfig.withAdditionalTag(STAT_MIN));
        }

        @Override
        public void update(StatsBuffer buffer) {
            gauge.set(buffer.getMin());
        }
    }

    private static class MaxGaugeWrapper extends LongGaugeWrapper {
        MaxGaugeWrapper(MonitorConfig baseConfig) {
            super(baseConfig.withAdditionalTag(STAT_MAX));
        }

        @Override
        public void update(StatsBuffer buffer) {
            gauge.set(buffer.getMax());
        }
    }

    private static class MeanGaugeWrapper extends DoubleGaugeWrapper {
        MeanGaugeWrapper(MonitorConfig baseConfig) {
            super(baseConfig.withAdditionalTag(STAT_MEAN));
        }

        @Override
        public void update(StatsBuffer buffer) {
            gauge.set(buffer.getMean());
        }
    }

    private static class VarianceGaugeWrapper extends DoubleGaugeWrapper {
        VarianceGaugeWrapper(MonitorConfig baseConfig) {
            super(baseConfig.withAdditionalTag(STAT_VARIANCE));
        }

        @Override
        public void update(StatsBuffer buffer) {
            gauge.set(buffer.getVariance());
        }
    }

    private static class StdDevGaugeWrapper extends DoubleGaugeWrapper {
        StdDevGaugeWrapper(MonitorConfig baseConfig) {
            super(baseConfig.withAdditionalTag(STAT_STDDEV));
        }

        @Override
        public void update(StatsBuffer buffer) {
            gauge.set(buffer.getStdDev());
        }
    }

    private static class PercentileGaugeWrapper extends DoubleGaugeWrapper {
        private final double percentile;
        private final int index;

        private static Tag percentileTag(double percentile) {
            String percentileStr = String.format(PERCENTILE_FMT, percentile);
            if (percentileStr.endsWith(".00")) {
                percentileStr = percentileStr.substring(0, percentileStr.length() - 3);
            }

            return Tags.newTag(STATISTIC, percentileStr);
        }

        PercentileGaugeWrapper(MonitorConfig baseConfig, double percentile, int index) {
            super(baseConfig.withAdditionalTag(percentileTag(percentile)));
            this.percentile = percentile;
            this.index = index;
        }

        @Override
        public void update(StatsBuffer buffer) {
            gauge.set(buffer.getPercentileValues()[index]);
        }

        @Override
        public String toString() {
            return Objects.toStringHelper(this)
                    .add("gauge", gauge)
                    .add("percentile", percentile)
                    .toString();
        }
    }

    private List<Counter> getCounters(StatsConfig config) {
        ImmutableList.Builder<Counter> monitors = ImmutableList.builder();
        if (config.getPublishCount()) {
            monitors.add(count);
        }
        if (config.getPublishTotal()) {
            monitors.add(totalMeasurement);
        }
        return monitors.build();
    }

    private List<GaugeWrapper> getGaugeWrappers(StatsConfig config) {
        final ImmutableList.Builder<GaugeWrapper> builder = ImmutableList.builder();

        if (config.getPublishMax()) {
            builder.add(new MaxGaugeWrapper(baseConfig));
        }
        if (config.getPublishMin()) {
            builder.add(new MinStatGaugeWrapper(baseConfig));
        }
        if (config.getPublishVariance()) {
            builder.add(new VarianceGaugeWrapper(baseConfig));
        }
        if (config.getPublishStdDev()) {
            builder.add(new StdDevGaugeWrapper(baseConfig));
        }
        if (config.getPublishMean()) {
            builder.add(new MeanGaugeWrapper(baseConfig));
        }

        final double[] percentiles = config.getPercentiles();
        for (int i = 0; i < percentiles.length; ++i) {
            builder.add(new PercentileGaugeWrapper(baseConfig, percentiles[i], i));
        }

        final List<GaugeWrapper> wrappers = builder.build();

        // do a sanity check to prevent duplicated monitor configurations
        final Set<MonitorConfig> seen = Sets.newHashSet();
        for (final GaugeWrapper wrapper : wrappers) {
            final MonitorConfig cfg = wrapper.getMonitor().getConfig();
            if (seen.contains(cfg)) {
                throw new IllegalArgumentException("Duplicated monitor configuration found: "
                        + cfg);
            }
            seen.add(cfg);
        }

        return wrappers;
    }

    /**
     * Creates a new instance of the timer with a unit of milliseconds,
     * using the {@link ScheduledExecutorService} provided by the user.
     */
    public StatsMonitor(final MonitorConfig config,
                        final StatsConfig statsConfig,
                        final ScheduledExecutorService executor,
                        final String totalTagName,
                        final boolean autoStart,
                        final Tag... additionalTags) {
        super(config);
        final Tag statsTotal = Tags.newTag(STATISTIC, totalTagName);
        TagList additionalTagList = new BasicTagList(Arrays.asList(additionalTags));
        this.baseConfig = config.withAdditionalTags(additionalTagList);
        this.cur = new StatsBuffer(statsConfig.getSampleSize(), statsConfig.getPercentiles());
        this.prev = new StatsBuffer(statsConfig.getSampleSize(), statsConfig.getPercentiles());
        this.count = new BasicCounter(baseConfig.withAdditionalTag(STAT_COUNT));
        this.totalMeasurement = new BasicCounter(baseConfig.withAdditionalTag(statsTotal));
        this.gaugeWrappers = getGaugeWrappers(statsConfig);
        final Collection<Monitor<?>> gaugeMonitors = Collections2.transform(gaugeWrappers,
                new Function<GaugeWrapper, Monitor<?>>() {
                    public Monitor<?> apply(GaugeWrapper perfStatGauge) {
                        return perfStatGauge.getMonitor();
                    }
                });
        this.monitors = new ImmutableList.Builder<Monitor<?>>()
                .addAll(getCounters(statsConfig))
                .addAll(gaugeMonitors)
                .build();
        this.startComputingAction = new Runnable() {
            public void run() {
                startComputingStats(executor, statsConfig.getFrequencyMillis());
            }
        };
        if (autoStart) {
            startComputingStats();
        }
    }

    /**
     * starts computation.
     * Because of potential race conditions, derived classes may wish
     * to define initial state before calling this function which starts the executor
     */
    public void startComputingStats() {
        this.startComputingAction.run();
    }

    private void startComputingStats(ScheduledExecutorService executor, long frequencyMillis) {
        Runnable command = new Runnable() {
            @Override
            public void run() {
                try {
                    synchronized (updateLock) {
                        final StatsBuffer tmp = prev;
                        prev = cur;
                        cur = tmp;
                    }
                    prev.computeStats();
                    updateGauges();
                    prev.reset();
                } catch (Exception e) {
                    handleException(e);
                }
            }
        };

        executor.scheduleWithFixedDelay(command, frequencyMillis, frequencyMillis,
                TimeUnit.MILLISECONDS);
    }

    private void updateGauges() {
        for (GaugeWrapper gauge : gaugeWrappers) {
            gauge.update(prev);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<Monitor<?>> getMonitors() {
        return monitors;
    }

    /**
     * Record the measurement we want to perform statistics on.
     */
    public void record(long measurement) {
        synchronized (updateLock) {
            cur.record(measurement);
        }
        count.increment();
        totalMeasurement.increment(measurement);
    }

    /**
     * Get the value of the measurement.
     */
    @Override
    public Long getValue(int pollerIndex) {
        final long n = getCount(pollerIndex);
        return n > 0 ? totalMeasurement.getValue(pollerIndex).longValue() / n : 0L;
    }


    @Override
    public Long getValue() {
        return getValue(0);
    }

    /**
     * This is called when we encounter an exception while processing the values
     * recorded to compute the stats.
     *
     * @param e Exception encountered.
     */
    protected void handleException(Exception e) {
        LOGGER.warn("Unable to compute stats: ", e);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return Objects.toStringHelper(this)
                .add("baseConfig", baseConfig)
                .add("monitors", monitors)
                .toString();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == null || !(obj instanceof StatsMonitor)) {
            return false;
        }

        final StatsMonitor m = (StatsMonitor) obj;
        return baseConfig.equals(m.baseConfig) && monitors.equals(m.monitors);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        return Objects.hashCode(baseConfig, monitors);
    }

    /**
     * Get the number of times this timer has been updated.
     */
    public long getCount(int pollerIndex) {
        return count.getValue(pollerIndex).longValue();
    }


    /**
     * Get the total time recorded for this timer.
     */
    public long getTotalMeasurement() {
        return totalMeasurement.getValue().longValue();
    }
}
TOP

Related Classes of com.netflix.servo.monitor.StatsMonitor$VarianceGaugeWrapper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.