diff --git a/tracing/tracing/src/main/java/io/helidon/tracing/TracerProviderHelper.java b/tracing/tracing/src/main/java/io/helidon/tracing/TracerProviderHelper.java index 3fd01aa0720..3009f423400 100644 --- a/tracing/tracing/src/main/java/io/helidon/tracing/TracerProviderHelper.java +++ b/tracing/tracing/src/main/java/io/helidon/tracing/TracerProviderHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022 Oracle and/or its affiliates. + * Copyright (c) 2019, 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. @@ -58,18 +58,32 @@ private TracerProviderHelper() { } public static Optional currentSpan() { - return TRACER_PROVIDER.currentSpan(); + /* + If a custom TracerProvider implementation indirectly tries to access the current span (for example, by triggering custom + logging that adds the current span to each message), then this method can be invoked before the static initializer has + completed and, therefore, before TRACER_PROVIDER is assigned. + */ + return (TRACER_PROVIDER == null) ? Optional.empty() : TRACER_PROVIDER.currentSpan(); } static Tracer global() { + if (TRACER_PROVIDER == null) { + throw new IllegalStateException("Use before initialization has completed"); + } return TRACER_PROVIDER.global(); } static void global(Tracer tracer) { + if (TRACER_PROVIDER == null) { + throw new IllegalStateException("Use before initialization has completed"); + } TRACER_PROVIDER.global(tracer); } static TracerBuilder findTracerBuilder() { + if (TRACER_PROVIDER == null) { + throw new IllegalStateException("Use before initialization has completed"); + } return TRACER_PROVIDER.createBuilder(); } } diff --git a/tracing/tracing/src/test/java/io/helidon/tracing/TestEarlyUseOfSpanCurrent.java b/tracing/tracing/src/test/java/io/helidon/tracing/TestEarlyUseOfSpanCurrent.java new file mode 100644 index 00000000000..1150ade91da --- /dev/null +++ b/tracing/tracing/src/test/java/io/helidon/tracing/TestEarlyUseOfSpanCurrent.java @@ -0,0 +1,30 @@ +/* + * 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; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.notNullValue; +class TestEarlyUseOfSpanCurrent { + + @Test + void ensureCurrentSpanNonNull() { + + Span.current(); // Trigger the service loading of the test provider which will itself invoke Span.current(). + assertThat("Early current span", TestTracerProvider.earlyCurrentSpan(), notNullValue()); + } +} diff --git a/tracing/tracing/src/test/java/io/helidon/tracing/TestTracerProvider.java b/tracing/tracing/src/test/java/io/helidon/tracing/TestTracerProvider.java new file mode 100644 index 00000000000..66abd446916 --- /dev/null +++ b/tracing/tracing/src/test/java/io/helidon/tracing/TestTracerProvider.java @@ -0,0 +1,41 @@ +/* + * 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; + +import java.util.Optional; + +/** + * Approximates the behavior of a custom tracer provider with a constructor that invokes a logger which invokes Span.current(). + */ +public class TestTracerProvider extends NoOpTracerProvider { + + private static Optional earlyCurrentSpan; + + public TestTracerProvider() { + Optional currentSpan; + try { + currentSpan = Span.current(); + } catch (NullPointerException e) { + // silent + currentSpan = null; + } + earlyCurrentSpan = currentSpan; + } + + static Optional earlyCurrentSpan() { + return earlyCurrentSpan; + } +} diff --git a/tracing/tracing/src/test/resources/META-INF/services/io.helidon.tracing.spi.TracerProvider b/tracing/tracing/src/test/resources/META-INF/services/io.helidon.tracing.spi.TracerProvider new file mode 100644 index 00000000000..d11cd061033 --- /dev/null +++ b/tracing/tracing/src/test/resources/META-INF/services/io.helidon.tracing.spi.TracerProvider @@ -0,0 +1,16 @@ +# +# 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. +# +io.helidon.tracing.TestTracerProvider \ No newline at end of file