Skip to content

Commit 3762a2e

Browse files
committed
Add SCSt 2.0 and Micrometer compatibility
Resolve spring-attic#51 Resolve spring-attic#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
1 parent d006c6e commit 3762a2e

File tree

15 files changed

+705
-148
lines changed

15 files changed

+705
-148
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/MetricsAggregator.java

+85-7
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,119 @@
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
/**
2938
* Adds the incoming {@link ApplicationMetrics} payload into the backend store
3039
* @author Vinicius Carvalho
40+
* @author Christian Tzolov
3141
*/
3242
@Component
3343
public class MetricsAggregator {
44+
private Logger logger = LoggerFactory.getLogger(MetricsAggregator.class);
3445

46+
private ObjectMapper mapper;
3547
private ApplicationMetricsService service;
3648

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

3950
public MetricsAggregator(ApplicationMetricsService service) {
4051
this.service = service;
52+
this.mapper = new ObjectMapper();
4153
}
4254

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

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
*/

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

+30-11
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.
@@ -23,13 +23,15 @@
2323
import com.fasterxml.jackson.annotation.JsonCreator;
2424
import com.fasterxml.jackson.annotation.JsonFormat;
2525
import com.fasterxml.jackson.annotation.JsonProperty;
26-
27-
import org.springframework.boot.actuate.metrics.Metric;
26+
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
2827

2928
/**
3029
* @author Vinicius Carvalho
30+
* @author Oleg Zhurakousky
31+
* @author Christian Tzolov
3132
*/
32-
public class ApplicationMetrics {
33+
@JsonPropertyOrder({ "name", "createdTime", "properties", "metrics" })
34+
public class ApplicationMetrics<T> {
3335

3436
public static final String STREAM_NAME = "spring.cloud.dataflow.stream.name";
3537
public static final String APPLICATION_NAME = "spring.cloud.dataflow.stream.app.label";
@@ -38,19 +40,25 @@ public class ApplicationMetrics {
3840
public static final String APPLICATION_GUID = "spring.cloud.application.guid";
3941
public static final String INSTANCE_INDEX = "spring.application.index";
4042
public static final String APPLICATION_METRICS_JSON = "application/vnd.spring.cloud.stream.metrics.v1+json";
43+
public static final String STREAM_METRICS_VERSION = "spring.cloud.dataflow.stream.metrics.version";
44+
45+
public static final String METRICS_VERSION_1 = "1.0";
46+
public static final String METRICS_VERSION_2 = "2.0";
47+
4148

4249
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone = "UTC")
43-
private final Date createdTime;
50+
private Date createdTime;
4451

4552
private String name;
4653

47-
private Collection<Metric<Double>> metrics;
54+
private Collection<T> metrics;
55+
56+
private long interval = 1000; //[ms]
4857

4958
private Map<String, Object> properties;
5059

5160
@JsonCreator
52-
public ApplicationMetrics(@JsonProperty("name") String name,
53-
@JsonProperty("metrics") Collection<Metric<Double>> metrics) {
61+
public ApplicationMetrics(@JsonProperty("name") String name, @JsonProperty("metrics") Collection<T> metrics) {
5462
this.name = name;
5563
this.metrics = metrics;
5664
this.createdTime = new Date();
@@ -64,18 +72,22 @@ public void setName(String name) {
6472
this.name = name;
6573
}
6674

67-
public Collection<Metric<Double>> getMetrics() {
75+
public Collection<T> getMetrics() {
6876
return metrics;
6977
}
7078

71-
public void setMetrics(Collection<Metric<Double>> metrics) {
79+
public void setMetrics(Collection<T> metrics) {
7280
this.metrics = metrics;
7381
}
7482

7583
public Date getCreatedTime() {
7684
return createdTime;
7785
}
7886

87+
public void setCreatedTime(Date createdTime) {
88+
this.createdTime = createdTime;
89+
}
90+
7991
public Map<String, Object> getProperties() {
8092
return properties;
8193
}
@@ -84,6 +96,14 @@ public void setProperties(Map<String, Object> properties) {
8496
this.properties = properties;
8597
}
8698

99+
public long getInterval() {
100+
return interval;
101+
}
102+
103+
public void setInterval(long interval) {
104+
this.interval = interval;
105+
}
106+
87107
@Override
88108
public boolean equals(Object o) {
89109
if (this == o)
@@ -100,5 +120,4 @@ public boolean equals(Object o) {
100120
public int hashCode() {
101121
return name != null ? name.hashCode() : 0;
102122
}
103-
104123
}

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

-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@
2121

2222
import com.fasterxml.jackson.annotation.JsonCreator;
2323

24-
import org.springframework.boot.actuate.metrics.Metric;
25-
2624
/**
2725
* @author Vinicius Carvalho
2826
*/

0 commit comments

Comments
 (0)