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

io.micrometer.core.instrument.AbstractTimer Maven / Gradle / Ivy

There is a newer version: 1.13.2
Show newest version
/*
 * Copyright 2017 VMware, 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
 *
 * https://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 io.micrometer.core.instrument;

import io.micrometer.core.instrument.distribution.*;
import io.micrometer.core.instrument.distribution.pause.ClockDriftPauseDetector;
import io.micrometer.core.instrument.distribution.pause.PauseDetector;
import io.micrometer.core.lang.Nullable;
import org.LatencyUtils.IntervalEstimator;
import org.LatencyUtils.SimplePauseDetector;
import org.LatencyUtils.TimeCappedMovingAverageIntervalEstimator;

import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

public abstract class AbstractTimer extends AbstractMeter implements Timer {

    private static Map pauseDetectorCache = new ConcurrentHashMap<>();

    protected final Clock clock;

    protected final Histogram histogram;

    private final TimeUnit baseTimeUnit;

    // Only used when pause detection is enabled
    @Nullable
    private IntervalEstimator intervalEstimator = null;

    @Nullable
    private org.LatencyUtils.PauseDetector pauseDetector;

    /**
     * Creates a new timer.
     * @param id The timer's name and tags.
     * @param clock The clock used to measure latency.
     * @param distributionStatisticConfig Configuration determining which distribution
     * statistics are sent.
     * @param pauseDetector Compensation for coordinated omission.
     * @param baseTimeUnit The time scale of this timer.
     * @deprecated Timer implementations should now declare at construction time whether
     * they support aggregable percentiles or not. By declaring it up front, Micrometer
     * can memory optimize the histogram structure used to store distribution statistics.
     */
    @Deprecated
    protected AbstractTimer(Id id, Clock clock, DistributionStatisticConfig distributionStatisticConfig,
            PauseDetector pauseDetector, TimeUnit baseTimeUnit) {
        this(id, clock, distributionStatisticConfig, pauseDetector, baseTimeUnit, false);
    }

    /**
     * Creates a new timer.
     * @param id The timer's name and tags.
     * @param clock The clock used to measure latency.
     * @param distributionStatisticConfig Configuration determining which distribution
     * statistics are sent.
     * @param pauseDetector Compensation for coordinated omission.
     * @param baseTimeUnit The time scale of this timer.
     * @param supportsAggregablePercentiles Indicates whether the registry supports
     * percentile approximations from histograms.
     */
    protected AbstractTimer(Id id, Clock clock, DistributionStatisticConfig distributionStatisticConfig,
            PauseDetector pauseDetector, TimeUnit baseTimeUnit, boolean supportsAggregablePercentiles) {
        super(id);
        this.clock = clock;
        this.baseTimeUnit = baseTimeUnit;

        initPauseDetector(pauseDetector);

        if (distributionStatisticConfig.isPublishingPercentiles()) {
            // hdr-based histogram
            this.histogram = new TimeWindowPercentileHistogram(clock, distributionStatisticConfig,
                    supportsAggregablePercentiles);
        }
        else if (distributionStatisticConfig.isPublishingHistogram()) {
            // fixed boundary histograms, which have a slightly better memory footprint
            // when we don't need Micrometer-computed percentiles
            this.histogram = new TimeWindowFixedBoundaryHistogram(clock, distributionStatisticConfig,
                    supportsAggregablePercentiles);
        }
        else {
            // noop histogram
            this.histogram = NoopHistogram.INSTANCE;
        }
    }

    private void initPauseDetector(PauseDetector pauseDetectorType) {
        pauseDetector = pauseDetectorCache.computeIfAbsent(pauseDetectorType, detector -> {
            if (detector instanceof ClockDriftPauseDetector) {
                ClockDriftPauseDetector clockDriftPauseDetector = (ClockDriftPauseDetector) detector;
                return new SimplePauseDetector(clockDriftPauseDetector.getSleepInterval().toNanos(),
                        clockDriftPauseDetector.getPauseThreshold().toNanos(), 1, false);
            }
            return null;
        });

        if (pauseDetector instanceof SimplePauseDetector) {
            this.intervalEstimator = new TimeCappedMovingAverageIntervalEstimator(128, 10000000000L, pauseDetector);

            pauseDetector.addListener((pauseLength, pauseEndTime) -> {
                if (intervalEstimator != null) {
                    long estimatedInterval = intervalEstimator.getEstimatedInterval(pauseEndTime);
                    long observedLatencyMinbar = pauseLength - estimatedInterval;
                    if (observedLatencyMinbar >= estimatedInterval) {
                        recordValueWithExpectedInterval(observedLatencyMinbar, estimatedInterval);
                    }
                }
            });
        }
    }

    private void recordValueWithExpectedInterval(long nanoValue, long expectedIntervalBetweenValueSamples) {
        record(nanoValue, TimeUnit.NANOSECONDS);
        if (expectedIntervalBetweenValueSamples <= 0)
            return;
        for (long missingValue = nanoValue
                - expectedIntervalBetweenValueSamples; missingValue >= expectedIntervalBetweenValueSamples; missingValue -= expectedIntervalBetweenValueSamples) {
            record(missingValue, TimeUnit.NANOSECONDS);
        }
    }

    @Override
    public  T recordCallable(Callable f) throws Exception {
        final long s = clock.monotonicTime();
        try {
            return f.call();
        }
        finally {
            final long e = clock.monotonicTime();
            record(e - s, TimeUnit.NANOSECONDS);
        }
    }

    @Override
    public  T record(Supplier f) {
        final long s = clock.monotonicTime();
        try {
            return f.get();
        }
        finally {
            final long e = clock.monotonicTime();
            record(e - s, TimeUnit.NANOSECONDS);
        }
    }

    @Override
    public void record(Runnable f) {
        final long s = clock.monotonicTime();
        try {
            f.run();
        }
        finally {
            final long e = clock.monotonicTime();
            record(e - s, TimeUnit.NANOSECONDS);
        }
    }

    @Override
    public final void record(long amount, TimeUnit unit) {
        if (amount >= 0) {
            histogram.recordLong(TimeUnit.NANOSECONDS.convert(amount, unit));
            recordNonNegative(amount, unit);

            if (intervalEstimator != null) {
                intervalEstimator.recordInterval(clock.monotonicTime());
            }
        }
    }

    protected abstract void recordNonNegative(long amount, TimeUnit unit);

    @Override
    public HistogramSnapshot takeSnapshot() {
        return histogram.takeSnapshot(count(), totalTime(TimeUnit.NANOSECONDS), max(TimeUnit.NANOSECONDS));
    }

    @Override
    public TimeUnit baseTimeUnit() {
        return baseTimeUnit;
    }

    @Override
    public void close() {
        histogram.close();
        if (pauseDetector != null) {
            pauseDetector.shutdown();
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy