Skip to content
This repository was archived by the owner on Jul 9, 2022. It is now read-only.

Commit 333486d

Browse files
tzolovmarkpollack
authored andcommitted
Add SCSt 2.0 and Micrometer compatibility
Resolve #51 Resolve #55 - Extend MetricAggregator to accept and dispatch Metrics coming from SpringBoot 1.x apps as well as MicrometerMetrics from SpringBoot2/SCSt2 apps. - Uses the spring.integration.send string as metrics type discriminator. - MetricAggregator converts on the fly the received MicrometerMetrics classes into Metrics such. - The MicrometerMetrics multidimensional (e.g. multi-tag) identity is converted into hierarchical Metrics name convention. - Only the spring.integration.x MicrometerMetrics metrics are filtered in. - The MicrometerMetrics have the rates precomputed. Therefore the ApplicationMetricsService rate computation logic is applied only to the Metrics (1.x) metrics. - New compute logic for the Application#getAggregateMetrics - Support for mixed SCSt1.x and SCSt2.x app metrics - Fix the Metrics 1.x tests and add MicrometerMetric tests - Cache timeout set to 90 seconds, default app publish period is 60 seconds
1 parent d006c6e commit 333486d

File tree

16 files changed

+713
-150
lines changed

16 files changed

+713
-150
lines changed

pom.xml

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
<modelVersion>4.0.0</modelVersion>
55
<groupId>org.springframework.cloud</groupId>
66
<artifactId>spring-cloud-dataflow-metrics-collector-build</artifactId>
7-
<version>1.0.1.BUILD-SNAPSHOT</version>
7+
<version>2.0.0.BUILD-SNAPSHOT</version>
88
<packaging>pom</packaging>
99

1010
<parent>
1111
<groupId>org.springframework.cloud.stream.app</groupId>
1212
<artifactId>app-starters-build</artifactId>
13-
<version>1.2.0.RELEASE</version>
13+
<version>2.0.0.BUILD-SNAPSHOT</version>
1414
</parent>
1515

1616
<modules>
@@ -24,7 +24,7 @@
2424
<dependency>
2525
<groupId>org.springframework.cloud</groupId>
2626
<artifactId>spring-cloud-dataflow-metrics-collector-dependencies</artifactId>
27-
<version>1.0.1.BUILD-SNAPSHOT</version>
27+
<version>2.0.0.BUILD-SNAPSHOT</version>
2828
<type>pom</type>
2929
<scope>import</scope>
3030
</dependency>

spring-cloud-dataflow-metrics-collector-dependencies/pom.xml

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
44
<modelVersion>4.0.0</modelVersion>
55
<artifactId>spring-cloud-dataflow-metrics-collector-dependencies</artifactId>
6-
<version>1.0.1.BUILD-SNAPSHOT</version>
6+
<version>2.0.0.BUILD-SNAPSHOT</version>
77
<packaging>pom</packaging>
88
<name>metrics-collector-dependencies</name>
99
<description>Spring Cloud Data Flow Metrics Collector App Dependencies</description>
1010

1111
<parent>
1212
<artifactId>spring-cloud-dependencies-parent</artifactId>
1313
<groupId>org.springframework.cloud</groupId>
14-
<version>1.3.1.RELEASE</version>
14+
<version>2.0.0.BUILD-SNAPSHOT</version>
1515
<relativePath/>
1616
</parent>
1717

@@ -20,7 +20,7 @@
2020
<dependency>
2121
<groupId>org.springframework.cloud</groupId>
2222
<artifactId>spring-cloud-starter-dataflow-metrics-collector</artifactId>
23-
<version>1.0.1.BUILD-SNAPSHOT</version>
23+
<version>2.0.0.BUILD-SNAPSHOT</version>
2424
</dependency>
2525
<dependency>
2626
<groupId>com.github.ben-manes.caffeine</groupId>
@@ -30,7 +30,7 @@
3030
<dependency>
3131
<groupId>org.springframework.boot</groupId>
3232
<artifactId>spring-boot-starter-hateoas</artifactId>
33-
<version>1.5.2.RELEASE</version>
33+
<version>2.0.1.RELEASE</version>
3434
</dependency>
3535
</dependencies>
3636
</dependencyManagement>

