From 1a4812d722d7c4f065b71864d2570cb2929a3a4a Mon Sep 17 00:00:00 2001 From: aietcn Date: Wed, 18 Oct 2017 17:01:51 -0500 Subject: [PATCH] BAEL-1064 (#2748) * BAEL-1064 * add spring-milestones repo to pom * more endpoints for spring integration metrics --- metrics/pom.xml | 58 +++- .../metrics/micrometer/MicrometerApp.java | 20 ++ .../metrics/micrometer/PeopleController.java | 54 +++ .../micrometer/MicrometerAtlasTest.java | 308 ++++++++++++++++++ 4 files changed, 438 insertions(+), 2 deletions(-) create mode 100644 metrics/src/main/java/com/baeldung/metrics/micrometer/MicrometerApp.java create mode 100644 metrics/src/main/java/com/baeldung/metrics/micrometer/PeopleController.java create mode 100644 metrics/src/test/java/com/baeldung/metrics/micrometer/MicrometerAtlasTest.java diff --git a/metrics/pom.xml b/metrics/pom.xml index 574c1ac132..926b6a95c5 100644 --- a/metrics/pom.xml +++ b/metrics/pom.xml @@ -16,6 +16,8 @@ 3.1.2 3.1.0 0.12.17 + 1.0.0-rc.2 + 2.0.0.M5 @@ -57,12 +59,64 @@ ${netflix.servo.ver} test + + + io.micrometer + micrometer-registry-atlas + ${micrometer.ver} + + + + io.micrometer + micrometer-spring-legacy + ${micrometer.ver} + + + + org.springframework.boot + spring-boot-starter-web + ${spring.boot.ver} + + + + com.fasterxml.jackson.core + jackson-databind + 2.9.1 + com.fasterxml.jackson.dataformat jackson-dataformat-smile - 2.8.9 - test + 2.9.1 + + + + com.netflix.spectator + spectator-api + 0.57.1 + + + + org.springframework.boot + spring-boot-dependencies + pom + import + ${spring.boot.ver} + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/libs-milestone + + false + + + + diff --git a/metrics/src/main/java/com/baeldung/metrics/micrometer/MicrometerApp.java b/metrics/src/main/java/com/baeldung/metrics/micrometer/MicrometerApp.java new file mode 100644 index 0000000000..cf818f6600 --- /dev/null +++ b/metrics/src/main/java/com/baeldung/metrics/micrometer/MicrometerApp.java @@ -0,0 +1,20 @@ +package com.baeldung.metrics.micrometer; + +import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +public class MicrometerApp { + + @Bean + JvmThreadMetrics threadMetrics() { + return new JvmThreadMetrics(); + } + + public static void main(String[] args) throws Exception { + SpringApplication.run(MicrometerApp.class, args); + } + +} diff --git a/metrics/src/main/java/com/baeldung/metrics/micrometer/PeopleController.java b/metrics/src/main/java/com/baeldung/metrics/micrometer/PeopleController.java new file mode 100644 index 0000000000..789e975a7f --- /dev/null +++ b/metrics/src/main/java/com/baeldung/metrics/micrometer/PeopleController.java @@ -0,0 +1,54 @@ +package com.baeldung.metrics.micrometer; + +import io.micrometer.core.annotation.Timed; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * @author aiet + */ +@RestController +@Timed("people") +public class PeopleController { + + private static final SecureRandom SECURE_RANDOM = new SecureRandom(); + + @GetMapping("/people") + @Timed(value = "people.all", longTask = true) + public List listPeople() throws InterruptedException { + int seconds2Sleep = SECURE_RANDOM.nextInt(500); + System.out.println(seconds2Sleep); + TimeUnit.MILLISECONDS.sleep(seconds2Sleep); + return Arrays.asList("Jim", "Tom", "Tim"); + } + + @PostMapping("/people") + @Timed(value = "people.update", longTask = true) + public List putPeople() throws InterruptedException { + int seconds2Sleep = SECURE_RANDOM.nextInt(1000); + System.out.println(seconds2Sleep); + TimeUnit.MILLISECONDS.sleep(seconds2Sleep); + return Arrays.asList("Jim", "Tom", "Tim"); + } + + @GetMapping("/asset") + @Timed(value = "people.asset", longTask = true) + public void test() throws Exception { + throw new Exception("error!"); + } + + @GetMapping("/property") + @Timed(value = "people.property", longTask = true) + public void property(HttpServletResponse response) throws IOException { + response.sendRedirect("/asset"); + } + +} diff --git a/metrics/src/test/java/com/baeldung/metrics/micrometer/MicrometerAtlasTest.java b/metrics/src/test/java/com/baeldung/metrics/micrometer/MicrometerAtlasTest.java new file mode 100644 index 0000000000..b2eb0ee7dc --- /dev/null +++ b/metrics/src/test/java/com/baeldung/metrics/micrometer/MicrometerAtlasTest.java @@ -0,0 +1,308 @@ +package com.baeldung.metrics.micrometer; + +import com.netflix.spectator.atlas.AtlasConfig; +import io.micrometer.atlas.AtlasMeterRegistry; +import io.micrometer.core.instrument.*; +import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.composite.CompositeMeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import io.micrometer.core.instrument.stats.hist.Histogram; +import io.micrometer.core.instrument.stats.quantile.WindowSketchQuantiles; +import org.junit.Before; +import org.junit.Test; + +import java.time.Duration; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.collection.IsMapContaining.hasEntry; +import static org.hamcrest.core.IsCollectionContaining.hasItems; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author aiet + */ +public class MicrometerAtlasTest { + + AtlasConfig atlasConfig; + + @Before + public void init() { + atlasConfig = new AtlasConfig() { + + @Override + public Duration step() { + return Duration.ofSeconds(1); + } + + @Override + public String get(String k) { + return null; + } + }; + } + + @Test + public void givenCompositeRegistries_whenRecordMeter_thenAllRegistriesRecorded() { + CompositeMeterRegistry compositeRegistry = new CompositeMeterRegistry(); + + SimpleMeterRegistry oneSimpleMeter = new SimpleMeterRegistry(); + AtlasMeterRegistry atlasMeterRegistry = new AtlasMeterRegistry(atlasConfig, Clock.SYSTEM); + + compositeRegistry.add(oneSimpleMeter); + compositeRegistry.add(atlasMeterRegistry); + + compositeRegistry.gauge("baeldung.heat", 90); + + Optional oneGauge = oneSimpleMeter + .find("baeldung.heat") + .gauge(); + assertTrue(oneGauge.isPresent()); + Iterator measurements = oneGauge + .get() + .measure() + .iterator(); + + assertTrue(measurements.hasNext()); + assertThat(measurements + .next() + .getValue(), equalTo(90.00)); + + Optional atlasGauge = atlasMeterRegistry + .find("baeldung.heat") + .gauge(); + assertTrue(atlasGauge.isPresent()); + Iterator anotherMeasurements = atlasGauge + .get() + .measure() + .iterator(); + + assertTrue(anotherMeasurements.hasNext()); + assertThat(anotherMeasurements + .next() + .getValue(), equalTo(90.00)); + } + + @Test + public void givenGlobalRegistry_whenIncrementAnywhere_thenCounted() { + class CountedObject { + private CountedObject() { + Metrics + .counter("objects.instance") + .increment(1.0); + } + } + Metrics.addRegistry(new SimpleMeterRegistry()); + + Metrics + .counter("objects.instance") + .increment(); + new CountedObject(); + + Optional counterOptional = Metrics.globalRegistry + .find("objects.instance") + .counter(); + + assertTrue(counterOptional.isPresent()); + assertTrue(counterOptional + .get() + .count() == 2.0); + } + + @Test + public void givenCounter_whenIncrement_thenValueChanged() { + SimpleMeterRegistry registry = new SimpleMeterRegistry(); + Counter counter = Counter + .builder("objects.instance") + .description("indicates instance count of the object") + .tags("dev", "performance") + .register(registry); + + counter.increment(2.0); + assertTrue(counter.count() == 2); + + counter.increment(-1); + assertTrue(counter.count() == 2); + } + + @Test + public void givenTimer_whenWrapTasks_thenTimeRecorded() { + SimpleMeterRegistry registry = new SimpleMeterRegistry(); + Timer timer = registry.timer("app.event"); + timer.record(() -> { + try { + TimeUnit.MILLISECONDS.sleep(1500); + } catch (InterruptedException ignored) { + } + }); + + timer.record(3000, TimeUnit.MILLISECONDS); + + assertTrue(2 == timer.count()); + assertTrue(4510 > timer.totalTime(TimeUnit.MILLISECONDS) && 4500 <= timer.totalTime(TimeUnit.MILLISECONDS)); + } + + @Test + public void givenLongTimer_whenRunTasks_thenTimerRecorded() { + SimpleMeterRegistry registry = new SimpleMeterRegistry(); + LongTaskTimer longTaskTimer = LongTaskTimer + .builder("3rdPartyService") + .register(registry); + + long currentTaskId = longTaskTimer.start(); + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException ignored) { + } + long timeElapsed = longTaskTimer.stop(currentTaskId); + + assertTrue(timeElapsed / (int) 1e9 == 2); + } + + @Test + public void givenGauge_whenMeterListSize_thenCurrentSizeMonitored() { + SimpleMeterRegistry registry = new SimpleMeterRegistry(); + List list = new ArrayList<>(4); + Gauge gauge = Gauge + .builder("cache.size", list, List::size) + .register(registry); + + assertTrue(gauge.value() == 0.0); + + list.add("1"); + assertTrue(gauge.value() == 1.0); + } + + @Test + public void givenDistributionSummary_whenRecord_thenSummarized() { + SimpleMeterRegistry registry = new SimpleMeterRegistry(); + DistributionSummary distributionSummary = DistributionSummary + .builder("request.size") + .baseUnit("bytes") + .register(registry); + distributionSummary.record(3); + distributionSummary.record(4); + distributionSummary.record(5); + + assertTrue(3 == distributionSummary.count()); + assertTrue(12 == distributionSummary.totalAmount()); + } + + @Test + public void givenTimer_whenEnrichWithQuantile_thenQuantilesComputed() { + SimpleMeterRegistry registry = new SimpleMeterRegistry(); + Timer timer = Timer + .builder("test.timer") + .quantiles(WindowSketchQuantiles + .quantiles(0.3, 0.5, 0.95) + .create()) + .register(registry); + + timer.record(2, TimeUnit.SECONDS); + timer.record(2, TimeUnit.SECONDS); + timer.record(3, TimeUnit.SECONDS); + timer.record(4, TimeUnit.SECONDS); + timer.record(8, TimeUnit.SECONDS); + timer.record(13, TimeUnit.SECONDS); + + List quantileGauges = registry + .getMeters() + .stream() + .filter(meter -> meter + .getType() + .name() + .equals("Gauge")) + .map(meter -> (Gauge) meter) + .collect(Collectors.toList()); + assert (3 == quantileGauges.size()); + + Map quantileMap = quantileGauges + .stream() + .collect(Collectors.toMap(gauge -> { + Tag tag = gauge + .getId() + .getTags() + .iterator() + .next(); + return tag.getKey() + "=" + tag.getValue(); + }, gauge -> (int) (gauge.value() / 1e9))); + + assertThat(quantileMap.keySet(), hasItems("quantile=0.3", "quantile=0.5", "quantile=0.95")); + assertThat(quantileMap.get("quantile=0.3"), is(2)); + assertThat(quantileMap.get("quantile=0.5"), is(3)); + assertThat(quantileMap.get("quantile=0.95"), is(8)); + } + + @Test + public void givenDistributionSummary_whenEnrichWithHistograms_thenDataAggregated() { + SimpleMeterRegistry registry = new SimpleMeterRegistry(); + DistributionSummary hist = DistributionSummary + .builder("summary") + .histogram(Histogram.linear(0, 10, 5)) + .register(registry); + hist.record(3); + hist.record(8); + hist.record(20); + hist.record(40); + hist.record(13); + hist.record(26); + + Map histograms = registry + .getMeters() + .stream() + .filter(meter -> meter.getType() == Meter.Type.Counter) + .collect(Collectors.toMap(counter -> { + Tag tag = counter + .getId() + .getTags() + .iterator() + .next(); + return tag.getKey() + "=" + tag.getValue(); + }, counter -> (int) counter + .measure() + .iterator() + .next() + .getValue())); + + assertThat(histograms, allOf(hasEntry("bucket=0.0", 0), hasEntry("bucket=10.0", 2), hasEntry("bucket=20.0", 2), hasEntry("bucket=30.0", 1), hasEntry("bucket=40.0", 1), hasEntry("bucket=Infinity", 0))); + } + + @Test + public void givenTimer_whenEnrichWithTimescaleHistogram_thenTimeScaleDataCollected() { + SimpleMeterRegistry registry = new SimpleMeterRegistry(); + Timer timer = Timer + .builder("timer") + .histogram(Histogram.linearTime(TimeUnit.MILLISECONDS, 0, 200, 3)) + .register(registry); + + timer.record(1000, TimeUnit.MILLISECONDS); + timer.record(23, TimeUnit.MILLISECONDS); + timer.record(450, TimeUnit.MILLISECONDS); + timer.record(341, TimeUnit.MILLISECONDS); + timer.record(500, TimeUnit.MILLISECONDS); + + Map histograms = registry + .getMeters() + .stream() + .filter(meter -> meter.getType() == Meter.Type.Counter) + .collect(Collectors.toMap(counter -> { + Tag tag = counter + .getId() + .getTags() + .iterator() + .next(); + return tag.getKey() + "=" + tag.getValue(); + }, counter -> (int) counter + .measure() + .iterator() + .next() + .getValue())); + + assertThat(histograms, allOf(hasEntry("bucket=0.0", 0), hasEntry("bucket=2.0E8", 1), hasEntry("bucket=4.0E8", 1), hasEntry("bucket=Infinity", 3))); + + } + +}