Skip to content
Open
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
Copy link
Member Author

Choose a reason for hiding this comment

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

changes copy-pasted from ExtendedArrayBackedAttributes.java

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

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

Expand Down Expand Up @@ -45,9 +47,117 @@ public AttributesBuilder toBuilder() {
@Override
@Nullable
public <T> T get(AttributeKey<T> key) {
if (key == null) {
return null;
}
if (key.getType() == AttributeType.VALUE) {
return (T) getAsValue(key.getKey());
}
// Check if we're looking for an array type but have a VALUE with empty array
if (isArrayType(key.getType())) {
T value = (T) super.get(key);
if (value == null) {
// Check if there's a VALUE with the same key that contains an empty array
Value<?> valueAttr = getValueAttribute(key.getKey());
if (valueAttr != null && isEmptyArray(valueAttr)) {
return (T) Collections.emptyList();
}
}
return value;
}
return (T) super.get(key);
}

private static boolean isArrayType(AttributeType type) {
return type == AttributeType.STRING_ARRAY
|| type == AttributeType.LONG_ARRAY
|| type == AttributeType.DOUBLE_ARRAY
|| type == AttributeType.BOOLEAN_ARRAY;
}

@Nullable
private Value<?> getValueAttribute(String keyName) {
List<Object> data = data();
for (int i = 0; i < data.size(); i += 2) {
AttributeKey<?> currentKey = (AttributeKey<?>) data.get(i);
if (currentKey.getKey().equals(keyName) && currentKey.getType() == AttributeType.VALUE) {
return (Value<?>) data.get(i + 1);
}
}
return null;
}

private static boolean isEmptyArray(Value<?> value) {
if (value.getType() != ValueType.ARRAY) {
return false;
}
@SuppressWarnings("unchecked")
List<Value<?>> arrayValues = (List<Value<?>>) value.getValue();
return arrayValues.isEmpty();
}

@Nullable
private Value<?> getAsValue(String keyName) {
// Find any attribute with the same key name and convert it to Value
List<Object> data = data();
for (int i = 0; i < data.size(); i += 2) {
AttributeKey<?> currentKey = (AttributeKey<?>) data.get(i);
if (currentKey.getKey().equals(keyName)) {
Object value = data.get(i + 1);
return asValue(currentKey.getType(), value);
}
}
return null;
}

@SuppressWarnings("unchecked")
@Nullable
private static Value<?> asValue(AttributeType type, Object value) {
switch (type) {
case STRING:
return Value.of((String) value);
case LONG:
return Value.of((Long) value);
case DOUBLE:
return Value.of((Double) value);
case BOOLEAN:
return Value.of((Boolean) value);
case STRING_ARRAY:
List<String> stringList = (List<String>) value;
Value<?>[] stringValues = new Value<?>[stringList.size()];
for (int i = 0; i < stringList.size(); i++) {
stringValues[i] = Value.of(stringList.get(i));
}
return Value.of(stringValues);
case LONG_ARRAY:
List<Long> longList = (List<Long>) value;
Value<?>[] longValues = new Value<?>[longList.size()];
for (int i = 0; i < longList.size(); i++) {
longValues[i] = Value.of(longList.get(i));
}
return Value.of(longValues);
case DOUBLE_ARRAY:
List<Double> doubleList = (List<Double>) value;
Value<?>[] doubleValues = new Value<?>[doubleList.size()];
for (int i = 0; i < doubleList.size(); i++) {
doubleValues[i] = Value.of(doubleList.get(i));
}
return Value.of(doubleValues);
case BOOLEAN_ARRAY:
List<Boolean> booleanList = (List<Boolean>) value;
Value<?>[] booleanValues = new Value<?>[booleanList.size()];
for (int i = 0; i < booleanList.size(); i++) {
booleanValues[i] = Value.of(booleanList.get(i));
}
return Value.of(booleanValues);
case VALUE:
// Already a Value
return (Value<?>) value;
}
// Should not reach here
return null;
}

static Attributes sortAndFilterToAttributes(Object... data) {
// null out any empty keys or keys with null values
// so they will then be removed by the sortAndFilter method.
Expand Down
Copy link
Member Author

Choose a reason for hiding this comment

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

changes copy-pasted from ExtendedArrayBackedAttributesBuilder.java

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@

package io.opentelemetry.api.common;

import static io.opentelemetry.api.common.AttributeKey.booleanArrayKey;
import static io.opentelemetry.api.common.AttributeKey.booleanKey;
import static io.opentelemetry.api.common.AttributeKey.doubleArrayKey;
import static io.opentelemetry.api.common.AttributeKey.doubleKey;
import static io.opentelemetry.api.common.AttributeKey.longArrayKey;
import static io.opentelemetry.api.common.AttributeKey.longKey;
import static io.opentelemetry.api.common.AttributeKey.stringArrayKey;
import static io.opentelemetry.api.common.AttributeKey.stringKey;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
Expand Down Expand Up @@ -42,11 +51,114 @@ public <T> AttributesBuilder put(AttributeKey<T> key, @Nullable T value) {
if (key == null || key.getKey().isEmpty() || value == null) {
return this;
}
if (key.getType() == AttributeType.VALUE && value instanceof Value) {
putValue(key, (Value<?>) value);
return this;
}
data.add(key);
data.add(value);
return this;
}

@SuppressWarnings("unchecked")
private void putValue(AttributeKey<?> key, Value<?> valueObj) {
// Convert VALUE type to narrower type when possible
String keyName = key.getKey();
switch (valueObj.getType()) {
case STRING:
put(stringKey(keyName), ((Value<String>) valueObj).getValue());
return;
case LONG:
put(longKey(keyName), ((Value<Long>) valueObj).getValue());
return;
case DOUBLE:
put(doubleKey(keyName), ((Value<Double>) valueObj).getValue());
return;
case BOOLEAN:
put(booleanKey(keyName), ((Value<Boolean>) valueObj).getValue());
return;
case ARRAY:
List<Value<?>> arrayValues = (List<Value<?>>) valueObj.getValue();
AttributeType attributeType = attributeType(arrayValues);
switch (attributeType) {
case STRING_ARRAY:
List<String> strings = new ArrayList<>(arrayValues.size());
for (Value<?> v : arrayValues) {
strings.add((String) v.getValue());
}
put(stringArrayKey(keyName), strings);
return;
case LONG_ARRAY:
List<Long> longs = new ArrayList<>(arrayValues.size());
for (Value<?> v : arrayValues) {
longs.add((Long) v.getValue());
}
put(longArrayKey(keyName), longs);
return;
case DOUBLE_ARRAY:
List<Double> doubles = new ArrayList<>(arrayValues.size());
for (Value<?> v : arrayValues) {
doubles.add((Double) v.getValue());
}
put(doubleArrayKey(keyName), doubles);
return;
case BOOLEAN_ARRAY:
List<Boolean> booleans = new ArrayList<>(arrayValues.size());
for (Value<?> v : arrayValues) {
booleans.add((Boolean) v.getValue());
}
put(booleanArrayKey(keyName), booleans);
return;
case VALUE:
// Not coercible (empty, non-homogeneous, or unsupported element type)
data.add(key);
data.add(valueObj);
return;
default:
throw new IllegalArgumentException("Unexpected array attribute type: " + attributeType);
}
case KEY_VALUE_LIST:
case BYTES:
case EMPTY:
// Keep as VALUE type
data.add(key);
data.add(valueObj);
}
}

/**
* Returns the AttributeType for a homogeneous array (STRING_ARRAY, LONG_ARRAY, DOUBLE_ARRAY, or
* BOOLEAN_ARRAY), or VALUE if the array is empty, non-homogeneous, or contains unsupported
* element types.
*/
private static AttributeType attributeType(List<Value<?>> arrayValues) {
if (arrayValues.isEmpty()) {
return AttributeType.VALUE;
}
ValueType elementType = arrayValues.get(0).getType();
for (Value<?> v : arrayValues) {
if (v.getType() != elementType) {
return AttributeType.VALUE;
}
}
switch (elementType) {
case STRING:
return AttributeType.STRING_ARRAY;
case LONG:
return AttributeType.LONG_ARRAY;
case DOUBLE:
return AttributeType.DOUBLE_ARRAY;
case BOOLEAN:
return AttributeType.BOOLEAN_ARRAY;
case ARRAY:
case KEY_VALUE_LIST:
case BYTES:
case EMPTY:
return AttributeType.VALUE;
}
throw new IllegalArgumentException("Unsupported element type: " + elementType);
}

@Override
@SuppressWarnings({"unchecked", "rawtypes"})
// Safe: Attributes guarantees iteration over matching AttributeKey<T> / value pairs.
Expand Down
Copy link
Member Author

Choose a reason for hiding this comment

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

changes copy-pasted from ExtendedAttributeKey.java

Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,19 @@ static AttributeKey<List<Long>> longArrayKey(String key) {
static AttributeKey<List<Double>> doubleArrayKey(String key) {
return InternalAttributeKeyImpl.create(key, AttributeType.DOUBLE_ARRAY);
}

/**
* Returns a new ExtendedAttributeKey for {@link Value} valued attributes.
*
* <p>Simple attributes ({@link AttributeType#STRING}, {@link AttributeType#LONG}, {@link
* AttributeType#DOUBLE}, {@link AttributeType#BOOLEAN}, {@link AttributeType#STRING_ARRAY},
* {@link AttributeType#LONG_ARRAY}, {@link AttributeType#DOUBLE_ARRAY}, {@link
* AttributeType#BOOLEAN_ARRAY}) should be used whenever possible. Instrumentations should assume
* that backends do not index individual properties of complex attributes, that querying or
* aggregating on such properties is inefficient and complicated, and that reporting complex
* attributes carries higher performance overhead.
*/
static AttributeKey<Value<?>> valueKey(String key) {
return InternalAttributeKeyImpl.create(key, AttributeType.VALUE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ public enum AttributeType {
STRING_ARRAY,
BOOLEAN_ARRAY,
LONG_ARRAY,
DOUBLE_ARRAY
DOUBLE_ARRAY,
VALUE
}
Copy link
Member Author

Choose a reason for hiding this comment

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

changes copy-pasted from ExtendedAttributes.java

Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,58 @@
@Immutable
public interface Attributes {

/** Returns the value for the given {@link AttributeKey}, or {@code null} if not found. */
/**
* Returns the value for the given {@link AttributeKey}, or {@code null} if not found.
*
* <p>Note: this method will automatically return the corresponding {@link
* io.opentelemetry.api.common.Value} instance when passed a key of type {@link
* AttributeType#VALUE} and a simple attribute is found. This is the inverse of {@link
* AttributesBuilder#put(AttributeKey, Object)} when the key is {@link AttributeType#VALUE}.
*
* <ul>
* <li>If {@code put(AttributeKey.stringKey("key"), "a")} was called, then {@code
* get(AttributeKey.valueKey("key"))} returns {@code Value.of("a")}.
* <li>If {@code put(AttributeKey.longKey("key"), 1L)} was called, then {@code
* get(AttributeKey.valueKey("key"))} returns {@code Value.of(1L)}.
* <li>If {@code put(AttributeKey.doubleKey("key"), 1.0)} was called, then {@code
* get(AttributeKey.valueKey("key"))} returns {@code Value.of(1.0)}.
* <li>If {@code put(AttributeKey.booleanKey("key"), true)} was called, then {@code
* get(AttributeKey.valueKey("key"))} returns {@code Value.of(true)}.
* <li>If {@code put(AttributeKey.stringArrayKey("key"), Arrays.asList("a", "b"))} was called,
* then {@code get(AttributeKey.valueKey("key"))} returns {@code Value.of(Value.of("a"),
* Value.of("b"))}.
* <li>If {@code put(AttributeKey.longArrayKey("key"), Arrays.asList(1L, 2L))} was called, then
* {@code get(AttributeKey.valueKey("key"))} returns {@code Value.of(Value.of(1L),
* Value.of(2L))}.
* <li>If {@code put(AttributeKey.doubleArrayKey("key"), Arrays.asList(1.0, 2.0))} was called,
* then {@code get(AttributeKey.valueKey("key"))} returns {@code Value.of(Value.of(1.0),
* Value.of(2.0))}.
* <li>If {@code put(AttributeKey.booleanArrayKey("key"), Arrays.asList(true, false))} was
* called, then {@code get(AttributeKey.valueKey("key"))} returns {@code
* Value.of(Value.of(true), Value.of(false))}.
* </ul>
*
* <p>Further, if {@code put(AttributeKey.valueKey("key"), Value.of(emptyList()))} was called,
* then
*
* <ul>
* <li>{@code get(AttributeKey.stringArrayKey("key"))}
* <li>{@code get(AttributeKey.longArrayKey("key"))}
* <li>{@code get(AttributeKey.booleanArrayKey("key"))}
* <li>{@code get(AttributeKey.doubleArrayKey("key"))}
* </ul>
*
* <p>all return an empty list (as opposed to {@code null}).
*/
@Nullable
<T> T get(AttributeKey<T> key);

/** Iterates over all the key-value pairs of attributes contained by this instance. */
/**
* Iterates over all the key-value pairs of attributes contained by this instance.
*
* <p>Note: {@link AttributeType#VALUE} attributes will be represented as simple attributes if
* possible. See {@link AttributesBuilder#put(AttributeKey, Object)} for more details.
*/
void forEach(BiConsumer<? super AttributeKey<?>, ? super Object> consumer);

/** The number of attributes contained in this. */
Expand All @@ -46,7 +93,12 @@ public interface Attributes {
/** Whether there are any attributes contained in this. */
boolean isEmpty();

/** Returns a read-only view of this {@link Attributes} as a {@link Map}. */
/**
* Returns a read-only view of this {@link Attributes} as a {@link Map}.
*
* <p>Note: {@link AttributeType#VALUE} attributes will be represented as simple attributes in
* this map if possible. See {@link AttributesBuilder#put(AttributeKey, Object)} for more details.
*/
Map<AttributeKey<?>, Object> asMap();

/** Returns a {@link Attributes} instance with no attributes. */
Expand Down
Loading
Loading