Skip to content

Commit c9792c5

Browse files
Document MeterProvider (#4874)
Closes gh-4872 Co-authored-by: Tommy Ludwig <[email protected]>
1 parent 68e4cc4 commit c9792c5

File tree

3 files changed

+120
-0
lines changed

3 files changed

+120
-0
lines changed

docs/modules/ROOT/nav.adoc

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
** xref:concepts/distribution-summaries.adoc[Distribution Summaries]
1414
** xref:concepts/long-task-timers.adoc[Long Task Timers]
1515
** xref:concepts/histogram-quantiles.adoc[Histograms and Percentiles]
16+
** xref:concepts/meter-provider.adoc[Meter Provider]
1617
* xref:implementations.adoc[Implementations]
1718
** xref:implementations/appOptics.adoc[AppOptics]
1819
** xref:implementations/atlas.adoc[Atlas]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
[[meter-provider]]
2+
= Meter Provider
3+
4+
It's a common use-case to attach tags dynamically to a `Meter`. Let's say we execute a job and we want to use a `Timer` to instrument it:
5+
6+
[source, java]
7+
----
8+
Timer.Sample sample = Timer.start(registry);
9+
10+
Result result = job.execute();
11+
12+
Timer timer = Timer.builder("job.execution")
13+
.tag("job.name", "job")
14+
.tag("status", result.status())
15+
.register(registry);
16+
sample.stop(timer);
17+
----
18+
19+
This lets us dynamically determine the `status` tag from the end state of the operation we are timing. There are two drawbacks of doing this:
20+
21+
1. Every time the above is executed, a new `Timer.Builder` instance is created. This increases the amount of data that the GC needs to collect.
22+
2. The code above is somewhat boilerplate, it does not let you define the common properties of a Timer and attach what is dynamically changing but everything is always present.
23+
24+
NOTE: In some cases you can use `registry.timer("job.execution", "job.name", "my-job", "status", result.status())` instead of using `Timer.Builder` which can save you some extra objects but this is not always possible.
25+
26+
You can resolve both of these issues by using a `MeterProvider`. It's a convenience interface to create new meters from tags using a common "template".
27+
28+
NOTE: Not every `Meter` can do this, `MeterProvider` can be used with `Counter`, `Timer`, `LongTaskTimer`, and `DistributionSummary`.
29+
30+
Here's what you can do instead of the above:
31+
32+
[source, java]
33+
----
34+
private MeterProvider<Timer> timerProvider = Timer.builder("job.execution")
35+
.tag("job.name", "my-job")
36+
.withRegistry(registry); <1>
37+
38+
// ...
39+
40+
Timer.Sample sample = Timer.start(registry);
41+
42+
Result result = job.execute();
43+
44+
sample.stop(timerProvider.withTags("status", result.status())); <2>
45+
----
46+
<1> Definition of the `MeterProvider` for `Timer` with all the "static" fields necessary. Please note the `withRegistry` method call.
47+
<2> Definition of the dynamic tags. Note that only those tags are defined here that are dynamic and everying else is defined where the `MeterProvider` is created. The `withTags` method returns a `Timer` that is created using the tags defined in `withTags` plus everything else that is defined by the `MeterProvider`.
48+
49+
This and the previous example produce the same output, the only difference is the amount of boilerplate in your code and the amount of builder objects created in the heap.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2024 VMware, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.micrometer.core.samples;
17+
18+
import io.micrometer.core.instrument.Meter.MeterProvider;
19+
import io.micrometer.core.instrument.MeterRegistry;
20+
import io.micrometer.core.instrument.Timer;
21+
import io.micrometer.core.samples.utils.SampleConfig;
22+
import reactor.core.publisher.Flux;
23+
24+
import java.time.Duration;
25+
26+
public class MeterProviderSample {
27+
28+
public static void main(String[] args) {
29+
MeterRegistry registry = SampleConfig.myMonitoringSystem();
30+
MeterProvider<Timer> timerProvider = Timer.builder("job.execution")
31+
.tag("job.name", "job")
32+
.withRegistry(registry);
33+
34+
Flux.interval(Duration.ofSeconds(1)).doOnEach(d -> {
35+
Timer.Sample sample = Timer.start(registry);
36+
Result result = new Job().execute();
37+
sample.stop(timerProvider.withTags("status", result.status()));
38+
}).blockLast();
39+
}
40+
41+
static class Job {
42+
43+
Result execute() {
44+
try {
45+
Thread.sleep((long) (Math.random() * 100 + 100));
46+
}
47+
catch (InterruptedException e) {
48+
// ignored
49+
}
50+
51+
return new Result(Math.random() > 0.2 ? "SUCCESS" : "FAILED");
52+
}
53+
54+
}
55+
56+
static class Result {
57+
58+
final String status;
59+
60+
Result(String status) {
61+
this.status = status;
62+
}
63+
64+
String status() {
65+
return status;
66+
}
67+
68+
}
69+
70+
}

0 commit comments

Comments
 (0)