Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sketch out ExtendedAttributes #6983

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/all/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ dependencies {

testImplementation("edu.berkeley.cs.jqf:jqf-fuzz")
testImplementation("com.google.guava:guava-testlib")
testImplementation(project(":sdk:all"))
testImplementation(project(":sdk:testing"))
}

tasks.test {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.common;

import io.opentelemetry.api.internal.ImmutableKeyValuePairs;
import java.util.ArrayList;
import java.util.Comparator;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

@Immutable
final class ArrayBackedExtendedAttributes
extends ImmutableKeyValuePairs<ExtendedAttributeKey<?>, Object> implements ExtendedAttributes {

// We only compare the key name, not type, when constructing, to allow deduping keys with the
// same name but different type.
private static final Comparator<ExtendedAttributeKey<?>> KEY_COMPARATOR_FOR_CONSTRUCTION =
Comparator.comparing(ExtendedAttributeKey::getKey);

static final ExtendedAttributes EMPTY = ExtendedAttributes.builder().build();

@Nullable private Attributes attributes;

private ArrayBackedExtendedAttributes(
Object[] data, Comparator<ExtendedAttributeKey<?>> keyComparator) {
super(data, keyComparator);
}

/**
* Only use this constructor if you can guarantee that the data has been de-duped, sorted by key
* and contains no null values or null/empty keys.
*
* @param data the raw data
*/
ArrayBackedExtendedAttributes(Object[] data) {
super(data);
}

@Override
public ExtendedAttributesBuilder toBuilder() {
return new ArrayBackedExtendedAttributesBuilder(new ArrayList<>(data()));

Check warning on line 44 in api/all/src/main/java/io/opentelemetry/api/common/ArrayBackedExtendedAttributes.java

View check run for this annotation

Codecov / codecov/patch

api/all/src/main/java/io/opentelemetry/api/common/ArrayBackedExtendedAttributes.java#L44

Added line #L44 was not covered by tests
}

@SuppressWarnings("unchecked")
@Override
@Nullable
public <T> T get(ExtendedAttributeKey<T> key) {
return (T) super.get(key);
}

@SuppressWarnings("unchecked")
@Override
public Attributes asAttributes() {
if (attributes == null) {
AttributesBuilder builder = Attributes.builder();
forEach(
(extendedAttributeKey, value) -> {
AttributeKey<Object> attributeKey =
(AttributeKey<Object>) extendedAttributeKey.asAttributeKey();
if (attributeKey != null) {
builder.put(attributeKey, value);
}
});
attributes = builder.build();
}
return attributes;
}

static ExtendedAttributes sortAndFilterToAttributes(Object... data) {
// null out any empty keys or keys with null values
// so they will then be removed by the sortAndFilter method.
for (int i = 0; i < data.length; i += 2) {
ExtendedAttributeKey<?> key = (ExtendedAttributeKey<?>) data[i];
if (key != null && key.getKey().isEmpty()) {
data[i] = null;

Check warning on line 78 in api/all/src/main/java/io/opentelemetry/api/common/ArrayBackedExtendedAttributes.java

View check run for this annotation

Codecov / codecov/patch

api/all/src/main/java/io/opentelemetry/api/common/ArrayBackedExtendedAttributes.java#L78

Added line #L78 was not covered by tests
}
}
return new ArrayBackedExtendedAttributes(data, KEY_COMPARATOR_FOR_CONSTRUCTION);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.common;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

class ArrayBackedExtendedAttributesBuilder implements ExtendedAttributesBuilder {
private final List<Object> data;

ArrayBackedExtendedAttributesBuilder() {
data = new ArrayList<>();
}

ArrayBackedExtendedAttributesBuilder(List<Object> data) {
this.data = data;
}

Check warning on line 21 in api/all/src/main/java/io/opentelemetry/api/common/ArrayBackedExtendedAttributesBuilder.java

View check run for this annotation

Codecov / codecov/patch

api/all/src/main/java/io/opentelemetry/api/common/ArrayBackedExtendedAttributesBuilder.java#L19-L21

Added lines #L19 - L21 were not covered by tests

@Override
public ExtendedAttributes build() {
// If only one key-value pair AND the entry hasn't been set to null (by #remove(AttributeKey<T>)
// or #removeIf(Predicate<AttributeKey<?>>)), then we can bypass sorting and filtering
if (data.size() == 2 && data.get(0) != null) {
return new ArrayBackedExtendedAttributes(data.toArray());
}
return ArrayBackedExtendedAttributes.sortAndFilterToAttributes(data.toArray());
}

@Override
public <T> ExtendedAttributesBuilder put(ExtendedAttributeKey<T> key, T value) {
if (key == null || key.getKey().isEmpty() || value == null) {
return this;

Check warning on line 36 in api/all/src/main/java/io/opentelemetry/api/common/ArrayBackedExtendedAttributesBuilder.java

View check run for this annotation

Codecov / codecov/patch

api/all/src/main/java/io/opentelemetry/api/common/ArrayBackedExtendedAttributesBuilder.java#L36

Added line #L36 was not covered by tests
}
data.add(key);
data.add(value);
return this;
}

@Override
public ExtendedAttributesBuilder removeIf(Predicate<ExtendedAttributeKey<?>> predicate) {
if (predicate == null) {
return this;

Check warning on line 46 in api/all/src/main/java/io/opentelemetry/api/common/ArrayBackedExtendedAttributesBuilder.java

View check run for this annotation

Codecov / codecov/patch

api/all/src/main/java/io/opentelemetry/api/common/ArrayBackedExtendedAttributesBuilder.java#L46

Added line #L46 was not covered by tests
}
for (int i = 0; i < data.size() - 1; i += 2) {
Object entry = data.get(i);

Check warning on line 49 in api/all/src/main/java/io/opentelemetry/api/common/ArrayBackedExtendedAttributesBuilder.java

View check run for this annotation

Codecov / codecov/patch

api/all/src/main/java/io/opentelemetry/api/common/ArrayBackedExtendedAttributesBuilder.java#L49

Added line #L49 was not covered by tests
if (entry instanceof ExtendedAttributeKey
&& predicate.test((ExtendedAttributeKey<?>) entry)) {
// null items are filtered out in ArrayBackedAttributes
data.set(i, null);
data.set(i + 1, null);

Check warning on line 54 in api/all/src/main/java/io/opentelemetry/api/common/ArrayBackedExtendedAttributesBuilder.java

View check run for this annotation

Codecov / codecov/patch

api/all/src/main/java/io/opentelemetry/api/common/ArrayBackedExtendedAttributesBuilder.java#L53-L54

Added lines #L53 - L54 were not covered by tests
}
}
return this;

Check warning on line 57 in api/all/src/main/java/io/opentelemetry/api/common/ArrayBackedExtendedAttributesBuilder.java

View check run for this annotation

Codecov / codecov/patch

api/all/src/main/java/io/opentelemetry/api/common/ArrayBackedExtendedAttributesBuilder.java#L57

Added line #L57 was not covered by tests
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
/** Returns the type of attribute for this key. Useful for building switch statements. */
AttributeType getType();

default ExtendedAttributeKey<T> asExtendedAttributeKey() {
return InternalAttributeKeyImpl.toExtendedAttributeKey(this);

Check warning on line 30 in api/all/src/main/java/io/opentelemetry/api/common/AttributeKey.java

View check run for this annotation

Codecov / codecov/patch

api/all/src/main/java/io/opentelemetry/api/common/AttributeKey.java#L30

Added line #L30 was not covered by tests
}

/** Returns a new AttributeKey for String valued attributes. */
static AttributeKey<String> stringKey(String key) {
return InternalAttributeKeyImpl.create(key, AttributeType.STRING);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.common;

import io.opentelemetry.api.internal.InternalExtendedAttributeKeyImpl;
import java.util.List;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

/** TODO. */
@Immutable
public interface ExtendedAttributeKey<T> {
/** Returns the underlying String representation of the key. */
String getKey();

/** Returns the type of attribute for this key. Useful for building switch statements. */
ExtendedAttributeType getType();

@Nullable
default AttributeKey<T> asAttributeKey() {
return InternalExtendedAttributeKeyImpl.toAttributeKey(this);

Check warning on line 24 in api/all/src/main/java/io/opentelemetry/api/common/ExtendedAttributeKey.java

View check run for this annotation

Codecov / codecov/patch

api/all/src/main/java/io/opentelemetry/api/common/ExtendedAttributeKey.java#L24

Added line #L24 was not covered by tests
}

/** Returns a new ExtendedAttributeKey for String valued attributes. */
static ExtendedAttributeKey<String> stringKey(String key) {
return AttributeKey.stringKey(key).asExtendedAttributeKey();
}

/** Returns a new ExtendedAttributeKey for Boolean valued attributes. */
static ExtendedAttributeKey<Boolean> booleanKey(String key) {
return AttributeKey.booleanKey(key).asExtendedAttributeKey();

Check warning on line 34 in api/all/src/main/java/io/opentelemetry/api/common/ExtendedAttributeKey.java

View check run for this annotation

Codecov / codecov/patch

api/all/src/main/java/io/opentelemetry/api/common/ExtendedAttributeKey.java#L34

Added line #L34 was not covered by tests
}

/** Returns a new ExtendedAttributeKey for Long valued attributes. */
static ExtendedAttributeKey<Long> longKey(String key) {
return AttributeKey.longKey(key).asExtendedAttributeKey();
}

/** Returns a new ExtendedAttributeKey for Double valued attributes. */
static ExtendedAttributeKey<Double> doubleKey(String key) {
return AttributeKey.doubleKey(key).asExtendedAttributeKey();

Check warning on line 44 in api/all/src/main/java/io/opentelemetry/api/common/ExtendedAttributeKey.java

View check run for this annotation

Codecov / codecov/patch

api/all/src/main/java/io/opentelemetry/api/common/ExtendedAttributeKey.java#L44

Added line #L44 was not covered by tests
}

/** Returns a new ExtendedAttributeKey for List&lt;String&gt; valued attributes. */
static ExtendedAttributeKey<List<String>> stringArrayKey(String key) {
return AttributeKey.stringArrayKey(key).asExtendedAttributeKey();

Check warning on line 49 in api/all/src/main/java/io/opentelemetry/api/common/ExtendedAttributeKey.java

View check run for this annotation

Codecov / codecov/patch

api/all/src/main/java/io/opentelemetry/api/common/ExtendedAttributeKey.java#L49

Added line #L49 was not covered by tests
}

/** Returns a new ExtendedAttributeKey for List&lt;Boolean&gt; valued attributes. */
static ExtendedAttributeKey<List<Boolean>> booleanArrayKey(String key) {
return AttributeKey.booleanArrayKey(key).asExtendedAttributeKey();

Check warning on line 54 in api/all/src/main/java/io/opentelemetry/api/common/ExtendedAttributeKey.java

View check run for this annotation

Codecov / codecov/patch

api/all/src/main/java/io/opentelemetry/api/common/ExtendedAttributeKey.java#L54

Added line #L54 was not covered by tests
}

/** Returns a new ExtendedAttributeKey for List&lt;Long&gt; valued attributes. */
static ExtendedAttributeKey<List<Long>> longArrayKey(String key) {
return AttributeKey.longArrayKey(key).asExtendedAttributeKey();

Check warning on line 59 in api/all/src/main/java/io/opentelemetry/api/common/ExtendedAttributeKey.java

View check run for this annotation

Codecov / codecov/patch

api/all/src/main/java/io/opentelemetry/api/common/ExtendedAttributeKey.java#L59

Added line #L59 was not covered by tests
}

/** Returns a new ExtendedAttributeKey for List&lt;Double&gt; valued attributes. */
static ExtendedAttributeKey<List<Double>> doubleArrayKey(String key) {
return AttributeKey.doubleArrayKey(key).asExtendedAttributeKey();

Check warning on line 64 in api/all/src/main/java/io/opentelemetry/api/common/ExtendedAttributeKey.java

View check run for this annotation

Codecov / codecov/patch

api/all/src/main/java/io/opentelemetry/api/common/ExtendedAttributeKey.java#L64

Added line #L64 was not covered by tests
}

/** Returns a new ExtendedAttributeKey for Map valued attributes. */
static ExtendedAttributeKey<ExtendedAttributes> mapKey(String key) {
return InternalExtendedAttributeKeyImpl.create(key, ExtendedAttributeType.MAP);
}

/** Returns a new ExtendedAttributeKey for Map array valued attributes. */
static ExtendedAttributeKey<List<ExtendedAttributes>> mapArrayKey(String key) {
return InternalExtendedAttributeKeyImpl.create(key, ExtendedAttributeType.MAP_ARRAY);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.common;

/** TODO. */
public enum ExtendedAttributeType {
STRING,
BOOLEAN,
LONG,
DOUBLE,
STRING_ARRAY,
BOOLEAN_ARRAY,
LONG_ARRAY,
DOUBLE_ARRAY,
MAP,
MAP_ARRAY;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.common;

import java.util.Map;
import java.util.function.BiConsumer;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

@SuppressWarnings("rawtypes")
@Immutable
public interface ExtendedAttributes {
Copy link
Member

@pellared pellared Jan 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about renaming to ComplexAttributes?

It does not suggest that there is some inheritance or that it e.g. supports more primitive types.
I think that "complex" is a better term for a type which supports nesting. I see that the "complex" term is even used in https://refactoring.guru/design-patterns/composite.
At last the proposed name is shorter 😉


/** Returns the value for the given {@link AttributeKey}, or {@code null} if not found. */
@Nullable
default <T> T get(AttributeKey<T> key) {
if (key == null) {
return null;

Check warning on line 21 in api/all/src/main/java/io/opentelemetry/api/common/ExtendedAttributes.java

View check run for this annotation

Codecov / codecov/patch

api/all/src/main/java/io/opentelemetry/api/common/ExtendedAttributes.java#L21

Added line #L21 was not covered by tests
}
return get(key.asExtendedAttributeKey());
}

/** Returns the value for the given {@link ExtendedAttributeKey}, or {@code null} if not found. */
@Nullable
<T> T get(ExtendedAttributeKey<T> key);

/** Iterates over all the key-value pairs of attributes contained by this instance. */
void forEach(BiConsumer<? super ExtendedAttributeKey<?>, ? super Object> consumer);

/** The number of attributes contained in this. */
int size();

/** Whether there are any attributes contained in this. */
boolean isEmpty();

/** Returns a read-only view of this {@link ExtendedAttributes} as a {@link Map}. */
Map<ExtendedAttributeKey<?>, Object> asMap();

/**
* Return a view of this extended attributes with entries limited to those representable as
* standard attributes.
*/
Attributes asAttributes();

/** Returns a {@link ExtendedAttributes} instance with no attributes. */
static ExtendedAttributes empty() {
return ArrayBackedExtendedAttributes.EMPTY;
}

/**
* Returns a new {@link ExtendedAttributesBuilder} instance for creating arbitrary {@link
* ExtendedAttributes}.
*/
static ExtendedAttributesBuilder builder() {
return new ArrayBackedExtendedAttributesBuilder();
}

/**
* Returns a new {@link ExtendedAttributesBuilder} instance populated with the data of this {@link
* ExtendedAttributes}.
*/
ExtendedAttributesBuilder toBuilder();
}
Loading
Loading