Compare commits

...

7 Commits
0.4.0 ... main

Author SHA1 Message Date
Andrei Isvoran
c8d77717ad Merge branch 'RED-9157' into 'main'
RED-9157 - Don't enable tracing if collector endpoint is not resolvable

See merge request fforesight/tracing-commons!5
2024-05-15 09:36:12 +02:00
Andrei Isvoran
3f7733e09b RED-9157 - Don't enable tracing if collector endpoint is not resolvable 2024-05-15 09:36:12 +02:00
Kevin Tumma
52b67346e9 Update .gitlab-ci.yml file 2024-05-14 12:59:11 +02:00
Andrei Isvoran
fad7aea7c8 Merge branch 'upgrade-spring' into 'main'
Upgrade spring

See merge request fforesight/tracing-commons!4
2024-05-14 12:56:06 +02:00
Andrei Isvoran
73aa1b6f51 Upgrade spring 2024-05-14 12:56:06 +02:00
Dominique Eifländer
0933adeaf1 Merge branch 'RED-8171' into 'main'
RED-8171: Fixed not working auto configuration

See merge request fforesight/tracing-commons!3
2024-02-02 12:23:28 +01:00
Dominique Eifländer
edf56412da RED-8171: Fixed not working auto configuration 2024-02-02 12:22:46 +01:00
8 changed files with 152 additions and 47 deletions

View File

@ -1,4 +1,8 @@
include: include:
- project: 'gitlab/gitlab' - project: 'gitlab/gitlab'
ref: 'main' ref: 'main'
file: 'ci-templates/maven_deps.yml' file: 'ci-templates/maven_deps.yml'
stages:
- test
- versioning
- deploy

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version> <version>3.2.3</version>
<relativePath/> <relativePath/>
</parent> </parent>

View File

