Skip to content

Commit 7c5255f

Browse files
authored
Complex attributes incubating implementation (#7814)
1 parent 683a447 commit 7c5255f

File tree

17 files changed

+979
-36
lines changed

17 files changed

+979
-36
lines changed

api/incubator/src/main/java/io/opentelemetry/api/incubator/common/ArrayBackedExtendedAttributes.java

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@
88
import io.opentelemetry.api.common.AttributeKey;
99
import io.opentelemetry.api.common.Attributes;
1010
import io.opentelemetry.api.common.AttributesBuilder;
11+
import io.opentelemetry.api.common.Value;
12+
import io.opentelemetry.api.common.ValueType;
1113
import io.opentelemetry.api.internal.ImmutableKeyValuePairs;
1214
import java.util.ArrayList;
15+
import java.util.Collections;
1316
import java.util.Comparator;
17+
import java.util.List;
1418
import javax.annotation.Nullable;
1519
import javax.annotation.concurrent.Immutable;
1620

@@ -51,9 +55,121 @@ public ExtendedAttributesBuilder toBuilder() {
5155
@Override
5256
@Nullable
5357
public <T> T get(ExtendedAttributeKey<T> key) {
58+
if (key == null) {
59+
return null;
60+
}
61+
if (key.getType() == ExtendedAttributeType.VALUE) {
62+
return (T) getAsValue(key.getKey());
63+
}
64+
// Check if we're looking for an array type but have a VALUE with empty array
65+
if (isArrayType(key.getType())) {
66+
T value = (T) super.get(key);
67+
if (value == null) {
68+
// Check if there's a VALUE with the same key that contains an empty array
69+
Value<?> valueAttr = getValueAttribute(key.getKey());
70+
if (valueAttr != null && isEmptyArray(valueAttr)) {
71+
return (T) Collections.emptyList();
72+
}
73+
}
74+
return value;
75+
}
5476
return (T) super.get(key);
5577
}
5678

79+
private static boolean isArrayType(ExtendedAttributeType type) {
80+
return type == ExtendedAttributeType.STRING_ARRAY
81+
|| type == ExtendedAttributeType.LONG_ARRAY
82+
|| type == ExtendedAttributeType.DOUBLE_ARRAY
83+
|| type == ExtendedAttributeType.BOOLEAN_ARRAY;
84+
}
85+
86+
@Nullable
87+
private Value<?> getValueAttribute(String keyName) {
88+
List<Object> data = data();
89+
for (int i = 0; i < data.size(); i += 2) {
90+
ExtendedAttributeKey<?> currentKey = (ExtendedAttributeKey<?>) data.get(i);
91+
if (currentKey.getKey().equals(keyName)
92+
&& currentKey.getType() == ExtendedAttributeType.VALUE) {
93+
return (Value<?>) data.get(i + 1);
94+
}
95+
}
96+
return null;
97+
}
98+
99+
private static boolean isEmptyArray(Value<?> value) {
100+
if (value.getType() != ValueType.ARRAY) {
101+
return false;
102+
}
103+
@SuppressWarnings("unchecked")
104+
List<Value<?>> arrayValues = (List<Value<?>>) value.getValue();
105+
return arrayValues.isEmpty();
106+
}
107+
108+
@Nullable
109+
private Value<?> getAsValue(String keyName) {
110+
// Find any attribute with the same key name and convert it to Value
111+
List<Object> data = data();
112+
for (int i = 0; i < data.size(); i += 2) {
113+
ExtendedAttributeKey<?> currentKey = (ExtendedAttributeKey<?>) data.get(i);
114+
if (currentKey.getKey().equals(keyName)) {
115+
Object value = data.get(i + 1);
116+
return asValue(currentKey.getType(), value);
117+
}
118+
}
119+
return null;
120+
}
121+
122+
@SuppressWarnings("unchecked")
123+
@Nullable
124+
private static Value<?> asValue(ExtendedAttributeType type, Object value) {
125+
switch (type) {
126+
case STRING:
127+
return Value.of((String) value);
128+
case LONG:
129+
return Value.of((Long) value);
130+
case DOUBLE:
131+
return Value.of((Double) value);
132+
case BOOLEAN:
133+
return Value.of((Boolean) value);
134+
case STRING_ARRAY:
135+
List<String> stringList = (List<String>) value;
136+
Value<?>[] stringValues = new Value<?>[stringList.size()];
137+
for (int i = 0; i < stringList.size(); i++) {
138+
stringValues[i] = Value.of(stringList.get(i));
139+
}
140+
return Value.of(stringValues);
141+
case LONG_ARRAY:
142+
List<Long> longList = (List<Long>) value;
143+
Value<?>[] longValues = new Value<?>[longList.size()];
144+
for (int i = 0; i < longList.size(); i++) {
145+
longValues[i] = Value.of(longList.get(i));
146+
}
147+
return Value.of(longValues);
148+
case DOUBLE_ARRAY:
149+
List<Double> doubleList = (List<Double>) value;
150+
Value<?>[] doubleValues = new Value<?>[doubleList.size()];
151+
for (int i = 0; i < doubleList.size(); i++) {
152+
doubleValues[i] = Value.of(doubleList.get(i));
153+
}
154+
return Value.of(doubleValues);
155+
case BOOLEAN_ARRAY:
156+
List<Boolean> booleanList = (List<Boolean>) value;
157+
Value<?>[] booleanValues = new Value<?>[booleanList.size()];
158+
for (int i = 0; i < booleanList.size(); i++) {
159+
booleanValues[i] = Value.of(booleanList.get(i));
160+
}
161+
return Value.of(booleanValues);
162+
case VALUE:
163+
// Already a Value
164+
return (Value<?>) value;
165+
case EXTENDED_ATTRIBUTES:
166+
// Cannot convert EXTENDED_ATTRIBUTES to Value
167+
return null;
168+
}
169+
// Should not reach here
170+
return null;
171+
}
172+
57173
@SuppressWarnings("unchecked")
58174
@Override
59175
public Attributes asAttributes() {

api/incubator/src/main/java/io/opentelemetry/api/incubator/common/ArrayBackedExtendedAttributesBuilder.java

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@
55

66
package io.opentelemetry.api.incubator.common;
77

8+
import static io.opentelemetry.api.incubator.common.ExtendedAttributeKey.booleanArrayKey;
9+
import static io.opentelemetry.api.incubator.common.ExtendedAttributeKey.booleanKey;
10+
import static io.opentelemetry.api.incubator.common.ExtendedAttributeKey.doubleArrayKey;
11+
import static io.opentelemetry.api.incubator.common.ExtendedAttributeKey.doubleKey;
12+
import static io.opentelemetry.api.incubator.common.ExtendedAttributeKey.longArrayKey;
13+
import static io.opentelemetry.api.incubator.common.ExtendedAttributeKey.longKey;
14+
import static io.opentelemetry.api.incubator.common.ExtendedAttributeKey.stringArrayKey;
15+
import static io.opentelemetry.api.incubator.common.ExtendedAttributeKey.stringKey;
16+
17+
import io.opentelemetry.api.common.Value;
18+
import io.opentelemetry.api.common.ValueType;
819
import java.util.ArrayList;
920
import java.util.Arrays;
1021
import java.util.List;
@@ -36,11 +47,118 @@ public <T> ExtendedAttributesBuilder put(ExtendedAttributeKey<T> key, T value) {
3647
if (key == null || key.getKey().isEmpty() || value == null) {
3748
return this;
3849
}
50+
if (key.getType() == ExtendedAttributeType.VALUE && value instanceof Value) {
51+
putValue(key, (Value<?>) value);
52+
return this;
53+
}
3954
data.add(key);
4055
data.add(value);
4156
return this;
4257
}
4358

59+
@SuppressWarnings("unchecked")
60+
private void putValue(ExtendedAttributeKey<?> key, Value<?> valueObj) {
61+
// Convert VALUE type to narrower type when possible
62+
String keyName = key.getKey();
63+
switch (valueObj.getType()) {
64+
case STRING:
65+
put(stringKey(keyName), ((Value<String>) valueObj).getValue());
66+
return;
67+
case LONG:
68+
put(longKey(keyName), ((Value<Long>) valueObj).getValue());
69+
return;
70+
case DOUBLE:
71+
put(doubleKey(keyName), ((Value<Double>) valueObj).getValue());
72+
return;
73+
case BOOLEAN:
74+
put(booleanKey(keyName), ((Value<Boolean>) valueObj).getValue());
75+
return;
76+
case ARRAY:
77+
List<Value<?>> arrayValues = (List<Value<?>>) valueObj.getValue();
78+
ExtendedAttributeType attributeType = attributeType(arrayValues);
79+
switch (attributeType) {
80+
case STRING_ARRAY:
81+
List<String> strings = new ArrayList<>(arrayValues.size());
82+
for (Value<?> v : arrayValues) {
83+
strings.add((String) v.getValue());
84+
}
85+
put(stringArrayKey(keyName), strings);
86+
return;
87+
case LONG_ARRAY:
88+
List<Long> longs = new ArrayList<>(arrayValues.size());
89+
for (Value<?> v : arrayValues) {
90+
longs.add((Long) v.getValue());
91+
}
92+
put(longArrayKey(keyName), longs);
93+
return;
94+
case DOUBLE_ARRAY:
95+
List<Double> doubles = new ArrayList<>(arrayValues.size());
96+
for (Value<?> v : arrayValues) {
97+
doubles.add((Double) v.getValue());
98+
}
99+
put(doubleArrayKey(keyName), doubles);
100+
return;
101+
case BOOLEAN_ARRAY:
102+
List<Boolean> booleans = new ArrayList<>(arrayValues.size());
103+
for (Value<?> v : arrayValues) {
104+
booleans.add((Boolean) v.getValue());
105+
}
106+
put(booleanArrayKey(keyName), booleans);
107+
return;
108+
case VALUE:
109+
// Not coercible (empty, non-homogeneous, or unsupported element type)
110+
data.add(key);
111+
data.add(valueObj);
112+
return;
113+
case EXTENDED_ATTRIBUTES:
114+
// Not coercible
115+
data.add(key);
116+
data.add(valueObj);
117+
return;
118+
default:
119+
throw new IllegalArgumentException("Unexpected array attribute type: " + attributeType);
120+
}
121+
case KEY_VALUE_LIST:
122+
case BYTES:
123+
// Keep as VALUE type
124+
data.add(key);
125+
data.add(valueObj);
126+
return;
127+
}
128+
}
129+
130+
/**
131+
* Returns the ExtendedAttributeType for a homogeneous array (STRING_ARRAY, LONG_ARRAY,
132+
* DOUBLE_ARRAY, or BOOLEAN_ARRAY), or VALUE if the array is empty, non-homogeneous, or contains
133+
* unsupported element types.
134+
*/
135+
private static ExtendedAttributeType attributeType(List<Value<?>> arrayValues) {
136+
if (arrayValues.isEmpty()) {
137+
return ExtendedAttributeType.VALUE;
138+
}
139+
ValueType elementType = arrayValues.get(0).getType();
140+
for (Value<?> v : arrayValues) {
141+
if (v.getType() != elementType) {
142+
return ExtendedAttributeType.VALUE;
143+
}
144+
}
145+
switch (elementType) {
146+
case STRING:
147+
return ExtendedAttributeType.STRING_ARRAY;
148+
case LONG:
149+
return ExtendedAttributeType.LONG_ARRAY;
150+
case DOUBLE:
151+
return ExtendedAttributeType.DOUBLE_ARRAY;
152+
case BOOLEAN:
153+
return ExtendedAttributeType.BOOLEAN_ARRAY;
154+
case ARRAY:
155+
case KEY_VALUE_LIST:
156+
case BYTES:
157+
return ExtendedAttributeType.VALUE;
158+
}
159+
throw new IllegalArgumentException("Unsupported element type: " + elementType);
160+
}
161+
44162
@Override
45163
public ExtendedAttributesBuilder removeIf(Predicate<ExtendedAttributeKey<?>> predicate) {
46164
if (predicate == null) {

api/incubator/src/main/java/io/opentelemetry/api/incubator/common/ExtendedAttributeKey.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package io.opentelemetry.api.incubator.common;
77

88
import io.opentelemetry.api.common.AttributeKey;
9+
import io.opentelemetry.api.common.Value;
910
import io.opentelemetry.api.incubator.internal.InternalExtendedAttributeKeyImpl;
1011
import java.util.List;
1112
import javax.annotation.Nullable;
@@ -93,8 +94,31 @@ static ExtendedAttributeKey<List<Double>> doubleArrayKey(String key) {
9394
return fromAttributeKey(AttributeKey.doubleArrayKey(key));
9495
}
9596

96-
/** Returns a new ExtendedAttributeKey for Map valued attributes. */
97+
/**
98+
* Returns a new ExtendedAttributeKey for {@link ExtendedAttributes} valued attributes.
99+
*
100+
* @deprecated Use {@link #valueKey(String)} in combination with {@link Value#of(java.util.Map)}
101+
* instead.
102+
*/
103+
@Deprecated
104+
@SuppressWarnings("deprecation")
97105
static ExtendedAttributeKey<ExtendedAttributes> extendedAttributesKey(String key) {
98106
return InternalExtendedAttributeKeyImpl.create(key, ExtendedAttributeType.EXTENDED_ATTRIBUTES);
99107
}
108+
109+
/**
110+
* Returns a new ExtendedAttributeKey for {@link Value} valued attributes.
111+
*
112+
* <p>Simple attributes ({@link ExtendedAttributeType#STRING}, {@link ExtendedAttributeType#LONG},
113+
* {@link ExtendedAttributeType#DOUBLE}, {@link ExtendedAttributeType#BOOLEAN}, {@link
114+
* ExtendedAttributeType#STRING_ARRAY}, {@link ExtendedAttributeType#LONG_ARRAY}, {@link
115+
* ExtendedAttributeType#DOUBLE_ARRAY}, {@link ExtendedAttributeType#BOOLEAN_ARRAY}) SHOULD be
116+
* used whenever possible. Instrumentations SHOULD assume that backends do not index individual
117+
* properties of complex attributes, that querying or aggregating on such properties is
118+
* inefficient and complicated, and that reporting complex attributes carries higher performance
119+
* overhead.
120+
*/
121+
static ExtendedAttributeKey<Value<?>> valueKey(String key) {
122+
return InternalExtendedAttributeKeyImpl.create(key, ExtendedAttributeType.VALUE);
123+
}
100124
}

api/incubator/src/main/java/io/opentelemetry/api/incubator/common/ExtendedAttributeType.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,13 @@ public enum ExtendedAttributeType {
2222
LONG_ARRAY,
2323
DOUBLE_ARRAY,
2424
// Extended types unique to ExtendedAttributes
25-
EXTENDED_ATTRIBUTES;
25+
/**
26+
* Complex attribute type for {@link io.opentelemetry.api.common.Value}-based maps.
27+
*
28+
* @deprecated Use {@link #VALUE} with {@link io.opentelemetry.api.common.Value}-based maps
29+
* instead.
30+
*/
31+
@Deprecated
32+
EXTENDED_ATTRIBUTES,
33+
VALUE;
2634
}

0 commit comments

Comments
 (0)