Skip to content

Commit 91e7246

Browse files
committed
Merge branch '1.13.x' into 1.14.x
2 parents 64b9194 + 2c46d0b commit 91e7246

File tree

6 files changed

+235
-70
lines changed

6 files changed

+235
-70
lines changed

docs/modules/ROOT/pages/concepts/counters.adoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,11 @@ FunctionCounter counter = FunctionCounter
153153
.tags("region", "test") // optional
154154
.register(registry);
155155
----
156+
157+
WARNING: Attempting to construct a function-tracking counter with a primitive number or one of its `java.lang` object forms is always incorrect. These numbers are immutable. Thus, the function-tracking counter cannot ever be changed. Attempting to "re-register" the function-tracking counter with a different number does not work, as the registry maintains only one meter for each unique combination of name and tags. "Re-registering" a function-tracking counter can happen indirectly for example as the result of a `MeterFilter` modifying the name and/or the tags of two different function-tracking counter so that they will be the same after the filter is applied.
158+
159+
Attempting to "re-register" a function-tracking counter will result in a warning like this:
160+
[source]
161+
----
162+
WARNING: This FunctionCounter has been already registered (MeterId{name='my.fc', tags=[]}), the registration will be ignored. Note that subsequent logs will be logged at debug level.
163+
----

docs/modules/ROOT/pages/concepts/gauges.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ This pattern should be less common than the `DoubleFunction` form. Remember that
4444

4545
WARNING: Attempting to construct a gauge with a primitive number or one of its `java.lang` object forms is always incorrect. These numbers are immutable. Thus, the gauge cannot ever be changed. Attempting to "re-register" the gauge with a different number does not work, as the registry maintains only one meter for each unique combination of name and tags. "Re-registering" a gauge can happen indirectly for example as the result of a `MeterFilter` modifying the name and/or the tags of two different gauges so that they will be the same after the filter is applied.
4646

47+
Attempting to "re-register" a gauge will result in a warning like this:
48+
[source]
49+
----
50+
WARNING: This Gauge has been already registered (MeterId{name='my.gauge', tags=[]}), the registration will be ignored. Note that subsequent logs will be logged at debug level.
51+
----
52+
4753
== Gauge Fluent Builder
4854

4955
The interface contains a fluent builder for gauges:

