diff --git a/exporters/otlp/common/build.gradle.kts b/exporters/otlp/common/build.gradle.kts index 0e74e8829a7..94d50e46fd0 100644 --- a/exporters/otlp/common/build.gradle.kts +++ b/exporters/otlp/common/build.gradle.kts @@ -15,6 +15,8 @@ val versions: Map by project dependencies { protoSource("io.opentelemetry.proto:opentelemetry-proto:${versions["io.opentelemetry.proto"]}") + annotationProcessor("com.google.auto.value:auto-value") + api(project(":exporters:common")) compileOnly(project(":sdk:metrics")) diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AttributeKeyValue.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AttributeKeyValue.java new file mode 100644 index 00000000000..2b3cc82519e --- /dev/null +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AttributeKeyValue.java @@ -0,0 +1,50 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.otlp; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.KeyValue; +import java.util.ArrayList; +import java.util.List; + +/** + * Key-value pair of {@link AttributeKey} key and its corresponding value. + * + *

Conceptually if {@link Attributes} is a Map, then this is a Map.Entry. Note that whilst {@link + * KeyValue} is similar, this class holds type information on the Key rather than the value. + * + *

NOTE: This class is only used in the profiling signal, and exists in this module and package + * because its a common dependency of the modules that use it. Long term, it probably makes more + * sense to live in {@code opentelemetry-sdk-profiles} once such a module exists. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public interface AttributeKeyValue { + + /** Returns a {@link AttributeKeyValue} for the given {@link AttributeKey} and {@code value}. */ + static AttributeKeyValue of(AttributeKey attributeKey, T value) { + return AttributeKeyValueImpl.create(attributeKey, value); + } + + /** Returns a List corresponding to the provided Map. This is a copy, not a view. */ + @SuppressWarnings("unchecked") + static List> of(Attributes attributes) { + List> result = new ArrayList<>(attributes.size()); + attributes.forEach( + (key, value) -> { + result.add(of((AttributeKey) key, (T) value)); + }); + return result; + } + + /** Returns the key. */ + AttributeKey getAttributeKey(); + + /** Returns the value. */ + T getValue(); +} diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AttributeKeyValueImpl.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AttributeKeyValueImpl.java new file mode 100644 index 00000000000..c4f6cdf89ca --- /dev/null +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AttributeKeyValueImpl.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.otlp; + +import com.google.auto.value.AutoValue; +import io.opentelemetry.api.common.AttributeKey; + +@AutoValue +abstract class AttributeKeyValueImpl implements AttributeKeyValue { + + AttributeKeyValueImpl() {} + + static AttributeKeyValueImpl create(AttributeKey attributeKey, T value) { + return new AutoValue_AttributeKeyValueImpl(attributeKey, value); + } +} diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/KeyValueMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/KeyValueMarshaler.java index 47a0a32a759..08b9699291d 100644 --- a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/KeyValueMarshaler.java +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/KeyValueMarshaler.java @@ -17,6 +17,7 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.function.BiConsumer; +import java.util.function.Consumer; /** * A Marshaler of key value pairs. @@ -65,6 +66,28 @@ public void accept(AttributeKey attributeKey, Object o) { return marshalers; } + @SuppressWarnings("AvoidObjectArrays") + public static KeyValueMarshaler[] createRepeated(List> items) { + if (items.isEmpty()) { + return EMPTY_REPEATED; + } + + KeyValueMarshaler[] keyValueMarshalers = new KeyValueMarshaler[items.size()]; + items.forEach( + item -> + new Consumer>() { + int index = 0; + + @Override + public void accept(AttributeKeyValue attributeKeyValue) { + keyValueMarshalers[index++] = + KeyValueMarshaler.create( + attributeKeyValue.getAttributeKey(), attributeKeyValue.getValue()); + } + }); + return keyValueMarshalers; + } + @SuppressWarnings("unchecked") private static KeyValueMarshaler create(AttributeKey attributeKey, Object value) { byte[] keyUtf8; diff --git a/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/AttributeKeyValueTest.java b/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/AttributeKeyValueTest.java new file mode 100644 index 00000000000..220b3845352 --- /dev/null +++ b/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/AttributeKeyValueTest.java @@ -0,0 +1,49 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.otlp; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.AttributeType; +import io.opentelemetry.api.common.Attributes; +import java.util.Collections; +import java.util.List; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +class AttributeKeyValueTest { + + @Test + void equalsVerifier() { + EqualsVerifier.forClass(AttributeKeyValue.class).verify(); + } + + @Test + void ofEmpty() { + assertThat(AttributeKeyValue.of(Attributes.empty())).isEmpty(); + } + + @Test + void ofOne() { + AttributeKeyValue input = AttributeKeyValue.of(AttributeKey.stringKey("foo"), "bar"); + Attributes attributes = Attributes.of(input.getAttributeKey(), input.getValue()); + List> list = AttributeKeyValue.of(attributes); + Assertions.assertThat(list).hasSize(1); + assertThat(list.get(0)).isEqualTo(input); + } + + @Test + void ofList() { + AttributeKeyValue> input = + AttributeKeyValue.of(AttributeKey.longArrayKey("foo"), Collections.emptyList()); + Attributes attributes = Attributes.of(input.getAttributeKey(), input.getValue()); + List> list = AttributeKeyValue.of(attributes); + Assertions.assertThat(list).hasSize(1); + assertThat(list.get(0).getAttributeKey().getType()).isEqualTo(AttributeType.LONG_ARRAY); + } +}