Last week, I wrote about how one could migrate metrics from Spring Boot 1.5 to Spring Boot 2.0.
This week, it’s time to check the different metrics available in Spring Boot 2.0 and how to create them.
Meter
There are 4 main types of metrics available:
- Counter
- Gauge
- Timer
- Distribution summary
To keep the post readable in one piece, it will be limited to Counter and Gauge
|
All metrics inherit from the base Meter
class.
A Meter
provides basic measurement storage capabilities.
As per the documentation:
A named and dimensioned producer of one or more measurements
Here’s a simplified class diagram:
Counter
A Counter
is a specialized Meter
that can be incremented by a positive number (0 included - it can stay the same).
For example, the number of pages views can be modeled as a counter, as it can only go up.
Creating a counter is quite straightforward, it all starts from the MeterRegistry
.
A MeterRegistry
bean is provided by Spring Boot, so it only needs to be injected.
@Bean
fun simpleCounter(registry: MeterRegistry) = registry.counter("simple.counter")
There’s another option available:
all of Meter
children interfaces embed a fluent builder.
This builder has a register(MeterRegistry)
method.
The following snippet is equivalent to the above one:
@Bean
fun simpleCounter2(registry: MeterRegistry) = Counter
.builder("simple.counter2")
.register(registry)
The builder function is in general more powerful as it provides more options |
An alternative to Counter
is FunctionCounter
, though they are un-related to one another interface-wise.
FunctionCounter
allows to track a monotonically increasing function - a function which value can only increase (or stay the same) over time.
class TrackedObject {
var value: Double = 0.0
get() {
field += 1
return field
}
}
@Bean
fun tracked() = TrackedObject()
@Bean
fun simpleFunctionCounter(registry: MeterRegistry,
tracked: TrackedObject) = registry
.more()
.counter("simple.functioncounter", arrayListOf(), tracked) { it.value }
@Bean
fun simpleFunctionCounter2(registry: MeterRegistry,
tracked: TrackedObject) = FunctionCounter
.builder("simple.functioncounter2", tracked) { it.value }
.register(registry)
}
Micrometer only keeps a weak reference to the In this snippet, it means that if |
Both above will create a meter that will increase by 1 after being queried. Obviously, this is not super useful. However, function counters can be bound to interesting metrics:
- Page views
- Cache eviction
- etc.
@Bean
fun guavaCache(registry: MeterRegistry, guava: GuavaCacheMetrics) = FunctionCounter
.builder("cache.evictions", guava) { it.evictionCount().doubleValue() }
.register(registry)
The Counter
class hierarchy looks like the following:
Gauge
While a counter must always go up, gauges have no such constraint: a gauge outputs a value, unrelated from one call to the next.
To create a gauge, use the builder from Gauge
.
@Bean
fun random() = SecureRandom()
@Bean
fun simpleGauge(registry: MeterRegistry, random: Random) = Gauge
.builder("simple.gauge", random)
{ it.nextInt(100).toDouble() }
.register(registry)
This creates a gauge that will randomly output a value.
Static methods on the MeterRegistry
do create a gauge object but do not return a reference to it, but to the parameter object instead.
It allows to "manually" change the value of a gauge:
val atomic = ref<MeterRegistry>().gauge("atomic.gauge", AtomicLong())
atomic?.getAndAdd(5)
atomic?.getAndDecrement()
atomic?.getAndSet(1)
On the flip side, it makes it impossible to register beans using those methods.
Micrometer offers a dedicate gauge to track time values, the TimeGauge
.
In addition to the value from Gauge
, it adds a TimeUnit
.
@Bean
fun simpleTimeGauge(registry: MeterRegistry, random: Random) = TimeGauge
.builder("simple.timegauge", random, TimeUnit.SECONDS)
{ it.nextInt(100).toDouble() }
.register(registry)
The Gauge
class hierarchy is as follows:
Conclusion
In this post, we covered counters and gauges. Also, Micrometer offers alternative flavors of them, such as function counters and time gauges. The main different between counters and gauges is that the former’s value can only increase, while the latter’s value is unrelated from one data point to another.