-
Notifications
You must be signed in to change notification settings - Fork 963
/
Timer.java
460 lines (396 loc) · 15.5 KB
/
Timer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
/*
* Copyright 2019 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.common.lang.Nullable;
import io.micrometer.core.annotation.Incubating;
import io.micrometer.core.annotation.Timed;
import io.micrometer.core.instrument.distribution.CountAtBucket;
import io.micrometer.core.instrument.distribution.HistogramSupport;
import io.micrometer.core.instrument.distribution.ValueAtPercentile;
import io.micrometer.core.instrument.distribution.pause.PauseDetector;
import java.time.Duration;
import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;
import java.util.function.DoubleSupplier;
import java.util.function.Function;
import java.util.function.IntSupplier;
import java.util.function.LongSupplier;
import java.util.function.Supplier;
/**
* Timer intended to track of a large number of short running events. Example would be
* something like an HTTP request. Though "short running" is a bit subjective the
* assumption is that it should be under a minute.
*
* @author Jon Schneider
* @author Oleksii Bondar
*/
public interface Timer extends Meter, HistogramSupport {
/**
* Start a timing sample using the {@link Clock#SYSTEM System clock}.
* @return A timing sample with start time recorded.
* @since 1.1.0
*/
static Sample start() {
return start(Clock.SYSTEM);
}
/**
* Start a timing sample.
* @param registry A meter registry whose clock is to be used
* @return A timing sample with start time recorded.
*/
static Sample start(MeterRegistry registry) {
return start(registry.config().clock());
}
/**
* Start a timing sample.
* @param clock a clock to be used
* @return A timing sample with start time recorded.
*/
static Sample start(Clock clock) {
return new Sample(clock);
}
static Builder builder(String name) {
return new Builder(name);
}
/**
* @param registry A meter registry against which the timer will be registered.
* @param name The name of the timer.
* @return A timing builder that automatically records a timing on close.
* @since 1.6.0
*/
@Incubating(since = "1.6.0")
static ResourceSample resource(MeterRegistry registry, String name) {
return new ResourceSample(registry, name);
}
/**
* Create a timer builder from a {@link Timed} annotation.
* @param timed The annotation instance to base a new timer on.
* @param defaultName A default name to use in the event that the value attribute is
* empty.
* @return This builder.
*/
static Builder builder(Timed timed, String defaultName) {
if (timed.longTask() && timed.value().isEmpty()) {
// the user MUST name long task timers, we don't lump them in with regular
// timers with the same name
throw new IllegalArgumentException(
"Long tasks instrumented with @Timed require the value attribute to be non-empty");
}
return new Builder(timed.value().isEmpty() ? defaultName : timed.value()).tags(timed.extraTags())
.description(timed.description().isEmpty() ? null : timed.description())
.publishPercentileHistogram(timed.histogram())
.publishPercentiles(timed.percentiles().length > 0 ? timed.percentiles() : null);
}
/**
* Updates the statistics kept by the timer with the specified amount.
* @param amount Duration of a single event being measured by this timer. If the
* amount is less than 0 the value will be dropped.
* @param unit Time unit for the amount being recorded.
*/
void record(long amount, TimeUnit unit);
/**
* Updates the statistics kept by the timer with the specified amount.
* @param duration Duration of a single event being measured by this timer.
*/
default void record(Duration duration) {
record(duration.toNanos(), TimeUnit.NANOSECONDS);
}
/**
* Executes the Supplier {@code f} and records the time taken.
* @param f Function to execute and measure the execution time.
* @param <T> The return type of the {@link Supplier}.
* @return The return value of {@code f}.
*/
@Nullable
<T> T record(Supplier<T> f);
/**
* Executes the Supplier {@code f} and records the time taken.
* @param f Function to execute and measure the execution time.
* @return The return value of {@code f}.
* @since 1.10.0
*/
default boolean record(BooleanSupplier f) {
return record((Supplier<Boolean>) f::getAsBoolean);
}
/**
* Executes the Supplier {@code f} and records the time taken.
* @param f Function to execute and measure the execution time.
* @return The return value of {@code f}.
* @since 1.10.0
*/
default int record(IntSupplier f) {
return record((Supplier<Integer>) f::getAsInt);
}
/**
* Executes the Supplier {@code f} and records the time taken.
* @param f Function to execute and measure the execution time.
* @return The return value of {@code f}.
* @since 1.10.0
*/
default long record(LongSupplier f) {
return record((Supplier<Long>) f::getAsLong);
}
/**
* Executes the Supplier {@code f} and records the time taken.
* @param f Function to execute and measure the execution time.
* @return The return value of {@code f}.
* @since 1.10.0
*/
default double record(DoubleSupplier f) {
return record((Supplier<Double>) f::getAsDouble);
}
/**
* Executes the callable {@code f} and records the time taken.
* @param f Function to execute and measure the execution time.
* @param <T> The return type of the {@link Callable}.
* @return The return value of {@code f}.
* @throws Exception Any exception bubbling up from the callable.
*/
@Nullable
<T> T recordCallable(Callable<T> f) throws Exception;
/**
* Executes the runnable {@code f} and records the time taken.
* @param f Function to execute and measure the execution time.
*/
void record(Runnable f);
/**
* Wrap a {@link Runnable} so that it is timed when invoked.
* @param f The Runnable to time when it is invoked.
* @return The wrapped Runnable.
*/
default Runnable wrap(Runnable f) {
return () -> record(f);
}
/**
* Wrap a {@link Callable} so that it is timed when invoked.
* @param f The Callable to time when it is invoked.
* @param <T> The return type of the callable.
* @return The wrapped callable.
*/
default <T> Callable<T> wrap(Callable<T> f) {
return () -> recordCallable(f);
}
/**
* Wrap a {@link Supplier} so that it is timed when invoked.
* @param f The {@code Supplier} to time when it is invoked.
* @param <T> The return type of the {@code Supplier} result.
* @return The wrapped supplier.
* @since 1.2.0
*/
default <T> Supplier<T> wrap(Supplier<T> f) {
return () -> record(f);
}
/**
* @return The number of times that stop has been called on this timer.
*/
long count();
/**
* @param unit The base unit of time to scale the total to.
* @return The total time of recorded events.
*/
double totalTime(TimeUnit unit);
/**
* @param unit The base unit of time to scale the mean to.
* @return The distribution average for all recorded events.
*/
default double mean(TimeUnit unit) {
long count = count();
return count == 0 ? 0 : totalTime(unit) / count;
}
/**
* @param unit The base unit of time to scale the max to.
* @return The maximum time of a single event.
*/
double max(TimeUnit unit);
@Override
default Iterable<Measurement> measure() {
return Arrays.asList(new Measurement(() -> (double) count(), Statistic.COUNT),
new Measurement(() -> totalTime(baseTimeUnit()), Statistic.TOTAL_TIME),
new Measurement(() -> max(baseTimeUnit()), Statistic.MAX));
}
/**
* Provides cumulative histogram counts.
* @param valueNanos The histogram bucket to retrieve a count for.
* @return The count of all events less than or equal to the bucket. If valueNanos
* does not match a preconfigured bucket boundary, returns NaN.
* @deprecated Use {@link #takeSnapshot()} to retrieve bucket counts.
*/
@Deprecated
default double histogramCountAtValue(long valueNanos) {
for (CountAtBucket countAtBucket : takeSnapshot().histogramCounts()) {
if ((long) countAtBucket.bucket(TimeUnit.NANOSECONDS) == valueNanos) {
return countAtBucket.count();
}
}
return Double.NaN;
}
/**
* @param percentile A percentile in the domain [0, 1]. For example, 0.5 represents
* the 50th percentile of the distribution.
* @param unit The base unit of time to scale the percentile value to.
* @return The latency at a specific percentile. This value is non-aggregable across
* dimensions. Returns NaN if percentile is not a preconfigured percentile that
* Micrometer is tracking.
* @deprecated Use {@link #takeSnapshot()} to retrieve bucket counts.
*/
@Deprecated
default double percentile(double percentile, TimeUnit unit) {
for (ValueAtPercentile valueAtPercentile : takeSnapshot().percentileValues()) {
if (valueAtPercentile.percentile() == percentile) {
return valueAtPercentile.value(unit);
}
}
return Double.NaN;
}
/**
* @return The base time unit of the timer to which all published metrics will be
* scaled
*/
TimeUnit baseTimeUnit();
/**
* Maintains state on the clock's start position for a latency sample. Complete the
* timing by calling {@link Sample#stop(Timer)}. Note how the {@link Timer} isn't
* provided until the sample is stopped, allowing you to determine the timer's tags at
* the last minute.
*/
class Sample {
private final long startTime;
private final Clock clock;
Sample(Clock clock) {
this.clock = clock;
this.startTime = clock.monotonicTime();
}
/**
* Records the duration of the operation.
* @param timer The timer to record the sample to.
* @return The total duration of the sample in nanoseconds
*/
public long stop(Timer timer) {
long durationNs = clock.monotonicTime() - startTime;
timer.record(durationNs, TimeUnit.NANOSECONDS);
return durationNs;
}
}
class ResourceSample extends AbstractTimerBuilder<ResourceSample> implements AutoCloseable {
private final MeterRegistry registry;
private final long startTime;
ResourceSample(MeterRegistry registry, String name) {
super(name);
this.registry = registry;
this.startTime = registry.config().clock().monotonicTime();
}
@Override
public void close() {
long durationNs = registry.config().clock().monotonicTime() - startTime;
registry
.timer(new Meter.Id(name, tags, null, description, Type.TIMER), distributionConfigBuilder.build(),
pauseDetector == null ? registry.config().pauseDetector() : pauseDetector)
.record(durationNs, TimeUnit.NANOSECONDS);
}
}
/**
* Fluent builder for timers.
*/
class Builder extends AbstractTimerBuilder<Builder> {
Builder(String name) {
super(name);
}
@Override
public Builder tags(String... tags) {
return super.tags(tags);
}
@Override
public Builder tags(Iterable<Tag> tags) {
return super.tags(tags);
}
@Override
public Builder tag(String key, String value) {
return super.tag(key, value);
}
@Override
public Builder publishPercentiles(double... percentiles) {
return super.publishPercentiles(percentiles);
}
@Override
public Builder percentilePrecision(Integer digitsOfPrecision) {
return super.percentilePrecision(digitsOfPrecision);
}
@Override
public Builder publishPercentileHistogram() {
return super.publishPercentileHistogram();
}
@Override
public Builder publishPercentileHistogram(Boolean enabled) {
return super.publishPercentileHistogram(enabled);
}
@SuppressWarnings("deprecation")
@Override
public Builder sla(Duration... sla) {
return super.sla(sla);
}
@Override
public Builder serviceLevelObjectives(Duration... slos) {
return super.serviceLevelObjectives(slos);
}
@Override
public Builder minimumExpectedValue(Duration min) {
return super.minimumExpectedValue(min);
}
@Override
public Builder maximumExpectedValue(Duration max) {
return super.maximumExpectedValue(max);
}
@Override
public Builder distributionStatisticExpiry(Duration expiry) {
return super.distributionStatisticExpiry(expiry);
}
@Override
public Builder distributionStatisticBufferLength(Integer bufferLength) {
return super.distributionStatisticBufferLength(bufferLength);
}
@Override
public Builder pauseDetector(PauseDetector pauseDetector) {
return super.pauseDetector(pauseDetector);
}
@Override
public Builder description(String description) {
return super.description(description);
}
/**
* Add the timer to a single registry, or return an existing timer in that
* registry. The returned timer will be unique for each registry, but each
* registry is guaranteed to only create one timer for the same combination of
* name and tags.
* @param registry A registry to add the timer to, if it doesn't already exist.
* @return A new or existing timer.
*/
public Timer register(MeterRegistry registry) {
return register(registry, tags);
}
public <K> Meter.Provider<K, Timer> register(MeterRegistry registry, Function<K, Tags> provider) {
return new Meter.Cache<>(key -> register(registry, tags.and(provider.apply(key))));
}
private Timer register(MeterRegistry registry, Tags tags) {
// the base unit for a timer will be determined by the monitoring system
// implementation
return registry.timer(new Meter.Id(name, tags, null, description, Type.TIMER),
distributionConfigBuilder.build(),
pauseDetector == null ? registry.config().pauseDetector() : pauseDetector);
}
}
}