@ -1,27 +1,27 @@
package com.knecon.fforesight.tracing; package com.knecon.fforesight.tracing;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.tracing.ConditionalOnEnabledTracing;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.server.observation.ServerRequestObservationContext;
import io.micrometer.context.ContextRegistry; import io.micrometer.context.ContextRegistry;
import io.micrometer.observation.ObservationPredicate; import io.micrometer.observation.ObservationPredicate;
import io.micrometer.tracing.Tracer; import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.contextpropagation.ObservationAwareSpanThreadLocalAccessor; import io.micrometer.tracing.contextpropagation.ObservationAwareSpanThreadLocalAccessor;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.tracing.ConditionalOnEnabledTracing;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.server.observation.ServerRequestObservationContext;
@AutoConfiguration @AutoConfiguration
@ConditionalOnBean({Tracer.class})
@ConditionalOnEnabledTracing @ConditionalOnEnabledTracing
@Import({ @Import({
TaskExecutionConfiguration.class, TaskExecutionConfiguration.class,
TracingRabbitConfiguration.class, TracingRabbitConfiguration.class,
WebMvcConfiguration.class WebMvcConfiguration.class,
TracingSafetyCheck.class
}) })
@PropertySource("classpath:tracing-task.properties") @PropertySource("classpath:tracing-task.properties")
@Slf4j @Slf4j
@ -30,12 +30,18 @@ public class DefaultTracingAutoConfiguration {
@Autowired @Autowired
Tracer tracer; Tracer tracer;
@Autowired
TracingSafetyCheck tracingSafetyCheck;
@PostConstruct @PostConstruct
public void postConstruct() { public void postConstruct() {
ContextRegistry.getInstance().registerThreadLocalAccessor(new ObservationAwareSpanThreadLocalAccessor(tracer)); if (tracingSafetyCheck.isEndpointResolvable()) {
ContextRegistry.getInstance().registerThreadLocalAccessor(new ObservationAwareSpanThreadLocalAccessor(tracer));
log.info("Tracing AutoConfiguration Loaded!"); log.info("Tracing AutoConfiguration Loaded!");
} else {
log.info("Hostname is not resolvable, tracing is disabled.");
}
} }

View File

@ -0,0 +1,22 @@
package com.knecon.fforesight.tracing;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Configuration
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OpenTelemetryConfig {
@Value("${management.otlp.tracing.endpoint}")
private String otlpEndpoint;
@Value("${management.tracing.enabled}")
private boolean tracingEnabled;
}

View File

@ -0,0 +1,45 @@
package com.knecon.fforesight.tracing;
import java.net.HttpURLConnection;
import java.net.URL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.Builder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@Service
@Builder
@Getter
@Slf4j
public class TracingSafetyCheck {
@Autowired
private OpenTelemetryConfig openTelemetryConfig;
public boolean isEndpointResolvable() {
String endpoint = openTelemetryConfig.getOtlpEndpoint();
boolean resolvable = endpoint != null && canResolveEndpoint(endpoint);
log.info("Endpoint {} is resolvable: {}", endpoint, resolvable);
return resolvable;
}
private boolean canResolveEndpoint(String endpoint) {
try {
URL url = new URL(endpoint);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("HEAD");
int responseCode = connection.getResponseCode();
return (200 <= responseCode && responseCode <= 399);
} catch (Exception e) {
return false;
}
}
}

View File

@ -1,8 +1,9 @@
package com.knecon.fforesight.tracing; package com.knecon.fforesight.tracing;
import com.knecon.fforesight.tenantcommons.RabbitTemplateMultiCustomizer; import static org.assertj.core.api.Assertions.assertThat;
import com.knecon.fforesight.tenantcommons.SimpleMessageListenerContainerCustomizer; import static org.mockito.Mockito.mock;
import lombok.extern.slf4j.Slf4j; import static org.mockito.Mockito.verify;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
@ -11,26 +12,29 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.boot.test.system.OutputCaptureExtension;
import static org.assertj.core.api.Assertions.assertThat; import com.knecon.fforesight.tenantcommons.RabbitTemplateMultiCustomizer;
import static org.mockito.Mockito.mock; import com.knecon.fforesight.tenantcommons.SimpleMessageListenerContainerCustomizer;
import static org.mockito.Mockito.verify;
import lombok.extern.slf4j.Slf4j;
@ExtendWith(OutputCaptureExtension.class) @ExtendWith(OutputCaptureExtension.class)
@Slf4j @Slf4j
public class ObservationEnabledTest { public class ObservationEnabledTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withPropertyValues("management.tracing.enabled=true")
.withPropertyValues("spring.profiles.active=test")
.withUserConfiguration(SharedTestConfiguration.class, DefaultTracingAutoConfiguration.class, OpenTelemetryConfig.class);
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withPropertyValues("management.tracing.enabled=true")
.withUserConfiguration(SharedTestConfiguration.class, DefaultTracingAutoConfiguration.class);
@Test @Test
public void testTracingAutoConfigurationLoaded(CapturedOutput output) { public void testTracingAutoConfigurationLoaded(CapturedOutput output) {
this.contextRunner.run(context -> { this.contextRunner.run(context -> {
assertThat(output.getOut()).contains("Tracing AutoConfiguration Loaded!"); assertThat(output.getOut()).contains("Tracing AutoConfiguration Loaded!");
}); });
} }
@Test @Test
public void testRabbitTracingEnabled() { public void testRabbitTracingEnabled() {

View File

@ -1,16 +1,31 @@
package com.knecon.fforesight.tracing; package com.knecon.fforesight.tracing;
import org.mockito.Mockito;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import io.micrometer.tracing.Tracer; import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.test.simple.SimpleTracer; import io.micrometer.tracing.test.simple.SimpleTracer;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
@AutoConfiguration @AutoConfiguration
@Profile("test")
public class SharedTestConfiguration { public class SharedTestConfiguration {
@Bean @Bean
Tracer simpleTracer() { Tracer simpleTracer() {
return new SimpleTracer(); return new SimpleTracer();
} }
@Bean
public TracingSafetyCheck tracingSafetyCheck() {
TracingSafetyCheck mock = Mockito.mock(TracingSafetyCheck.class);
Mockito.when(mock.isEndpointResolvable()).thenReturn(true);
return mock;
}
} }

View File

@ -1,11 +1,11 @@
package com.knecon.fforesight.tracing; package com.knecon.fforesight.tracing;
import io.micrometer.observation.Observation; import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.tracing.Span; import java.util.concurrent.CompletableFuture;
import io.micrometer.tracing.Tracer; import java.util.concurrent.Future;
import io.micrometer.tracing.test.simple.SimpleTracer; import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.api.BDDAssertions; import org.assertj.core.api.BDDAssertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
@ -18,11 +18,12 @@ import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture; import io.micrometer.observation.Observation;
import java.util.concurrent.Future; import io.micrometer.observation.ObservationRegistry;
import java.util.concurrent.TimeUnit; import io.micrometer.tracing.Span;
import io.micrometer.tracing.Tracer;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import io.micrometer.tracing.test.simple.SimpleTracer;
import lombok.extern.slf4j.Slf4j;
@ExtendWith(OutputCaptureExtension.class) @ExtendWith(OutputCaptureExtension.class)
@Slf4j @Slf4j
@ -30,18 +31,20 @@ public class TaskExecutionTracingTest {
private static final String ASYNC_SPAN_NAME = "Async Span"; private static final String ASYNC_SPAN_NAME = "Async Span";
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withPropertyValues("management.tracing.enabled=true")
.withPropertyValues("management.tracing.enabled=true")
.withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class)) .withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class))
.withUserConfiguration(SharedTestConfiguration.class, AsyncLogic.class, DefaultTracingAutoConfiguration.class); .withPropertyValues("spring.profiles.active=test")
.withUserConfiguration(SharedTestConfiguration.class, AsyncLogic.class, DefaultTracingAutoConfiguration.class, OpenTelemetryConfig.class);
private final ObservationRegistry observationRegistry = ObservationRegistry.create(); private final ObservationRegistry observationRegistry = ObservationRegistry.create();
@Test @Test
public void asyncMethodTraceTest(CapturedOutput output) { public void asyncMethodTraceTest(CapturedOutput output) {
this.contextRunner.run(context -> { this.contextRunner.run(context -> {
SimpleTracer tracer = (SimpleTracer)context.getBean(Tracer.class); SimpleTracer tracer = (SimpleTracer) context.getBean(Tracer.class);
var asyncLogic = context.getBean(AsyncLogic.class); AsyncLogic asyncLogic = context.getBean(AsyncLogic.class);
Observation firstSpan = Observation.createNotStarted("First span", observationRegistry).highCardinalityKeyValue("test", "test 1"); Observation firstSpan = Observation.createNotStarted("First span", observationRegistry).highCardinalityKeyValue("test", "test 1");
try (Observation.Scope scope = firstSpan.start().openScope()) { try (Observation.Scope scope = firstSpan.start().openScope()) {
@ -54,18 +57,18 @@ public class TaskExecutionTracingTest {
String spanIdFromFuture = future.get(1, TimeUnit.SECONDS); String spanIdFromFuture = future.get(1, TimeUnit.SECONDS);
log.info("Async in test with span - after call"); log.info("Async in test with span - after call");
BDDAssertions.then(spanIdFromFuture).isEqualTo(secondSpan.context().spanId()); BDDAssertions.then(spanIdFromFuture).isEqualTo(secondSpan.context().spanId());
} } finally {
finally {
secondSpan.end(); secondSpan.end();
} }
log.info("Async in test with observation - after call"); log.info("Async in test with observation - after call");
} } finally {
finally {
firstSpan.stop(); firstSpan.stop();
} }
var tracerSpans = tracer.getSpans().stream().toList(); var tracerSpans = tracer.getSpans()
.stream()
.toList();
assertThat(tracerSpans.size()).isEqualTo(2); assertThat(tracerSpans.size()).isEqualTo(2);
assertThat(tracerSpans.get(1).getName()).isEqualTo(ASYNC_SPAN_NAME); assertThat(tracerSpans.get(1).getName()).isEqualTo(ASYNC_SPAN_NAME);
@ -73,17 +76,23 @@ public class TaskExecutionTracingTest {
}); });
} }
@Component @Component
static class AsyncLogic { static class AsyncLogic {
@Autowired @Autowired
Tracer tracer; Tracer tracer;
@Async("applicationTaskExecutor") @Async("applicationTaskExecutor")
public Future<String> asyncCall() { public Future<String> asyncCall() {
log.info("TASK EXECUTOR"); log.info("TASK EXECUTOR");
tracer.nextSpan().name(ASYNC_SPAN_NAME).tag("test", "test 3").start().end(); tracer.nextSpan().name(ASYNC_SPAN_NAME).tag("test", "test 3").start().end();
String spanId = tracer.currentSpan().context().spanId(); String spanId = tracer.currentSpan().context().spanId();
return CompletableFuture.supplyAsync(() -> spanId); return CompletableFuture.supplyAsync(() -> spanId);
} }
} }
} }