diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ComponentRegistry.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ComponentRegistry.java
index 952451df410..93215cf2bf4 100644
--- a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ComponentRegistry.java
+++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ComponentRegistry.java
@@ -5,18 +5,32 @@
package io.opentelemetry.sdk.internal;
+import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
+import javax.annotation.Nullable;
/**
* Component (tracer, meter, etc) registry class for all the provider classes (TracerProvider,
* MeterProvider, etc.).
*
+ *
Components are identified by name, version, and schema. Name is required, but version and
+ * schema are optional. Therefore, we have 4 possible scenarios for component keys:
+ *
+ *
+ * - Only name is provided, represented by {@link #componentByName}
+ *
- Name and version are provided, represented by {@link #componentByNameAndVersion}
+ *
- Name and schema are provided, represented by {@link #componentByNameAndSchema}
+ *
- Name, version and schema are provided, represented by {@link
+ * #componentByNameVersionAndSchema}
+ *
+ *
* This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*
@@ -24,7 +38,14 @@
*/
public final class ComponentRegistry {
- private final ConcurrentMap registry = new ConcurrentHashMap<>();
+ private final Map componentByName = new ConcurrentHashMap<>();
+ private final Map> componentByNameAndVersion = new ConcurrentHashMap<>();
+ private final Map> componentByNameAndSchema = new ConcurrentHashMap<>();
+ private final Map>> componentByNameVersionAndSchema =
+ new ConcurrentHashMap<>();
+
+ private final Set allComponents = Collections.newSetFromMap(new IdentityHashMap<>());
+
private final Function factory;
public ComponentRegistry(Function factory) {
@@ -32,19 +53,64 @@ public ComponentRegistry(Function factory) {
}
/**
- * Returns the registered value associated with this {@link InstrumentationScopeInfo scope} if
- * any, otherwise creates a new instance and associates it with the given scope.
+ * Returns the component associated with the {@code name}, {@code version}, and {@code schemaUrl}.
+ * {@link Attributes} are not part of component identity. Behavior is undefined when different
+ * {@link Attributes} are provided where {@code name}, {@code version}, and {@code schemaUrl} are
+ * identical.
*/
- public V get(InstrumentationScopeInfo instrumentationScopeInfo) {
- // Optimistic lookup, before creating the new component.
- V component = registry.get(instrumentationScopeInfo);
- if (component != null) {
- return component;
+ public V get(
+ String name, @Nullable String version, @Nullable String schemaUrl, Attributes attributes) {
+ if (version != null && schemaUrl != null) {
+ Map> componentByVersionAndSchema =
+ componentByNameVersionAndSchema.computeIfAbsent(
+ name, unused -> new ConcurrentHashMap<>());
+ Map componentBySchema =
+ componentByVersionAndSchema.computeIfAbsent(version, unused -> new ConcurrentHashMap<>());
+ return componentBySchema.computeIfAbsent(
+ schemaUrl,
+ schemaUrl1 ->
+ buildComponent(
+ InstrumentationScopeInfo.builder(name)
+ .setVersion(version)
+ .setSchemaUrl(schemaUrl1)
+ .setAttributes(attributes)
+ .build()));
+ } else if (version != null) { // schemaUrl == null
+ Map componentByVersion =
+ componentByNameAndVersion.computeIfAbsent(name, unused -> new ConcurrentHashMap<>());
+ return componentByVersion.computeIfAbsent(
+ version,
+ version1 ->
+ buildComponent(
+ InstrumentationScopeInfo.builder(name)
+ .setVersion(version1)
+ .setAttributes(attributes)
+ .build()));
+ }
+ if (schemaUrl != null) { // version == null
+ Map componentBySchema =
+ componentByNameAndSchema.computeIfAbsent(name, unused -> new ConcurrentHashMap<>());
+ return componentBySchema.computeIfAbsent(
+ schemaUrl,
+ schemaUrl1 ->
+ buildComponent(
+ InstrumentationScopeInfo.builder(name)
+ .setSchemaUrl(schemaUrl1)
+ .setAttributes(attributes)
+ .build()));
+ } else { // schemaUrl == null && version == null
+ return componentByName.computeIfAbsent(
+ name,
+ name1 ->
+ buildComponent(
+ InstrumentationScopeInfo.builder(name1).setAttributes(attributes).build()));
}
+ }
- V newComponent = factory.apply(instrumentationScopeInfo);
- V oldComponent = registry.putIfAbsent(instrumentationScopeInfo, newComponent);
- return oldComponent != null ? oldComponent : newComponent;
+ private V buildComponent(InstrumentationScopeInfo instrumentationScopeInfo) {
+ V component = factory.apply(instrumentationScopeInfo);
+ allComponents.add(component);
+ return component;
}
/**
@@ -53,6 +119,6 @@ public V get(InstrumentationScopeInfo instrumentationScopeInfo) {
* @return a {@code Collection} view of the registered components.
*/
public Collection getComponents() {
- return Collections.unmodifiableCollection(new ArrayList<>(registry.values()));
+ return Collections.unmodifiableCollection(allComponents);
}
}
diff --git a/sdk/common/src/test/java/io/opentelemetry/sdk/internal/ComponentRegistryTest.java b/sdk/common/src/test/java/io/opentelemetry/sdk/internal/ComponentRegistryTest.java
index db52d7c71d1..1e566b28972 100644
--- a/sdk/common/src/test/java/io/opentelemetry/sdk/internal/ComponentRegistryTest.java
+++ b/sdk/common/src/test/java/io/opentelemetry/sdk/internal/ComponentRegistryTest.java
@@ -8,7 +8,6 @@
import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.api.common.Attributes;
-import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import org.junit.jupiter.api.Test;
class ComponentRegistryTest {
@@ -22,83 +21,35 @@ class ComponentRegistryTest {
@Test
void get_SameInstance() {
- assertThat(registry.get(InstrumentationScopeInfo.builder(NAME).build()))
- .isSameAs(registry.get(InstrumentationScopeInfo.builder(NAME).build()));
- assertThat(registry.get(InstrumentationScopeInfo.builder(NAME).setVersion(VERSION).build()))
- .isSameAs(registry.get(InstrumentationScopeInfo.builder(NAME).setVersion(VERSION).build()));
- assertThat(
- registry.get(InstrumentationScopeInfo.builder(NAME).setSchemaUrl(SCHEMA_URL).build()))
+ assertThat(registry.get(NAME, null, null, Attributes.empty()))
+ .isSameAs(registry.get(NAME, null, null, Attributes.empty()))
+ .isSameAs(registry.get(NAME, null, null, Attributes.builder().put("k1", "v2").build()));
+
+ assertThat(registry.get(NAME, VERSION, null, Attributes.empty()))
+ .isSameAs(registry.get(NAME, VERSION, null, Attributes.empty()))
+ .isSameAs(registry.get(NAME, VERSION, null, Attributes.builder().put("k1", "v2").build()));
+ assertThat(registry.get(NAME, null, SCHEMA_URL, Attributes.empty()))
+ .isSameAs(registry.get(NAME, null, SCHEMA_URL, Attributes.empty()))
.isSameAs(
- registry.get(InstrumentationScopeInfo.builder(NAME).setSchemaUrl(SCHEMA_URL).build()));
- assertThat(
- registry.get(InstrumentationScopeInfo.builder(NAME).setAttributes(ATTRIBUTES).build()))
+ registry.get(NAME, null, SCHEMA_URL, Attributes.builder().put("k1", "v2").build()));
+ assertThat(registry.get(NAME, VERSION, SCHEMA_URL, Attributes.empty()))
+ .isSameAs(registry.get(NAME, VERSION, SCHEMA_URL, Attributes.empty()))
.isSameAs(
- registry.get(InstrumentationScopeInfo.builder(NAME).setAttributes(ATTRIBUTES).build()));
- assertThat(
- registry.get(
- InstrumentationScopeInfo.builder(NAME)
- .setVersion(VERSION)
- .setSchemaUrl(SCHEMA_URL)
- .setAttributes(ATTRIBUTES)
- .build()))
- .isSameAs(
- registry.get(
- InstrumentationScopeInfo.builder(NAME)
- .setVersion(VERSION)
- .setSchemaUrl(SCHEMA_URL)
- .setAttributes(ATTRIBUTES)
- .build()));
+ registry.get(NAME, VERSION, SCHEMA_URL, Attributes.builder().put("k1", "v2").build()));
}
@Test
void get_DifferentInstance() {
- InstrumentationScopeInfo allFields =
- InstrumentationScopeInfo.builder(NAME)
- .setVersion(VERSION)
- .setSchemaUrl(SCHEMA_URL)
- .setAttributes(ATTRIBUTES)
- .build();
+ assertThat(registry.get(NAME, VERSION, SCHEMA_URL, ATTRIBUTES))
+ .isNotSameAs(registry.get(NAME + "_1", VERSION, SCHEMA_URL, ATTRIBUTES))
+ .isNotSameAs(registry.get(NAME, VERSION + "_1", SCHEMA_URL, ATTRIBUTES))
+ .isNotSameAs(registry.get(NAME, VERSION, SCHEMA_URL + "_1", ATTRIBUTES));
+
+ assertThat(registry.get(NAME, VERSION, null, Attributes.empty()))
+ .isNotSameAs(registry.get(NAME, null, null, Attributes.empty()));
- assertThat(registry.get(allFields))
- .isNotSameAs(
- registry.get(
- InstrumentationScopeInfo.builder(NAME + "_1")
- .setVersion(VERSION)
- .setSchemaUrl(SCHEMA_URL)
- .setAttributes(ATTRIBUTES)
- .build()));
- assertThat(registry.get(allFields))
- .isNotSameAs(
- registry.get(
- InstrumentationScopeInfo.builder(NAME)
- .setVersion(VERSION + "_1")
- .setSchemaUrl(SCHEMA_URL)
- .setAttributes(ATTRIBUTES)
- .build()));
- assertThat(registry.get(allFields))
- .isNotSameAs(
- registry.get(
- InstrumentationScopeInfo.builder(NAME)
- .setVersion(VERSION)
- .setSchemaUrl(SCHEMA_URL + "_1")
- .setAttributes(ATTRIBUTES)
- .build()));
- assertThat(registry.get(allFields))
- .isNotSameAs(
- registry.get(
- InstrumentationScopeInfo.builder(NAME)
- .setVersion(VERSION)
- .setSchemaUrl(SCHEMA_URL)
- .setAttributes(Attributes.builder().put("k1", "v2").build())
- .build()));
- assertThat(registry.get(InstrumentationScopeInfo.builder(NAME).setVersion(VERSION).build()))
- .isNotSameAs(registry.get(InstrumentationScopeInfo.builder(NAME).build()));
- assertThat(
- registry.get(InstrumentationScopeInfo.builder(NAME).setSchemaUrl(SCHEMA_URL).build()))
- .isNotSameAs(registry.get(InstrumentationScopeInfo.builder(NAME).build()));
- assertThat(
- registry.get(InstrumentationScopeInfo.builder(NAME).setAttributes(ATTRIBUTES).build()))
- .isNotSameAs(registry.get(InstrumentationScopeInfo.builder(NAME).build()));
+ assertThat(registry.get(NAME, null, SCHEMA_URL, Attributes.empty()))
+ .isNotSameAs(registry.get(NAME, null, null, Attributes.empty()));
}
private static final class TestComponent {}
diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerBuilder.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerBuilder.java
index 8d3eeaeb7c1..d497e25f7cd 100644
--- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerBuilder.java
+++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerBuilder.java
@@ -5,21 +5,22 @@
package io.opentelemetry.sdk.logs;
+import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.logs.LoggerBuilder;
-import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
-import io.opentelemetry.sdk.common.InstrumentationScopeInfoBuilder;
import io.opentelemetry.sdk.internal.ComponentRegistry;
import javax.annotation.Nullable;
final class SdkLoggerBuilder implements LoggerBuilder {
private final ComponentRegistry registry;
- private final InstrumentationScopeInfoBuilder scopeBuilder;
+ private final String instrumentationScopeName;
+ @Nullable private String instrumentationScopeVersion;
+ @Nullable private String schemaUrl;
@Nullable private String eventDomain;
SdkLoggerBuilder(ComponentRegistry registry, String instrumentationScopeName) {
this.registry = registry;
- this.scopeBuilder = InstrumentationScopeInfo.builder(instrumentationScopeName);
+ this.instrumentationScopeName = instrumentationScopeName;
}
@Override
@@ -30,19 +31,21 @@ public LoggerBuilder setEventDomain(String eventDomain) {
@Override
public SdkLoggerBuilder setSchemaUrl(String schemaUrl) {
- scopeBuilder.setSchemaUrl(schemaUrl);
+ this.schemaUrl = schemaUrl;
return this;
}
@Override
public SdkLoggerBuilder setInstrumentationVersion(String instrumentationScopeVersion) {
- scopeBuilder.setVersion(instrumentationScopeVersion);
+ this.instrumentationScopeVersion = instrumentationScopeVersion;
return this;
}
@Override
public SdkLogger build() {
- SdkLogger logger = registry.get(scopeBuilder.build());
+ SdkLogger logger =
+ registry.get(
+ instrumentationScopeName, instrumentationScopeVersion, schemaUrl, Attributes.empty());
return eventDomain == null ? logger : logger.withEventDomain(eventDomain);
}
}
diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProvider.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProvider.java
index e64190d4d94..478700742d6 100644
--- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProvider.java
+++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProvider.java
@@ -5,6 +5,7 @@
package io.opentelemetry.sdk.logs;
+import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.logs.Logger;
import io.opentelemetry.api.logs.LoggerBuilder;
import io.opentelemetry.api.logs.LoggerProvider;
@@ -17,6 +18,7 @@
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.logging.Level;
+import javax.annotation.Nullable;
/** SDK implementation for {@link LoggerProvider}. */
public final class SdkLoggerProvider implements LoggerProvider, Closeable {
@@ -61,7 +63,8 @@ public static SdkLoggerProviderBuilder builder() {
*/
@Override
public Logger get(String instrumentationScopeName) {
- return loggerBuilder(instrumentationScopeName).build();
+ return loggerComponentRegistry.get(
+ instrumentationNameOrDefault(instrumentationScopeName), null, null, Attributes.empty());
}
/**
@@ -75,11 +78,16 @@ public LoggerBuilder loggerBuilder(String instrumentationScopeName) {
if (isNoopLogRecordProcessor) {
return LoggerProvider.noop().loggerBuilder(instrumentationScopeName);
}
+ return new SdkLoggerBuilder(
+ loggerComponentRegistry, instrumentationNameOrDefault(instrumentationScopeName));
+ }
+
+ private static String instrumentationNameOrDefault(@Nullable String instrumentationScopeName) {
if (instrumentationScopeName == null || instrumentationScopeName.isEmpty()) {
LOGGER.fine("Logger requested without instrumentation scope name.");
- instrumentationScopeName = DEFAULT_LOGGER_NAME;
+ return DEFAULT_LOGGER_NAME;
}
- return new SdkLoggerBuilder(loggerComponentRegistry, instrumentationScopeName);
+ return instrumentationScopeName;
}
/**
diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/SdkMeterBuilder.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/SdkMeterBuilder.java
index 317b140c033..fde47070f29 100644
--- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/SdkMeterBuilder.java
+++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/SdkMeterBuilder.java
@@ -5,36 +5,39 @@
package io.opentelemetry.sdk.metrics;
+import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.MeterBuilder;
-import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
-import io.opentelemetry.sdk.common.InstrumentationScopeInfoBuilder;
import io.opentelemetry.sdk.internal.ComponentRegistry;
+import javax.annotation.Nullable;
class SdkMeterBuilder implements MeterBuilder {
private final ComponentRegistry registry;
- private final InstrumentationScopeInfoBuilder scopeBuilder;
+ private final String instrumentationScopeName;
+ @Nullable private String instrumentationScopeVersion;
+ @Nullable private String schemaUrl;
SdkMeterBuilder(ComponentRegistry registry, String instrumentationScopeName) {
this.registry = registry;
- this.scopeBuilder = InstrumentationScopeInfo.builder(instrumentationScopeName);
+ this.instrumentationScopeName = instrumentationScopeName;
}
@Override
public MeterBuilder setSchemaUrl(String schemaUrl) {
- scopeBuilder.setSchemaUrl(schemaUrl);
+ this.schemaUrl = schemaUrl;
return this;
}
@Override
public MeterBuilder setInstrumentationVersion(String instrumentationScopeVersion) {
- scopeBuilder.setVersion(instrumentationScopeVersion);
+ this.instrumentationScopeVersion = instrumentationScopeVersion;
return this;
}
@Override
public Meter build() {
- return registry.get(scopeBuilder.build());
+ return registry.get(
+ instrumentationScopeName, instrumentationScopeVersion, schemaUrl, Attributes.empty());
}
}
diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerBuilder.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerBuilder.java
index 6e9e4db321a..6c6976a7d52 100644
--- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerBuilder.java
+++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerBuilder.java
@@ -5,36 +5,39 @@
package io.opentelemetry.sdk.trace;
+import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.TracerBuilder;
-import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
-import io.opentelemetry.sdk.common.InstrumentationScopeInfoBuilder;
import io.opentelemetry.sdk.internal.ComponentRegistry;
+import javax.annotation.Nullable;
class SdkTracerBuilder implements TracerBuilder {
private final ComponentRegistry registry;
- private final InstrumentationScopeInfoBuilder scopeBuilder;
+ private final String instrumentationScopeName;
+ @Nullable private String instrumentationScopeVersion;
+ @Nullable private String schemaUrl;
SdkTracerBuilder(ComponentRegistry registry, String instrumentationScopeName) {
this.registry = registry;
- this.scopeBuilder = InstrumentationScopeInfo.builder(instrumentationScopeName);
+ this.instrumentationScopeName = instrumentationScopeName;
}
@Override
public TracerBuilder setSchemaUrl(String schemaUrl) {
- scopeBuilder.setSchemaUrl(schemaUrl);
+ this.schemaUrl = schemaUrl;
return this;
}
@Override
public TracerBuilder setInstrumentationVersion(String instrumentationScopeVersion) {
- scopeBuilder.setVersion(instrumentationScopeVersion);
+ this.instrumentationScopeVersion = instrumentationScopeVersion;
return this;
}
@Override
public Tracer build() {
- return registry.get(scopeBuilder.build());
+ return registry.get(
+ instrumentationScopeName, instrumentationScopeVersion, schemaUrl, Attributes.empty());
}
}