|
45 | 45 | import java.util.Timer; |
46 | 46 | import java.util.TimerTask; |
47 | 47 | import java.util.concurrent.ConcurrentHashMap; |
| 48 | +import java.util.function.ToDoubleFunction; |
48 | 49 |
|
49 | 50 | public class Micrometer implements IStatCollector { |
50 | 51 |
|
@@ -82,15 +83,15 @@ public void init() { |
82 | 83 | userLogouts = registry.counter("userLogouts"); |
83 | 84 | appStartFailedCounter = registry.counter("startFailed"); |
84 | 85 | authFailedCounter = registry.counter("authFailed"); |
85 | | - registry.gauge("absolute_users_logged_in", Tags.empty(), sessionService, ISessionService::getLoggedInUsersCount); |
86 | | - registry.gauge("absolute_users_active", Tags.empty(), sessionService, ISessionService::getActiveUsersCount); |
| 86 | + registry.gauge("absolute_users_logged_in", Tags.empty(), sessionService, wrapHandleNull(ISessionService::getLoggedInUsersCount)); |
| 87 | + registry.gauge("absolute_users_active", Tags.empty(), sessionService, wrapHandleNull(ISessionService::getActiveUsersCount)); |
87 | 88 |
|
88 | 89 | for (ProxySpec spec : proxyService.getProxySpecs(null, true)) { |
89 | 90 | registry.counter("appStarts", "spec.id", spec.getId()); |
90 | 91 | registry.counter("appStops", "spec.id", spec.getId()); |
91 | 92 | ProxyCounter proxyCounter = new ProxyCounter(spec.getId()); |
92 | 93 | proxyCounters.add(proxyCounter); |
93 | | - registry.gauge("absolute_apps_running", Tags.of("spec.id", spec.getId()), proxyCounter, ProxyCounter::getProxyCount); |
| 94 | + registry.gauge("absolute_apps_running", Tags.of("spec.id", spec.getId()), proxyCounter, wrapHandleNull(ProxyCounter::getProxyCount)); |
94 | 95 | registry.timer("startupTime", "spec.id", spec.getId()); |
95 | 96 | registry.timer("usageTime", "spec.id", spec.getId()); |
96 | 97 | } |
@@ -165,10 +166,31 @@ public ProxyCounter(String specId) { |
165 | 166 | this.specId = specId; |
166 | 167 | } |
167 | 168 |
|
168 | | - public int getProxyCount() { |
| 169 | + public Integer getProxyCount() { |
169 | 170 | return proxyCountCache.getOrDefault(specId, null); |
170 | 171 | } |
171 | 172 |
|
172 | 173 | } |
173 | 174 |
|
| 175 | + /** |
| 176 | + * Wraps a function that returns an Integer into a function that returns a double. |
| 177 | + * When the provided Integer is null, the resulting function returns Double.NaN. |
| 178 | + * |
| 179 | + * We need this function because Micrometer cannot handle null values for Gauges. |
| 180 | + */ |
| 181 | + private static <T> ToDoubleFunction<T> wrapHandleNull(ToIntegerFunction<T> producer) { |
| 182 | + return (state) -> { |
| 183 | + Integer res = producer.applyAsDouble(state); |
| 184 | + if (res == null) { |
| 185 | + return Double.NaN; |
| 186 | + } |
| 187 | + return res; |
| 188 | + }; |
| 189 | + } |
| 190 | + |
| 191 | + @FunctionalInterface |
| 192 | + private interface ToIntegerFunction<T> { |
| 193 | + Integer applyAsDouble(T var1); |
| 194 | + } |
| 195 | + |
174 | 196 | } |
0 commit comments