Skip to content

Commit 415a7a6

Browse files
tkvangordermarcingrzejszczak
authored andcommitted
First attempt to add GRPC instrumentation (#1139)
* Adding preliminary GRPC instrumentation to Sleuth fixes #305
1 parent a1832f6 commit 415a7a6

File tree

16 files changed

+2148
-0
lines changed

16 files changed

+2148
-0
lines changed

docs/src/main/asciidoc/spring-cloud-sleuth.adoc

+38
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,44 @@ You can disable it by setting `spring.sleuth.feign.processor.enabled` to `false`
12101210
If you set it to `false`, Spring Cloud Sleuth does not instrument any of your custom Feign components.
12111211
However, all the default instrumentation is still there.
12121212

1213+
=== gRPC
1214+
1215+
Spring Cloud Sleuth provides instrumentation for https://grpc.io/[gRPC] through `TraceGrpcAutoConfiguration`. You can disable it entirely by setting `spring.sleuth.grpc.enabled` to `false`.
1216+
1217+
==== Dependencies
1218+
IMPORTANT: The gRPC integration relies on two external libraries to instrument clients and servers and both of those libraries must be on the class path to enable the instrumentation.
1219+
1220+
Maven:
1221+
```
1222+
<dependency>
1223+
<groupId>io.github.lognet</groupId>
1224+
<artifactId>grpc-spring-boot-starter</artifactId>
1225+
</dependency>
1226+
<dependency>
1227+
<groupId>io.zipkin.brave</groupId>
1228+
<artifactId>brave-instrumentation-grpc</artifactId>
1229+
</dependency>
1230+
```
1231+
Gradle:
1232+
```
1233+
compile("io.github.lognet:grpc-spring-boot-starter")
1234+
compile("io.zipkin.brave:brave-instrumentation-grpc")
1235+
```
1236+
1237+
==== Server Instrumentation
1238+
1239+
Spring Cloud Sleuth leverages grpc-spring-boot-starter to register Brave's gRPC server interceptor with all services annotated with `@GRpcService`.
1240+
1241+
==== Client Instrumentation
1242+
1243+
gRPC clients leverage a `ManagedChannelBuilder` to construct a `ManagedChannel` used to communicate to the gRPC server. The native `ManagedChannelBuilder` provides static methods as entry points for construction of `ManagedChannel` instances, however, this mechanism is outside the influence of the Spring application context.
1244+
1245+
IMPORTANT: Spring Cloud Sleuth provides a `SpringAwareManagedChannelBuilder` that can be customized through the Spring application context and injected by gRPC clients. *This builder must be used when creating `ManagedChannel` instances.*
1246+
1247+
1248+
Sleuth creates a `TracingManagedChannelBuilderCustomizer` which inject Brave's client interceptor into the `SpringAwareManagedChannelBuilder`.
1249+
1250+
12131251
=== Asynchronous Communication
12141252

12151253
==== `@Async` Annotated methods

spring-cloud-sleuth-core/pom.xml

+18
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,17 @@
233233
<artifactId>spring-jms</artifactId>
234234
<optional>true</optional>
235235
</dependency>
236+
<!-- GRPC Optional Dependencies -->
237+
<dependency>
238+
<groupId>io.github.lognet</groupId>
239+
<artifactId>grpc-spring-boot-starter</artifactId>
240+
<optional>true</optional>
241+
</dependency>
242+
<dependency>
243+
<groupId>io.zipkin.brave</groupId>
244+
<artifactId>brave-instrumentation-grpc</artifactId>
245+
<optional>true</optional>
246+
</dependency>
236247
<dependency>
237248
<groupId>org.springframework.boot</groupId>
238249
<artifactId>spring-boot-autoconfigure-processor</artifactId>
@@ -311,6 +322,13 @@
311322
<artifactId>activemq-ra</artifactId>
312323
<scope>test</scope>
313324
</dependency>
325+
<!-- This forces the guava version to 20 within the test scope, which is required by GRPC -->
326+
<dependency>
327+
<groupId>com.google.guava</groupId>
328+
<artifactId>guava</artifactId>
329+
<version>20.0</version>
330+
<scope>test</scope>
331+
</dependency>
314332
</dependencies>
315333

316334
<profiles>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.sleuth.instrument.grpc;
18+
19+
import io.grpc.ManagedChannelBuilder;
20+
21+
/**
22+
* Callback interface that can be implemented by beans wishing to further customize the
23+
* {@link io.grpc.ManagedChannelBuilder} via the {@link SpringAwareManagedChannelBuilder}.
24+
*
25+
* @author tyler.vangorder
26+
*/
27+
public interface GrpcManagedChannelBuilderCustomizer {
28+
29+
void customize(ManagedChannelBuilder<?> managedChannelBuilder);
30+
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2013-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.sleuth.instrument.grpc;
18+
19+
import java.util.List;
20+
import java.util.Optional;
21+
22+
import io.grpc.ManagedChannelBuilder;
23+
import io.grpc.inprocess.InProcessChannelBuilder;
24+
25+
/**
26+
* This is a Spring-aware managed channel builder that wraps the static entry points of
27+
* gRPC's ManagedChannelBuilder to allow the configuration of the builder to be influenced
28+
* by the Spring Context. All GrpcManagedChannelBuilderCustomizer instances included in
29+
* the application context will have the opportunity to customize the builder.
30+
*
31+
* NOTE: There is nothing "Sleuth-specific" about this, however, there is currently not a
32+
* good spring abstraction for client-side gRPC. Ideally, this could be moved up into
33+
* grpc-spring-boot-starter or a new project could be created
34+
* "spring-grpc"/"spring-cloud-grpc"?
35+
*
36+
* @author tyler.vangorder
37+
*/
38+
public class SpringAwareManagedChannelBuilder {
39+
40+
private List<GrpcManagedChannelBuilderCustomizer> customizers;
41+
42+
public SpringAwareManagedChannelBuilder(
43+
Optional<List<GrpcManagedChannelBuilderCustomizer>> customizers) {
44+
this.customizers = customizers.orElse(null);
45+
}
46+
47+
public ManagedChannelBuilder<?> forAddress(String name, int port) {
48+
49+
ManagedChannelBuilder<?> builder = ManagedChannelBuilder.forAddress(name, port);
50+
51+
if (this.customizers != null) {
52+
this.customizers.stream()
53+
.forEach(customizer -> customizer.customize(builder));
54+
}
55+
return builder;
56+
}
57+
58+
public ManagedChannelBuilder<?> forTarget(String target) {
59+
ManagedChannelBuilder<?> builder = ManagedChannelBuilder.forTarget(target);
60+
if (this.customizers != null) {
61+
this.customizers.stream()
62+
.forEach(customizer -> customizer.customize(builder));
63+
}
64+
return builder;
65+
}
66+
67+
public ManagedChannelBuilder<?> inProcessChannelBuilder(String serverName) {
68+
ManagedChannelBuilder<?> builder = InProcessChannelBuilder.forName(serverName);
69+
if (this.customizers != null) {
70+
this.customizers.stream()
71+
.forEach(customizer -> customizer.customize(builder));
72+
}
73+
return builder;
74+
}
75+
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2013-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.sleuth.instrument.grpc;
18+
19+
import java.util.List;
20+
import java.util.Optional;
21+
22+
import brave.Tracing;
23+
import brave.grpc.GrpcTracing;
24+
import io.grpc.ServerInterceptor;
25+
import org.lognet.springboot.grpc.GRpcGlobalInterceptor;
26+
27+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
28+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
29+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
30+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
31+
import org.springframework.context.annotation.Bean;
32+
33+
/**
34+
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
35+
* Auto-configuration} enables span information propagation when using GRPC.
36+
*
37+
* This configuration is only enabled when both grpc-spring-boot-starter and
38+
* brave-instrumentation-grpc are on the classpath.
39+
*
40+
* @author tyler.vangorder
41+
*/
42+
@ConditionalOnClass({ GrpcTracing.class, GRpcGlobalInterceptor.class })
43+
@ConditionalOnProperty(value = "spring.sleuth.grpc.enabled", matchIfMissing = true)
44+
@ConditionalOnBean(Tracing.class)
45+
public class TraceGrpcAutoConfiguration {
46+
47+
@Bean
48+
public GrpcTracing grpcTracing(Tracing tracing) {
49+
return GrpcTracing.create(tracing);
50+
}
51+
52+
// Register a global interceptor for both the server
53+
@Bean
54+
@GRpcGlobalInterceptor
55+
ServerInterceptor grpcServerBraveInterceptor(GrpcTracing grpcTracing) {
56+
return grpcTracing.newServerInterceptor();
57+
}
58+
59+
// This is wrapper around gRPC's managed channel builder that is spring-aware
60+
@Bean
61+
@ConditionalOnMissingBean(SpringAwareManagedChannelBuilder.class)
62+
public SpringAwareManagedChannelBuilder managedChannelBuilder(
63+
Optional<List<GrpcManagedChannelBuilderCustomizer>> customizers) {
64+
return new SpringAwareManagedChannelBuilder(customizers);
65+
}
66+
67+
@Bean
68+
GrpcManagedChannelBuilderCustomizer tracingManagedChannelBuilderCustomizer(
69+
GrpcTracing grpcTracing) {
70+
return new TracingManagedChannelBuilderCustomizer(grpcTracing);
71+
}
72+
73+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.sleuth.instrument.grpc;
18+
19+
import brave.grpc.GrpcTracing;
20+
import io.grpc.ManagedChannelBuilder;
21+
22+
/**
23+
* @author tyler.vangorder
24+
*/
25+
public class TracingManagedChannelBuilderCustomizer
26+
implements GrpcManagedChannelBuilderCustomizer {
27+
28+
GrpcTracing grpcTracing;
29+
30+
public TracingManagedChannelBuilderCustomizer(GrpcTracing grpcTracing) {
31+
this.grpcTracing = grpcTracing;
32+
}
33+
34+
/**
35+
* Add brave's client interceptor to the builder.
36+
*/
37+
@Override
38+
public void customize(ManagedChannelBuilder<?> managedChannelBuilder) {
39+
managedChannelBuilder.intercept(this.grpcTracing.newClientInterceptor());
40+
}
41+
42+
}

spring-cloud-sleuth-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json

+6
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,11 @@
4646
"type": "java.lang.Boolean",
4747
"description": "Enable span information propagation when using Zuul.",
4848
"defaultValue": true
49+
},
50+
{
51+
"name": "spring.sleuth.grpc.enabled",
52+
"type": "java.lang.Boolean",
53+
"description": "Enable span information propagation when using GRPC.",
54+
"defaultValue": true
4955
}
5056
]}

spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ org.springframework.cloud.sleuth.instrument.rxjava.RxJavaAutoConfiguration,\
1818
org.springframework.cloud.sleuth.instrument.reactor.TraceReactorAutoConfiguration,\
1919
org.springframework.cloud.sleuth.instrument.web.TraceWebFluxAutoConfiguration,\
2020
org.springframework.cloud.sleuth.instrument.zuul.TraceZuulAutoConfiguration,\
21+
org.springframework.cloud.sleuth.instrument.grpc.TraceGrpcAutoConfiguration,\
2122
org.springframework.cloud.sleuth.instrument.messaging.TraceMessagingAutoConfiguration,\
2223
org.springframework.cloud.sleuth.instrument.messaging.TraceSpringIntegrationAutoConfiguration,\
2324
org.springframework.cloud.sleuth.instrument.messaging.websocket.TraceWebSocketAutoConfiguration,\

0 commit comments

Comments
 (0)