-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Closes gh-4872 Co-authored-by: Tommy Ludwig <[email protected]>
- Loading branch information
1 parent
68e4cc4
commit c9792c5
Showing
3 changed files
with
120 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
[[meter-provider]] | ||
= Meter Provider | ||
|
||
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: | ||
|
||
[source, java] | ||
---- | ||
Timer.Sample sample = Timer.start(registry); | ||
Result result = job.execute(); | ||
Timer timer = Timer.builder("job.execution") | ||
.tag("job.name", "job") | ||
.tag("status", result.status()) | ||
.register(registry); | ||
sample.stop(timer); | ||
---- | ||
|
||
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: | ||
|
||
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. | ||
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. | ||
|
||
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. | ||
|
||
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". | ||
|
||
NOTE: Not every `Meter` can do this, `MeterProvider` can be used with `Counter`, `Timer`, `LongTaskTimer`, and `DistributionSummary`. | ||
|
||
Here's what you can do instead of the above: | ||
|
||
[source, java] | ||
---- | ||
private MeterProvider<Timer> timerProvider = Timer.builder("job.execution") | ||
.tag("job.name", "my-job") | ||
.withRegistry(registry); <1> | ||
// ... | ||
Timer.Sample sample = Timer.start(registry); | ||
Result result = job.execute(); | ||
sample.stop(timerProvider.withTags("status", result.status())); <2> | ||
---- | ||
<1> Definition of the `MeterProvider` for `Timer` with all the "static" fields necessary. Please note the `withRegistry` method call. | ||
<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`. | ||
|
||
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. |
70 changes: 70 additions & 0 deletions
70
...micrometer-samples-core/src/main/java/io/micrometer/core/samples/MeterProviderSample.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* | ||
* Copyright 2024 VMware, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package io.micrometer.core.samples; | ||
|
||
import io.micrometer.core.instrument.Meter.MeterProvider; | ||
import io.micrometer.core.instrument.MeterRegistry; | ||
import io.micrometer.core.instrument.Timer; | ||
import io.micrometer.core.samples.utils.SampleConfig; | ||
import reactor.core.publisher.Flux; | ||
|
||
import java.time.Duration; | ||
|
||
public class MeterProviderSample { | ||
|
||
public static void main(String[] args) { | ||
MeterRegistry registry = SampleConfig.myMonitoringSystem(); | ||
MeterProvider<Timer> timerProvider = Timer.builder("job.execution") | ||
.tag("job.name", "job") | ||
.withRegistry(registry); | ||
|
||
Flux.interval(Duration.ofSeconds(1)).doOnEach(d -> { | ||
Timer.Sample sample = Timer.start(registry); | ||
Result result = new Job().execute(); | ||
sample.stop(timerProvider.withTags("status", result.status())); | ||
}).blockLast(); | ||
} | ||
|
||
static class Job { | ||
|
||
Result execute() { | ||
try { | ||
Thread.sleep((long) (Math.random() * 100 + 100)); | ||
} | ||
catch (InterruptedException e) { | ||
// ignored | ||
} | ||
|
||
return new Result(Math.random() > 0.2 ? "SUCCESS" : "FAILED"); | ||
} | ||
|
||
} | ||
|
||
static class Result { | ||
|
||
final String status; | ||
|
||
Result(String status) { | ||
this.status = status; | ||
} | ||
|
||
String status() { | ||
return status; | ||
} | ||
|
||
} | ||
|
||
} |