Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Polish micrometer-registry-health module #2441

Merged
merged 1 commit into from Feb 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -21,6 +21,12 @@

import static io.micrometer.core.instrument.config.validate.PropertyValidator.getDuration;

/**
* {@link MeterRegistryConfig} for {@link HealthMeterRegistry}.
*
* @author Jon Schneider
* @since 1.6.0
*/
public interface HealthConfig extends MeterRegistryConfig {
HealthConfig DEFAULT = key -> null;

Expand Down
Expand Up @@ -17,24 +17,23 @@

import java.util.function.BinaryOperator;

/**
* Utilities for queries.
*
* @author Jon Schneider
*/
class QueryUtils {
public static final BinaryOperator<Double> SUM_OR_NAN = (v1, v2) -> {
static final BinaryOperator<Double> SUM_OR_NAN = (v1, v2) -> {
if (Double.isNaN(v1)) {
if (Double.isNaN(v2)) {
return Double.NaN;
}
return v2;
} else if (Double.isNaN(v2)) {
return v1;
}
return v1 + v2;
};

public static final BinaryOperator<Double> MAX_OR_NAN = (v1, v2) -> {
static final BinaryOperator<Double> MAX_OR_NAN = (v1, v2) -> {
if (Double.isNaN(v1)) {
if (Double.isNaN(v2)) {
return Double.NaN;
}
return v2;
} else if (Double.isNaN(v2)) {
return v1;
Expand Down
Expand Up @@ -20,6 +20,7 @@
import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.core.instrument.distribution.HistogramSupport;
import io.micrometer.core.instrument.distribution.ValueAtPercentile;
import io.micrometer.core.instrument.search.Search;
import io.micrometer.core.lang.Nullable;
Expand Down Expand Up @@ -64,11 +65,14 @@ public abstract class ServiceLevelObjective {
@Nullable
private final String failedMessage;

private final Meter.Id id;

protected ServiceLevelObjective(String name, Tags tags, @Nullable String baseUnit, @Nullable String failedMessage) {
this.name = name;
this.tags = tags;
this.baseUnit = baseUnit;
this.failedMessage = failedMessage;
this.id = new Meter.Id(name, tags, baseUnit, failedMessage, Meter.Type.GAUGE);
}

public String getName() {
Expand All @@ -85,7 +89,7 @@ public String getBaseUnit() {
}

public Meter.Id getId() {
return new Meter.Id(name, tags, baseUnit, failedMessage, Meter.Type.GAUGE);
return id;
}

@Nullable
Expand Down Expand Up @@ -125,7 +129,7 @@ protected SingleIndicator(NumericQuery query, String testDescription, Predicate<

@Override
public boolean healthy(MeterRegistry registry) {
Double v = query.getValue(registry);
Double v = getValue(registry);
return v.isNaN() || test.test(v);
}

Expand Down Expand Up @@ -181,8 +185,7 @@ public static class Builder {
private final Collection<MeterBinder> requires;

Builder(String name) {
this.name = name;
this.requires = new ArrayList<>();
this(name, null, new ArrayList<>());
}

Builder(String name, @Nullable String failedMessage, Collection<MeterBinder> requires) {
Expand Down Expand Up @@ -215,7 +218,7 @@ public final Builder tags(String... tags) {
}

/**
* @param tags Tags to add to the eventual timer.
* @param tags Tags to add to the single indicator.
* @return The builder with added tags.
*/
public final Builder tags(Iterable<Tag> tags) {
Expand All @@ -226,7 +229,7 @@ public final Builder tags(Iterable<Tag> tags) {
/**
* @param key The tag key.
* @param value The tag value.
* @return The timer builder with a single added tag.
* @return The single indicator builder with a single added tag.
*/
public final Builder tag(String key, String value) {
this.tags = tags.and(key, value);
Expand Down Expand Up @@ -278,15 +281,11 @@ public final NumericQuery total(Function<Search, Search> search) {
public final NumericQuery maxPercentile(Function<Search, Search> search, double percentile) {
return new Instant(name, tags, baseUnit, failedMessage, requires, search, s -> s.meters().stream()
.map(m -> {
ValueAtPercentile[] valueAtPercentiles = new ValueAtPercentile[0];
if (m instanceof DistributionSummary) {
valueAtPercentiles = ((DistributionSummary) m).takeSnapshot().percentileValues();
} else if (m instanceof Timer) {
valueAtPercentiles = ((Timer) m).takeSnapshot().percentileValues();
} else if (m instanceof LongTaskTimer) {
valueAtPercentiles = ((LongTaskTimer) m).takeSnapshot().percentileValues();
if (!(m instanceof HistogramSupport)) {
return Double.NaN;
}

ValueAtPercentile[] valueAtPercentiles = ((HistogramSupport) m).takeSnapshot().percentileValues();
return Arrays.stream(valueAtPercentiles)
.filter(vap -> vap.percentile() == percentile)
.map(ValueAtPercentile::value)
Expand Down Expand Up @@ -653,7 +652,7 @@ public Builder tags(String... tags) {
}

/**
* @param tags Tags to add to the eventual timer.
* @param tags Tags to add to the multiple indicator.
* @return The builder with added tags.
*/
public Builder tags(Iterable<Tag> tags) {
Expand Down
Expand Up @@ -23,6 +23,8 @@
import java.time.Duration;

/**
* {@link ServiceLevelObjective ServiceLevelObjectives} for Java Virtual Machine.
*
* @author Jon Schneider
* @since 1.6.0
*/
Expand All @@ -31,7 +33,7 @@ public class JvmServiceLevelObjectives {
* A series of high-level heap monitors originally defined in
* <a href="https://www.jetbrains.com/help/teamcity/teamcity-memory-monitor.html">Team City's memory monitor</a>.
*/
public static final ServiceLevelObjective[] MEMORY = new ServiceLevelObjective[]{
public static final ServiceLevelObjective[] MEMORY = new ServiceLevelObjective[] {
ServiceLevelObjective
.build("jvm.pool.memory")
.failedMessage("Memory usage in a single memory pool exceeds 90% after garbage collection.")
Expand All @@ -44,6 +46,7 @@ public class JvmServiceLevelObjectives {
.build("jvm.gc.load")
.failedMessage("Memory cleaning is taking more than 50% of CPU resources on average. " +
"This usually means really serious problems with memory resulting in high performance degradation.")
.requires(new JvmHeapPressureMetrics())
.baseUnit("percent CPU time spent")
.value(s -> s.name("jvm.gc.overhead"))
.isLessThan(0.5),
Expand Down Expand Up @@ -72,7 +75,7 @@ public class JvmServiceLevelObjectives {
.and()
};

public static final ServiceLevelObjective[] ALLOCATIONS = new ServiceLevelObjective[]{
public static final ServiceLevelObjective[] ALLOCATIONS = new ServiceLevelObjective[] {
ServiceLevelObjective
.build("jvm.allocations.g1.humongous")
.failedMessage("A single object was allocated that exceeded 50% of the total size of the eden space.")
Expand Down
Expand Up @@ -19,11 +19,13 @@
import io.micrometer.health.ServiceLevelObjective;

/**
* {@link ServiceLevelObjective ServiceLevelObjectives} for Operating System.
*
* @author Jon Schneider
* @since 1.6.0
*/
public class OperatingSystemServiceLevelObjectives {
public static final ServiceLevelObjective[] DISK = new ServiceLevelObjective[]{
public static final ServiceLevelObjective[] DISK = new ServiceLevelObjective[] {
ServiceLevelObjective.build("os.file.descriptors")
.failedMessage("Too many file descriptors are open. When the max is reached, " +
"further attempts to retrieve a file descriptor will block indefinitely.")
Expand Down
Expand Up @@ -13,6 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* SLO-based health meter registry.
*/
@NonNullApi
@NonNullFields
package io.micrometer.health;
Expand Down
Expand Up @@ -29,6 +29,11 @@
import static io.micrometer.core.instrument.MockClock.clock;
import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests for {@link HealthMeterRegistry}.
*
* @author Jon Schneider
*/
class HealthMeterRegistryTest {
@Test
void healthFromServiceLevelObjective() {
Expand Down Expand Up @@ -123,8 +128,7 @@ void applyRequiredBinders() {
.build();

assertThat(registry.getMeters().stream().map(m -> m.getId().getName()))
.containsOnly("jvm.memory.used")
.isNotEmpty();
.containsOnly("jvm.memory.used");
}

@Test
Expand All @@ -143,7 +147,6 @@ public Meter.Id map(Meter.Id id) {

assertThat(registry.getServiceLevelObjectives().stream().map(ServiceLevelObjective::getName))
.contains("jvm.collection.load")
.doesNotContain("jvm.pool.memory")
.isNotEmpty();
.doesNotContain("jvm.pool.memory");
}
}
Expand Up @@ -156,15 +156,15 @@ void isLessThan() {
.count(s -> s.name("my.timer"))
.isLessThan(4)
.healthy(registry)
).isEqualTo(true);
).isTrue();

assertThat(
ServiceLevelObjective
.build("sum")
.total(s -> s.name("my.timer"))
.isLessThan(Duration.ofSeconds(6))
.healthy(registry)
).isEqualTo(true);
).isTrue();
}

@Test
Expand All @@ -175,14 +175,14 @@ void isLessThanOrEqualTo() {
.count(s -> s.name("my.timer"))
.isLessThanOrEqualTo(3)
.healthy(registry)
).isEqualTo(true);
).isTrue();

assertThat(ServiceLevelObjective
.build("sum")
.total(s -> s.name("my.timer"))
.isLessThanOrEqualTo(Duration.ofSeconds(5))
.healthy(registry)
).isEqualTo(true);
).isTrue();
}

@Test
Expand All @@ -193,15 +193,15 @@ void isGreaterThan() {
.count(s -> s.name("my.timer"))
.isGreaterThan(2)
.healthy(registry)
).isEqualTo(true);
).isTrue();

assertThat(
ServiceLevelObjective
.build("sum")
.total(s -> s.name("my.timer"))
.isGreaterThan(Duration.ofSeconds(4))
.healthy(registry)
).isEqualTo(true);
).isTrue();
}

@Test
Expand All @@ -212,15 +212,15 @@ void isGreaterThanOrEqualTo() {
.count(s -> s.name("my.timer"))
.isGreaterThanOrEqualTo(3)
.healthy(registry)
).isEqualTo(true);
).isTrue();

assertThat(
ServiceLevelObjective
.build("sum")
.total(s -> s.name("my.timer"))
.isGreaterThanOrEqualTo(Duration.ofSeconds(5))
.healthy(registry)
).isEqualTo(true);
).isTrue();
}

@Test
Expand All @@ -231,14 +231,14 @@ void isEqualTo() {
.count(s -> s.name("my.timer"))
.isEqualTo(3)
.healthy(registry)
).isEqualTo(true);
).isTrue();

assertThat(
ServiceLevelObjective
.build("sum")
.total(s -> s.name("my.timer"))
.isEqualTo(Duration.ofSeconds(5))
.healthy(registry)
).isEqualTo(true);
).isTrue();
}
}