diff --git a/api/all/src/main/java/io/opentelemetry/api/common/AttributeKey.java b/api/all/src/main/java/io/opentelemetry/api/common/AttributeKey.java index 6d6e66eac1..7743c315c5 100644 --- a/api/all/src/main/java/io/opentelemetry/api/common/AttributeKey.java +++ b/api/all/src/main/java/io/opentelemetry/api/common/AttributeKey.java @@ -5,6 +5,7 @@ package io.opentelemetry.api.common; +import io.opentelemetry.api.internal.InternalAttributeKeyImpl; import java.util.List; import javax.annotation.concurrent.Immutable; @@ -27,41 +28,41 @@ public interface AttributeKey { /** Returns a new AttributeKey for String valued attributes. */ static AttributeKey stringKey(String key) { - return AttributeKeyImpl.create(key, AttributeType.STRING); + return InternalAttributeKeyImpl.create(key, AttributeType.STRING); } /** Returns a new AttributeKey for Boolean valued attributes. */ static AttributeKey booleanKey(String key) { - return AttributeKeyImpl.create(key, AttributeType.BOOLEAN); + return InternalAttributeKeyImpl.create(key, AttributeType.BOOLEAN); } /** Returns a new AttributeKey for Long valued attributes. */ static AttributeKey longKey(String key) { - return AttributeKeyImpl.create(key, AttributeType.LONG); + return InternalAttributeKeyImpl.create(key, AttributeType.LONG); } /** Returns a new AttributeKey for Double valued attributes. */ static AttributeKey doubleKey(String key) { - return AttributeKeyImpl.create(key, AttributeType.DOUBLE); + return InternalAttributeKeyImpl.create(key, AttributeType.DOUBLE); } /** Returns a new AttributeKey for List<String> valued attributes. */ static AttributeKey> stringArrayKey(String key) { - return AttributeKeyImpl.create(key, AttributeType.STRING_ARRAY); + return InternalAttributeKeyImpl.create(key, AttributeType.STRING_ARRAY); } /** Returns a new AttributeKey for List<Boolean> valued attributes. */ static AttributeKey> booleanArrayKey(String key) { - return AttributeKeyImpl.create(key, AttributeType.BOOLEAN_ARRAY); + return InternalAttributeKeyImpl.create(key, AttributeType.BOOLEAN_ARRAY); } /** Returns a new AttributeKey for List<Long> valued attributes. */ static AttributeKey> longArrayKey(String key) { - return AttributeKeyImpl.create(key, AttributeType.LONG_ARRAY); + return InternalAttributeKeyImpl.create(key, AttributeType.LONG_ARRAY); } /** Returns a new AttributeKey for List<Double> valued attributes. */ static AttributeKey> doubleArrayKey(String key) { - return AttributeKeyImpl.create(key, AttributeType.DOUBLE_ARRAY); + return InternalAttributeKeyImpl.create(key, AttributeType.DOUBLE_ARRAY); } } diff --git a/api/all/src/main/java/io/opentelemetry/api/common/AttributeKeyImpl.java b/api/all/src/main/java/io/opentelemetry/api/internal/InternalAttributeKeyImpl.java similarity index 66% rename from api/all/src/main/java/io/opentelemetry/api/common/AttributeKeyImpl.java rename to api/all/src/main/java/io/opentelemetry/api/internal/InternalAttributeKeyImpl.java index 2b2890b417..ce02e2f8e3 100644 --- a/api/all/src/main/java/io/opentelemetry/api/common/AttributeKeyImpl.java +++ b/api/all/src/main/java/io/opentelemetry/api/internal/InternalAttributeKeyImpl.java @@ -3,18 +3,28 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.api.common; +package io.opentelemetry.api.internal; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.AttributeType; +import java.nio.charset.StandardCharsets; import javax.annotation.Nullable; -@SuppressWarnings("rawtypes") -final class AttributeKeyImpl implements AttributeKey { +/** + * Default AttributeKey implementation which preencodes to UTF8 for OTLP export. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class InternalAttributeKeyImpl implements AttributeKey { private final AttributeType type; private final String key; private final int hashCode; - private AttributeKeyImpl(AttributeType type, String key) { + private byte[] keyUtf8; + + private InternalAttributeKeyImpl(AttributeType type, String key) { if (type == null) { throw new NullPointerException("Null type"); } @@ -38,8 +48,8 @@ final class AttributeKeyImpl implements AttributeKey { // the class loader automatically resolves its super classes (interfaces), which in this case is // Context, 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 AttributeKey create(@Nullable String key, AttributeType type) { - return new AttributeKeyImpl<>(type, key != null ? key : ""); + public static AttributeKey create(@Nullable String key, AttributeType type) { + return new InternalAttributeKeyImpl<>(type, key != null ? key : ""); } @Override @@ -52,13 +62,23 @@ final class AttributeKeyImpl implements AttributeKey { return key; } + /** Returns the key, encoded as UTF-8 bytes. */ + public byte[] getKeyUtf8() { + byte[] keyUtf8 = this.keyUtf8; + if (keyUtf8 == null) { + keyUtf8 = key.getBytes(StandardCharsets.UTF_8); + this.keyUtf8 = keyUtf8; + } + return keyUtf8; + } + @Override public boolean equals(@Nullable Object o) { if (o == this) { return true; } - if (o instanceof AttributeKeyImpl) { - AttributeKeyImpl that = (AttributeKeyImpl) o; + if (o instanceof InternalAttributeKeyImpl) { + InternalAttributeKeyImpl that = (InternalAttributeKeyImpl) o; return this.type.equals(that.getType()) && this.key.equals(that.getKey()); } return false; diff --git a/api/all/src/test/java/io/opentelemetry/api/common/AttributeKeyTest.java b/api/all/src/test/java/io/opentelemetry/api/common/AttributeKeyTest.java index 2c6179c93e..4b39e1d6a9 100644 --- a/api/all/src/test/java/io/opentelemetry/api/common/AttributeKeyTest.java +++ b/api/all/src/test/java/io/opentelemetry/api/common/AttributeKeyTest.java @@ -7,6 +7,7 @@ package io.opentelemetry.api.common; import static org.assertj.core.api.Assertions.assertThat; +import io.opentelemetry.api.internal.InternalAttributeKeyImpl; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.jupiter.api.Test; @@ -14,9 +15,12 @@ class AttributeKeyTest { @Test void equalsVerifier() { - EqualsVerifier.forClass(AttributeKeyImpl.class) + EqualsVerifier.forClass(InternalAttributeKeyImpl.class) + .withIgnoredFields("keyUtf8") .withCachedHashCode( - "hashCode", "buildHashCode", (AttributeKeyImpl) AttributeKey.stringKey("test")) + "hashCode", + "buildHashCode", + (InternalAttributeKeyImpl) AttributeKey.stringKey("test")) .verify(); } diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/otlp/internal/KeyValueMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/otlp/internal/KeyValueMarshaler.java index 6a4c540883..a96294fda6 100644 --- a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/otlp/internal/KeyValueMarshaler.java +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/otlp/internal/KeyValueMarshaler.java @@ -7,6 +7,7 @@ package io.opentelemetry.exporter.otlp.internal; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.internal.InternalAttributeKeyImpl; import io.opentelemetry.proto.common.v1.internal.AnyValue; import io.opentelemetry.proto.common.v1.internal.ArrayValue; import io.opentelemetry.proto.common.v1.internal.KeyValue; @@ -41,7 +42,14 @@ final class KeyValueMarshaler extends MarshalerWithSize { @SuppressWarnings("unchecked") static KeyValueMarshaler create(AttributeKey attributeKey, Object value) { - byte[] keyUtf8 = MarshalerUtil.toBytes(attributeKey.getKey()); + final byte[] keyUtf8; + if (attributeKey.getKey().isEmpty()) { + keyUtf8 = MarshalerUtil.EMPTY_BYTES; + } else if (attributeKey instanceof InternalAttributeKeyImpl) { + keyUtf8 = ((InternalAttributeKeyImpl) attributeKey).getKeyUtf8(); + } else { + keyUtf8 = attributeKey.getKey().getBytes(StandardCharsets.UTF_8); + } switch (attributeKey.getType()) { case STRING: return new KeyValueMarshaler(