docs/modules/ROOT/pages/concepts/timers.adoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,14 @@ FunctionTimer.builder("cache.gets.latency", cache,
186186
.register(registry);
187187
----
188188

189+
WARNING: Attempting to construct a function-tracking timer with a primitive number or one of its `java.lang` object forms is always incorrect. These numbers are immutable. Thus, the function-tracking timer cannot ever be changed. Attempting to "re-register" the function-tracking timer with a different number does not work, as the registry maintains only one meter for each unique combination of name and tags. "Re-registering" a function-tracking timer can happen indirectly for example as the result of a `MeterFilter` modifying the name and/or the tags of two different function-tracking timer so that they will be the same after the filter is applied.
190+
191+
Attempting to "re-register" a function-tracking timer will result in a warning like this:
192+
[source]
193+
----
194+
WARNING: This FunctionTimer has been already registered (MeterId{name='my.ft', tags=[]}), the registration will be ignored. Note that subsequent logs will be logged at debug level.
195+
----
196+
189197
== Pause Detection
190198

191199
Micrometer uses the `LatencyUtils` package to compensate for https://highscalability.com/blog/2015/10/5/your-load-generator-is-probably-lying-to-you-take-the-red-pi.html[coordinated omission] -- extra latency arising from system and VM pauses that skew your latency statistics downward. Distribution statistics, such as percentiles and SLO counts, are influenced by a pause detector implementation that adds additional latency here and there to compensate for pauses.

micrometer-core/src/main/java/io/micrometer/core/instrument/MeterRegistry.java

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,7 @@
6767
*/
6868
public abstract class MeterRegistry {
6969

70-
private static final WarnThenDebugLogger gaugeDoubleRegistrationLogger = new WarnThenDebugLogger(
71-
MeterRegistry.class);
70+
private static final WarnThenDebugLogger doubleRegistrationLogger = new WarnThenDebugLogger(MeterRegistry.class);
7271

7372
// @formatter:off
7473
private static final EnumMap<TimeUnit, String> BASE_TIME_UNIT_STRING_CACHE = Arrays.stream(TimeUnit.values())
@@ -640,7 +639,7 @@ private Meter getOrCreateMeter(@Nullable DistributionStatisticConfig config,
640639

641640
Meter m = preFilterIdToMeterMap.get(originalId);
642641
if (m != null && !isStaleId(originalId)) {
643-
checkAndWarnAboutGaugeDoubleRegistration(m);
642+
checkAndWarnAboutDoubleRegistration(m);
644643
return m;
645644
}
646645

@@ -653,7 +652,7 @@ private Meter getOrCreateMeter(@Nullable DistributionStatisticConfig config,
653652
if (isStaleId(originalId)) {
654653
unmarkStaleId(originalId);
655654
}
656-
checkAndWarnAboutGaugeDoubleRegistration(m);
655+
checkAndWarnAboutDoubleRegistration(m);
657656
}
658657
else {
659658
if (isClosed()) {
@@ -712,12 +711,21 @@ private boolean unmarkStaleId(Id originalId) {
712711
return !stalePreFilterIds.isEmpty() && stalePreFilterIds.remove(originalId);
713712
}
714713

715-
private void checkAndWarnAboutGaugeDoubleRegistration(Meter meter) {
716-
if (meter instanceof Gauge) {
717-
gaugeDoubleRegistrationLogger.log(() -> String.format(
718-
"This Gauge has been already registered (%s), the Gauge registration will be ignored.",
719-
meter.getId()));
714+
private void checkAndWarnAboutDoubleRegistration(Meter meter) {
715+
if (meter instanceof Gauge) { // also TimeGauge
716+
warnAboutDoubleRegistration("Gauge", meter.getId());
720717
}
718+
else if (meter instanceof FunctionCounter) {
719+
warnAboutDoubleRegistration("FunctionCounter", meter.getId());
720+
}
721+
else if (meter instanceof FunctionTimer) {
722+
warnAboutDoubleRegistration("FunctionTimer", meter.getId());
723+
}
724+
}
725+
726+
private void warnAboutDoubleRegistration(String type, Meter.Id id) {
727+
doubleRegistrationLogger.log(() -> String
728+
.format("This %s has been already registered (%s), the registration will be ignored.", type, id));
721729
}
722730

723731
private boolean accept(Meter.Id id) {

micrometer-core/src/main/java/io/micrometer/core/instrument/binder/kafka/KafkaMetrics.java

Lines changed: 64 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ private Iterable<Tag> getCommonTags(MeterRegistry registry) {
150150
// FIXME hack until we have proper API to retrieve common tags
151151
Meter.Id dummyId = Meter.builder("delete.this", OTHER, Collections.emptyList()).register(registry).getId();
152152
registry.remove(dummyId);
153-
return dummyId.getTags();
153+
return dummyId.getTagsAsIterable();
154154
}
155155

156156
/**
@@ -191,74 +191,77 @@ void checkAndBindMetrics(MeterRegistry registry) {
191191
Map<MetricName, ? extends Metric> currentMetrics = this.metricsSupplier.get();
192192
this.metrics.set(currentMetrics);
193193

194-
if (!currentMeters.equals(currentMetrics.keySet())) {
195-
Set<MetricName> metricsToRemove = currentMeters.stream()
196-
.filter(metricName -> !currentMetrics.containsKey(metricName))
197-
.collect(Collectors.toSet());
194+
if (currentMeters.equals(currentMetrics.keySet())) {
195+
return;
196+
}
198197

199-
for (MetricName metricName : metricsToRemove) {
200-
Meter.Id id = meterIdForComparison(metricName);
201-
registry.remove(id);
202-
registeredMeterIds.remove(id);
203-
}
198+
Set<MetricName> metricsToRemove = currentMeters.stream()
199+
.filter(metricName -> !currentMetrics.containsKey(metricName))
200+
.collect(Collectors.toSet());
201+
202+
for (MetricName metricName : metricsToRemove) {
203+
Meter.Id id = meterIdForComparison(metricName);
204+
registry.remove(id);
205+
registeredMeterIds.remove(id);
206+
}
204207

205-
currentMeters = new HashSet<>(currentMetrics.keySet());
208+
currentMeters = new HashSet<>(currentMetrics.keySet());
206209

207-
Map<String, List<Meter>> registryMetersByNames = registry.getMeters()
208-
.stream()
209-
.collect(Collectors.groupingBy(meter -> meter.getId().getName()));
210+
Map<String, List<Meter>> registryMetersByNames = registry.getMeters()
211+
.stream()
212+
.collect(Collectors.groupingBy(meter -> meter.getId().getName()));
210213

211-
currentMetrics.forEach((name, metric) -> {
212-
// Filter out non-numeric values
213-
// Filter out metrics from groups that include metadata
214-
if (!(metric.metricValue() instanceof Number) || METRIC_GROUP_APP_INFO.equals(name.group())
215-
|| METRIC_GROUP_METRICS_COUNT.equals(name.group())) {
216-
return;
217-
}
214+
currentMetrics.forEach((name, metric) -> {
215+
// Filter out non-numeric values
216+
// Filter out metrics from groups that include metadata
217+
if (!(metric.metricValue() instanceof Number) || METRIC_GROUP_APP_INFO.equals(name.group())
218+
|| METRIC_GROUP_METRICS_COUNT.equals(name.group())) {
219+
return;
220+
}
218221

219-
String meterName = meterName(name);
220-
221-
// Kafka has metrics with lower number of tags (e.g. with/without
222-
// topic or partition tag)
223-
// Remove meters with lower number of tags
224-
boolean hasLessTags = false;
225-
for (Meter other : registryMetersByNames.getOrDefault(meterName, emptyList())) {
226-
Meter.Id otherId = other.getId();
227-
List<Tag> tags = otherId.getTags();
228-
List<Tag> meterTagsWithCommonTags = meterTags(name, true);
229-
if (tags.size() < meterTagsWithCommonTags.size()) {
230-
registry.remove(otherId);
231-
registeredMeterIds.remove(otherId);
232-
}
233-
// Check if already exists
234-
else if (tags.size() == meterTagsWithCommonTags.size()) {
235-
if (tags.containsAll(meterTagsWithCommonTags))
236-
return;
237-
}
238-
else
239-
hasLessTags = true;
222+
String meterName = meterName(name);
223+
224+
// Kafka has metrics with lower number of tags (e.g. with/without
225+
// topic or partition tag)
226+
// Remove meters with lower number of tags
227+
boolean hasLessTags = false;
228+
for (Meter other : registryMetersByNames.getOrDefault(meterName, emptyList())) {
229+
Meter.Id otherId = other.getId();
230+
List<Tag> otherTags = otherId.getTags();
231+
List<Tag> meterTagsWithCommonTags = meterTags(name, true);
232+
if (otherTags.size() < meterTagsWithCommonTags.size()) {
233+
registry.remove(otherId);
234+
registeredMeterIds.remove(otherId);
240235
}
241-
if (hasLessTags)
242-
return;
243-
244-
List<Tag> tags = meterTags(name);
245-
try {
246-
Meter meter = bindMeter(registry, metric, meterName, tags);
247-
List<Meter> meters = registryMetersByNames.computeIfAbsent(meterName, k -> new ArrayList<>());
248-
meters.add(meter);
236+
// Check if already exists
237+
else if (otherTags.size() == meterTagsWithCommonTags.size()) {
238+
// https://www.jetbrains.com/help/inspectopedia/SlowListContainsAll.html
239+
if (new HashSet<>(otherTags).containsAll(meterTagsWithCommonTags))
240+
return;
249241
}
250-
catch (Exception ex) {
251-
String message = ex.getMessage();
252-
if (message != null && message.contains("Prometheus requires")) {
253-
warnThenDebugLogger.log(() -> "Failed to bind meter: " + meterName + " " + tags
254-
+ ". However, this could happen and might be restored in the next refresh.");
255-
}
256-
else {
257-
log.warn("Failed to bind meter: " + meterName + " " + tags + ".", ex);
258-
}
242+
else
243+
hasLessTags = true;
244+
}
245+
if (hasLessTags)
246+
return;
247+
248+
List<Tag> tags = meterTags(name);
249+
try {
250+
Meter meter = bindMeter(registry, metric, meterName, tags);
251+
List<Meter> meters = registryMetersByNames.computeIfAbsent(meterName, k -> new ArrayList<>());
252+
meters.add(meter);
253+
}
254+
catch (Exception ex) {
255+
String message = ex.getMessage();
256+
if (message != null && message.contains("Prometheus requires")) {
257+
warnThenDebugLogger.log(() -> "Failed to bind meter: " + meterName + " " + tags
258+
+ ". However, this could happen and might be restored in the next refresh.");
259259
}
260-
});
261-
}
260+
else {
261+
log.warn("Failed to bind meter: " + meterName + " " + tags + ".", ex);
262+
}
263+
}
264+
});
262265
}
263266
catch (Exception e) {
264267
log.warn("Failed to bind KafkaMetric", e);

0 commit comments

Comments
 (0)