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

io.micrometer.core.instrument.internal.DefaultLongTaskTimer Maven / Gradle / Ivy

There is a newer version: 1.13.0
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.internal; import io.micrometer.core.instrument.AbstractMeter; import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.LongTaskTimer; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.distribution.CountAtBucket; import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; import io.micrometer.core.instrument.distribution.HistogramSnapshot; import io.micrometer.core.instrument.distribution.ValueAtPercentile; import io.micrometer.core.instrument.util.MeterEquivalence; import io.micrometer.core.instrument.util.TimeUtils; import java.util.*; import java.util.concurrent.*; import java.util.function.Consumer; import java.util.stream.Collectors; public class DefaultLongTaskTimer extends AbstractMeter implements LongTaskTimer { /** * Preferring {@link ConcurrentLinkedDeque} over {@link CopyOnWriteArrayList} here because... *

* Retrieval of percentile values will be O(N) but starting/stopping tasks will be O(1). Starting/stopping * tasks happen in the same thread as the main application code, where publishing generally happens in a separate * thread. Also, shipping client-side percentiles should be relatively uncommon. *

* Histogram creation is O(N) for both the queue and list options, because we have to consider which bucket each * active task belongs. */ private final Deque activeTasks = new ConcurrentLinkedDeque<>(); private final Clock clock; private final TimeUnit baseTimeUnit; private final DistributionStatisticConfig distributionStatisticConfig; private final boolean supportsAggregablePercentiles; /** * Create a {@code DefaultLongTaskTimer} instance. * * @param id ID * @param clock clock * @deprecated Use {@link #DefaultLongTaskTimer(Meter.Id, Clock, TimeUnit, DistributionStatisticConfig, boolean)} instead. */ @Deprecated public DefaultLongTaskTimer(Id id, Clock clock) { this(id, clock, TimeUnit.MILLISECONDS, DistributionStatisticConfig.DEFAULT, false); } /** * Create a {@code DefaultLongTaskTimer} instance. * * @param id ID * @param clock clock * @param baseTimeUnit base time unit * @param distributionStatisticConfig distribution statistic configuration * @param supportsAggregablePercentiles whether it supports aggregable percentiles * @since 1.5.0 */ public DefaultLongTaskTimer(Id id, Clock clock, TimeUnit baseTimeUnit, DistributionStatisticConfig distributionStatisticConfig, boolean supportsAggregablePercentiles) { super(id); this.clock = clock; this.baseTimeUnit = baseTimeUnit; this.distributionStatisticConfig = distributionStatisticConfig; this.supportsAggregablePercentiles = supportsAggregablePercentiles; } @Override public Sample start() { SampleImpl sample = new SampleImpl(); activeTasks.add(sample); return sample; } @Override public double duration(TimeUnit unit) { long now = clock.monotonicTime(); long sum = 0L; for (SampleImpl task : activeTasks) { sum += now - task.startTime(); } return TimeUtils.nanosToUnit(sum, unit); } @Override public double max(TimeUnit unit) { Sample oldest = activeTasks.peek(); return oldest == null ? 0.0 : oldest.duration(unit); } @Override public int activeTasks() { return activeTasks.size(); } protected void forEachActive(Consumer sample) { activeTasks.forEach(sample); } @Override public TimeUnit baseTimeUnit() { return baseTimeUnit; } @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") @Override public boolean equals(Object o) { return MeterEquivalence.equals(this, o); } @Override public int hashCode() { return MeterEquivalence.hashCode(this); } @Override public HistogramSnapshot takeSnapshot() { Queue percentilesRequested = new ArrayBlockingQueue<>(distributionStatisticConfig.getPercentiles() == null ? 1 : distributionStatisticConfig.getPercentiles().length); double[] percentilesRequestedArr = distributionStatisticConfig.getPercentiles(); if (percentilesRequestedArr != null && percentilesRequestedArr.length > 0) { Arrays.stream(percentilesRequestedArr).sorted().boxed().forEach(percentilesRequested::add); } NavigableSet buckets = distributionStatisticConfig.getHistogramBuckets(supportsAggregablePercentiles); CountAtBucket[] countAtBucketsArr = new CountAtBucket[0]; List percentilesAboveInterpolatableLine = percentilesRequested.stream() .filter(p -> p * (activeTasks.size() + 1) > activeTasks.size()) .collect(Collectors.toList()); percentilesRequested.removeAll(percentilesAboveInterpolatableLine); List valueAtPercentiles = new ArrayList<>(percentilesRequested.size()); if (!percentilesRequested.isEmpty() || !buckets.isEmpty()) { Double percentile = percentilesRequested.poll(); Double bucket = buckets.pollFirst(); List countAtBuckets = new ArrayList<>(buckets.size()); SampleImpl priorActiveTask = null; int i = 0; Iterator youngestToOldest = activeTasks.descendingIterator(); while (youngestToOldest.hasNext()) { SampleImpl activeTask = youngestToOldest.next(); i++; if (bucket != null) { if (activeTask.duration(TimeUnit.NANOSECONDS) > bucket) { countAtBuckets.add(new CountAtBucket(bucket, i - 1)); bucket = buckets.pollFirst(); } } if (percentile != null) { double rank = percentile * (activeTasks.size() + 1); if (i >= rank) { double percentileValue = activeTask.duration(TimeUnit.NANOSECONDS); if (i != rank && priorActiveTask != null) { // interpolate the percentile value when the active task rank is non-integral double priorPercentileValue = priorActiveTask.duration(TimeUnit.NANOSECONDS); percentileValue = priorPercentileValue + ((percentileValue - priorPercentileValue) * (rank - (int) rank)); } valueAtPercentiles.add(new ValueAtPercentile(percentile, percentileValue)); percentile = percentilesRequested.poll(); } } priorActiveTask = activeTask; } // fill out the rest of the cumulative histogram while (bucket != null) { countAtBuckets.add(new CountAtBucket(bucket, i)); bucket = buckets.pollFirst(); } countAtBucketsArr = countAtBuckets.toArray(countAtBucketsArr); } double duration = duration(TimeUnit.NANOSECONDS); double max = max(TimeUnit.NANOSECONDS); // we wouldn't need to iterate over all the active tasks just to calculate the 100th percentile, which is just the max. for (Double percentile : percentilesAboveInterpolatableLine) { valueAtPercentiles.add(new ValueAtPercentile(percentile, max)); } ValueAtPercentile[] valueAtPercentilesArr = valueAtPercentiles.toArray(new ValueAtPercentile[0]); return new HistogramSnapshot( activeTasks.size(), duration, max, valueAtPercentilesArr, countAtBucketsArr, (ps, scaling) -> ps.print("Summary output for LongTaskTimer histograms is not supported.") ); } class SampleImpl extends Sample { private final long startTime; private volatile boolean stopped; private SampleImpl() { this.startTime = clock.monotonicTime(); } @Override public long stop() { activeTasks.remove(this); long duration = (long) duration(TimeUnit.NANOSECONDS); stopped = true; return duration; } @Override public double duration(TimeUnit unit) { return stopped ? -1 : TimeUtils.nanosToUnit(clock.monotonicTime() - startTime, unit); } private long startTime() { return startTime; } @Override public String toString() { double durationInNanoseconds = duration(TimeUnit.NANOSECONDS); return "SampleImpl{" + "duration(seconds)=" + TimeUtils.nanosToUnit(durationInNanoseconds, TimeUnit.SECONDS) + ", " + "duration(nanos)=" + durationInNanoseconds + ", " + "startTimeNanos=" + startTime + '}'; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy