/* * Copyright 2016-2020 The OpenTracing Authors * * 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.opentracing.util; import io.opentracing.Scope; import io.opentracing.ScopeManager; import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer; import io.opentracing.noop.NoopTracer; import io.opentracing.noop.NoopTracerFactory; import io.opentracing.propagation.Format; import java.util.concurrent.Callable; /** * Global tracer that forwards all methods to another tracer that can be * configured by calling {@link #registerIfAbsent(Tracer) register}. * *

* The {@linkplain #registerIfAbsent(Tracer) register} method should only be called once * during the application initialization phase.
* If the {@linkplain #registerIfAbsent(Tracer)} register} method is never called, * the default {@link NoopTracer} is used. * *

* Where possible, use some form of dependency injection (of which there are * many) to access the `Tracer` instance. For vanilla application code, this is * often reasonable and cleaner for all of the usual DI reasons. * *

* That said, instrumentation for packages that are themselves statically * configured (e.g., JDBC drivers) may be unable to make use of said DI * mechanisms for {@link Tracer} access, and as such they should fall back on * {@link GlobalTracer}. By and large, OpenTracing instrumentation should * always allow the programmer to specify a {@link Tracer} instance to use for * instrumentation, though the {@link GlobalTracer} is a reasonable fallback or * default value. */ public final class GlobalTracer implements Tracer { /** * Singleton instance. *

* Since we cannot prevent people using {@linkplain #get() GlobalTracer.get()} as a constant, * this guarantees that references obtained before, during or after initialization * all behave as if obtained after initialization once properly initialized.
* As a minor additional benefit it makes it harder to circumvent the {@link Tracer} API. */ private static final GlobalTracer INSTANCE = new GlobalTracer(); /** * The registered {@link Tracer} delegate or the {@link NoopTracer} if none was registered yet. * Never {@code null}. */ private static volatile Tracer tracer = NoopTracerFactory.create(); private static volatile boolean isRegistered = false; private GlobalTracer() { } /** * Returns the constant {@linkplain GlobalTracer}. *

* All methods are forwarded to the currently configured tracer.
* Until a tracer is {@link #registerIfAbsent(Tracer) explicitly configured}, * the {@link io.opentracing.noop.NoopTracer NoopTracer} is used. * * @return The global tracer constant. * @see #registerIfAbsent(Tracer) and {@link #registerIfAbsent(Callable)} */ public static Tracer get() { return INSTANCE; } /** * Identify whether a {@link Tracer} has previously been registered. *

* This check is useful in scenarios where more than one component may be responsible * for registering a tracer. For example, when using a Java Agent, it will need to determine * if the application has already registered a tracer, and if not attempt to resolve and * register one itself. * * @return Whether a tracer has been registered */ public static boolean isRegistered() { return isRegistered; } /** * Register a {@link Tracer} to back the behaviour of the {@link #get()}. *

* The tracer is provided through a {@linkplain Callable} that will only be called if the global tracer is absent. * Registration is a one-time operation. Once a tracer has been registered, all attempts at re-registering * will return {@code false}. *

* Every application intending to use the global tracer is responsible for registering it once * during its initialization. * * @param provider Provider for the tracer to use as global tracer. * @return {@code true} if the provided tracer was registered as a result of this call, * {@code false} otherwise. * @throws NullPointerException if the tracer provider is {@code null} or provides a {@code null} Tracer. * @throws RuntimeException any exception thrown by the provider gets rethrown, * checked exceptions will be wrapped into appropriate runtime exceptions. */ public static synchronized boolean registerIfAbsent(final Callable provider) { requireNonNull(provider, "Cannot register GlobalTracer from provider ."); if (!isRegistered()) { try { final Tracer suppliedTracer = requireNonNull(provider.call(), "Cannot register GlobalTracer ."); if (!(suppliedTracer instanceof GlobalTracer)) { GlobalTracer.tracer = suppliedTracer; isRegistered = true; return true; } } catch (RuntimeException rte) { throw rte; // Re-throw as-is } catch (Exception ex) { throw new IllegalStateException("Exception obtaining tracer from provider: " + ex.getMessage(), ex); } } return false; } /** * Register a {@link Tracer} to back the behaviour of the {@link #get()}. *

* Registration is a one-time operation. Once a tracer has been registered, all attempts at re-registering * will return {@code false}. Use {@link #registerIfAbsent(Callable)} for lazy initiation to avoid multiple * instantiations of tracer. *

* Every application intending to use the global tracer is responsible for registering it once * during its initialization. * * @param tracer tracer to be registered. * @return {@code true} if the provided tracer was registered as a result of this call, * {@code false} otherwise. * @throws NullPointerException if the tracer {@code null}. * @throws RuntimeException any exception thrown by the provider gets rethrown, * checked exceptions will be wrapped into appropriate runtime exceptions. * * @see #registerIfAbsent(Callable) */ public static synchronized boolean registerIfAbsent(final Tracer tracer) { requireNonNull(tracer, "Cannot register GlobalTracer. Tracer is null"); return registerIfAbsent(new Callable() { @Override public Tracer call() { return tracer; } }); } /** * Register a {@link Tracer} to back the behaviour of the {@link #get() global tracer}. *

* Registration is a one-time operation, attempting to call it more often will result in a runtime exception. *

* Every application intending to use the global tracer is responsible for registering it once * during its initialization. * * @param tracer Tracer to use as global tracer. * @throws RuntimeException if there is already a current tracer registered * @see #registerIfAbsent(Callable) * @deprecated Please use {@link #registerIfAbsent(Tracer)} or {@link #registerIfAbsent(Callable)} instead. */ @Deprecated public static void register(final Tracer tracer) { if (!registerIfAbsent(provide(tracer)) && !tracer.equals(GlobalTracer.tracer) && !(tracer instanceof GlobalTracer)) { throw new IllegalStateException("There is already a current global Tracer registered."); } } @Override public ScopeManager scopeManager() { return tracer.scopeManager(); } @Override public SpanBuilder buildSpan(String operationName) { return tracer.buildSpan(operationName); } @Override public void inject(SpanContext spanContext, Format format, C carrier) { tracer.inject(spanContext, format, carrier); } @Override public SpanContext extract(Format format, C carrier) { return tracer.extract(format, carrier); } @Override public Span activeSpan() { return tracer.activeSpan(); } @Override public Scope activateSpan(Span span) { return tracer.activateSpan(span); } @Override public void close() { tracer.close(); } @Override public String toString() { return GlobalTracer.class.getSimpleName() + '{' + tracer + '}'; } private static Callable provide(final Tracer tracer) { return new Callable() { public Tracer call() { return tracer; } }; } private static T requireNonNull(T value, String message) { if (value == null) { throw new NullPointerException(message); } return value; } }