From 91627f70709222d8eaaab75958c2ff0d14ae2988 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Mon, 14 Oct 2024 07:43:49 -0500 Subject: [PATCH] Add context propagation to the Zipkin tracing provider (#9119) * Add context propagation to the Zipkin tracing provider Signed-off-by: Tim Quinn --- .../OpenTelemetryDataPropagationProvider.java | 2 +- .../OpenTracingTracerProvider.java | 27 ++++++- tracing/providers/zipkin/pom.xml | 34 ++++++-- .../zipkin/ZipkinDataPropagationProvider.java | 80 +++++++++++++++++++ .../zipkin/ZipkinTracerProvider.java | 6 ++ .../zipkin/src/main/java/module-info.java | 3 + .../src/test/resources/application.yaml | 5 +- 7 files changed, 145 insertions(+), 12 deletions(-) create mode 100644 tracing/providers/zipkin/src/main/java/io/helidon/tracing/providers/zipkin/ZipkinDataPropagationProvider.java diff --git a/tracing/providers/opentelemetry/src/main/java/io/helidon/tracing/providers/opentelemetry/OpenTelemetryDataPropagationProvider.java b/tracing/providers/opentelemetry/src/main/java/io/helidon/tracing/providers/opentelemetry/OpenTelemetryDataPropagationProvider.java index 014c1283c32..1705dc40ea7 100644 --- a/tracing/providers/opentelemetry/src/main/java/io/helidon/tracing/providers/opentelemetry/OpenTelemetryDataPropagationProvider.java +++ b/tracing/providers/opentelemetry/src/main/java/io/helidon/tracing/providers/opentelemetry/OpenTelemetryDataPropagationProvider.java @@ -62,7 +62,7 @@ public void propagateData(OpenTelemetryContext context) { } /** - * OpenTelementry context. + * OpenTelemetry context. */ public static class OpenTelemetryContext { private final Span span; diff --git a/tracing/providers/opentracing/src/main/java/io/helidon/tracing/providers/opentracing/OpenTracingTracerProvider.java b/tracing/providers/opentracing/src/main/java/io/helidon/tracing/providers/opentracing/OpenTracingTracerProvider.java index 2926fa3f385..c3c887be51e 100644 --- a/tracing/providers/opentracing/src/main/java/io/helidon/tracing/providers/opentracing/OpenTracingTracerProvider.java +++ b/tracing/providers/opentracing/src/main/java/io/helidon/tracing/providers/opentracing/OpenTracingTracerProvider.java @@ -23,6 +23,8 @@ import io.helidon.common.LazyValue; import io.helidon.common.Weight; import io.helidon.common.Weighted; +import io.helidon.common.config.Config; +import io.helidon.common.config.GlobalConfig; import io.helidon.tracing.Span; import io.helidon.tracing.SpanListener; import io.helidon.tracing.Tracer; @@ -35,6 +37,10 @@ /** * {@link java.util.ServiceLoader} service implementation of {@link io.helidon.tracing.spi.TracerProvider} for Open Tracing * tracers. + *

+ * When dealing with the global tracer, manage both the Helidon one and also the OpenTracing one in concert, whether + * defaulting them or assigning them via {@link #global(io.helidon.tracing.Tracer)}. + *

*/ @Weight(Weighted.DEFAULT_WEIGHT - 50) // low weight, so it is easy to override public class OpenTracingTracerProvider implements TracerProvider { @@ -42,6 +48,19 @@ public class OpenTracingTracerProvider implements TracerProvider { private static final LazyValue> SPAN_LISTENERS = LazyValue.create(() -> HelidonServiceLoader.create(ServiceLoader.load(SpanListener.class)).asList()); + private LazyValue globalHelidonTracer = LazyValue.create(() -> { + Config tracingConfig = GlobalConfig.config().get("tracing"); + + // Set up to create an explicit OpenTracing tracer only if we have config for tracing, indicating that the user wants + // something other than the no-op implementation. + if (tracingConfig.exists()) { + io.opentracing.Tracer openTracingTracer = OpenTracingTracerBuilder.create(tracingConfig) + .build(); + GlobalTracer.registerIfAbsent(openTracingTracer); + } + return OpenTracingTracer.create(GlobalTracer.get()); + }); + @Override public TracerBuilder createBuilder() { return OpenTracingTracer.builder(); @@ -49,13 +68,17 @@ public TracerBuilder createBuilder() { @Override public Tracer global() { - return OpenTracingTracer.create(GlobalTracer.get()); + return globalHelidonTracer.get(); } @Override public void global(Tracer tracer) { if (tracer instanceof OpenTracingTracer opt) { - GlobalTracer.registerIfAbsent(opt.openTracing()); + GlobalTracer.registerIfAbsent(() -> { + io.opentracing.Tracer openTracingTracer = opt.openTracing(); + globalHelidonTracer = LazyValue.create(OpenTracingTracer.create(openTracingTracer)); + return openTracingTracer; + }); } } diff --git a/tracing/providers/zipkin/pom.xml b/tracing/providers/zipkin/pom.xml index bdde6132ee3..6073a49b77a 100644 --- a/tracing/providers/zipkin/pom.xml +++ b/tracing/providers/zipkin/pom.xml @@ -57,6 +57,10 @@ io.opentracing.brave brave-opentracing + + io.helidon.common + helidon-common-context + io.helidon.common.features helidon-common-features-api @@ -138,17 +142,33 @@ org.apache.maven.plugins maven-surefire-plugin - - - io.helidon.tracing.providers.tests.TestTracerAndSpanPropagation.java - - + + + default-test + + + io.helidon.tracing.providers.tests.TestTracerAndSpanPropagation.java + + + + + zipkin-propagation-test + + test + + + + io.helidon.tracing.providers.tests.TestTracerAndSpanPropagation.java + + + + diff --git a/tracing/providers/zipkin/src/main/java/io/helidon/tracing/providers/zipkin/ZipkinDataPropagationProvider.java b/tracing/providers/zipkin/src/main/java/io/helidon/tracing/providers/zipkin/ZipkinDataPropagationProvider.java new file mode 100644 index 00000000000..123d32fb83b --- /dev/null +++ b/tracing/providers/zipkin/src/main/java/io/helidon/tracing/providers/zipkin/ZipkinDataPropagationProvider.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024 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.providers.zipkin; + +import io.helidon.common.context.Contexts; +import io.helidon.common.context.spi.DataPropagationProvider; + +import io.opentracing.Scope; +import io.opentracing.Span; +import io.opentracing.Tracer; +import io.opentracing.util.GlobalTracer; + +/** + * Data propagation provider for the Helidon Zipkin tracing provider. + */ +public class ZipkinDataPropagationProvider implements DataPropagationProvider { + + private static final System.Logger LOGGER = System.getLogger(ZipkinDataPropagationProvider.class.getName()); + + /** + * Creates new provider; public for service loading. + */ + public ZipkinDataPropagationProvider() { + } + + @Override + public ZipkinContext data() { + Tracer tracer = Contexts.context() + .flatMap(ctx -> ctx.get(Tracer.class)) + .orElseGet(GlobalTracer::get); + Span span = tracer.activeSpan(); + return new ZipkinContext(tracer, span); + } + + @Override + public void propagateData(ZipkinContext data) { + if (data != null && data.span != null) { + data.scope = data.tracer.activateSpan(data.span); + } + } + + @Override + public void clearData(ZipkinContext data) { + if (data != null && data.scope != null) { + try { + data.scope.close(); + } catch (Exception e) { + LOGGER.log(System.Logger.Level.TRACE, "Cannot close tracing span", e); + } + } + } + + /** + * Zipkin-specific propagation context. + */ + static class ZipkinContext { + + private final Tracer tracer; + private final Span span; + private Scope scope; + + private ZipkinContext(Tracer tracer, Span span) { + this.tracer = tracer; + this.span = span; + } + } +} diff --git a/tracing/providers/zipkin/src/main/java/io/helidon/tracing/providers/zipkin/ZipkinTracerProvider.java b/tracing/providers/zipkin/src/main/java/io/helidon/tracing/providers/zipkin/ZipkinTracerProvider.java index 2077f3901bf..fcdc8f2e816 100644 --- a/tracing/providers/zipkin/src/main/java/io/helidon/tracing/providers/zipkin/ZipkinTracerProvider.java +++ b/tracing/providers/zipkin/src/main/java/io/helidon/tracing/providers/zipkin/ZipkinTracerProvider.java @@ -48,6 +48,12 @@ public class ZipkinTracerProvider implements OpenTracingProvider { private static final List TRACING_CONTEXT_PROPAGATION_HEADERS = List.of(X_OT_SPAN_CONTEXT, X_B3_TRACE_ID, X_B3_SPAN_ID, X_B3_PARENT_SPAN_ID, X_B3_SAMPLED, X_B3_FLAGS); + /** + * Public constructor for service loading. + */ + public ZipkinTracerProvider() { + } + @Override public ZipkinTracerBuilder createBuilder() { return ZipkinTracerBuilder.create(); diff --git a/tracing/providers/zipkin/src/main/java/module-info.java b/tracing/providers/zipkin/src/main/java/module-info.java index afc01904698..23d95ba0206 100644 --- a/tracing/providers/zipkin/src/main/java/module-info.java +++ b/tracing/providers/zipkin/src/main/java/module-info.java @@ -30,6 +30,7 @@ requires brave.opentracing; requires brave; requires io.helidon.common; + requires io.helidon.common.context; requires io.helidon.tracing.providers.opentracing; requires io.opentracing.noop; requires io.opentracing.util; @@ -51,4 +52,6 @@ provides io.helidon.tracing.providers.opentracing.spi.OpenTracingProvider with io.helidon.tracing.providers.zipkin.ZipkinTracerProvider; + provides io.helidon.common.context.spi.DataPropagationProvider + with io.helidon.tracing.providers.zipkin.ZipkinDataPropagationProvider; } \ No newline at end of file diff --git a/tracing/providers/zipkin/src/test/resources/application.yaml b/tracing/providers/zipkin/src/test/resources/application.yaml index 0f359e5fb7e..57a4e673af9 100644 --- a/tracing/providers/zipkin/src/test/resources/application.yaml +++ b/tracing/providers/zipkin/src/test/resources/application.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2023 Oracle and/or its affiliates. +# Copyright (c) 2017, 2024 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. @@ -45,4 +45,5 @@ tracing: int-tags: tag5: 145 tag6: 741 - + # With changes to OpenTracing global tracer handling, provide a service name for the Zipkin implementation to use. + service: "helidon-test-service"