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

com.netflix.spectator.atlas.AtlasTimer Maven / Gradle / Ivy

There is a newer version: 1.7.21
Show newest version
/*
 * Copyright 2014-2023 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.spectator.atlas;

import com.netflix.spectator.api.Clock;
import com.netflix.spectator.api.Id;
import com.netflix.spectator.api.Statistic;
import com.netflix.spectator.api.Timer;
import com.netflix.spectator.impl.StepDouble;
import com.netflix.spectator.impl.StepLong;
import com.netflix.spectator.impl.StepValue;

import java.time.Duration;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Timer that reports four measurements to Atlas:
 *
 * 
    *
  • count: counter incremented each time record is called
  • *
  • totalTime: counter incremented by the recorded amount
  • *
  • totalOfSquares: counter incremented by the recorded amount2
  • *
  • max: maximum recorded amount
  • *
* *

Having an explicit {@code totalTime} and {@code count} on the backend * can be used to calculate an accurate average for an arbitrary grouping. The * {@code totalOfSquares} is used for computing a standard deviation.

* *

Note that the {@link #count()} and {@link #totalTime()} will report * the values since the last complete interval rather than the total for the * life of the process.

*/ class AtlasTimer extends AtlasMeter implements Timer { private final StepLong count; private final StepDouble total; private final StepDouble totalOfSquares; private final StepLong max; private final Id[] stats; /** Create a new instance. */ AtlasTimer(Id id, Clock clock, long ttl, long step) { super(id, clock, ttl); this.count = new StepLong(0L, clock, step); this.total = new StepDouble(0.0, clock, step); this.totalOfSquares = new StepDouble(0.0, clock, step); this.max = new StepLong(0L, clock, step); this.stats = new Id[] { id.withTags(DsType.rate, Statistic.count), id.withTags(DsType.rate, Statistic.totalTime), id.withTags(DsType.rate, Statistic.totalOfSquares), id.withTags(DsType.gauge, Statistic.max) }; } @Override void measure(long now, MeasurementConsumer consumer) { reportMeasurement(now, consumer, stats[0], count, 1.0); reportMeasurement(now, consumer, stats[1], total, 1e-9); reportMeasurement(now, consumer, stats[2], totalOfSquares, 1e-18); reportMaxMeasurement(now, consumer, stats[3], max); } private void reportMeasurement(long now, MeasurementConsumer consumer, Id mid, StepValue v, double f) { // poll needs to be called before accessing the timestamp to ensure // the counters have been rotated if there was no activity in the // current interval. double rate = v.pollAsRate(now) * f; long timestamp = v.timestamp(); consumer.accept(mid, timestamp, rate); } private void reportMaxMeasurement(long now, MeasurementConsumer consumer, Id mid, StepLong v) { // poll needs to be called before accessing the timestamp to ensure // the counters have been rotated if there was no activity in the // current interval. double maxValue = v.poll(now) / 1e9; long timestamp = v.timestamp(); consumer.accept(mid, timestamp, maxValue); } @Override public Clock clock() { return clock; } @Override public void record(long amount, TimeUnit unit) { long now = clock.wallTime(); count.getCurrent(now).incrementAndGet(); if (amount > 0) { final long nanos = unit.toNanos(amount); total.getCurrent(now).addAndGet(nanos); totalOfSquares.getCurrent(now).addAndGet((double) nanos * nanos); updateMax(max.getCurrent(now), nanos); } updateLastModTime(now); } @Override public void record(long[] amounts, int n, TimeUnit unit) { final int limit = Math.min(Math.max(0, n), amounts.length); double accumulatedTotal = 0.0; long accumulatedMax = Long.MIN_VALUE; double accumulatedTotalOfSquares = 0.0; // accumulate results for (int i = 0; i < limit; i++) { final long nanos = unit.toNanos(amounts[i]); if (nanos > 0) { accumulatedTotal += nanos; accumulatedTotalOfSquares += ((double) nanos * nanos); accumulatedMax = Math.max(nanos, accumulatedMax); } } // issue updates as a batch final long now = clock.wallTime(); count.getCurrent(now).addAndGet(limit); total.getCurrent(now).addAndGet(accumulatedTotal); totalOfSquares.getCurrent(now).addAndGet(accumulatedTotalOfSquares); updateMax(max.getCurrent(now), accumulatedMax); updateLastModTime(now); } @Override public void record(Duration[] amounts, int n) { final int limit = Math.min(Math.max(0, n), amounts.length); double accumulatedTotal = 0.0; long accumulatedMax = Long.MIN_VALUE; double accumulatedTotalOfSquares = 0.0; // accumulate results for (int i = 0; i < limit; i++) { final long nanos = amounts[i].toNanos(); if (nanos > 0) { accumulatedTotal += nanos; accumulatedTotalOfSquares += ((double) nanos * nanos); accumulatedMax = Math.max(nanos, accumulatedMax); } } // issue updates as a batch final long now = clock.wallTime(); count.getCurrent(now).addAndGet(limit); total.getCurrent(now).addAndGet(accumulatedTotal); totalOfSquares.getCurrent(now).addAndGet(accumulatedTotalOfSquares); updateMax(max.getCurrent(now), accumulatedMax); updateLastModTime(now); } private void updateMax(AtomicLong maxValue, long v) { long p = maxValue.get(); while (v > p && !maxValue.compareAndSet(p, v)) { p = maxValue.get(); } } @Override public long count() { return count.poll(); } @Override public long totalTime() { // Cannot change the return type since this is a public API so the result of this can // potentially overflow and result in a negative value. This is predominately used for // unit tests so it is rarely a problem in practice. API can be revisited in 2.0. return (long) total.poll(); } @Override public BatchUpdater batchUpdater(int batchSize) { AtlasTimerBatchUpdater updater = new AtlasTimerBatchUpdater(batchSize); updater.accept(() -> this); return updater; } /** * Helper to allow the batch updater to directly update the individual stats. */ void update(long count, double total, double totalOfSquares, long max) { long now = clock.wallTime(); this.count.getCurrent(now).addAndGet(count); this.total.getCurrent(now).addAndGet(total); this.totalOfSquares.getCurrent(now).addAndGet(totalOfSquares); updateMax(this.max.getCurrent(now), max); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy