getCommonTags(MeterRegistry registry) {
@@ -173,10 +179,6 @@ else if (START_TIME_METRIC_NAME.equals(name.name())) {
}
}
- private long getRefreshIntervalInMillis() {
- return refreshInterval.toMillis();
- }
-
/**
* Gather metrics from Kafka metrics API and register Meters.
*
@@ -295,6 +297,10 @@ private static Class extends Measurable> getMeasurableClass(Metric metric) {
}
}
+ private static ScheduledExecutorService createDefaultScheduler() {
+ return Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("micrometer-kafka-metrics"));
+ }
+
private Gauge registerGauge(MeterRegistry registry, MetricName metricName, String meterName, Iterable tags) {
return Gauge.builder(meterName, this.metrics, toMetricValue(metricName))
.tags(tags)
@@ -344,7 +350,9 @@ private Meter.Id meterIdForComparison(MetricName metricName) {
@Override
public void close() {
- this.scheduler.shutdownNow();
+ if (!schedulerExternallyManaged) {
+ this.scheduler.shutdownNow();
+ }
for (Meter.Id id : registeredMeterIds) {
registry.remove(id);
diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/kafka/KafkaStreamsMetrics.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/kafka/KafkaStreamsMetrics.java
index 3f0f7d569a..07ff3ccb69 100644
--- a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/kafka/KafkaStreamsMetrics.java
+++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/kafka/KafkaStreamsMetrics.java
@@ -22,6 +22,8 @@
import org.apache.kafka.common.Metric;
import org.apache.kafka.streams.KafkaStreams;
+import java.util.concurrent.ScheduledExecutorService;
+
/**
* Kafka Streams metrics binder. This should be closed on application shutdown to clean up
* resources.
@@ -58,4 +60,19 @@ public KafkaStreamsMetrics(KafkaStreams kafkaStreams) {
super(kafkaStreams::metrics);
}
+ /**
+ * {@link KafkaStreams} metrics binder. The lifecycle of the custom scheduler passed
+ * is the responsibility of the caller. It will not be shut down when this instance is
+ * {@link #close() closed}. A scheduler can be shared among multiple instances of
+ * {@link KafkaStreamsMetrics} to reduce resource usage by reducing the number of
+ * threads if there will be many instances.
+ * @param kafkaStreams instance to be instrumented
+ * @param tags additional tags
+ * @param scheduler customer scheduler to run the task that checks and binds metrics
+ * @since 1.14.0
+ */
+ public KafkaStreamsMetrics(KafkaStreams kafkaStreams, Iterable tags, ScheduledExecutorService scheduler) {
+ super(kafkaStreams::metrics, tags, scheduler);
+ }
+
}
diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/logging/Log4j2Metrics.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/logging/Log4j2Metrics.java
index 166b424db9..98e9b772f5 100644
--- a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/logging/Log4j2Metrics.java
+++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/logging/Log4j2Metrics.java
@@ -31,9 +31,9 @@
import org.apache.logging.log4j.core.filter.AbstractFilter;
import org.apache.logging.log4j.core.filter.CompositeFilter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import static java.util.Collections.emptyList;
@@ -61,7 +61,7 @@ public class Log4j2Metrics implements MeterBinder, AutoCloseable {
private final LoggerContext loggerContext;
- private final List metricsFilters = new ArrayList<>();
+ private final ConcurrentMap metricsFilters = new ConcurrentHashMap<>();
public Log4j2Metrics() {
this(emptyList());
@@ -80,7 +80,7 @@ public Log4j2Metrics(Iterable tags, LoggerContext loggerContext) {
public void bindTo(MeterRegistry registry) {
Configuration configuration = loggerContext.getConfiguration();
LoggerConfig rootLoggerConfig = configuration.getRootLogger();
- rootLoggerConfig.addFilter(createMetricsFilterAndStart(registry));
+ rootLoggerConfig.addFilter(getOrCreateMetricsFilterAndStart(registry));
loggerContext.getConfiguration()
.getLoggers()
@@ -102,17 +102,18 @@ public void bindTo(MeterRegistry registry) {
if (logFilter instanceof MetricsFilter) {
return;
}
- loggerConfig.addFilter(createMetricsFilterAndStart(registry));
+ loggerConfig.addFilter(getOrCreateMetricsFilterAndStart(registry));
});
loggerContext.updateLoggers(configuration);
}
- private MetricsFilter createMetricsFilterAndStart(MeterRegistry registry) {
- MetricsFilter metricsFilter = new MetricsFilter(registry, tags);
- metricsFilter.start();
- metricsFilters.add(metricsFilter);
- return metricsFilter;
+ private MetricsFilter getOrCreateMetricsFilterAndStart(MeterRegistry registry) {
+ return metricsFilters.computeIfAbsent(registry, r -> {
+ MetricsFilter metricsFilter = new MetricsFilter(r, tags);
+ metricsFilter.start();
+ return metricsFilter;
+ });
}
@Override
@@ -120,7 +121,7 @@ public void close() {
if (!metricsFilters.isEmpty()) {
Configuration configuration = loggerContext.getConfiguration();
LoggerConfig rootLoggerConfig = configuration.getRootLogger();
- metricsFilters.forEach(rootLoggerConfig::removeFilter);
+ metricsFilters.values().forEach(rootLoggerConfig::removeFilter);
loggerContext.getConfiguration()
.getLoggers()
@@ -129,12 +130,13 @@ public void close() {
.filter(loggerConfig -> !loggerConfig.isAdditive())
.forEach(loggerConfig -> {
if (loggerConfig != rootLoggerConfig) {
- metricsFilters.forEach(loggerConfig::removeFilter);
+ metricsFilters.values().forEach(loggerConfig::removeFilter);
}
});
loggerContext.updateLoggers(configuration);
- metricsFilters.forEach(MetricsFilter::stop);
+ metricsFilters.values().forEach(MetricsFilter::stop);
+ metricsFilters.clear();
}
}
diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/okhttp3/DefaultOkHttpObservationConvention.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/okhttp3/DefaultOkHttpObservationConvention.java
index a4756b4235..0ef6d36bfd 100644
--- a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/okhttp3/DefaultOkHttpObservationConvention.java
+++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/okhttp3/DefaultOkHttpObservationConvention.java
@@ -88,7 +88,7 @@ public KeyValues getLowCardinalityKeyValues(OkHttpContext context) {
// TODO: Tags to key values and back - maybe we can improve this?
KeyValues keyValues = KeyValues
.of(METHOD.withValue(requestAvailable ? request.method() : TAG_VALUE_UNKNOWN),
- URI.withValue(getUriTag(urlMapper, state, request)),
+ URI.withValue(getUriTag(urlMapper, request)),
STATUS.withValue(getStatusMessage(state.response, state.exception)),
OUTCOME.withValue(getStatusOutcome(state.response).name()))
.and(extraTags)
@@ -105,13 +105,11 @@ public KeyValues getLowCardinalityKeyValues(OkHttpContext context) {
return keyValues;
}
- private String getUriTag(Function urlMapper, OkHttpObservationInterceptor.CallState state,
- @Nullable Request request) {
+ private String getUriTag(Function urlMapper, @Nullable Request request) {
if (request == null) {
return TAG_VALUE_UNKNOWN;
}
- return state.response != null && (state.response.code() == 404 || state.response.code() == 301) ? "NOT_FOUND"
- : urlMapper.apply(request);
+ return urlMapper.apply(request);
}
private Outcome getStatusOutcome(@Nullable Response response) {
diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/okhttp3/OkHttpMetricsEventListener.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/okhttp3/OkHttpMetricsEventListener.java
index 8b1809303d..c19ff9d6df 100644
--- a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/okhttp3/OkHttpMetricsEventListener.java
+++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/okhttp3/OkHttpMetricsEventListener.java
@@ -167,8 +167,8 @@ void time(CallState state) {
boolean requestAvailable = request != null;
Iterable tags = Tags
- .of("method", requestAvailable ? request.method() : TAG_VALUE_UNKNOWN, "uri", getUriTag(state, request),
- "status", getStatusMessage(state.response, state.exception))
+ .of("method", requestAvailable ? request.method() : TAG_VALUE_UNKNOWN, "uri", getUriTag(request), "status",
+ getStatusMessage(state.response, state.exception))
.and(getStatusOutcome(state.response).asTag())
.and(extraTags)
.and(stream(contextSpecificTags.spliterator(), false)
@@ -196,12 +196,11 @@ private Tags generateTagsForRoute(@Nullable Request request) {
TAG_TARGET_PORT, Integer.toString(request.url().port()));
}
- private String getUriTag(CallState state, @Nullable Request request) {
+ private String getUriTag(@Nullable Request request) {
if (request == null) {
return TAG_VALUE_UNKNOWN;
}
- return state.response != null && (state.response.code() == 404 || state.response.code() == 301) ? "NOT_FOUND"
- : urlMapper.apply(request);
+ return urlMapper.apply(request);
}
private Iterable getRequestTags(@Nullable Request request) {
diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/config/InvalidConfigurationException.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/config/InvalidConfigurationException.java
index 69e4d7700b..dddd699596 100644
--- a/micrometer-core/src/main/java/io/micrometer/core/instrument/config/InvalidConfigurationException.java
+++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/config/InvalidConfigurationException.java
@@ -15,6 +15,8 @@
*/
package io.micrometer.core.instrument.config;
+import io.micrometer.common.lang.Nullable;
+
/**
* Signals that a piece of provided configuration is not acceptable for some reason. For
* example negative SLO boundaries.
@@ -31,12 +33,12 @@ public class InvalidConfigurationException extends IllegalStateException {
* indicates that the cause is nonexistent or unknown.)
* @since 1.11.9
*/
- public InvalidConfigurationException(String message, Throwable cause) {
+ public InvalidConfigurationException(String message, @Nullable Throwable cause) {
super(message, cause);
}
- public InvalidConfigurationException(String s) {
- super(s);
+ public InvalidConfigurationException(String message) {
+ super(message);
}
}
diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/config/NamingConvention.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/config/NamingConvention.java
index 209e0accb8..ab4b7be0d4 100644
--- a/micrometer-core/src/main/java/io/micrometer/core/instrument/config/NamingConvention.java
+++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/config/NamingConvention.java
@@ -109,7 +109,7 @@ public String tagKey(String key) {
}
private String capitalize(String name) {
- if (name.length() == 0 || Character.isUpperCase(name.charAt(0))) {
+ if (name.isEmpty() || Character.isUpperCase(name.charAt(0))) {
return name;
}
diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/config/validate/DurationValidator.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/config/validate/DurationValidator.java
index 1608e54391..b0172f63b0 100644
--- a/micrometer-core/src/main/java/io/micrometer/core/instrument/config/validate/DurationValidator.java
+++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/config/validate/DurationValidator.java
@@ -23,6 +23,7 @@
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -44,7 +45,7 @@ public enum DurationValidator {
"^\\s*([\\+]?\\d{0,3}([_,]?\\d{3})*(\\.\\d*)?)\\s*([a-zA-Z]{0,2})\\s*") {
@Override
protected Validated doParse(String property, String value) {
- Matcher matcher = patterns.get(0).matcher(value.toLowerCase().replaceAll("[,_\\s]", ""));
+ Matcher matcher = patterns.get(0).matcher(value.toLowerCase(Locale.ROOT).replaceAll("[,_\\s]", ""));
if (!matcher.matches()) {
return Validated.invalid(property, value, "must be a valid duration", InvalidReason.MALFORMED);
}
@@ -143,7 +144,7 @@ public static Validated validateChronoUnit(String property, @Nullabl
return Validated.valid(property, null);
}
- switch (unit.toLowerCase()) {
+ switch (unit.toLowerCase(Locale.ROOT)) {
case "ns":
case "nanoseconds":
case "nanosecond":
diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/FixedBoundaryHistogram.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/FixedBoundaryHistogram.java
index d740c3a21f..bbbc5a06a9 100644
--- a/micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/FixedBoundaryHistogram.java
+++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/FixedBoundaryHistogram.java
@@ -29,8 +29,8 @@ class FixedBoundaryHistogram {
/**
* Creates a FixedBoundaryHistogram which tracks the count of values for each bucket
- * bound).
- * @param buckets - sorted bucket boundaries
+ * bound.
+ * @param buckets sorted bucket boundaries
* @param isCumulativeBucketCounts - whether the count values should be cumulative
* count of lower buckets and current bucket.
*/
@@ -46,10 +46,10 @@ class FixedBoundaryHistogram {
/**
* Returns the number of values that was recorded between previous bucket and the
- * queried bucket (upper bound inclusive)
+ * queried bucket (upper bound inclusive).
* @param bucket - the bucket to find values for
* @return 0 if bucket is not a valid bucket otherwise number of values recorded
- * between (index(bucket) - 1, bucket]
+ * between (previous bucket, bucket]
*/
private long countAtBucket(double bucket) {
int index = Arrays.binarySearch(buckets, bucket);
@@ -75,7 +75,7 @@ void record(long value) {
* valueToRecord is greater than the highest bucket.
*/
// VisibleForTesting
- int leastLessThanOrEqualTo(long valueToRecord) {
+ int leastLessThanOrEqualTo(double valueToRecord) {
int low = 0;
int high = buckets.length - 1;
@@ -121,7 +121,7 @@ public CountAtBucket next() {
* Returns the list of {@link CountAtBucket} for each of the buckets tracked by this
* histogram.
*/
- CountAtBucket[] getCountsAtBucket() {
+ CountAtBucket[] getCountAtBuckets() {
CountAtBucket[] countAtBuckets = new CountAtBucket[this.buckets.length];
long cumulativeCount = 0;
diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/StepBucketHistogram.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/StepBucketHistogram.java
index 5ce563def1..e81725339e 100644
--- a/micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/StepBucketHistogram.java
+++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/StepBucketHistogram.java
@@ -64,7 +64,7 @@ protected Supplier valueSupplier() {
return () -> {
CountAtBucket[] countAtBuckets;
synchronized (fixedBoundaryHistogram) {
- countAtBuckets = fixedBoundaryHistogram.getCountsAtBucket();
+ countAtBuckets = fixedBoundaryHistogram.getCountAtBuckets();
fixedBoundaryHistogram.reset();
}
return countAtBuckets;
diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/logging/LoggingMeterRegistry.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/logging/LoggingMeterRegistry.java
index 6cbea5b75b..3387642151 100644
--- a/micrometer-core/src/main/java/io/micrometer/core/instrument/logging/LoggingMeterRegistry.java
+++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/logging/LoggingMeterRegistry.java
@@ -42,6 +42,7 @@
import java.util.stream.StreamSupport;
import static io.micrometer.core.instrument.util.DoubleFormat.decimalOrNan;
+import static io.micrometer.core.instrument.util.DoubleFormat.wholeOrDecimal;
import static java.util.stream.Collectors.joining;
/**
@@ -49,6 +50,7 @@
*
* @author Jon Schneider
* @author Matthieu Borgraeve
+ * @author Francois Staudt
* @since 1.1.0
*/
@Incubating(since = "1.1.0")
@@ -126,22 +128,24 @@ protected void publish() {
double count = counter.count();
if (!config.logInactive() && count == 0)
return;
- loggingSink.accept(print.id() + " throughput=" + print.rate(count));
+ loggingSink.accept(print.id() + " delta_count=" + print.humanReadableBaseUnit(count)
+ + " throughput=" + print.rate(count));
}, timer -> {
HistogramSnapshot snapshot = timer.takeSnapshot();
long count = snapshot.count();
if (!config.logInactive() && count == 0)
return;
- loggingSink.accept(print.id() + " throughput=" + print.unitlessRate(count) + " mean="
- + print.time(snapshot.mean(getBaseTimeUnit())) + " max="
- + print.time(snapshot.max(getBaseTimeUnit())));
+ loggingSink.accept(print.id() + " delta_count=" + wholeOrDecimal(count) + " throughput="
+ + print.unitlessRate(count) + " mean=" + print.time(snapshot.mean(getBaseTimeUnit()))
+ + " max=" + print.time(snapshot.max(getBaseTimeUnit())));
}, summary -> {
HistogramSnapshot snapshot = summary.takeSnapshot();
long count = snapshot.count();
if (!config.logInactive() && count == 0)
return;
- loggingSink.accept(print.id() + " throughput=" + print.unitlessRate(count) + " mean="
- + print.value(snapshot.mean()) + " max=" + print.value(snapshot.max()));
+ loggingSink.accept(print.id() + " delta_count=" + wholeOrDecimal(count) + " throughput="
+ + print.unitlessRate(count) + " mean=" + print.value(snapshot.mean()) + " max="
+ + print.value(snapshot.max()));
}, longTaskTimer -> {
int activeTasks = longTaskTimer.activeTasks();
if (!config.logInactive() && activeTasks == 0)
@@ -157,13 +161,14 @@ protected void publish() {
double count = counter.count();
if (!config.logInactive() && count == 0)
return;
- loggingSink.accept(print.id() + " throughput=" + print.rate(count));
+ loggingSink.accept(print.id() + " delta_count=" + print.humanReadableBaseUnit(count)
+ + " throughput=" + print.rate(count));
}, timer -> {
double count = timer.count();
if (!config.logInactive() && count == 0)
return;
- loggingSink.accept(print.id() + " throughput=" + print.rate(count) + " mean="
- + print.time(timer.mean(getBaseTimeUnit())));
+ loggingSink.accept(print.id() + " delta_count=" + wholeOrDecimal(count) + " throughput="
+ + print.unitlessRate(count) + " mean=" + print.time(timer.mean(getBaseTimeUnit())));
}, meter -> loggingSink.accept(writeMeter(meter, print)));
});
}
@@ -181,7 +186,8 @@ String writeMeter(Meter meter, Printer print) {
case DURATION:
return msLine + print.time(ms.getValue());
case COUNT:
- return "throughput=" + print.rate(ms.getValue());
+ return "delta_count=" + print.humanReadableBaseUnit(ms.getValue()) + ", throughput="
+ + print.rate(ms.getValue());
default:
return msLine + decimalOrNan(ms.getValue());
}
diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/simple/SimpleMeterRegistry.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/simple/SimpleMeterRegistry.java
index 213d915b82..98d85fe790 100644
--- a/micrometer-core/src/main/java/io/micrometer/core/instrument/simple/SimpleMeterRegistry.java
+++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/simple/SimpleMeterRegistry.java
@@ -28,6 +28,7 @@
import io.micrometer.core.instrument.step.*;
import java.util.Comparator;
+import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.function.ToDoubleFunction;
import java.util.function.ToLongFunction;
@@ -203,7 +204,7 @@ private String toString(Tag tag) {
private String toString(Measurement measurement, String meterUnitSuffix) {
Statistic statistic = measurement.getStatistic();
- return String.format("%s=%s%s", statistic.toString().toLowerCase(), measurement.getValue(),
+ return String.format("%s=%s%s", statistic.toString().toLowerCase(Locale.ROOT), measurement.getValue(),
getUnitSuffix(statistic, meterUnitSuffix));
}
diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/util/TimeUtils.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/util/TimeUtils.java
index 7ec63516f7..779bfa8d7c 100644
--- a/micrometer-core/src/main/java/io/micrometer/core/instrument/util/TimeUtils.java
+++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/util/TimeUtils.java
@@ -20,6 +20,7 @@
import java.time.Duration;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
+import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
@@ -218,7 +219,7 @@ public static double daysToUnit(double days, TimeUnit destinationUnit) {
*/
@Deprecated
public static Duration simpleParse(String time) {
- String timeLower = PARSE_PATTERN.matcher(time.toLowerCase()).replaceAll("");
+ String timeLower = PARSE_PATTERN.matcher(time.toLowerCase(Locale.ROOT)).replaceAll("");
if (timeLower.endsWith("ns")) {
return Duration.ofNanos(Long.parseLong(timeLower.substring(0, timeLower.length() - 2)));
}
diff --git a/micrometer-core/src/main/java11/io/micrometer/core/instrument/binder/jdk/DefaultHttpClientObservationConvention.java b/micrometer-core/src/main/java11/io/micrometer/core/instrument/binder/jdk/DefaultHttpClientObservationConvention.java
index f0b4901c7a..a0de050c31 100644
--- a/micrometer-core/src/main/java11/io/micrometer/core/instrument/binder/jdk/DefaultHttpClientObservationConvention.java
+++ b/micrometer-core/src/main/java11/io/micrometer/core/instrument/binder/jdk/DefaultHttpClientObservationConvention.java
@@ -45,29 +45,36 @@ public KeyValues getLowCardinalityKeyValues(HttpClientContext context) {
return KeyValues.empty();
}
HttpRequest httpRequest = context.getCarrier().build();
- KeyValues keyValues = KeyValues.of(
+ return KeyValues.of(
HttpClientObservationDocumentation.LowCardinalityKeys.METHOD.withValue(httpRequest.method()),
HttpClientObservationDocumentation.LowCardinalityKeys.URI
- .withValue(getUriTag(httpRequest, context.getResponse(), context.getUriMapper())));
- if (context.getResponse() != null) {
- keyValues = keyValues
- .and(HttpClientObservationDocumentation.LowCardinalityKeys.STATUS
- .withValue(String.valueOf(context.getResponse().statusCode())))
- .and(HttpClientObservationDocumentation.LowCardinalityKeys.OUTCOME
- .withValue(Outcome.forStatus(context.getResponse().statusCode()).name()));
- }
- return keyValues;
+ .withValue(getUriTag(httpRequest, context.getResponse(), context.getUriMapper())),
+ HttpClientObservationDocumentation.LowCardinalityKeys.STATUS
+ .withValue(getStatus(context.getResponse())),
+ HttpClientObservationDocumentation.LowCardinalityKeys.OUTCOME
+ .withValue(getOutcome(context.getResponse())));
}
- String getUriTag(@Nullable HttpRequest request, @Nullable HttpResponse> httpResponse,
+ String getUriTag(HttpRequest request, @Nullable HttpResponse> httpResponse,
Function uriMapper) {
- if (request == null) {
- return null;
- }
return httpResponse != null && (httpResponse.statusCode() == 404 || httpResponse.statusCode() == 301)
? "NOT_FOUND" : uriMapper.apply(request);
}
+ String getStatus(@Nullable HttpResponse> response) {
+ if (response == null) {
+ return "UNKNOWN";
+ }
+ return String.valueOf(response.statusCode());
+ }
+
+ String getOutcome(@Nullable HttpResponse> response) {
+ if (response == null) {
+ return Outcome.UNKNOWN.name();
+ }
+ return Outcome.forStatus(response.statusCode()).name();
+ }
+
@Override
@NonNull
public String getName() {
diff --git a/micrometer-core/src/main/java11/io/micrometer/core/instrument/binder/jdk/MicrometerHttpClient.java b/micrometer-core/src/main/java11/io/micrometer/core/instrument/binder/jdk/MicrometerHttpClient.java
index 178ea68cb7..f28c1afe9e 100644
--- a/micrometer-core/src/main/java11/io/micrometer/core/instrument/binder/jdk/MicrometerHttpClient.java
+++ b/micrometer-core/src/main/java11/io/micrometer/core/instrument/binder/jdk/MicrometerHttpClient.java
@@ -17,9 +17,7 @@
import io.micrometer.common.lang.Nullable;
import io.micrometer.core.instrument.MeterRegistry;
-import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
-import io.micrometer.core.instrument.binder.http.Outcome;
import io.micrometer.core.instrument.observation.ObservationOrTimerCompatibleInstrumentation;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
@@ -36,6 +34,7 @@
import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor;
import java.util.function.Function;
@@ -236,19 +235,13 @@ private void stopObservationOrTimer(
ObservationOrTimerCompatibleInstrumentation instrumentation, HttpRequest request,
@Nullable HttpResponse res) {
instrumentation.stop(DefaultHttpClientObservationConvention.INSTANCE.getName(), "Timer for JDK's HttpClient",
- () -> {
- Tags tags = Tags.of(HttpClientObservationDocumentation.LowCardinalityKeys.METHOD.asString(),
- request.method(), HttpClientObservationDocumentation.LowCardinalityKeys.URI.asString(),
- DefaultHttpClientObservationConvention.INSTANCE.getUriTag(request, res, uriMapper));
- if (res != null) {
- tags = tags
- .and(Tag.of(HttpClientObservationDocumentation.LowCardinalityKeys.STATUS.asString(),
- String.valueOf(res.statusCode())))
- .and(Tag.of(HttpClientObservationDocumentation.LowCardinalityKeys.OUTCOME.asString(),
- Outcome.forStatus(res.statusCode()).name()));
- }
- return tags;
- });
+ () -> Tags.of(HttpClientObservationDocumentation.LowCardinalityKeys.METHOD.asString(), request.method(),
+ HttpClientObservationDocumentation.LowCardinalityKeys.URI.asString(),
+ DefaultHttpClientObservationConvention.INSTANCE.getUriTag(request, res, uriMapper),
+ HttpClientObservationDocumentation.LowCardinalityKeys.STATUS.asString(),
+ DefaultHttpClientObservationConvention.INSTANCE.getStatus(res),
+ HttpClientObservationDocumentation.LowCardinalityKeys.OUTCOME.asString(),
+ DefaultHttpClientObservationConvention.INSTANCE.getOutcome(res)));
}
private ObservationOrTimerCompatibleInstrumentation observationOrTimer(
@@ -274,12 +267,16 @@ public CompletableFuture> sendAsync(HttpRequest httpRequest,
httpRequestBuilder);
HttpRequest request = httpRequestBuilder.build();
return client.sendAsync(request, bodyHandler, pushPromiseHandler).handle((response, throwable) -> {
+ instrumentation.setResponse(response);
if (throwable != null) {
instrumentation.setThrowable(throwable);
+ stopObservationOrTimer(instrumentation, request, response);
+ throw new CompletionException(throwable);
+ }
+ else {
+ stopObservationOrTimer(instrumentation, request, response);
+ return response;
}
- instrumentation.setResponse(response);
- stopObservationOrTimer(instrumentation, request, response);
- return response;
});
}
diff --git a/micrometer-core/src/main/resources/META-INF/native-image/io.micrometer/micrometer-core/reflect-config.json b/micrometer-core/src/main/resources/META-INF/native-image/io.micrometer/micrometer-core/reflect-config.json
index 01c1060f64..9b3a95b4ac 100644
--- a/micrometer-core/src/main/resources/META-INF/native-image/io.micrometer/micrometer-core/reflect-config.json
+++ b/micrometer-core/src/main/resources/META-INF/native-image/io.micrometer/micrometer-core/reflect-config.json
@@ -27,5 +27,71 @@
{
"name":"org.HdrHistogram.Histogram",
"methods":[{"name":"","parameterTypes":["long","long","int"] }]
+ },
+ {
+ "name":"com.hazelcast.core.DistributedObject",
+ "methods":[{"name":"getName","parameterTypes":[] }]
+ },
+ {
+ "name":"com.hazelcast.map.IMap",
+ "methods":[{"name":"getLocalMapStats","parameterTypes":[] }]
+ },
+ {
+ "name":"com.hazelcast.core.IMap",
+ "methods":[{"name":"getLocalMapStats","parameterTypes":[] }]
+ },
+ {
+ "name":"com.hazelcast.map.LocalMapStats",
+ "methods":[
+ {"name":"getNearCacheStats","parameterTypes":[] },
+ {"name":"getOwnedEntryCount","parameterTypes":[] },
+ {"name":"getHits","parameterTypes":[] },
+ {"name":"getPutOperationCount","parameterTypes":[] },
+ {"name":"getSetOperationCount","parameterTypes":[] },
+ {"name":"getBackupEntryCount","parameterTypes":[] },
+ {"name":"getBackupEntryMemoryCost","parameterTypes":[] },
+ {"name":"getOwnedEntryMemoryCost","parameterTypes":[] },
+ {"name":"getGetOperationCount","parameterTypes":[] },
+ {"name":"getTotalGetLatency","parameterTypes":[] },
+ {"name":"getTotalPutLatency","parameterTypes":[] },
+ {"name":"getRemoveOperationCount","parameterTypes":[] },
+ {"name":"getTotalRemoveLatency","parameterTypes":[] }
+ ]
+ },
+ {
+ "name":"com.hazelcast.monitor.LocalMapStats",
+ "methods":[
+ {"name":"getNearCacheStats","parameterTypes":[] },
+ {"name":"getOwnedEntryCount","parameterTypes":[] },
+ {"name":"getHits","parameterTypes":[] },
+ {"name":"getPutOperationCount","parameterTypes":[] },
+ {"name":"getSetOperationCount","parameterTypes":[] },
+ {"name":"getBackupEntryCount","parameterTypes":[] },
+ {"name":"getBackupEntryMemoryCost","parameterTypes":[] },
+ {"name":"getOwnedEntryMemoryCost","parameterTypes":[] },
+ {"name":"getGetOperationCount","parameterTypes":[] },
+ {"name":"getTotalGetLatency","parameterTypes":[] },
+ {"name":"getTotalPutLatency","parameterTypes":[] },
+ {"name":"getRemoveOperationCount","parameterTypes":[] },
+ {"name":"getTotalRemoveLatency","parameterTypes":[] }
+ ]
+ },
+ {
+ "name":"com.hazelcast.nearcache.NearCacheStats",
+ "methods":[
+ {"name":"getHits","parameterTypes":[] },
+ {"name":"getMisses","parameterTypes":[] },
+ {"name":"getEvictions","parameterTypes":[] },
+ {"name":"getPersistenceCount","parameterTypes":[] }
+ ]
+ },
+ {
+ "name":"com.hazelcast.monitor.NearCacheStats",
+ "methods":[
+ {"name":"getHits","parameterTypes":[] },
+ {"name":"getMisses","parameterTypes":[] },
+ {"name":"getEvictions","parameterTypes":[] },
+ {"name":"getPersistenceCount","parameterTypes":[] }
+ ]
}
]
diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/MeterRegistryTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/MeterRegistryTest.java
index 25246d662b..678583b234 100644
--- a/micrometer-core/src/test/java/io/micrometer/core/instrument/MeterRegistryTest.java
+++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/MeterRegistryTest.java
@@ -287,10 +287,12 @@ void differentPreFilterIdsMapToSameId_thenCacheIsBounded() {
// to the map because it would result in a memory leak with a high cardinality tag
// that's otherwise limited in cardinality by a MeterFilter
assertThat(registry._getPreFilterIdToMeterMap()).hasSize(1);
+ assertThat(registry._getMeterToPreFilterIdMap()).hasSize(1);
assertThat(registry.remove(c1)).isSameAs(c2);
assertThat(registry.getMeters()).isEmpty();
assertThat(registry._getPreFilterIdToMeterMap()).isEmpty();
+ assertThat(registry._getMeterToPreFilterIdMap()).isEmpty();
}
@Test
@@ -318,6 +320,7 @@ void removingStaleMeterRemovesItFromAllInternalState() {
registry.remove(c1.getId());
assertThat(registry.getMeters()).isEmpty();
assertThat(registry._getPreFilterIdToMeterMap()).isEmpty();
+ assertThat(registry._getMeterToPreFilterIdMap()).isEmpty();
assertThat(registry._getStalePreFilterIds()).isEmpty();
}
@@ -331,6 +334,8 @@ void multiplePreFilterIdsMapToSameId_removeByPreFilterId() {
Meter.Id preFilterId = new Meter.Id("counter", Tags.of("secret", "value2"), null, null, Meter.Type.COUNTER);
assertThat(registry.removeByPreFilterId(preFilterId)).isSameAs(c1).isSameAs(c2);
assertThat(registry.getMeters()).isEmpty();
+ assertThat(registry._getPreFilterIdToMeterMap()).isEmpty();
+ assertThat(registry._getMeterToPreFilterIdMap()).isEmpty();
}
@Test
diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/MultiGaugeTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/MultiGaugeTest.java
index d080c217d2..5ea202712a 100644
--- a/micrometer-core/src/test/java/io/micrometer/core/instrument/MultiGaugeTest.java
+++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/MultiGaugeTest.java
@@ -26,6 +26,7 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -93,6 +94,14 @@ void rowGaugesHoldStrongReferences() {
assertThat(registry.get("colors").tag("color", "red").gauge().value()).isEqualTo(1);
}
+ @Test
+ void rowGaugesCanTakeSubClassOfNumberSuppliers() {
+ final Supplier supplier = () -> 1L;
+ colorGauges.register(Collections.singletonList(Row.of(Tags.of("color", "red"), supplier)));
+
+ assertThat(registry.get("colors").tag("color", "red").gauge().value()).isEqualTo(1);
+ }
+
@Test
void overwrite() {
testOverwrite();
diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/cache/CaffeineStatsCounterTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/cache/CaffeineStatsCounterTest.java
index 358969bd7c..b10fb495a6 100644
--- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/cache/CaffeineStatsCounterTest.java
+++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/cache/CaffeineStatsCounterTest.java
@@ -94,7 +94,7 @@ void loadFailure() {
}
@ParameterizedTest
- @EnumSource(RemovalCause.class)
+ @EnumSource
void evictionWithCause(RemovalCause cause) {
stats.recordEviction(3, cause);
DistributionSummary summary = fetch("cache.evictions", "cause", cause.name()).summary();
diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/grpc/GrpcObservationTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/grpc/GrpcObservationTest.java
index d15752a19e..84fe509575 100644
--- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/grpc/GrpcObservationTest.java
+++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/grpc/GrpcObservationTest.java
@@ -65,6 +65,7 @@
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.ObservationTextPublisher;
+import io.micrometer.observation.tck.ObservationContextAssert;
import io.micrometer.observation.tck.TestObservationRegistry;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -169,9 +170,13 @@ void unaryRpc() {
assertThat(clientHandler.getEvents()).containsExactly(GrpcClientEvents.MESSAGE_SENT,
GrpcClientEvents.MESSAGE_RECEIVED);
// tag::assertion[]
- assertThat(observationRegistry)
- .hasAnObservation(observationContextAssert -> observationContextAssert.hasNameEqualTo("grpc.client"))
- .hasAnObservation(observationContextAssert -> observationContextAssert.hasNameEqualTo("grpc.server"));
+ assertThat(observationRegistry).hasAnObservation(observationContextAssert -> {
+ observationContextAssert.hasNameEqualTo("grpc.client");
+ assertCommonKeyValueNames(observationContextAssert);
+ }).hasAnObservation(observationContextAssert -> {
+ observationContextAssert.hasNameEqualTo("grpc.server");
+ assertCommonKeyValueNames(observationContextAssert);
+ });
// end::assertion[]
verifyHeaders();
}
@@ -204,9 +209,7 @@ public void onFailure(Throwable t) {
await().until(() -> futures.stream().allMatch(Future::isDone));
assertThat(responses).hasSize(count).containsExactlyInAnyOrderElementsOf(messages);
- assertThat(observationRegistry)
- .hasAnObservation(observationContextAssert -> observationContextAssert.hasNameEqualTo("grpc.client"))
- .hasAnObservation(observationContextAssert -> observationContextAssert.hasNameEqualTo("grpc.server"));
+ assertClientAndServerObservations();
verifyHeaders();
}
@@ -247,9 +250,7 @@ void clientStreamingRpc() {
verifyServerContext("grpc.testing.SimpleService", "ClientStreamingRpc",
"grpc.testing.SimpleService/ClientStreamingRpc", MethodType.CLIENT_STREAMING);
assertThat(serverHandler.getContext().getStatusCode()).isEqualTo(Code.OK);
- assertThat(observationRegistry)
- .hasAnObservation(observationContextAssert -> observationContextAssert.hasNameEqualTo("grpc.client"))
- .hasAnObservation(observationContextAssert -> observationContextAssert.hasNameEqualTo("grpc.server"));
+ assertClientAndServerObservations();
verifyHeaders();
}
@@ -282,9 +283,7 @@ void serverStreamingRpc() {
assertThat(clientHandler.getContext().getStatusCode()).isEqualTo(Code.OK);
assertThat(clientHandler.getEvents()).containsExactly(GrpcClientEvents.MESSAGE_SENT,
GrpcClientEvents.MESSAGE_RECEIVED, GrpcClientEvents.MESSAGE_RECEIVED);
- assertThat(observationRegistry)
- .hasAnObservation(observationContextAssert -> observationContextAssert.hasNameEqualTo("grpc.client"))
- .hasAnObservation(observationContextAssert -> observationContextAssert.hasNameEqualTo("grpc.server"));
+ assertClientAndServerObservations();
verifyHeaders();
}
@@ -335,9 +334,7 @@ void bidiStreamingRpc() {
assertThat(serverHandler.getContext().getStatusCode()).isEqualTo(Code.OK);
assertThat(clientHandler.getContext().getStatusCode()).isEqualTo(Code.OK);
- assertThat(observationRegistry)
- .hasAnObservation(observationContextAssert -> observationContextAssert.hasNameEqualTo("grpc.client"))
- .hasAnObservation(observationContextAssert -> observationContextAssert.hasNameEqualTo("grpc.server"));
+ assertClientAndServerObservations();
verifyHeaders();
}
@@ -374,6 +371,16 @@ private void verifyHeaders() {
}
+ private void assertClientAndServerObservations() {
+ assertThat(observationRegistry).hasAnObservation(observationContextAssert -> {
+ observationContextAssert.hasNameEqualTo("grpc.client");
+ assertCommonKeyValueNames(observationContextAssert);
+ }).hasAnObservation(observationContextAssert -> {
+ observationContextAssert.hasNameEqualTo("grpc.server");
+ assertCommonKeyValueNames(observationContextAssert);
+ });
+ }
+
@Nested
class WithExceptionService {
@@ -407,8 +414,7 @@ void unaryRpcFailure() {
assertThat(clientHandler.getContext().getStatusCode()).isEqualTo(Code.UNKNOWN);
assertThat(serverHandler.getEvents()).containsExactly(GrpcServerEvents.MESSAGE_RECEIVED);
assertThat(clientHandler.getEvents()).containsExactly(GrpcClientEvents.MESSAGE_SENT);
- assertThat(observationRegistry).hasAnObservation(
- observationContextAssert -> observationContextAssert.hasNameEqualTo("grpc.server").hasError());
+ assertServerErrorObservation();
}
@Test
@@ -430,8 +436,7 @@ void clientStreamingRpcFailure() {
assertThat(serverHandler.getContext().getStatusCode()).isNull();
assertThat(clientHandler.getEvents()).isEmpty();
assertThat(serverHandler.getEvents()).isEmpty();
- assertThat(observationRegistry).hasAnObservation(
- observationContextAssert -> observationContextAssert.hasNameEqualTo("grpc.server").hasError());
+ assertServerErrorObservation();
}
@Test
@@ -455,8 +460,7 @@ void serverStreamingRpcFailure() {
assertThat(serverHandler.getContext().getStatusCode()).isNull();
assertThat(clientHandler.getEvents()).containsExactly(GrpcClientEvents.MESSAGE_SENT);
assertThat(serverHandler.getEvents()).containsExactly(GrpcServerEvents.MESSAGE_RECEIVED);
- assertThat(observationRegistry).hasAnObservation(
- observationContextAssert -> observationContextAssert.hasNameEqualTo("grpc.server").hasError());
+ assertServerErrorObservation();
}
@Test
@@ -479,8 +483,14 @@ void bidiStreamingRpcFailure() {
assertThat(serverHandler.getContext().getStatusCode()).isNull();
assertThat(clientHandler.getEvents()).isEmpty();
assertThat(serverHandler.getEvents()).isEmpty();
- assertThat(observationRegistry).hasAnObservation(
- observationContextAssert -> observationContextAssert.hasNameEqualTo("grpc.server").hasError());
+ assertServerErrorObservation();
+ }
+
+ private void assertServerErrorObservation() {
+ assertThat(observationRegistry).hasAnObservation(observationContextAssert -> {
+ observationContextAssert.hasNameEqualTo("grpc.server").hasError();
+ assertCommonKeyValueNames(observationContextAssert);
+ });
}
private StreamObserver createResponseObserver(AtomicBoolean errored) {
@@ -605,6 +615,8 @@ void verifyServerContext(String serviceName, String methodName, String contextua
assertThat(serverContext.getFullMethodName()).isEqualTo(contextualName);
assertThat(serverContext.getMethodType()).isEqualTo(methodType);
assertThat(serverContext.getAuthority()).isEqualTo("localhost");
+ assertThat(serverContext.getPeerName()).isEqualTo("localhost");
+ assertThat(serverContext.getPeerPort()).isEqualTo(-1);
});
}
@@ -617,9 +629,21 @@ void verifyClientContext(String serviceName, String methodName, String contextua
assertThat(clientContext.getFullMethodName()).isEqualTo(contextualName);
assertThat(clientContext.getMethodType()).isEqualTo(methodType);
assertThat(clientContext.getAuthority()).isEqualTo("localhost");
+ assertThat(clientContext.getPeerName()).isEqualTo("localhost");
+ assertThat(clientContext.getPeerPort()).isEqualTo(-1);
});
}
+ void assertCommonKeyValueNames(ObservationContextAssert> observationContextAssert) {
+ observationContextAssert
+ .hasLowCardinalityKeyValueWithKey(GrpcObservationDocumentation.LowCardinalityKeyNames.METHOD.asString())
+ .hasLowCardinalityKeyValueWithKey(
+ GrpcObservationDocumentation.LowCardinalityKeyNames.METHOD_TYPE.asString())
+ .hasLowCardinalityKeyValueWithKey(GrpcObservationDocumentation.LowCardinalityKeyNames.SERVICE.asString())
+ .hasLowCardinalityKeyValueWithKey(
+ GrpcObservationDocumentation.LowCardinalityKeyNames.STATUS_CODE.asString());
+ }
+
// GRPC service extending SimpleService and provides echo implementation.
static class EchoService extends SimpleServiceImplBase {
diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/DefaultApacheHttpClientObservationConventionTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/DefaultApacheHttpClientObservationConventionTest.java
index 465f1510d2..e465f778b0 100644
--- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/DefaultApacheHttpClientObservationConventionTest.java
+++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/DefaultApacheHttpClientObservationConventionTest.java
@@ -150,8 +150,9 @@ void shouldContributeTargetWhenUnknown() {
SimpleHttpRequest request = SimpleRequestBuilder.get("https://example.org/resource").build();
HttpClientContext clientContext = HttpClientContext.create();
ApacheHttpClientContext context = new ApacheHttpClientContext(request, clientContext);
- assertThat(observationConvention.getLowCardinalityKeyValues(context)).contains(TARGET_HOST.withValue("UNKNOWN"),
- TARGET_PORT.withValue("UNKNOWN"), TARGET_SCHEME.withValue("UNKNOWN"));
+ assertThat(observationConvention.getLowCardinalityKeyValues(context)).contains(
+ TARGET_HOST.withValue("example.org"), TARGET_PORT.withValue("UNKNOWN"),
+ TARGET_SCHEME.withValue("https"));
}
@Test
diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpRequestExecutorTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpRequestExecutorTest.java
index 3a1683def6..eb8151bd8c 100644
--- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpRequestExecutorTest.java
+++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/MicrometerHttpRequestExecutorTest.java
@@ -37,7 +37,6 @@
import org.apache.hc.core5.http.io.HttpClientResponseHandler;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.util.Timeout;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
@@ -182,12 +181,12 @@ void routeTaggedIfEnabled(boolean configureObservationRegistry, @WiremockResolve
}
@Test
- @Disabled("brittle test using reflection to check internals of third-party code")
void waitForContinueGetsPassedToSuper() {
MicrometerHttpRequestExecutor requestExecutor = MicrometerHttpRequestExecutor.builder(registry)
.waitForContinue(Timeout.ofMilliseconds(1000))
.build();
- assertThat(requestExecutor).hasFieldOrPropertyWithValue("waitForContinue", Timeout.ofMilliseconds(1000));
+ assertThat(requestExecutor).extracting("http1Config.waitForContinueTimeout")
+ .isEqualTo(Timeout.ofMilliseconds(1000));
}
@ParameterizedTest
diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/ObservationExecChainHandlerIntegrationTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/ObservationExecChainHandlerIntegrationTest.java
index 9bad5e85ac..ad78a173bc 100644
--- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/ObservationExecChainHandlerIntegrationTest.java
+++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/hc5/ObservationExecChainHandlerIntegrationTest.java
@@ -17,6 +17,7 @@
import com.github.tomakehurst.wiremock.WireMockServer;
import io.micrometer.observation.tck.TestObservationRegistry;
+import org.apache.hc.client5.http.HttpHostConnectException;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.classic.methods.HttpGet;
@@ -55,8 +56,7 @@
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
import static io.micrometer.core.instrument.binder.httpcomponents.hc5.ApacheHttpClientObservationDocumentation.ApacheHttpClientKeyNames.*;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.Assertions.*;
/**
* Wiremock-based integration tests for {@link ObservationExecChainHandler}.
@@ -237,6 +237,18 @@ void recordAggregateRetriesWithSuccess(@WiremockResolver.Wiremock WireMockServer
.doesNotHaveAnyRemainingCurrentObservation();
}
+ @Test
+ void targetHostPortAndSchemeShouldBeProvidedEvenWhenHttpHostConnectExceptionIsThrown() throws IOException {
+ try (CloseableHttpClient client = classicClient()) {
+ assertThatExceptionOfType(HttpHostConnectException.class)
+ .isThrownBy(() -> executeClassic(client, new HttpGet("http://localhost:777/123")));
+ }
+ assertThat(observationRegistry).hasAnObservationWithAKeyValue(TARGET_HOST.withValue("localhost"))
+ .hasAnObservationWithAKeyValue(TARGET_PORT.withValue("777"))
+ .hasAnObservationWithAKeyValue(TARGET_SCHEME.withValue("http"))
+ .hasNumberOfObservationsWithNameEqualTo(DEFAULT_METER_NAME, 1);
+ }
+
}
@Nested
@@ -443,14 +455,12 @@ private CloseableHttpClient classicClient_aggregateRetries() {
.setConnectTimeout(2000L, TimeUnit.MILLISECONDS)
.build();
- // tag::setup_classic_aggregate_retries[]
HttpClientBuilder clientBuilder = HttpClients.custom()
.setRetryStrategy(retryStrategy)
.addExecInterceptorFirst("micrometer", new ObservationExecChainHandler(observationRegistry))
.setConnectionManager(PoolingHttpClientConnectionManagerBuilder.create()
.setDefaultConnectionConfig(connectionConfig)
.build());
- // end::setup_classic_aggregate_retries[]
return clientBuilder.build();
}
diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/hystrix/MicrometerMetricsPublisherCommandTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/hystrix/MicrometerMetricsPublisherCommandTest.java
index 5d7d579bb8..b6ce283813 100644
--- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/hystrix/MicrometerMetricsPublisherCommandTest.java
+++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/hystrix/MicrometerMetricsPublisherCommandTest.java
@@ -26,6 +26,8 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import java.util.Locale;
+
import static org.assertj.core.api.Assertions.assertThat;
class MicrometerMetricsPublisherCommandTest {
@@ -86,7 +88,7 @@ void cumulativeCounters() throws Exception {
}
private void assertExecutionMetric(Iterable tags, HystrixEventType eventType, double count) {
- Iterable myTags = Tags.concat(tags, "event", eventType.name().toLowerCase(), "terminal",
+ Iterable myTags = Tags.concat(tags, "event", eventType.name().toLowerCase(Locale.ROOT), "terminal",
Boolean.toString(eventType.isTerminal()));
assertThat(registry.get("hystrix.execution").tags(myTags).counter().count()).isEqualTo(count);
}
diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/jvm/ExecutorServiceMetricsTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/jvm/ExecutorServiceMetricsTest.java
index c8af1a4d8b..03ecb3b831 100644
--- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/jvm/ExecutorServiceMetricsTest.java
+++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/jvm/ExecutorServiceMetricsTest.java
@@ -31,6 +31,7 @@
import org.junit.jupiter.params.provider.CsvSource;
import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicBoolean;
import static org.assertj.core.api.AssertionsForClassTypes.*;
import static org.awaitility.Awaitility.await;
@@ -302,6 +303,32 @@ void monitorScheduledExecutorServiceWithRepetitiveTasks(String metricPrefix, Str
assertThat(registry.get(expectedMetricPrefix + "executor.idle").tags(userTags).timer().count()).isEqualTo(0L);
}
+ @Test
+ @Issue("#5650")
+ void queuedSubmissionsAreIncludedInExecutorQueuedMetric() {
+ ForkJoinPool pool = new ForkJoinPool(1, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, false, 1, 1, 1,
+ a -> true, 555, TimeUnit.MILLISECONDS);
+ ExecutorServiceMetrics.monitor(registry, pool, "myForkJoinPool");
+ AtomicBoolean busy = new AtomicBoolean(true);
+
+ // will be an active task
+ pool.execute(() -> {
+ while (busy.get()) {
+ }
+ });
+
+ // will be queued for submission
+ pool.execute(() -> {
+ });
+ pool.execute(() -> {
+ });
+
+ double queued = registry.get("executor.queued").tag("name", "myForkJoinPool").gauge().value();
+ busy.set(false);
+
+ assertThat(queued).isEqualTo(2.0);
+ }
+
@SuppressWarnings("unchecked")
private T monitorExecutorService(String executorName, String metricPrefix, T exec) {
if (metricPrefix == null) {
diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/jvm/JvmGcMetricsTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/jvm/JvmGcMetricsTest.java
index 26547c4622..f3b3f06bf2 100644
--- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/jvm/JvmGcMetricsTest.java
+++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/jvm/JvmGcMetricsTest.java
@@ -107,17 +107,17 @@ void gcTimingIsCorrectForPauseCycleCollectors() {
// get initial GC timing metrics from JMX, if any
// GC could have happened before this test due to testing infrastructure
// If it did, it will not be captured in the metrics
- long initialPausePhaseCount = 0;
+ long initialPauseCount = 0;
long initialPauseTimeMs = 0;
- long initialConcurrentPhaseCount = 0;
+ long initialConcurrentCount = 0;
long initialConcurrentTimeMs = 0;
for (GarbageCollectorMXBean mbean : ManagementFactory.getGarbageCollectorMXBeans()) {
if (mbean.getName().contains("Pauses")) {
- initialPausePhaseCount += mbean.getCollectionCount();
+ initialPauseCount += mbean.getCollectionCount();
initialPauseTimeMs += mbean.getCollectionTime();
}
else if (mbean.getName().contains("Cycles")) {
- initialConcurrentPhaseCount += mbean.getCollectionCount();
+ initialConcurrentCount += mbean.getCollectionCount();
initialConcurrentTimeMs += mbean.getCollectionTime();
}
}
@@ -127,33 +127,11 @@ else if (mbean.getName().contains("Cycles")) {
// cause GC to record new metrics
System.gc();
- // get metrics from JMX again to obtain difference
- long pausePhaseCount = 0;
- long pauseTimeMs = 0;
- long concurrentPhaseCount = 0;
- long concurrentTimeMs = 0;
- for (GarbageCollectorMXBean mbean : ManagementFactory.getGarbageCollectorMXBeans()) {
- if (mbean.getName().contains("Pauses")) {
- pausePhaseCount += mbean.getCollectionCount();
- pauseTimeMs += mbean.getCollectionTime();
- }
- else if (mbean.getName().contains("Cycles")) {
- concurrentPhaseCount += mbean.getCollectionCount();
- concurrentTimeMs += mbean.getCollectionTime();
- }
- }
-
- // subtract any difference
- pausePhaseCount -= initialPausePhaseCount;
- pauseTimeMs -= initialPauseTimeMs;
- concurrentPhaseCount -= initialConcurrentPhaseCount;
- concurrentTimeMs -= initialConcurrentTimeMs;
-
- checkPhaseCount(pausePhaseCount, concurrentPhaseCount);
- checkCollectionTime(pauseTimeMs, concurrentTimeMs);
+ checkPhaseCountAndCollectionTime(initialPauseCount, initialConcurrentCount, initialPauseTimeMs,
+ initialConcurrentTimeMs);
}
- boolean isPauseCyclesGc() {
+ static boolean isPauseCyclesGc() {
return ManagementFactory.getGarbageCollectorMXBeans()
.stream()
.map(MemoryManagerMXBean::getName)
@@ -225,8 +203,31 @@ public void handleNotification(Notification notification, Object handback) {
}
- private void checkPhaseCount(long expectedPauseCount, long expectedConcurrentCount) {
+ private void checkPhaseCountAndCollectionTime(long initialPauseCount, long initialConcurrentCount,
+ long initialPauseTimeMs, long initialConcurrentTimeMs) {
await().atMost(200, TimeUnit.MILLISECONDS).untilAsserted(() -> {
+ long pauseCount = 0;
+ long concurrentCount = 0;
+ long pauseTimeMs = 0;
+ long concurrentTimeMs = 0;
+
+ // get metrics from JMX again to obtain the difference
+ for (GarbageCollectorMXBean mbean : ManagementFactory.getGarbageCollectorMXBeans()) {
+ if (mbean.getName().contains("Pauses")) {
+ pauseCount += mbean.getCollectionCount();
+ pauseTimeMs += mbean.getCollectionTime();
+ }
+ else if (mbean.getName().contains("Cycles")) {
+ concurrentCount += mbean.getCollectionCount();
+ concurrentTimeMs += mbean.getCollectionTime();
+ }
+ }
+
+ long expectedPauseCount = pauseCount - initialPauseCount;
+ long expectedConcurrentCount = concurrentCount - initialConcurrentCount;
+ long expectedPauseTimeMs = pauseTimeMs - initialPauseTimeMs;
+ long expectedConcurrentTimeMs = concurrentTimeMs - initialConcurrentTimeMs;
+
long observedPauseCount = registry.find("jvm.gc.pause").timers().stream().mapToLong(Timer::count).sum();
long observedConcurrentCount = registry.find("jvm.gc.concurrent.phase.time")
.timers()
@@ -235,11 +236,7 @@ private void checkPhaseCount(long expectedPauseCount, long expectedConcurrentCou
.sum();
assertThat(observedPauseCount).isEqualTo(expectedPauseCount);
assertThat(observedConcurrentCount).isEqualTo(expectedConcurrentCount);
- });
- }
- private void checkCollectionTime(long expectedPauseTimeMs, long expectedConcurrentTimeMs) {
- await().atMost(200, TimeUnit.MILLISECONDS).untilAsserted(() -> {
double observedPauseTimeMs = registry.find("jvm.gc.pause")
.timers()
.stream()
diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/kafka/KafkaClientMetricsAdminTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/kafka/KafkaClientMetricsAdminTest.java
index e060af2ffe..4dae4edf95 100644
--- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/kafka/KafkaClientMetricsAdminTest.java
+++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/kafka/KafkaClientMetricsAdminTest.java
@@ -23,6 +23,8 @@
import org.junit.jupiter.api.Test;
import java.util.Properties;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
import static io.micrometer.core.instrument.binder.kafka.KafkaClientMetrics.METRIC_NAME_PREFIX;
import static org.apache.kafka.clients.admin.AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG;
@@ -32,7 +34,7 @@ class KafkaClientMetricsAdminTest {
private static final String BOOTSTRAP_SERVERS = "localhost:9092";
- private Tags tags = Tags.of("app", "myapp", "version", "1");
+ private final Tags tags = Tags.of("app", "myapp", "version", "1");
KafkaClientMetrics metrics;
@@ -69,6 +71,27 @@ void shouldCreateMetersWithTags() {
}
}
+ @Test
+ void shouldCreateMetersWithTagsAndCustomScheduler() {
+ try (AdminClient adminClient = createAdmin()) {
+ ScheduledExecutorService customScheduler = Executors.newScheduledThreadPool(1);
+ metrics = new KafkaClientMetrics(adminClient, tags, customScheduler);
+ MeterRegistry registry = new SimpleMeterRegistry();
+
+ metrics.bindTo(registry);
+
+ assertThat(registry.getMeters()).hasSizeGreaterThan(0)
+ .extracting(meter -> meter.getId().getTag("app"))
+ .allMatch(s -> s.equals("myapp"));
+
+ metrics.close();
+ assertThat(customScheduler.isShutdown()).isFalse();
+
+ customScheduler.shutdownNow();
+ assertThat(customScheduler.isShutdown()).isTrue();
+ }
+ }
+
private AdminClient createAdmin() {
Properties adminConfig = new Properties();
adminConfig.put(BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAP_SERVERS);
diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/kafka/KafkaClientMetricsConsumerTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/kafka/KafkaClientMetricsConsumerTest.java
index 7908f318d2..eb783f143c 100644
--- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/kafka/KafkaClientMetricsConsumerTest.java
+++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/kafka/KafkaClientMetricsConsumerTest.java
@@ -25,6 +25,8 @@
import org.junit.jupiter.api.Test;
import java.util.Properties;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
import static io.micrometer.core.instrument.binder.kafka.KafkaClientMetrics.METRIC_NAME_PREFIX;
import static org.apache.kafka.clients.consumer.ConsumerConfig.*;
@@ -34,7 +36,7 @@ class KafkaClientMetricsConsumerTest {
private static final String BOOTSTRAP_SERVERS = "localhost:9092";
- private Tags tags = Tags.of("app", "myapp", "version", "1");
+ private final Tags tags = Tags.of("app", "myapp", "version", "1");
KafkaClientMetrics metrics;
@@ -71,6 +73,27 @@ void shouldCreateMetersWithTags() {
}
}
+ @Test
+ void shouldCreateMetersWithTagsAndCustomScheduler() {
+ try (Consumer consumer = createConsumer()) {
+ ScheduledExecutorService customScheduler = Executors.newScheduledThreadPool(1);
+ metrics = new KafkaClientMetrics(consumer, tags, customScheduler);
+ MeterRegistry registry = new SimpleMeterRegistry();
+
+ metrics.bindTo(registry);
+
+ assertThat(registry.getMeters()).hasSizeGreaterThan(0)
+ .extracting(meter -> meter.getId().getTag("app"))
+ .allMatch(s -> s.equals("myapp"));
+
+ metrics.close();
+ assertThat(customScheduler.isShutdown()).isFalse();
+
+ customScheduler.shutdownNow();
+ assertThat(customScheduler.isShutdown()).isTrue();
+ }
+ }
+
private Consumer createConsumer() {
Properties consumerConfig = new Properties();
consumerConfig.put(BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAP_SERVERS);
diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/kafka/KafkaClientMetricsProducerTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/kafka/KafkaClientMetricsProducerTest.java
index 7d8131ff52..3d0d94ec02 100644
--- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/kafka/KafkaClientMetricsProducerTest.java
+++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/kafka/KafkaClientMetricsProducerTest.java
@@ -25,6 +25,8 @@
import org.junit.jupiter.api.Test;
import java.util.Properties;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
import static io.micrometer.core.instrument.binder.kafka.KafkaClientMetrics.METRIC_NAME_PREFIX;
import static org.apache.kafka.clients.producer.ProducerConfig.*;
@@ -34,7 +36,7 @@ class KafkaClientMetricsProducerTest {
private static final String BOOTSTRAP_SERVERS = "localhost:9092";
- private Tags tags = Tags.of("app", "myapp", "version", "1");
+ private final Tags tags = Tags.of("app", "myapp", "version", "1");
KafkaClientMetrics metrics;
@@ -71,6 +73,27 @@ void shouldCreateMetersWithTags() {
}
}
+ @Test
+ void shouldCreateMetersWithTagsAndCustomScheduler() {
+ try (Producer producer = createProducer()) {
+ ScheduledExecutorService customScheduler = Executors.newScheduledThreadPool(1);
+ metrics = new KafkaClientMetrics(producer, tags, customScheduler);
+ MeterRegistry registry = new SimpleMeterRegistry();
+
+ metrics.bindTo(registry);
+
+ assertThat(registry.getMeters()).hasSizeGreaterThan(0)
+ .extracting(meter -> meter.getId().getTag("app"))
+ .allMatch(s -> s.equals("myapp"));
+
+ metrics.close();
+ assertThat(customScheduler.isShutdown()).isFalse();
+
+ customScheduler.shutdownNow();
+ assertThat(customScheduler.isShutdown()).isTrue();
+ }
+ }
+
private Producer createProducer() {
Properties producerConfig = new Properties();
producerConfig.put(BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAP_SERVERS);
diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/kafka/KafkaMetricsTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/kafka/KafkaMetricsTest.java
index 452c5254f8..ff44ffd1a8 100644
--- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/kafka/KafkaMetricsTest.java
+++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/kafka/KafkaMetricsTest.java
@@ -34,10 +34,13 @@
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
class KafkaMetricsTest {
@@ -68,7 +71,7 @@ void shouldKeepMetersWhenMetricsDoNotChange() {
}
@Test
- void closeShouldRemoveAllMeters() {
+ void closeShouldRemoveAllMetersAndShutdownDefaultScheduler() {
// Given
Supplier