RED-9157 - Don't enable tracing if collector endpoint is not resolvable
This commit is contained in:
parent
52b67346e9
commit
3f7733e09b
@ -1,26 +1,27 @@
|
||||
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.observation.ObservationPredicate;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
import io.micrometer.tracing.contextpropagation.ObservationAwareSpanThreadLocalAccessor;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
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
|
||||
@ConditionalOnEnabledTracing
|
||||
@Import({
|
||||
TaskExecutionConfiguration.class,
|
||||
TracingRabbitConfiguration.class,
|
||||
WebMvcConfiguration.class
|
||||
WebMvcConfiguration.class,
|
||||
TracingSafetyCheck.class
|
||||
})
|
||||
@PropertySource("classpath:tracing-task.properties")
|
||||
@Slf4j
|
||||
@ -29,12 +30,18 @@ public class DefaultTracingAutoConfiguration {
|
||||
@Autowired
|
||||
Tracer tracer;
|
||||
|
||||
@Autowired
|
||||
TracingSafetyCheck tracingSafetyCheck;
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct() {
|
||||
|
||||
if (tracingSafetyCheck.isEndpointResolvable()) {
|
||||
ContextRegistry.getInstance().registerThreadLocalAccessor(new ObservationAwareSpanThreadLocalAccessor(tracer));
|
||||
|
||||
log.info("Tracing AutoConfiguration Loaded!");
|
||||
} else {
|
||||
log.info("Hostname is not resolvable, tracing is disabled.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,8 +1,9 @@
|
||||
package com.knecon.fforesight.tracing;
|
||||
|
||||
import com.knecon.fforesight.tenantcommons.RabbitTemplateMultiCustomizer;
|
||||
import com.knecon.fforesight.tenantcommons.SimpleMessageListenerContainerCustomizer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
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.OutputCaptureExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import com.knecon.fforesight.tenantcommons.RabbitTemplateMultiCustomizer;
|
||||
import com.knecon.fforesight.tenantcommons.SimpleMessageListenerContainerCustomizer;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
@Slf4j
|
||||
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
|
||||
public void testTracingAutoConfigurationLoaded(CapturedOutput output) {
|
||||
|
||||
this.contextRunner.run(context -> {
|
||||
assertThat(output.getOut()).contains("Tracing AutoConfiguration Loaded!");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testRabbitTracingEnabled() {
|
||||
|
||||
|
||||
@ -1,16 +1,31 @@
|
||||
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.test.simple.SimpleTracer;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@AutoConfiguration
|
||||
@Profile("test")
|
||||
public class SharedTestConfiguration {
|
||||
|
||||
|
||||
@Bean
|
||||
Tracer simpleTracer() {
|
||||
|
||||
return new SimpleTracer();
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public TracingSafetyCheck tracingSafetyCheck() {
|
||||
|
||||
TracingSafetyCheck mock = Mockito.mock(TracingSafetyCheck.class);
|
||||
Mockito.when(mock.isEndpointResolvable()).thenReturn(true);
|
||||
return mock;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
package com.knecon.fforesight.tracing;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.micrometer.tracing.Span;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
import io.micrometer.tracing.test.simple.SimpleTracer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
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.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.micrometer.tracing.Span;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
import io.micrometer.tracing.test.simple.SimpleTracer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
@Slf4j
|
||||
@ -30,18 +31,20 @@ public class TaskExecutionTracingTest {
|
||||
|
||||
private static final String ASYNC_SPAN_NAME = "Async Span";
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withPropertyValues("management.tracing.enabled=true")
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withPropertyValues("management.tracing.enabled=true")
|
||||
.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();
|
||||
|
||||
|
||||
@Test
|
||||
public void asyncMethodTraceTest(CapturedOutput output) {
|
||||
|
||||
this.contextRunner.run(context -> {
|
||||
SimpleTracer tracer = (SimpleTracer)context.getBean(Tracer.class);
|
||||
var asyncLogic = context.getBean(AsyncLogic.class);
|
||||
SimpleTracer tracer = (SimpleTracer) context.getBean(Tracer.class);
|
||||
AsyncLogic asyncLogic = context.getBean(AsyncLogic.class);
|
||||
|
||||
Observation firstSpan = Observation.createNotStarted("First span", observationRegistry).highCardinalityKeyValue("test", "test 1");
|
||||
try (Observation.Scope scope = firstSpan.start().openScope()) {
|
||||
@ -54,18 +57,18 @@ public class TaskExecutionTracingTest {
|
||||
String spanIdFromFuture = future.get(1, TimeUnit.SECONDS);
|
||||
log.info("Async in test with span - after call");
|
||||
BDDAssertions.then(spanIdFromFuture).isEqualTo(secondSpan.context().spanId());
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
secondSpan.end();
|
||||
}
|
||||
|
||||
log.info("Async in test with observation - after call");
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
firstSpan.stop();
|
||||
}
|
||||
|
||||
var tracerSpans = tracer.getSpans().stream().toList();
|
||||
var tracerSpans = tracer.getSpans()
|
||||
.stream()
|
||||
.toList();
|
||||
|
||||
assertThat(tracerSpans.size()).isEqualTo(2);
|
||||
assertThat(tracerSpans.get(1).getName()).isEqualTo(ASYNC_SPAN_NAME);
|
||||
@ -73,17 +76,23 @@ public class TaskExecutionTracingTest {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Component
|
||||
static class AsyncLogic {
|
||||
|
||||
@Autowired
|
||||
Tracer tracer;
|
||||
|
||||
|
||||
@Async("applicationTaskExecutor")
|
||||
public Future<String> asyncCall() {
|
||||
|
||||
log.info("TASK EXECUTOR");
|
||||
tracer.nextSpan().name(ASYNC_SPAN_NAME).tag("test", "test 3").start().end();
|
||||
String spanId = tracer.currentSpan().context().spanId();
|
||||
return CompletableFuture.supplyAsync(() -> spanId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user