1818import io .opentelemetry .context .propagation .ContextPropagators ;
1919import java .lang .reflect .InvocationTargetException ;
2020import java .lang .reflect .Method ;
21+ import java .util .Objects ;
2122import java .util .function .Supplier ;
2223import java .util .logging .Level ;
2324import java .util .logging .Logger ;
2425import javax .annotation .Nullable ;
2526import javax .annotation .concurrent .ThreadSafe ;
2627
2728/**
28- * A global singleton for the entrypoint to telemetry functionality for tracing, metrics and
29- * baggage.
29+ * Provides access to a global singleton {@link OpenTelemetry} instance.
3030 *
31- * <p>If using the OpenTelemetry SDK, you may want to instantiate the {@link OpenTelemetry} to
32- * provide configuration, for example of {@code Resource} or {@code Sampler}. See {@code
33- * OpenTelemetrySdk} and {@code OpenTelemetrySdk.builder} for information on how to construct the
34- * SDK's {@link OpenTelemetry} implementation.
31+ * <p>WARNING: To avoid inherent complications around initialization ordering, it's best-practice to
32+ * pass around instances of {@link OpenTelemetry} rather than using {@link GlobalOpenTelemetry}.
33+ * However, the OpenTelemetry javagent makes the {@link OpenTelemetry} instance it installs
34+ * available via {@link GlobalOpenTelemetry}. As a result, {@link GlobalOpenTelemetry} plays an
35+ * important role in native instrumentation and application custom instrumentation.
3536 *
36- * <p>WARNING: Due to the inherent complications around initialization order involving this class
37- * and its single global instance, we strongly recommend *not* using GlobalOpenTelemetry unless you
38- * have a use-case that absolutely requires it. Please favor using instances of OpenTelemetry
39- * wherever possible.
37+ * <p>Native instrumentation should use {@link #getOrNoop()} as the default {@link OpenTelemetry}
38+ * instance, and expose APIs for setting a custom instance. This results in the following behavior:
4039 *
41- * <p>If you are using the OpenTelemetry javaagent, it is generally best to only call
42- * GlobalOpenTelemetry.get() once, and then pass the resulting reference where you need to use it.
40+ * <ul>
41+ * <li>If the OpenTelemetry javaagent is installed, the native instrumentation will default to the
42+ * {@link OpenTelemetry} it installs.
43+ * <li>If the OpenTelemetry javaagent is not installed, the native instrumentation will default to
44+ * a noop instance.
45+ * <li>If the user explicitly sets a custom instance, it will be used, regardless of whether or
46+ * not the OpenTelemetry javaagent is installed.
47+ * </ul>
4348 *
44- * @see TracerProvider
45- * @see ContextPropagators
49+ * <p>Applications with custom instrumentation should call {@link #isSet()} once during
50+ * initialization to access the javaagent instance or initialize (e.g. {@code isSet() ?
51+ * GlobalOpenTelemetry.get() : initializeSdk()}), and pass the resulting instance around manually
52+ * (or with dependency injection) to install custom instrumentation. This results in the following
53+ * behavior:
54+ *
55+ * <ul>
56+ * <li>If the OpenTelemetry javaagent is installed, custom instrumentation will use the {@link
57+ * OpenTelemetry} it installs.
58+ * <li>If the OpenTelemetry javaagent is not installed, custom instrumentation will use an {@link
59+ * OpenTelemetry} instance initialized by the application.
60+ * </ul>
4661 */
47- // We intentionally assign to be use for error reporting.
62+ // We intentionally assign for error reporting.
4863@ SuppressWarnings ("StaticAssignmentOfThrowable" )
4964public final class GlobalOpenTelemetry {
5065
@@ -67,10 +82,56 @@ public final class GlobalOpenTelemetry {
6782 private GlobalOpenTelemetry () {}
6883
6984 /**
70- * Returns the registered global {@link OpenTelemetry}.
85+ * Returns the registered global {@link OpenTelemetry} if set, or else {@link
86+ * OpenTelemetry#noop()}.
87+ *
88+ * <p>NOTE: if the global instance is set, the response is obfuscated to prevent callers from
89+ * casting to SDK implementation instances and inappropriately accessing non-instrumentation APIs.
90+ *
91+ * <p>NOTE: This does not result in the {@link #set(OpenTelemetry)} side effects of {@link
92+ * #get()}.
93+ *
94+ * <p>Native instrumentation should use this method to initialize their default {@link
95+ * OpenTelemetry} instance. See class javadoc for more details.
96+ */
97+ public static OpenTelemetry getOrNoop () {
98+ synchronized (mutex ) {
99+ return globalOpenTelemetry != null ? globalOpenTelemetry : OpenTelemetry .noop ();
100+ }
101+ }
102+
103+ /**
104+ * Returns {@code true} if {@link GlobalOpenTelemetry} is set, otherwise {@code false}.
105+ *
106+ * <p>Application custom instrumentation should use this method during initialization. See class
107+ * javadoc for more details.
108+ */
109+ public static boolean isSet () {
110+ synchronized (mutex ) {
111+ return globalOpenTelemetry != null ;
112+ }
113+ }
114+
115+ /**
116+ * Returns the registered global {@link OpenTelemetry} if set, else calls {@link
117+ * GlobalOpenTelemetry#set(OpenTelemetry)} with a no-op {@link OpenTelemetry} instance and returns
118+ * that.
119+ *
120+ * <p>NOTE: all returned instanced are obfuscated to prevent callers from casting to SDK
121+ * implementation instances and inappropriately accessing non-instrumentation APIs.
122+ *
123+ * <p>Native instrumentations should use {@link #getOrNoop()} instead. See class javadoc for more
124+ * details.
125+ *
126+ * <p>Application custom instrumentation should use {@link #isSet()} and only call this if the
127+ * response is {@code true}. See class javadoc for more details.
128+ *
129+ * <p>If the global instance has not been set, and {@code
130+ * io.opentelemetry:opentelemetry-sdk-extension-autoconfigure} is present, and {@value
131+ * GLOBAL_AUTOCONFIGURE_ENABLED_PROPERTY} is {@code true}, the global instance will be set to an
132+ * autoconfigured instance instead of {@link OpenTelemetry#noop()}.
71133 *
72- * @throws IllegalStateException if a provider has been specified by system property using the
73- * interface FQCN but the specified provider cannot be found.
134+ * @throws IllegalStateException if autoconfigure initialization is triggered and fails.
74135 */
75136 public static OpenTelemetry get () {
76137 OpenTelemetry openTelemetry = globalOpenTelemetry ;
@@ -85,7 +146,7 @@ public static OpenTelemetry get() {
85146 }
86147
87148 set (OpenTelemetry .noop ());
88- return OpenTelemetry . noop ( );
149+ openTelemetry = Objects . requireNonNull ( globalOpenTelemetry );
89150 }
90151 }
91152 }
0 commit comments