Skip to content

Commit

Permalink
Add AttributeKeyValue abstraction to common otlp exporters (#7026)
Browse files Browse the repository at this point in the history
  • Loading branch information
jhalliday authored Jan 16, 2025
1 parent c8da020 commit 2fb3eba
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 0 deletions.
2 changes: 2 additions & 0 deletions exporters/otlp/common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ val versions: Map<String, String> 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"))
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
*
* <p>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.
*
* <p>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.
*
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
public interface AttributeKeyValue<T> {

/** Returns a {@link AttributeKeyValue} for the given {@link AttributeKey} and {@code value}. */
static <T> AttributeKeyValue<T> of(AttributeKey<T> 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 <T> List<AttributeKeyValue<?>> of(Attributes attributes) {
List<AttributeKeyValue<?>> result = new ArrayList<>(attributes.size());
attributes.forEach(
(key, value) -> {
result.add(of((AttributeKey<T>) key, (T) value));
});
return result;
}

/** Returns the key. */
AttributeKey<T> getAttributeKey();

/** Returns the value. */
T getValue();
}
Original file line number Diff line number Diff line change
@@ -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<T> implements AttributeKeyValue<T> {

AttributeKeyValueImpl() {}

static <T> AttributeKeyValueImpl<T> create(AttributeKey<T> attributeKey, T value) {
return new AutoValue_AttributeKeyValueImpl<T>(attributeKey, value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -65,6 +66,28 @@ public void accept(AttributeKey<?> attributeKey, Object o) {
return marshalers;
}

@SuppressWarnings("AvoidObjectArrays")
public static KeyValueMarshaler[] createRepeated(List<AttributeKeyValue<?>> items) {
if (items.isEmpty()) {
return EMPTY_REPEATED;
}

KeyValueMarshaler[] keyValueMarshalers = new KeyValueMarshaler[items.size()];
items.forEach(
item ->
new Consumer<AttributeKeyValue<?>>() {
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String> input = AttributeKeyValue.of(AttributeKey.stringKey("foo"), "bar");
Attributes attributes = Attributes.of(input.getAttributeKey(), input.getValue());
List<AttributeKeyValue<?>> list = AttributeKeyValue.of(attributes);
Assertions.assertThat(list).hasSize(1);
assertThat(list.get(0)).isEqualTo(input);
}

@Test
void ofList() {
AttributeKeyValue<List<Long>> input =
AttributeKeyValue.of(AttributeKey.longArrayKey("foo"), Collections.emptyList());
Attributes attributes = Attributes.of(input.getAttributeKey(), input.getValue());
List<AttributeKeyValue<?>> list = AttributeKeyValue.of(attributes);
Assertions.assertThat(list).hasSize(1);
assertThat(list.get(0).getAttributeKey().getType()).isEqualTo(AttributeType.LONG_ARRAY);
}
}

0 comments on commit 2fb3eba

Please sign in to comment.