Skip to content

Commit 738a49d

Browse files
duoyuligeoand
authored andcommitted
Add instrumentation for spring-cloud-gateway (#247)
* feat(spring-cloud-gateway): add instrument
1 parent f852a60 commit 738a49d

File tree

9 files changed

+350
-0
lines changed

9 files changed

+350
-0
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Copyright 2017-2019 The OpenTracing Authors
5+
6+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7+
in compliance with the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software distributed under the License
12+
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13+
or implied. See the License for the specific language governing permissions and limitations under
14+
the License.
15+
16+
-->
17+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
18+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
19+
<modelVersion>4.0.0</modelVersion>
20+
<parent>
21+
<groupId>io.opentracing.contrib</groupId>
22+
<artifactId>opentracing-spring-cloud-parent</artifactId>
23+
<version>0.3.8-SNAPSHOT</version>
24+
<relativePath>../../</relativePath>
25+
</parent>
26+
27+
<artifactId>opentracing-spring-cloud-gateway-starter</artifactId>
28+
29+
<properties>
30+
<main.basedir>${project.basedir}/../../</main.basedir>
31+
</properties>
32+
33+
<dependencies>
34+
<dependency>
35+
<groupId>io.opentracing.contrib</groupId>
36+
<artifactId>opentracing-spring-tracer-configuration-starter</artifactId>
37+
</dependency>
38+
39+
<dependency>
40+
<groupId>org.springframework</groupId>
41+
<artifactId>spring-web</artifactId>
42+
</dependency>
43+
44+
<dependency>
45+
<groupId>org.springframework.cloud</groupId>
46+
<artifactId>spring-cloud-gateway-core</artifactId>
47+
<optional>true</optional>
48+
</dependency>
49+
50+
<dependency>
51+
<groupId>io.opentracing.contrib</groupId>
52+
<artifactId>opentracing-spring-web-starter</artifactId>
53+
<version>${version.io.opentracing.contrib-opentracing-spring-web}</version>
54+
</dependency>
55+
56+
<dependency>
57+
<groupId>org.springframework.boot</groupId>
58+
<artifactId>spring-boot-starter-test</artifactId>
59+
<scope>test</scope>
60+
</dependency>
61+
62+
<dependency>
63+
<groupId>org.springframework.boot</groupId>
64+
<artifactId>spring-boot-starter-web</artifactId>
65+
<scope>test</scope>
66+
</dependency>
67+
<dependency>
68+
<groupId>io.opentracing.contrib</groupId>
69+
<artifactId>opentracing-spring-cloud-core</artifactId>
70+
<scope>test</scope>
71+
</dependency>
72+
73+
</dependencies>
74+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* Copyright 2017-2019 The OpenTracing Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package io.opentracing.contrib.spring.cloud.gateway;
15+
16+
import io.opentracing.Span;
17+
import io.opentracing.Tracer;
18+
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;
19+
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
20+
import org.springframework.http.server.reactive.ServerHttpRequest;
21+
22+
/**
23+
* AbstractHttpHeadersFilter
24+
*
25+
* @author Weichao Li ([email protected])
26+
* @since 2019/9/28
27+
*/
28+
public abstract class AbstractHttpHeadersFilter implements HttpHeadersFilter {
29+
30+
protected static final String SPAN_ATTRIBUTE = Span.class.getName();
31+
32+
protected static final String ROUTE_ATTRIBUTE = ServerWebExchangeUtils.class.getName() + ".gatewayRoute";
33+
34+
protected final Tracer tracer;
35+
36+
protected AbstractHttpHeadersFilter(Tracer tracer) {
37+
this.tracer = tracer;
38+
}
39+
40+
public String path(ServerHttpRequest.Builder request) {
41+
return request.build().getPath().value();
42+
}
43+
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* Copyright 2017-2019 The OpenTracing Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package io.opentracing.contrib.spring.cloud.gateway;
15+
16+
import io.opentracing.Tracer;
17+
import io.opentracing.contrib.spring.tracer.configuration.TracerAutoConfiguration;
18+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
19+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
20+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
21+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
22+
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
23+
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
24+
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;
25+
import org.springframework.context.annotation.Bean;
26+
import org.springframework.context.annotation.Configuration;
27+
28+
/**
29+
* GatewayTracingAutoConfiguration
30+
*
31+
* @author Weichao Li ([email protected])
32+
* @since 2019/9/27
33+
*/
34+
@Configuration
35+
@ConditionalOnWebApplication
36+
@AutoConfigureAfter(TracerAutoConfiguration.class)
37+
@ConditionalOnClass(GatewayFilterChain.class)
38+
@ConditionalOnProperty(name = "opentracing.spring.cloud.gateway.enabled", havingValue = "true", matchIfMissing = true)
39+
public class GatewayTracingAutoConfiguration {
40+
41+
@Bean
42+
@ConditionalOnClass(HttpHeadersFilter.class)
43+
@ConditionalOnBean(Tracer.class)
44+
HttpHeadersFilter traceRequestHttpHeadersFilter(Tracer tracer) {
45+
return new TraceRequestHttpHeadersFilter(tracer);
46+
}
47+
48+
@Bean
49+
@ConditionalOnClass(HttpHeadersFilter.class)
50+
@ConditionalOnBean(Tracer.class)
51+
HttpHeadersFilter traceResponseHttpHeadersFilter(Tracer tracer) {
52+
return new TraceResponseHttpHeadersFilter(tracer);
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* Copyright 2017-2019 The OpenTracing Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package io.opentracing.contrib.spring.cloud.gateway;
15+
16+
import io.opentracing.Span;
17+
import io.opentracing.Tracer;
18+
import io.opentracing.contrib.spring.web.client.HttpHeadersCarrier;
19+
import io.opentracing.propagation.Format;
20+
import io.opentracing.tag.Tags;
21+
import java.util.List;
22+
import java.util.Map;
23+
import java.util.Objects;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
import org.springframework.cloud.gateway.route.Route;
27+
import org.springframework.http.HttpHeaders;
28+
import org.springframework.http.server.reactive.ServerHttpRequest;
29+
import org.springframework.web.server.ServerWebExchange;
30+
31+
32+
33+
/**
34+
* TraceRequestHttpHeadersFilter
35+
*
36+
* @author Weichao Li ([email protected])
37+
* @since 2019/9/26
38+
*/
39+
final class TraceRequestHttpHeadersFilter extends AbstractHttpHeadersFilter {
40+
41+
private final Logger log = LoggerFactory.getLogger(TraceRequestHttpHeadersFilter.class);
42+
43+
private static final String ROUTE_ID = "route.id";
44+
45+
private static final String COMPONENT = "java-spring-cloud-gateway";
46+
47+
protected TraceRequestHttpHeadersFilter(Tracer tracer) {
48+
super(tracer);
49+
}
50+
51+
@Override
52+
public HttpHeaders filter(HttpHeaders input, ServerWebExchange exchange) {
53+
log.debug("Will instrument spring cloud gateway the HTTP request headers");
54+
ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
55+
Span span = this.tracer.buildSpan(path(builder))
56+
.asChildOf(tracer.activeSpan())
57+
.withTag(Tags.COMPONENT.getKey(), COMPONENT)
58+
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
59+
.withTag(ROUTE_ID, getRouteId(exchange))
60+
.start();
61+
log.debug("Client span {} created for the request. New headers are {}", span, builder.build().getHeaders().toSingleValueMap());
62+
exchange.getAttributes().put(SPAN_ATTRIBUTE, span);
63+
HttpHeaders headersWithInput = new HttpHeaders();
64+
try {
65+
this.tracer.inject(span.context(), Format.Builtin.HTTP_HEADERS, new HttpHeadersCarrier(headersWithInput));
66+
} catch (Exception ignore) {
67+
log.error("TraceRequestHttpHeadersFilter error", ignore);
68+
}
69+
headersWithInput.addAll(input);
70+
addHeadersWithInput(builder, headersWithInput);
71+
return headersWithInput;
72+
}
73+
74+
private String getRouteId(ServerWebExchange exchange) {
75+
String routeId = "unknown";
76+
Route route = exchange.getAttribute(ROUTE_ATTRIBUTE);
77+
if (Objects.nonNull(route)) {
78+
return route.getId();
79+
}
80+
return routeId;
81+
}
82+
83+
private void addHeadersWithInput(ServerHttpRequest.Builder builder,
84+
HttpHeaders headersWithInput) {
85+
for (Map.Entry<String, List<String>> entry : builder.build().getHeaders()
86+
.entrySet()) {
87+
String key = entry.getKey();
88+
List<String> value = entry.getValue();
89+
headersWithInput.put(key, value);
90+
}
91+
}
92+
93+
@Override
94+
public boolean supports(Type type) {
95+
return type.equals(Type.REQUEST);
96+
}
97+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Copyright 2017-2019 The OpenTracing Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package io.opentracing.contrib.spring.cloud.gateway;
15+
16+
import io.opentracing.Span;
17+
import io.opentracing.Tracer;
18+
import java.util.Objects;
19+
import org.slf4j.Logger;
20+
import org.slf4j.LoggerFactory;
21+
import org.springframework.http.HttpHeaders;
22+
import org.springframework.web.server.ServerWebExchange;
23+
24+
25+
26+
/**
27+
* TraceResponseHttpHeadersFilter
28+
*
29+
* @author Weichao Li ([email protected])
30+
* @since 2019/9/28
31+
*/
32+
public class TraceResponseHttpHeadersFilter extends AbstractHttpHeadersFilter {
33+
34+
private final Logger log = LoggerFactory.getLogger(TraceResponseHttpHeadersFilter.class);
35+
36+
protected TraceResponseHttpHeadersFilter(Tracer tracer) {
37+
super(tracer);
38+
}
39+
40+
@Override
41+
public HttpHeaders filter(HttpHeaders input, ServerWebExchange exchange) {
42+
Object storedSpan = exchange.getAttribute(SPAN_ATTRIBUTE);
43+
if (storedSpan == null) {
44+
return input;
45+
}
46+
log.debug("Will instrument the response");
47+
Span span = (Span) storedSpan;
48+
if (Objects.nonNull(span)) {
49+
span.finish();
50+
}
51+
log.debug("The response was handled for span " + storedSpan);
52+
return input;
53+
}
54+
55+
@Override
56+
public boolean supports(Type type) {
57+
return type.equals(Type.RESPONSE);
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"properties": [
3+
{
4+
"name": "opentracing.spring.cloud.gateway.enabled",
5+
"type": "java.lang.Boolean",
6+
"description": "Add standard logging output to tracing system.",
7+
"defaultValue": true
8+
}
9+
]
10+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2+
io.opentracing.contrib.spring.cloud.gateway.GatewayTracingAutoConfiguration

opentracing-spring-cloud-starter/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@
8686
<groupId>${project.groupId}</groupId>
8787
<artifactId>opentracing-spring-cloud-zuul-starter</artifactId>
8888
</dependency>
89+
<dependency>
90+
<groupId>${project.groupId}</groupId>
91+
<artifactId>opentracing-spring-cloud-gateway-starter</artifactId>
92+
</dependency>
8993

9094
<dependency>
9195
<groupId>junit</groupId>

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
<module>instrument-starters/opentracing-spring-cloud-reactor-starter</module>
3434
<module>instrument-starters/opentracing-spring-cloud-rxjava-starter</module>
3535
<module>instrument-starters/opentracing-spring-cloud-redis-starter</module>
36+
<module>instrument-starters/opentracing-spring-cloud-gateway-starter</module>
3637
</modules>
3738
<packaging>pom</packaging>
3839

@@ -173,6 +174,11 @@
173174
<artifactId>opentracing-spring-cloud-zuul-starter</artifactId>
174175
<version>${project.version}</version>
175176
</dependency>
177+
<dependency>
178+
<groupId>${project.groupId}</groupId>
179+
<artifactId>opentracing-spring-cloud-gateway-starter</artifactId>
180+
<version>${project.version}</version>
181+
</dependency>
176182

177183
<dependency>
178184
<groupId>io.opentracing</groupId>

0 commit comments

Comments
 (0)