BAEL-1064 (#2748)
* BAEL-1064 * add spring-milestones repo to pom * more endpoints for spring integration metrics
This commit is contained in:
parent
f753a86e84
commit
1a4812d722
@ -16,6 +16,8 @@
|
|||||||
<dep.ver.metrics>3.1.2</dep.ver.metrics>
|
<dep.ver.metrics>3.1.2</dep.ver.metrics>
|
||||||
<dep.ver.servlet>3.1.0</dep.ver.servlet>
|
<dep.ver.servlet>3.1.0</dep.ver.servlet>
|
||||||
<netflix.servo.ver>0.12.17</netflix.servo.ver>
|
<netflix.servo.ver>0.12.17</netflix.servo.ver>
|
||||||
|
<micrometer.ver>1.0.0-rc.2</micrometer.ver>
|
||||||
|
<spring.boot.ver>2.0.0.M5</spring.boot.ver>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -57,12 +59,64 @@
|
|||||||
<version>${netflix.servo.ver}</version>
|
<version>${netflix.servo.ver}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-registry-atlas</artifactId>
|
||||||
|
<version>${micrometer.ver}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-spring-legacy</artifactId>
|
||||||
|
<version>${micrometer.ver}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<version>${spring.boot.ver}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>2.9.1</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||||
<artifactId>jackson-dataformat-smile</artifactId>
|
<artifactId>jackson-dataformat-smile</artifactId>
|
||||||
<version>2.8.9</version>
|
<version>2.9.1</version>
|
||||||
<scope>test</scope>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.netflix.spectator</groupId>
|
||||||
|
<artifactId>spectator-api</artifactId>
|
||||||
|
<version>0.57.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-dependencies</artifactId>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
<version>${spring.boot.ver}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/libs-milestone</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<String> 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<String> 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<Gauge> oneGauge = oneSimpleMeter
|
||||||
|
.find("baeldung.heat")
|
||||||
|
.gauge();
|
||||||
|
assertTrue(oneGauge.isPresent());
|
||||||
|
Iterator<Measurement> measurements = oneGauge
|
||||||
|
.get()
|
||||||
|
.measure()
|
||||||
|
.iterator();
|
||||||
|
|
||||||
|
assertTrue(measurements.hasNext());
|
||||||
|
assertThat(measurements
|
||||||
|
.next()
|
||||||
|
.getValue(), equalTo(90.00));
|
||||||
|
|
||||||
|
Optional<Gauge> atlasGauge = atlasMeterRegistry
|
||||||
|
.find("baeldung.heat")
|
||||||
|
.gauge();
|
||||||
|
assertTrue(atlasGauge.isPresent());
|
||||||
|
Iterator<Measurement> 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<Counter> 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<String> 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<Gauge> quantileGauges = registry
|
||||||
|
.getMeters()
|
||||||
|
.stream()
|
||||||
|
.filter(meter -> meter
|
||||||
|
.getType()
|
||||||
|
.name()
|
||||||
|
.equals("Gauge"))
|
||||||
|
.map(meter -> (Gauge) meter)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
assert (3 == quantileGauges.size());
|
||||||
|
|
||||||
|
Map<String, Integer> 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<String, Integer> 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<String, Integer> 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)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user