spring-cloud-starter-dataflow-metrics-collector/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<parent>
1212
<groupId>org.springframework.cloud</groupId>
1313
<artifactId>spring-cloud-dataflow-metrics-collector-build</artifactId>
14-
<version>1.0.1.BUILD-SNAPSHOT</version>
14+
<version>2.0.0.BUILD-SNAPSHOT</version>
1515
</parent>
1616

1717
<properties>

spring-cloud-starter-dataflow-metrics-collector/src/main/java/org/springframework/cloud/dataflow/metrics/collector/MetricCollectorProperties.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@
2323
*/
2424
@ConfigurationProperties(prefix = "spring.cloud.dataflow.metrics.collector")
2525
public class MetricCollectorProperties {
26-
private Integer evictionTimeout = 30;
26+
private Integer evictionTimeout = 90;
2727

28+
/**
29+
* Timeout (in seconds) before metric entry is removed from cache. Default value is 90 seconds
30+
* @return Timeout (in seconds)
31+
*/
2832
public Integer getEvictionTimeout() {
2933
return evictionTimeout;
3034
}

spring-cloud-starter-dataflow-metrics-collector/src/main/java/org/springframework/cloud/dataflow/metrics/collector/MetricsAggregator.java

+88-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017 the original author or authors.
2+
* Copyright 2017-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,41 +16,121 @@
1616

1717
package org.springframework.cloud.dataflow.metrics.collector;
1818

19+
import java.io.IOException;
20+
import java.util.List;
21+
import java.util.stream.Collectors;
22+
23+
import com.fasterxml.jackson.core.type.TypeReference;
24+
import com.fasterxml.jackson.databind.ObjectMapper;
1925
import org.slf4j.Logger;
2026
import org.slf4j.LoggerFactory;
2127

2228
import org.springframework.cloud.dataflow.metrics.collector.model.ApplicationMetrics;
29+
import org.springframework.cloud.dataflow.metrics.collector.model.Metric;
30+
import org.springframework.cloud.dataflow.metrics.collector.model.MicrometerMetric;
2331
import org.springframework.cloud.dataflow.metrics.collector.services.ApplicationMetricsService;
2432
import org.springframework.cloud.stream.annotation.StreamListener;
2533
import org.springframework.cloud.stream.messaging.Sink;
2634
import org.springframework.stereotype.Component;
35+
import org.springframework.util.StringUtils;
2736

2837
/**
29-
* Adds the incoming {@link ApplicationMetrics} payload into the backend store
38+
* Adds the incoming {@link ApplicationMetrics} payload into the in memory cache.
39+
* Supports metrics sent from Spring Cloud Stream 1.x and 2.x applications
40+
*
3041
* @author Vinicius Carvalho
42+
* @author Christian Tzolov
3143
*/
3244
@Component
3345
public class MetricsAggregator {
46+
private Logger logger = LoggerFactory.getLogger(MetricsAggregator.class);
3447

48+
private ObjectMapper mapper;
3549
private ApplicationMetricsService service;
3650

37-
private Logger logger = LoggerFactory.getLogger(MetricsAggregator.class);
3851

3952
public MetricsAggregator(ApplicationMetricsService service) {
4053
this.service = service;
54+
this.mapper = new ObjectMapper();
4155
}
4256

57+
private final static class Metric1TypeReference extends TypeReference<ApplicationMetrics<Metric<Number>>> {}
58+
59+
private final static class Metric2TypeReference extends TypeReference<ApplicationMetrics<MicrometerMetric<Number>>> {}
60+
4361
@StreamListener(Sink.INPUT)
44-
public void receive(ApplicationMetrics metrics) {
62+
public void receive(String metrics) {
63+
64+
ApplicationMetrics<Metric<Double>> applicationMetrics;
65+
try {
66+
// Use the "spring.integration.send" metric name as a version discriminator for old and new metrics
67+
if (StringUtils.hasText(metrics) && metrics.contains("spring.integration.send")) {
68+
ApplicationMetrics<MicrometerMetric<Number>> applicationMetrics2 = mapper.readValue(metrics, new Metric2TypeReference());
69+
applicationMetrics = convertMetric2ToMetric(applicationMetrics2);
70+
applicationMetrics.getProperties().put(ApplicationMetrics.STREAM_METRICS_VERSION, ApplicationMetrics.METRICS_VERSION_2);
71+
}
72+
else {
73+
applicationMetrics = mapper.readValue(metrics, new Metric1TypeReference());
74+
applicationMetrics.getProperties().put(ApplicationMetrics.STREAM_METRICS_VERSION, ApplicationMetrics.METRICS_VERSION_1);
75+
}
76+
77+
this.processApplicationMetrics(applicationMetrics);
78+
}
79+
catch (IOException e) {
80+
logger.warn("Invalid metrics Json", e);
81+
}
82+
83+
}
84+
85+
/**
86+
* Converts the new Micrometer metrics into the previous {@link Metric}
87+
* format (e.g. to the Spring Boot 1.x actuator metrics)
88+
* Only the successful Spring Integration channel metrics are filtered in. All other metrics are discarded!
89+
* @param applicationMetrics2 {@link ApplicationMetrics} with Micrometer Metrics collection
90+
* @return Returns {@link ApplicationMetrics} with SpringBoot1.5's Metric collection.
91+
*/
92+
private ApplicationMetrics<Metric<Double>> convertMetric2ToMetric(ApplicationMetrics<MicrometerMetric<Number>> applicationMetrics2) {
93+
List<Metric<Double>> metrics = applicationMetrics2.getMetrics().stream()
94+
.filter(metric -> metric.getId().getName().matches("spring\\.integration\\.send"))
95+
.filter(metric -> metric.getId().getTag("type").equals("channel"))
96+
.filter(metric -> metric.getId().getTag("result").equals("success"))
97+
.map(m2 -> new Metric<>(
98+
generateOldMetricName(m2),
99+
m2.getCount().doubleValue() / (applicationMetrics2.getInterval() / 1000), // normalize rate
100+
m2.getTimestamp()))
101+
.collect(Collectors.toList());
102+
ApplicationMetrics<Metric<Double>> applicationMetrics = new ApplicationMetrics(applicationMetrics2.getName(), metrics);
103+
applicationMetrics.setCreatedTime(applicationMetrics2.getCreatedTime());
104+
applicationMetrics.setProperties(applicationMetrics2.getProperties());
105+
return applicationMetrics;
106+
}
107+
108+
/**
109+
* Build an hierarchical Metric Ver.1 name from the the multi-dimension (e.g. multi-tag) micrometer Metric
110+
* @param metric2 Micrometer based metrics
111+
* @return Returns an hierarchical name compatible with the Metric ver.1 name convention.
112+
*/
113+
private String generateOldMetricName(MicrometerMetric<Number> metric2) {
114+
String oldMetricName = metric2.getId().getName();
115+
if (metric2.getId().getName().startsWith("spring.integration.")) {
116+
String channelName = metric2.getId().getTag("name");
117+
String metricResult = metric2.getId().getTag("result");
118+
String successSuffix = "success".equals(metricResult) ? "" : "." + metricResult;
119+
oldMetricName = "integration.channel." + channelName + ".send.mean" + successSuffix;
120+
}
121+
return oldMetricName;
122+
}
123+
124+
private void processApplicationMetrics(ApplicationMetrics<Metric<Double>> metrics) {
45125
if (metrics.getProperties().get(ApplicationMetrics.APPLICATION_GUID) != null
46126
&& metrics.getProperties().get(ApplicationMetrics.APPLICATION_NAME) != null
47127
&& metrics.getProperties().get(ApplicationMetrics.STREAM_NAME) != null) {
48128
this.service.add(metrics);
49-
}else{
50-
if(logger.isDebugEnabled()){
51-
logger.debug("Metric : {} is missing key properties and will not be consumed by the collector",metrics.getName());
129+
}
130+
else {
131+
if (logger.isDebugEnabled()) {
132+
logger.debug("Metric : {} is missing key properties and will not be consumed by the collector", metrics.getName());
52133
}
53134
}
54135
}
55-
56136
}

spring-cloud-starter-dataflow-metrics-collector/src/main/java/org/springframework/cloud/dataflow/metrics/collector/MetricsCollectorConfiguration.java

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017 the original author or authors.
2+
* Copyright 2017-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
2727
import org.springframework.cloud.dataflow.metrics.collector.endpoint.MetricsCollectorEndpoint;
2828
import org.springframework.cloud.dataflow.metrics.collector.endpoint.RootEndpoint;
2929
import org.springframework.cloud.dataflow.metrics.collector.model.ApplicationMetrics;
30+
import org.springframework.cloud.dataflow.metrics.collector.model.Metric;
3031
import org.springframework.cloud.dataflow.metrics.collector.services.ApplicationMetricsService;
3132
import org.springframework.cloud.dataflow.metrics.collector.support.CaffeineHealthIndicator;
3233
import org.springframework.cloud.dataflow.metrics.collector.support.MetricJsonSerializer;
@@ -39,6 +40,7 @@
3940
/**
4041
* @author Mark Pollack
4142
* @author Vinicius Carvalho
43+
* @author Christian Tzolov
4244
*/
4345
@Configuration
4446
@EnableBinding(Sink.class)
@@ -54,14 +56,14 @@ public MetricJsonSerializer jsonSerializer() {
5456
}
5557

5658
@Bean
57-
public Cache<String, LinkedList<ApplicationMetrics>> metricsStorage() {
58-
return Caffeine.<String, ApplicationMetrics> newBuilder()
59+
public Cache<String, LinkedList<ApplicationMetrics<Metric<Double>>>> metricsStorage() {
60+
return Caffeine.<String, ApplicationMetrics<Metric<Double>>>newBuilder()
5961
.expireAfterWrite(properties.getEvictionTimeout(), TimeUnit.SECONDS).recordStats().build();
6062
}
6163

6264
@Bean
6365
public ApplicationMetricsService applicationMetricsService(
64-
Cache<String, LinkedList<ApplicationMetrics>> metricsStorage) throws Exception {
66+
Cache<String, LinkedList<ApplicationMetrics<Metric<Double>>>> metricsStorage) {
6567
return new ApplicationMetricsService(metricsStorage);
6668
}
6769

@@ -82,7 +84,7 @@ public RootEndpoint rootEndpoint(EntityLinks entityLinks) {
8284

8385
@Bean
8486
public CaffeineHealthIndicator caffeineHealthIndicator(
85-
Cache<String, LinkedList<ApplicationMetrics>> metricsStorage) {
87+
Cache<String, LinkedList<ApplicationMetrics<Metric<Double>>>> metricsStorage) {
8688
return new CaffeineHealthIndicator(metricsStorage);
8789
}
8890
}

spring-cloud-starter-dataflow-metrics-collector/src/main/java/org/springframework/cloud/dataflow/metrics/collector/endpoint/MetricsCollectorEndpoint.java

+1-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package org.springframework.cloud.dataflow.metrics.collector.endpoint;
1818

1919
import java.util.Collection;
20-
import java.util.regex.Pattern;
2120

2221
import org.springframework.cloud.dataflow.metrics.collector.model.StreamMetrics;
2322
import org.springframework.cloud.dataflow.metrics.collector.services.ApplicationMetricsService;
@@ -41,8 +40,6 @@
4140
@ExposesResourceFor(StreamMetrics.class)
4241
public class MetricsCollectorEndpoint {
4342

44-
private final Pattern pattern = Pattern.compile("integration\\.channel\\.(\\w*)\\.sendCount");
45-
4643
private ApplicationMetricsService service;
4744

4845
public MetricsCollectorEndpoint(ApplicationMetricsService service) {
@@ -61,7 +58,7 @@ public ResponseEntity<PagedResources<StreamMetrics>> fetchMetrics(
6158
PagedResources<StreamMetrics> pagedResources = new PagedResources<>(entries, pageMetadata,
6259
ControllerLinkBuilder.linkTo(MetricsCollectorEndpoint.class).withRel(Link.REL_SELF));
6360

64-
return new ResponseEntity<PagedResources<StreamMetrics>>(pagedResources, HttpStatus.OK);
61+
return new ResponseEntity<>(pagedResources, HttpStatus.OK);
6562
}
6663

6764
}

spring-cloud-starter-dataflow-metrics-collector/src/main/java/org/springframework/cloud/dataflow/metrics/collector/model/Application.java

-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424

2525
import com.fasterxml.jackson.annotation.JsonCreator;
2626

27-
import org.springframework.boot.actuate.metrics.Metric;
28-
2927
/**
3028
* @author Vinicius Carvalho
3129
*/

0 commit comments

Comments
 (0)