Skip to content

Add Spring WebFlux and WebClient tracing.#95

Merged
geoand merged 15 commits into
opentracing-contrib:masterfrom
csabakos:add-webflux-and-webclient-tracing
Mar 4, 2019
Merged

Add Spring WebFlux and WebClient tracing.#95
geoand merged 15 commits into
opentracing-contrib:masterfrom
csabakos:add-webflux-and-webclient-tracing

Conversation

@csabakos

@csabakos csabakos commented Feb 22, 2019

Copy link
Copy Markdown
Contributor

This adds Spring WebFlux and WebClient tracing.

I will also add auto-configuration for https://github.com/opentracing-contrib/java-reactor to opentracing-spring-cloud, which will complement these changes with automatic activation of the span in handler functions.

</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to replace MockRestServiceServer in the tests with something else (WireMock), because MockRestServiceServer does not support WebClient.

Comment thread pom.xml
<properties>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spring Framework 5 requires JRE 8 so I believe this is safe to change.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this is not only SB, the web package can be used with old spring web apps.

@geoand geoand Feb 25, 2019

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pavolloffay Spring Framework 5+ requires JDK 8+. See here

@pavolloffay pavolloffay left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@csabakos thanks for the PR! It looks good.

I would like to see a test where a client is used to from multiple threads (assuming it supports concurrent processing) and creates requests to instrumented web endpoint (can be reactive too)

