/
ExpiringTimerBuilder.java
147 lines (129 loc) · 5.17 KB
/
ExpiringTimerBuilder.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
/*
* Copyright (c) 2017 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.services.utils.metrics.instruments.timer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Builds a {@link org.eclipse.ditto.services.utils.metrics.instruments.timer.PreparedTimer} and guarantees that no
* timer will run without a timeout, because the timer is always returned started.
*/
public final class ExpiringTimerBuilder implements TimerBuilder<ExpiringTimerBuilder, StartedTimer> {
private static final Logger LOGGER = LoggerFactory.getLogger(ExpiringTimerBuilder.class);
private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private final String name;
private final Map<String, String> additionalTags;
private long maximumDuration = 5;
private TimeUnit maximumDurationTimeUnit = TimeUnit.MINUTES;
private Consumer<StartedTimer> additionalExpirationHandling;
private ScheduledFuture<?> expirationHandlingFuture;
public ExpiringTimerBuilder(final String name) {
this.name = name;
this.additionalTags = new HashMap<>();
}
/**
* Adds tags to the timer.
* Already existing tags with the same key will be overridden.
*
* @param additionalTags Additional tags for this tracing
* @return The TracingTimerBuilder
*/
@Override
public ExpiringTimerBuilder tags(final Map<String, String> additionalTags) {
this.additionalTags.putAll(additionalTags);
return this;
}
/**
* Adds the given tag to the timer.
* Already existing tags with the same key will be overridden.
*
* @param key They key of the tag
* @param value The value of the tag
* @return The TracingTimerBuilder
*/
public ExpiringTimerBuilder tag(final String key, final String value) {
this.additionalTags.put(key, value);
return this;
}
/**
* Sets the handling of a timer after expiration.
*
* @param additionalExpirationHandling custom handling of timer expiration.
* @return The TracingTimerBuilder
*/
public ExpiringTimerBuilder expirationHandling(final Consumer<StartedTimer> additionalExpirationHandling) {
this.additionalExpirationHandling = additionalExpirationHandling;
return this;
}
/**
* Specifies the maximum duration this timer should be running. It will expire after this time.
*
* @param maximumDuration The maximum duration.
* @param maximumDurationTimeUnit The unit of the maximum duration.
* @return The TracingTimerBuilder
*/
public ExpiringTimerBuilder maximumDuration(final long maximumDuration,
final TimeUnit maximumDurationTimeUnit) {
this.maximumDuration = maximumDuration;
this.maximumDurationTimeUnit = maximumDurationTimeUnit;
return this;
}
/**
* Starts the timer.
*
* @return The timer that will be stopped after running more than the defined
* {@link ExpiringTimerBuilder#maximumDuration maximum duration}
*/
@Override
public StartedTimer build() {
final StartedTimer timer = PreparedKamonTimer.newTimer(name).tags(additionalTags).start();
expirationHandlingFuture =
scheduler.schedule(() -> defaultExpirationHandling(name, timer, additionalExpirationHandling),
maximumDuration, maximumDurationTimeUnit);
timer.onStop(new OnStopHandler(this::cancelScheduledExpirationFuture));
return timer;
}
private void cancelScheduledExpirationFuture(final StoppedTimer timer) {
if (!expirationHandlingFuture.isDone()) {
final boolean canceled = expirationHandlingFuture.cancel(false);
if (canceled) {
LOGGER.trace("Canceled expiration handling of MutableKamonTimer <{}> because it has been stopped " +
"before timeout", timer.getName());
}
}
}
private static void defaultExpirationHandling(final String tracingFilter, final StartedTimer timer,
@Nullable Consumer<StartedTimer> additionalExpirationHandling) {
LOGGER.trace("Trace for {} stopped. Cause: Timer expired", tracingFilter);
if (additionalExpirationHandling != null) {
try {
additionalExpirationHandling.accept(timer);
} finally {
if (timer.isRunning()) {
timer.stop();
}
}
} else {
if (timer.isRunning()) {
timer.stop();
}
}
}
}