I'm using Java Micrometer gauge to measure a metric that has a range of labels. The label values are dynamically retrieved but bounded.
For example: I have withdraw.cycle
metric with label currency
.
Because Micrometer doesn't hold the reference to the gauge itself, it makes the code look quite cumbersome.
I've got two ways to do it:
private void updateGauge(String currency, int minutes) {
Tags tags = Tags.of("currency", currency);
Gauge.builder("withdraw.cycle",() -> minutes)
.tags(tags)
.strongReference(true)
.register(meterRegistry);
}
OR
private final Map<Tags, AtomicLong> cycleGaugeMap = new ConcurrentHashMap<>();
// Use cycleGaugeMap to hold the reference to the gauge
private void updateGauge(String currency, int minutes) {
Tags tags = Tags.of("currency", currency);
if (cycleGaugeMap.containsKey(tags)) {
cycleGaugeMap.get(tags).set(minutes);
} else {
AtomicLong old = cycleGaugeMap.putIfAbsent(tags, meterRegistry.gauge("withdraw.cycle", tags,
new AtomicLong(minutes)));
if (old != null) {
old.set(minutes);
}
}
}
Question: Are they the right ways to do it or does Micrometer have an API that I'm not aware of for such cases? I'd really want to know a "standard" and elegant way to do it.
Kindly follow this github issue here.
To Summarize:
By definition, Micrometer supports one special type of Gauge, called a
MultiGauge
, to help manage gauging a growing or shrinking list of criteria. This feature lets you select a set of well-bounded but slightly changing set of criteria from something like an SQL query and report some metric for each row as a Gauge.
Working example:
public class Example {
private static final SimpleMeterRegistry registry = new SimpleMeterRegistry();
private static final MultiGauge gauge = MultiGauge.builder("withdraw.cycle").register(registry);
private static final Map<String, Number> values = new ConcurrentHashMap<>();
private static void updateGauge(String currency, int minutes) {
values.put(currency, minutes);
List<MultiGauge.Row<?>> rows = values.entrySet()
.stream()
.map(row -> MultiGauge.Row.of(Tags.of("currency", row.getKey()), row.getValue()))
.collect(Collectors.toList());
gauge.register(rows, true);
}
public static void main(String[] args) throws InterruptedException {
updateGauge("Rupee", 1);
System.out.println(registry.getMetersAsString());
System.out.println("--------");
updateGauge("Dollar", 2);
System.out.println(registry.getMetersAsString());
System.out.println("--------");
updateGauge("Rupee", 3);
System.out.println(registry.getMetersAsString());
}
Output:
withdraw.cycle(GAUGE)[currency='Rupee']; value=1.0
--------
withdraw.cycle(GAUGE)[currency='Dollar']; value=2.0
withdraw.cycle(GAUGE)[currency='Rupee']; value=1.0
--------
withdraw.cycle(GAUGE)[currency='Dollar']; value=2.0
withdraw.cycle(GAUGE)[currency='Rupee']; value=3.0
Gauge
itself.Working example:
public class Example {
private static final SimpleMeterRegistry registry = new SimpleMeterRegistry();
private static final Map<String, Number> values = new ConcurrentHashMap<>();
private static void updateGauge(String currency, int minutes) {
Tags tags = Tags.of("currency", currency);
values.put(currency, minutes);
if (registry.find("withdraw.cycle").tags(tags).gauge() == null) {
Gauge.builder("withdraw.cycle", () -> values.get(currency))
.tags(tags)
.register(registry);
}
}
public static void main(String[] args) throws InterruptedException {
updateGauge("Rupee", 1);
System.out.println(registry.getMetersAsString());
System.out.println("--------");
updateGauge("Dollar", 2);
System.out.println(registry.getMetersAsString());
System.out.println("--------");
updateGauge("Rupee", 3);
System.out.println(registry.getMetersAsString());
}
}
Output:
withdraw.cycle(GAUGE)[currency='Rupee']; value=1.0
--------
withdraw.cycle(GAUGE)[currency='Dollar']; value=2.0
withdraw.cycle(GAUGE)[currency='Rupee']; value=1.0
--------
withdraw.cycle(GAUGE)[currency='Dollar']; value=2.0
withdraw.cycle(GAUGE)[currency='Rupee']; value=3.0
I guess this is the optimal approach that you were looking for.