diff --git a/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerTracerBuilder.java b/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerTracerBuilder.java index 254a1888276..91abf856a63 100644 --- a/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerTracerBuilder.java +++ b/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerTracerBuilder.java @@ -169,6 +169,7 @@ public class JaegerTracerBuilder implements TracerBuilder { private byte[] certificate; private byte[] trustedCertificates; private String path; + private Config config; /** * Default constructor, does not modify any state. @@ -253,6 +254,7 @@ public JaegerTracerBuilder addTracerTag(String key, boolean value) { @Override public JaegerTracerBuilder config(Config config) { + this.config = config; config.get("enabled").asBoolean().ifPresent(this::enabled); config.get("service").asString().ifPresent(this::serviceName); config.get("protocol").asString().ifPresent(this::collectorProtocol); @@ -419,6 +421,12 @@ public JaegerTracerBuilder addPropagation(PropagationFormat propagationFormat) { public Tracer build() { Tracer result; + if (HelidonOpenTelemetry.AgentDetector.isAgentPresent(config)) { + return HelidonOpenTelemetry.create(GlobalOpenTelemetry.get(), + GlobalOpenTelemetry.getTracer(this.serviceName), + tags); + } + if (enabled) { if (serviceName == null) { throw new IllegalArgumentException( @@ -449,10 +457,10 @@ public Tracer build() { Resource serviceName = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, this.serviceName)); OpenTelemetry ot = OpenTelemetrySdk.builder() .setTracerProvider(SdkTracerProvider.builder() - .addSpanProcessor(SimpleSpanProcessor.create(exporter)) - .setSampler(sampler) - .setResource(serviceName) - .build()) + .addSpanProcessor(SimpleSpanProcessor.create(exporter)) + .setSampler(sampler) + .setResource(serviceName) + .build()) .setPropagators(ContextPropagators.create(TextMapPropagator.composite(createPropagators()))) .build(); diff --git a/tracing/opentelemetry/pom.xml b/tracing/opentelemetry/pom.xml index 19866db6991..bf302256f1b 100644 --- a/tracing/opentelemetry/pom.xml +++ b/tracing/opentelemetry/pom.xml @@ -58,5 +58,15 @@ hamcrest-all test + + io.helidon.microprofile.tests + helidon-microprofile-tests-junit5 + test + + + io.helidon.microprofile.server + helidon-microprofile-server + test + diff --git a/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/HelidonOpenTelemetry.java b/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/HelidonOpenTelemetry.java index 2d074232265..e37f315f54e 100644 --- a/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/HelidonOpenTelemetry.java +++ b/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/HelidonOpenTelemetry.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Oracle and/or its affiliates. + * Copyright (c) 2022, 2023 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,9 @@ import java.util.Map; +import java.util.Optional; + +import io.helidon.config.Config; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.Span; @@ -26,6 +29,10 @@ * Open Telemetry factory methods to create wrappers for Open Telemetry types. */ public final class HelidonOpenTelemetry { + + private static final System.Logger LOGGER = System.getLogger(HelidonOpenTelemetry.class.getName()); + static final String OTEL_AGENT_PRESENT_PROPERTY = "otel.agent.present"; + static final String IO_OPENTELEMETRY_JAVAAGENT = "io.opentelemetry.javaagent"; private HelidonOpenTelemetry() { } /** @@ -49,4 +56,55 @@ public static OpenTelemetryTracer create(OpenTelemetry telemetry, Tracer tracer, public static io.helidon.tracing.Span create(Span span) { return new OpenTelemetrySpan(span); } + + + /** + * Check if OpenTelemetry is present by indirect properties. + * This class does best explicit check if OTEL_AGENT_PRESENT_PROPERTY config property is set and uses its + * value to set the behaviour of OpenTelemetry producer. + * + * If the value is not explicitly set, the detector does best effort to estimate indirect means if the agent is present. + * This detector may stop working if OTEL changes the indirect indicators. + */ + public static final class AgentDetector { + + //Private constructor for a utility class. + private AgentDetector() { + } + + /** + * Check if the OTEL Agent is present. + * + * @param config Configuration + * @return boolean + */ + public static boolean isAgentPresent(Config config) { + + //Explicitly check if agent property is set + if (config != null) { + Optional agentPresent = config.get(OTEL_AGENT_PRESENT_PROPERTY).asBoolean().asOptional(); + if (agentPresent.isPresent()) { + return agentPresent.get(); + } + } + + if (checkContext() || checkSystemProperties()) { + if (LOGGER.isLoggable(System.Logger.Level.INFO)) { + LOGGER.log(System.Logger.Level.INFO, "OpenTelemetry Agent detected"); + } + return true; + } + return false; + } + + private static boolean checkSystemProperties() { + return System.getProperties().stringPropertyNames() + .stream() + .anyMatch(property -> property.contains(IO_OPENTELEMETRY_JAVAAGENT)); + } + + private static boolean checkContext() { + return io.opentelemetry.context.Context.current().getClass().getName().contains("agent"); + } + } } diff --git a/tracing/opentelemetry/src/test/java/io/helidon/tracing/opentelemetry/AgentDetectorTest.java b/tracing/opentelemetry/src/test/java/io/helidon/tracing/opentelemetry/AgentDetectorTest.java new file mode 100644 index 00000000000..af99738670b --- /dev/null +++ b/tracing/opentelemetry/src/test/java/io/helidon/tracing/opentelemetry/AgentDetectorTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.helidon.tracing.opentelemetry; + +import io.helidon.config.Config; +import io.helidon.microprofile.server.ServerCdiExtension; +import io.helidon.microprofile.tests.junit5.AddConfig; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.HelidonTest; +import jakarta.enterprise.inject.spi.CDI; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + + +/** + * Check Agent Detector working correctly. + */ +@HelidonTest(resetPerTest = true) +@AddExtension(ServerCdiExtension.class) +class AgentDetectorTest { + + @Test + @AddConfig(key = HelidonOpenTelemetry.OTEL_AGENT_PRESENT_PROPERTY, value = "true") + void shouldBeNoOpTelemetry() { + Config config = CDI.current().select(Config.class).get(); + boolean present = HelidonOpenTelemetry.AgentDetector.isAgentPresent(config); + assertThat(present, is(true)); + } + + @Test + @AddConfig(key = HelidonOpenTelemetry.OTEL_AGENT_PRESENT_PROPERTY, value = "false") + void shouldNotBeNoOpTelemetry() { + Config config = CDI.current().select(Config.class).get(); + boolean present = HelidonOpenTelemetry.AgentDetector.isAgentPresent(config); + assertThat(present, is(false)); + } + + @Test + void checkEnvVariable() { + System.setProperty(HelidonOpenTelemetry.IO_OPENTELEMETRY_JAVAAGENT, "true"); + Config config = CDI.current().select(Config.class).get(); + boolean present = HelidonOpenTelemetry.AgentDetector.isAgentPresent(config); + assertThat(present, is(true)); + } +} \ No newline at end of file diff --git a/tracing/opentelemetry/src/test/java/io/helidon/tracing/opentelemetery/BaggageTest.java b/tracing/opentelemetry/src/test/java/io/helidon/tracing/opentelemetry/BaggageTest.java similarity index 95% rename from tracing/opentelemetry/src/test/java/io/helidon/tracing/opentelemetery/BaggageTest.java rename to tracing/opentelemetry/src/test/java/io/helidon/tracing/opentelemetry/BaggageTest.java index 5cc9a1ad0ab..216df0bce93 100644 --- a/tracing/opentelemetry/src/test/java/io/helidon/tracing/opentelemetery/BaggageTest.java +++ b/tracing/opentelemetry/src/test/java/io/helidon/tracing/opentelemetry/BaggageTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.tracing.opentelemetery; +package io.helidon.tracing.opentelemetry; import java.util.Optional; @@ -37,7 +37,7 @@ class BaggageTest { private final Tracer tracer = TracerBuilder.create("test-service").registerGlobal(false).build(); @Test - void testBaggage(){ + void testBaggage() { Span span = tracer.spanBuilder("test-span").start(); Span spanWithBaggage = span.baggage("key", "value"); Optional result = spanWithBaggage.baggage("key"); @@ -47,7 +47,7 @@ void testBaggage(){ } @Test - void testBadBaggage(){ + void testBadBaggage() { Span span = tracer.spanBuilder("test-bad-span").start(); assertThrows(NullPointerException.class, () -> span.baggage(null, "value")); assertThrows(NullPointerException.class, () -> span.baggage("key", null));