Micrometer: updating the gauge value without a function

Created on 7 Mar 2018  路  13Comments  路  Source: micrometer-metrics/micrometer

The following test fails.


@Test
void updatingGauge() {
  Metrics.addRegistry(new SimpleMeterRegistry());
  Metrics.gauge("my gauge", 3);
  Metrics.gauge("my gauge", 2);
  Gauge gauge = Metric.globalRegistry.find("my gauge").gauge();

  assert(2.0, gauge.value(), 0.4);  //****FAILS, VALUE IS 3, NOT 2
}

(I hope that there are no typing errors.)

I am finding the project very useful - thanks for your good work.
Shalom Deitch

question

Most helpful comment

If you need to manually increment/decrement a gauge, the suggested pattern is:

AtomicInteger n = new AtomicInteger(0);
Metrics.gauge("my.gauge", n);
n.set(3);
n.set(2);

In general, the registry creates a meter only once for each unique combination of name and tags. So your second registration just does nothing. This is equally true of counters and gauges.

Also, part of the contract of gauges is that you need to hold a strong reference to the object being gauged. Ultimately, the current construct would report NaN.

All this said, generally there is a better way than manually inc/dec. Remember, the intermediate values between publishes are effectively discarded.

All 13 comments

Metrics.globalRegistry.find("my gauge") returns a io.micrometer.core.instrument.search.Search. According to the documentation of Search#gauge(), it returns the first matching gauge or null if none match.

On a very technical level you may be right
metrics.gauge(..) registers a gauge
However, if I write code that looks the same with a counter, I will get the same counter back. I feel that this is a "little" inconsistent.

If you need to manually increment/decrement a gauge, the suggested pattern is:

AtomicInteger n = new AtomicInteger(0);
Metrics.gauge("my.gauge", n);
n.set(3);
n.set(2);

In general, the registry creates a meter only once for each unique combination of name and tags. So your second registration just does nothing. This is equally true of counters and gauges.

Also, part of the contract of gauges is that you need to hold a strong reference to the object being gauged. Ultimately, the current construct would report NaN.

All this said, generally there is a better way than manually inc/dec. Remember, the intermediate values between publishes are effectively discarded.

Thanks for all the replies.

What would you suggest as the "better way" ?

The second call to Metrics.gauge returns an Integer with the new value.
With Metrics.counter a new OR existing counter is returned.

The API (not the documentation) indicates to me that Metrics.gauge should update the gauge and return the new value, just like calling Metrics.counter returns the ORIGINAL counter, which I can then increment, without the need to save state (even if there is a performance cost).

Since Metrics.gauge is documented as "registers" and, as markbigler said, find().gauge returns the first instance, the implementation does what the documentation states.

However, the API I would like, is to allow me to access the gauge and update it. Since an int is a natural value, having the short cut you gave for create is a nice feature BUT NOT if I can not update it.
If Metrics.gauge("", int-value) is not update-able then an Exception would be better then effectively ignoring the subsequent calls and I am not sure of how much real value this signature has.

I understand that self management using a ToDoubleFunction is a far nicer way to go, but it does not fit the particular use case that I have (old code).

Thanks for your attention, it is not something I feel should be taken for granted.
Shalom Deitch

the short cut you gave for create is a nice feature BUT NOT if I can not update it.

I'm not clear why you can't use the AtomicInteger sample, which allows for arbitrary updates.

Of course we can go that way, and access the gauge using find.
For my project, that is a solution.

However, you are making a tool that is a great concept.
I still think that it would be nicer if you were to look for a "Number" instances and replace them with Atomic<type> (does not solve the weak reference issue) + let Metrics.gauge act like with counter and return any existing entry.
If not, then removing the option of making errors like I made would help avoid clients debugging or losing data.

Either way thanks for the product + your notes. It is helping me a lot.
I think that we have finished this discussion.

@ShalomDeitch1 Thanks for your feedback. We're aware of how common your attempted use is, and thinking of the best solution to it. I wish there was a common base class above Number that could cover atomic number types across the JDK and third-party libraries, but that simply isn't the case.

A runtime exception when you provide a java.lang.* based Number is possible. An annotation processor that fails compilation is also possible. Perhaps we do both.

Since it's a define-once-and-forget construct, we've optimized the gauge interface to return the object being observed so you can create an object and gauge it at the same time, e.g. in a field:

class MyComponent {
   AtomicInteger n = Metrics.gauge("my.gauge", Tags.empty(), new AtomicInteger(0));
}

If we make the signature look like a Counter, it pushes the monitoring to a constructor or other init code (so I don't think we want this):

class MyComponent {
   AtomicInteger n = new AtomicInteger(0);

   public MyComponent {
      // most of the time, I don't care about g, since it takes care of itself.
      Gauge g = Metrics.gauge("my.gauge", Tags.empty(), n);
   }
}

It's also possible we add a strong-reference backed MutableGauge at some point:

MutableGauge g = MutableGauge.builder("my.gauge")
    .initialValue(0)
    .register(registry);

g.set(1);
g.set(2);

All of these things are possible for 1.1 (and indeed the implementation for MutableGauge is trivial), just trying to find the right balance.

The use of atomic is really just a container for a value. In a sense it is a misuse. A possible solution would be to make your own container, where an immutable value could be replaced.
I like your annotation idea. :-)

The use of atomic is really just a container for a value. In a sense it is a misuse.

Yes, really this signature was meant as a convenience for atomics. I think we're observing that people key in on it as the primary signature, which is a problem.

You cannot access a gauge using the same shortcut like during creating a gauge, and imagine a case where you cannot hold a reference for an object that is being gauged, e.g. in reactive streams, but you still want to have a meter that carries some info about current state of something. Gauges in Dropwizard works differently.

Actually I cannot find a way to return this AtomicInteger I set in the gauge.

Ok, I found the way, but anyway it's a little bit confusing how it works, and to find a proper doc you need to go very deep. I mean it's not documented right here:
```java
/**
* Register a gauge that reports the value of the {@link java.lang.Number}.
*
* @param name Name of the gauge being registered.
* @param tags Sequence of dimensions for breaking down the name.
* @param number Thread-safe implementation of {@link Number} used to access the value.
* @param The type of the state object from which the gauge value is extracted.
* @return The number that was passed in so the registration can be done as part of an assignment
* statement.
*/
@Nullable
public static T gauge(String name, Iterable tags, T number) {
return globalRegistry.gauge(name, tags, number);
}

@maxio89 thank you for the feedback on this. Have you checked the documentation on the official website: https://micrometer.io/docs/concepts#_gauges? Is that clear? If you have suggestions for improvement, would you open a pull request or issue at https://github.com/micrometer-metrics/micrometer-docs/?
In addition, you can chat with us about things on the Micrometer Slack: http://slack.micrometer.io/

Was this page helpful?
0 / 5 - 0 ratings