-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
BatchMetrics.java
165 lines (148 loc) · 5.37 KB
/
BatchMetrics.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
/*
* Copyright 2019-2022 the original author or authors.
*
* 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 org.springframework.batch.core.observability;
import java.time.Duration;
import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import io.micrometer.core.instrument.LongTaskTimer;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.instrument.observation.TimerObservationHandler;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.lang.Nullable;
/**
* Central class for batch metrics. It provides:
*
* <ul>
* <li>the main entry point to interact with Micrometer's {@link Metrics#globalRegistry}
* with common metrics such as {@link Timer} and {@link LongTaskTimer}.</li>
* <li>Some utility methods like calculating durations and formatting them in
* a human readable format.</li>
* </ul>
*
* Only intended for internal use.
*
* @author Mahmoud Ben Hassine
* @since 4.2
*/
public final class BatchMetrics {
public static final String METRICS_PREFIX = "spring.batch.";
public static final String STATUS_SUCCESS = "SUCCESS";
public static final String STATUS_FAILURE = "FAILURE";
/**
* Global {@link ObservationRegistry}. A {@link TimerObservationHandler} is attached
* to create a {@link Timer} for every finished {@link Observation}.
*/
public static final ObservationRegistry observationRegistry;
static {
observationRegistry = ObservationRegistry.create();
observationRegistry.observationConfig().observationHandler(new TimerObservationHandler(Metrics.globalRegistry));
}
private BatchMetrics() {
}
/**
* Create a {@link Timer}.
* @param name of the timer. Will be prefixed with {@link BatchMetrics#METRICS_PREFIX}.
* @param description of the timer
* @param tags of the timer
* @return a new timer instance
*/
public static Timer createTimer(String name, String description, Tag... tags) {
return Timer.builder(METRICS_PREFIX + name)
.description(description)
.tags(Arrays.asList(tags))
.register(Metrics.globalRegistry);
}
/**
* Create a new {@link Observation}. It's not started, you must
* explicitly call {@link Observation#start()} to start it.
*
* Remember to register the {@link TimerObservationHandler}
* via the {@code Metrics.globalRegistry.withTimerObservationHandler()}
* in the user code. Otherwise you won't observe any metrics.
* @param name of the observation
* @param context of the observation
* @return a new observation instance
* @since 5.0
*/
public static Observation createObservation(String name, Observation.Context context) {
return Observation.createNotStarted(name, context, observationRegistry);
}
/**
* Create a new {@link Timer.Sample}.
* @return a new timer sample instance
*/
public static Timer.Sample createTimerSample() {
return Timer.start(Metrics.globalRegistry);
}
/**
* Create a new {@link LongTaskTimer}.
* @param name of the long task timer. Will be prefixed with {@link BatchMetrics#METRICS_PREFIX}.
* @param description of the long task timer.
* @param tags of the timer
* @return a new long task timer instance
*/
public static LongTaskTimer createLongTaskTimer(String name, String description, Tag... tags) {
return LongTaskTimer.builder(METRICS_PREFIX + name)
.description(description)
.tags(Arrays.asList(tags))
.register(Metrics.globalRegistry);
}
/**
* Calculate the duration between two dates.
* @param startTime the start time
* @param endTime the end time
* @return the duration between start time and end time
*/
@Nullable
public static Duration calculateDuration(@Nullable Date startTime, @Nullable Date endTime) {
if (startTime == null || endTime == null) {
return null;
}
return Duration.between(startTime.toInstant(), endTime.toInstant());
}
/**
* Format a duration in a human readable format like: 2h32m15s10ms.
* @param duration to format
* @return A human readable duration
*/
public static String formatDuration(@Nullable Duration duration) {
if (duration == null || duration.isZero() || duration.isNegative()) {
return "";
}
StringBuilder formattedDuration = new StringBuilder();
long hours = duration.toHours();
long minutes = duration.toMinutes();
long seconds = duration.getSeconds();
long millis = duration.toMillis();
if (hours != 0) {
formattedDuration.append(hours).append("h");
}
if (minutes != 0) {
formattedDuration.append(minutes - TimeUnit.HOURS.toMinutes(hours)).append("m");
}
if (seconds != 0) {
formattedDuration.append(seconds - TimeUnit.MINUTES.toSeconds(minutes)).append("s");
}
if (millis != 0) {
formattedDuration.append(millis - TimeUnit.SECONDS.toMillis(seconds)).append("ms");
}
return formattedDuration.toString();
}
}