public void testMultipleRequests() throws InterruptedException, ExecutionException {

I have noticed that some files were taken from spring-cloud-sleuth. I think we should add their license (We already doing it for some files in spring-cloud repository)

Comment thread README.md
get automatically traced different set of technologies e.g. `spring-cloud-netflix`, JMS or even more then
use project [opentracing-spring-cloud](https://github.com/opentracing-contrib/java-spring-cloud) instead.

For reactive applications, it is especially recommended to use `reactor` tracing from

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is reactor supported in opentracing-spring-cloud?

@csabakos csabakos Feb 27, 2019

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm planning to add Spring Boot auto-configuration for that in a separate pull request. I already have the code ready for it, but opentracing-reactor needs to be published to Maven central first. (See opentracing-contrib/java-reactor#1)

Assert.assertEquals("GET", span.tags().get(Tags.HTTP_METHOD.getKey()));
Assert.assertEquals(TEST_REST_TEMPLATE.getRootUri() + "/", span.tags().get(Tags.HTTP_URL.getKey()));
Assert.assertEquals(200, span.tags().get(Tags.HTTP_STATUS.getKey()));
Assert.assertEquals("WebFlux/java-spring-web", span.tags().get(Tags.COMPONENT.getKey()));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change java-spring-webflux to be consistent with other component names in this repo?

Comment thread opentracing-spring-web-starter/pom.xml Outdated
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<scope>provided</scope>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be optional? We don't want users to depend on webflux

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, optional makes more sense than provided.

*/
@Configuration
@ConditionalOnBean(Tracer.class)
@ConditionalOnClass(WebClient.class)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also do conditional on reactive?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ConditionalOnWebApplication refers to the server technology.

One could use the reactive WebClient in a Servlet-based server application, so I don't think that adding that conditional would be appropriate.

}

@Override
public void onCancel(final ClientRequest httpRequest, final Span span) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When does cancel happen?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent question! It looks like cancellation can happen in various circumstances:

  • If the user of the WebClient "unsubscribes" from the request (for example because they have a timeout that they reached and they no longer wish to wait for the result). I think in this case I think it is correct to log an error on the span, since timeout can be considered an error.
  • In various Reactor constructs, cancel() is called even when the Mono (successfully) emits an item. An example of this is Flux#next(). Since this is part of the normal (successful) behavior, I don't think we should log an error.

There does not seem to be a good way to differentiate between the above two cases in the tracing code, so I changed this to log a cancelled event instead of an error. It will be up to the user who is looking at the trace logs to determine if the cancellation indicates an error or not. (For example the user can infer this from the HTTP status code, etc.)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is a part of normal execution flow and it happes often we can avoid adding meta-data at all.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As an application developer, it is still valuable information that the subscription was cancelled. Cancellation does not happen in simple WebClient flows, only if you're doing something more complex (like retries, or firing off multiple requests and waiting for the fastest one to return).

I think it's worth have these events in the trace logs.

If other people find them bothersome, they can file an issue and we can make it optional, but I think that won't be the case.

@csabakos

Copy link
Copy Markdown
Contributor Author

@csabakos thanks for the PR! It looks good.

@pavolloffay Thank you for the review!

I would like to see a test where a client is used to from multiple threads (assuming it supports concurrent processing) and creates requests to instrumented web endpoint (can be reactive too)

java-spring-web/opentracing-spring-web/src/test/java/io/opentracing/contrib/spring/web/client/TracingAsyncRestTemplateTest.java

Line 65 in 5d7207a

public void testMultipleRequests() throws InterruptedException, ExecutionException {

I implemented the exact test that you linked to for WebClient: opentracing-spring-web/src/test/java/io/opentracing/contrib/spring/web/client/TracingWebClientTest.java. Is that sufficient or did you have something else in mind?

I have noticed that some files were taken from spring-cloud-sleuth. I think we should add their license (We already doing it for some files in spring-cloud repository)

Conveniently their license is also Apache License 2.0, but I will gladly add it. Would you mind pointing me to an existing example of what exactly needs to be done?

@pavolloffay

Copy link
Copy Markdown
Collaborator

Conveniently their license is also Apache License 2.0, but I will gladly add it. Would you mind pointing me to an existing example of what exactly needs to be done?

I think just add the license to the files and then exclude them in pom https://github.com/opentracing-contrib/java-spring-cloud/blob/master/pom.xml#L380

@pavolloffay pavolloffay left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@geoand could you please review too?

@AutoConfigureAfter(TracerAutoConfiguration.class)
@EnableConfigurationProperties(WebTracingProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnProperty(name = "opentracing.spring.web.enabled", havingValue = "true", matchIfMissing = true)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CONFIGURATION_PREFIX can be used

}

@Override
public void onCancel(final ClientRequest httpRequest, final Span span) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is a part of normal execution flow and it happes often we can avoid adding meta-data at all.

final String requestUrl = url + i;

final Scope parentSpan = mockTracer.buildSpan("foo").startActive(false);
parentSpan.span().setTag("request-url", requestUrl);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add tag on spanBuilder

for (int i = 0; i < numberOfCalls; i++) {
final String requestUrl = url + i;

final Scope parentSpan = mockTracer.buildSpan("foo").startActive(false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a span directly and remove cont. cont does not seems to be needed

@geoand

geoand commented Feb 27, 2019

Copy link
Copy Markdown
Collaborator

@geoand could you please review too?

I will do that tomorrow :)

…nfigure license-maven-plugin, and update license headers.
@csabakos

Copy link
Copy Markdown
Contributor Author

Conveniently their license is also Apache License 2.0, but I will gladly add it. Would you mind pointing me to an existing example of what exactly needs to be done?

I think just add the license to the files and then exclude them in pom https://github.com/opentracing-contrib/java-spring-cloud/blob/master/pom.xml#L380

Done. The pom did not have license-maven-plugin so I went ahead and added that and updated all of the license headers (so that the build passes).

@geoand

geoand commented Feb 28, 2019

Copy link
Copy Markdown
Collaborator

@csabakos Can you please resolve the conflict? Thanks

@csabakos

Copy link
Copy Markdown
Contributor Author

@csabakos Can you please resolve the conflict? Thanks

@geoand resolved by merging master into my feature branch. (Let me know if that's not the preferred way to do this.)

@geoand

geoand commented Feb 28, 2019

Copy link
Copy Markdown
Collaborator

That's up to @pavollofay to decide 😎

@ConditionalOnClass(WebClient.class)
@ConditionalOnProperty(prefix = WebClientTracingProperties.CONFIGURATION_PREFIX, name = "enabled", matchIfMissing = true)
@AutoConfigureAfter(TracerAutoConfiguration.class)
@EnableConfigurationProperties(WebClientTracingProperties.class)

@geoand geoand Feb 28, 2019

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should WebClientTracingProperties be renamed to ClientTracingProperties?
I mean the name is a little misleading since it's configuration for RestTemplate, AsyncRestTemplate and WebClient.
Also, the comments in WebClientTracingProperties should be updated to reflect this fact

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have an opinion about renaming - I could see arguments both pro and con. I'll leave the decision up to you and @pavolloffay. (Because the class is public, it is in theory part of the public API, so that'd be one reason for not renaming it.)

I will update the comments though.

@geoand geoand Feb 28, 2019

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's true that it's public, public configuration classes would rarely be used directly in application code. I would assume that at least a minor version bump would be warranted even without the rename, so some minor breaking could probably be expected.
Let's leave it up to @pavolloffay to decide :)

Comment thread opentracing-spring-web-starter/pom.xml Outdated
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<scope>compile</scope>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is default scope, so perhaps best to omit it?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be optional?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch! Looks like I only updated the POM in opentracing-spring-web-starter but not in opentracing-spring-web. Fixed now.

@geoand

geoand commented Feb 28, 2019

Copy link
Copy Markdown
Collaborator

Looks good from a Spring perspective 👍

@pavolloffay

Copy link
Copy Markdown
Collaborator

@geoand can this be merged?

@geoand

geoand commented Mar 4, 2019

Copy link
Copy Markdown
Collaborator

@pavolloffay yeah it LGTM. Should I merge and cut a release?

@pavolloffay

Copy link
Copy Markdown
Collaborator

👍

@geoand geoand merged commit 0ac36c3 into opentracing-contrib:master Mar 4, 2019
@geoand

geoand commented Mar 4, 2019

Copy link
Copy Markdown
Collaborator

Thanks for the awesome work @csabakos!

@csabakos

csabakos commented Mar 4, 2019

Copy link
Copy Markdown
Contributor Author

Thanks y'all for the prompt review and good comments!

I'll work on adding the Spring Boot starter for opentracing-reactor to opentracing-spring-cloud after opentracing-contrib/java-reactor#1 is completed.

@geoand

geoand commented Mar 4, 2019

Copy link
Copy Markdown
Collaborator

Thanks again @csabakos!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants