org.apache.rocketmq.shaded.io.opentelemetry.context.LazyStorage Maven / Gradle / Ivy
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
// Includes work from:
/*
* Copyright 2015 The gRPC 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.
*/
/*
* Copyright 2020 LINE Corporation
*
* LINE Corporation licenses this file to you 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:
*
* https://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 org.apache.rocketmq.shaded.io.opentelemetry.context;
import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
// Lazy-loaded storage. Delaying storage initialization until after class initialization makes it
// much easier to avoid circular loading since there can still be references to Context as long as
// they don't depend on storage, like key() and currentContextExecutor(). It also makes it easier
// to handle exceptions.
final class LazyStorage {
// Used by auto-instrumentation agent. Check with auto-instrumentation before making changes to
// this method.
//
// Ideally auto-instrumentation would hijack the public ContextStorage.get() instead of this
// method, but auto-instrumentation also needs to inject its own implementation of ContextStorage
// into the class loader at the same time, which causes a problem because injecting a class into
// the class loader automatically resolves its super classes (interfaces), which in this case is
// ContextStorage, which would be the same class (interface) being instrumented at that time,
// which would lead to the JVM throwing a LinkageError "attempted duplicate interface definition"
static ContextStorage get() {
return storage;
}
private static final String CONTEXT_STORAGE_PROVIDER_PROPERTY =
"org.apache.rocketmq.shaded.io.opentelemetry.context.contextStorageProvider";
private static final String ENFORCE_DEFAULT_STORAGE_VALUE = "default";
private static final String ENABLE_STRICT_CONTEXT_PROVIDER_PROPERTY =
"org.apache.rocketmq.shaded.io.opentelemetry.context.enableStrictContext";
private static final Logger logger = Logger.getLogger(LazyStorage.class.getName());
private static final ContextStorage storage;
static {
AtomicReference deferredStorageFailure = new AtomicReference<>();
ContextStorage created = createStorage(deferredStorageFailure);
if (Boolean.getBoolean(ENABLE_STRICT_CONTEXT_PROVIDER_PROPERTY)) {
created = StrictContextStorage.create(created);
}
for (Function wrapper :
ContextStorageWrappers.getWrappers()) {
created = wrapper.apply(created);
}
storage = created;
ContextStorageWrappers.setStorageInitialized();
Throwable failure = deferredStorageFailure.get();
// Logging must happen after storage has been set, as loggers may use Context.
if (failure != null) {
logger.log(
Level.WARNING, "ContextStorageProvider initialized failed. Using default", failure);
}
}
static ContextStorage createStorage(AtomicReference deferredStorageFailure) {
// Get the specified SPI implementation first here
String providerClassName = System.getProperty(CONTEXT_STORAGE_PROVIDER_PROPERTY, "");
// Allow user to enforce default ThreadLocalContextStorage
if (ENFORCE_DEFAULT_STORAGE_VALUE.equals(providerClassName)) {
return ContextStorage.defaultStorage();
}
List providers = new ArrayList<>();
for (ContextStorageProvider provider : ServiceLoader.load(ContextStorageProvider.class)) {
if (provider
.getClass()
.getName()
.equals("org.apache.rocketmq.shaded.io.opentelemetry.sdk.testing.context.SettableContextStorageProvider")) {
// Always use our testing helper context storage provider if it is on the classpath.
return provider.get();
}
providers.add(provider);
}
if (providers.isEmpty()) {
return ContextStorage.defaultStorage();
}
if (providerClassName.isEmpty()) {
if (providers.size() == 1) {
return providers.get(0).get();
}
deferredStorageFailure.set(
new IllegalStateException(
"Found multiple ContextStorageProvider. Set the "
+ "org.apache.rocketmq.shaded.io.opentelemetry.context.ContextStorageProvider property to the fully "
+ "qualified class name of the provider to use. Falling back to default "
+ "ContextStorage. Found providers: "
+ providers));
return ContextStorage.defaultStorage();
}
for (ContextStorageProvider provider : providers) {
if (provider.getClass().getName().equals(providerClassName)) {
return provider.get();
}
}
deferredStorageFailure.set(
new IllegalStateException(
"org.apache.rocketmq.shaded.io.opentelemetry.context.ContextStorageProvider property set but no matching class "
+ "could be found, requested: "
+ providerClassName
+ " but found providers: "
+ providers));
return ContextStorage.defaultStorage();
}
private LazyStorage() {}
}