Skip to content

Commit

Permalink
Support Spring's new RestClient with auto configuration (#3198) (#3199)
Browse files Browse the repository at this point in the history
* Support Spring's new RestClient with auto configuration (#3198)

- Spring RestClient support

* Support Spring's new RestClient with auto configuration (#3198)

- Test when HTTP call fails is fixed with disabling retry mechanism in the Apache Http Client

* apply code formatter etc.

* Use separate trace origin; Add to Spring Boot 3 sample

* changelog

---------

Co-authored-by: Nándor Holozsnyák <[email protected]>
Co-authored-by: Alexander Dinauer <[email protected]>
Co-authored-by: Alexander Dinauer <[email protected]>
  • Loading branch information
4 people authored Mar 21, 2024
1 parent 256ae96 commit 955f1ff
Show file tree
Hide file tree
Showing 9 changed files with 418 additions and 3 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Features

- Add support for Spring Rest Client ([#3199](https://github.com/getsentry/sentry-java/pull/3199))

## 7.6.0

### Features
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;

Expand All @@ -34,6 +35,11 @@ WebClient webClient(WebClient.Builder builder) {
return builder.build();
}

@Bean
RestClient restClient(RestClient.Builder builder) {
return builder.build();
}

@Bean
public JobDetailFactoryBean jobDetail() {
JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Hooks;
Expand All @@ -14,10 +15,12 @@
public class TodoController {
private final RestTemplate restTemplate;
private final WebClient webClient;
private final RestClient restClient;

public TodoController(RestTemplate restTemplate, WebClient webClient) {
public TodoController(RestTemplate restTemplate, WebClient webClient, RestClient restClient) {
this.restTemplate = restTemplate;
this.webClient = webClient;
this.restClient = restClient;
}

@GetMapping("/todo/{id}")
Expand All @@ -42,4 +45,13 @@ Todo todoWebClient(@PathVariable Long id) {
.map(response -> response)))
.block();
}

@GetMapping("/todo-restclient/{id}")
Todo todoRestClient(@PathVariable Long id) {
return restClient
.get()
.uri("https://jsonplaceholder.typicode.com/todos/{id}", id)
.retrieve()
.body(Todo.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
Expand All @@ -65,6 +66,7 @@
import org.springframework.graphql.execution.DataFetcherExceptionResolverAdapter;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.servlet.HandlerExceptionResolver;
Expand Down Expand Up @@ -357,6 +359,17 @@ public SentrySpanRestTemplateCustomizer sentrySpanRestTemplateCustomizer(IHub hu
}
}

@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(RestClientAutoConfiguration.class)
@ConditionalOnClass(RestClient.class)
@Open
static class SentrySpanRestClientConfiguration {
@Bean
public SentrySpanRestClientCustomizer sentrySpanRestClientCustomizer(IHub hub) {
return new SentrySpanRestClientCustomizer(hub);
}
}

@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(WebClientAutoConfiguration.class)
@ConditionalOnClass(WebClient.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.sentry.spring.boot.jakarta;

import com.jakewharton.nopen.annotation.Open;
import io.sentry.IHub;
import io.sentry.spring.jakarta.tracing.SentrySpanClientHttpRequestInterceptor;
import org.jetbrains.annotations.NotNull;
import org.springframework.boot.web.client.RestClientCustomizer;
import org.springframework.web.client.RestClient;

@Open
class SentrySpanRestClientCustomizer implements RestClientCustomizer {
private final @NotNull SentrySpanClientHttpRequestInterceptor interceptor;

public SentrySpanRestClientCustomizer(final @NotNull IHub hub) {
this.interceptor = new SentrySpanClientHttpRequestInterceptor(hub, false);
}

@Override
public void customize(final @NotNull RestClient.Builder restClientBuilder) {
restClientBuilder.requestInterceptors(
clientHttpRequestInterceptors -> {
// As the SentrySpanClientHttpRequestInterceptor is being created in this class, this
// might not work
// if somebody registers it from an outside.
if (!clientHttpRequestInterceptors.contains(interceptor)) {
clientHttpRequestInterceptors.add(interceptor);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.scheduling.quartz.SchedulerFactoryBean
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.client.RestClient
import org.springframework.web.client.RestTemplate
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.servlet.HandlerExceptionResolver
Expand Down Expand Up @@ -649,6 +650,23 @@ class SentryAutoConfigurationTest {
}
}

@Test
fun `when tracing is enabled and RestClient is on the classpath, SentrySpanRestClientCustomizer bean is created`() {
contextRunner.withPropertyValues("sentry.dsn=http://key@localhost/proj", "sentry.traces-sample-rate=1.0")
.run {
assertThat(it).hasSingleBean(SentrySpanRestClientCustomizer::class.java)
}
}

@Test
fun `when tracing is enabled and RestClient is not on the classpath, SentrySpanRestClientCustomizer bean is not created`() {
contextRunner.withPropertyValues("sentry.dsn=http://key@localhost/proj", "sentry.traces-sample-rate=1.0")
.withClassLoader(FilteredClassLoader(RestClient::class.java))
.run {
assertThat(it).doesNotHaveBean(SentrySpanRestClientCustomizer::class.java)
}
}

@Test
fun `when tracing is enabled and WebClient is on the classpath, SentrySpanWebClientCustomizer bean is created`() {
contextRunner.withPropertyValues("sentry.dsn=http://key@localhost/proj", "sentry.traces-sample-rate=1.0")
Expand Down
Loading

0 comments on commit 955f1ff

Please sign in to comment.