Skip to content

Commit 2a8fd46

Browse files
committed
Update to latest spec PR
1 parent facd585 commit 2a8fd46

File tree

11 files changed

+70
-45
lines changed

11 files changed

+70
-45
lines changed

api/all/src/main/java/io/opentelemetry/api/common/Value.java

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -113,26 +113,32 @@ static Value<Empty> empty() {
113113
T getValue();
114114

115115
/**
116-
* Returns a JSON encoding of this {@link Value}.
116+
* Returns a string representation of this {@link Value}.
117117
*
118-
* <p>The output follows the <a href="https://protobuf.dev/programming-guides/json/">ProtoJSON</a>
119-
* specification:
118+
* <p>The output follows the <a
119+
* href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/README.md#anyvalue-representation-for-non-otlp-protocols">
120+
* string representation guidance</a> for complex attribute value types:
120121
*
121122
* <ul>
122-
* <li>{@link ValueType#STRING} JSON string (including escaping and surrounding quotes)
123-
* <li>{@link ValueType#BOOLEAN} JSON boolean ({@code true} or {@code false})
124-
* <li>{@link ValueType#LONG} JSON number
123+
* <li>{@link ValueType#STRING} String as-is without surrounding quotes. Examples: {@code hello
124+
* world}, (empty string)
125+
* <li>{@link ValueType#BOOLEAN} JSON boolean. Examples: {@code true}, {@code false}
126+
* <li>{@link ValueType#LONG} JSON number. Examples: {@code 42}, {@code -123}
125127
* <li>{@link ValueType#DOUBLE} JSON number, or {@code "NaN"}, {@code "Infinity"}, {@code
126-
* "-Infinity"} for special values
127-
* <li>{@link ValueType#ARRAY} JSON array (e.g. {@code [1,"two",true]})
128-
* <li>{@link ValueType#KEY_VALUE_LIST} JSON object (e.g. {@code {"key1":"value1","key2":2}})
129-
* <li>{@link ValueType#BYTES} JSON string (including surrounding double quotes) containing
130-
* base64 encoded bytes
131-
* <li>{@link ValueType#EMPTY} JSON {@code null} (the string {@code "null"} without the
132-
* surrounding quotes)
128+
* "-Infinity"} for special values. Examples: {@code 3.14159}, {@code 1.23e10}, {@code
129+
* "NaN"}, {@code "-Infinity"}
130+
* <li>{@link ValueType#ARRAY} JSON array. Nested strings and byte arrays are encoded as JSON
131+
* strings (with surrounding quotes). Nested empty values are encoded as JSON {@code null}.
132+
* Examples: {@code []}, {@code [1, "a", true, {"nested": "aGVsbG8gd29ybGQ="}]}
133+
* <li>{@link ValueType#KEY_VALUE_LIST} JSON object. Nested strings and byte arrays are encoded
134+
* as JSON strings (with surrounding quotes). Nested empty values are encoded as JSON {@code
135+
* null}. Examples: {@code {}}, {@code {"a": "1", "b": 2, "c": [3, null]}}
136+
* <li>{@link ValueType#BYTES} Base64-encoded bytes without surrounding quotes. Example: {@code
137+
* aGVsbG8gd29ybGQ=}
138+
* <li>{@link ValueType#EMPTY} The empty string.
133139
* </ul>
134140
*
135-
* @return a JSON encoding of this value
141+
* @return a string representation of this value
136142
*/
137143
String asString();
138144
}

api/all/src/main/java/io/opentelemetry/api/common/ValueBytes.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import java.nio.ByteBuffer;
99
import java.util.Arrays;
10+
import java.util.Base64;
1011
import java.util.Objects;
1112

1213
final class ValueBytes implements Value<ByteBuffer> {
@@ -34,9 +35,8 @@ public ByteBuffer getValue() {
3435

3536
@Override
3637
public String asString() {
37-
StringBuilder sb = new StringBuilder();
38-
ProtoJson.append(sb, this);
39-
return sb.toString();
38+
byte[] bytes = raw;
39+
return Base64.getEncoder().encodeToString(bytes);
4040
}
4141

4242
@Override

api/all/src/main/java/io/opentelemetry/api/common/ValueEmpty.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public Empty getValue() {
2727

2828
@Override
2929
public String asString() {
30-
return "null";
30+
return "";
3131
}
3232

3333
@Override

api/all/src/main/java/io/opentelemetry/api/common/ValueString.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ public String getValue() {
3232

3333
@Override
3434
public String asString() {
35-
StringBuilder sb = new StringBuilder();
36-
ProtoJson.append(sb, this);
37-
return sb.toString();
35+
return value;
3836
}
3937

4038
@Override

api/all/src/test/java/io/opentelemetry/api/common/ValueToProtoJsonTest.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ void valueString(String input, String expectedJson) {
2626

2727
private static Stream<Arguments> stringValueProvider() {
2828
return Stream.of(
29-
Arguments.of("hello", "\"hello\""),
30-
Arguments.of("", "\"\""),
31-
Arguments.of("line1\nline2\ttab", "\"line1\\nline2\\ttab\""),
32-
Arguments.of("say \"hello\"", "\"say \\\"hello\\\"\""),
33-
Arguments.of("path\\to\\file", "\"path\\\\to\\\\file\""),
34-
Arguments.of("\u0000\u0001\u001F", "\"\\u0000\\u0001\\u001f\""),
35-
Arguments.of("Hello 世界 🌍", "\"Hello 世界 🌍\""));
29+
Arguments.of("hello", "hello"),
30+
Arguments.of("", ""),
31+
Arguments.of("line1\nline2\ttab", "line1\nline2\ttab"),
32+
Arguments.of("say \"hello\"", "say \"hello\""),
33+
Arguments.of("path\\to\\file", "path\\to\\file"),
34+
Arguments.of("\u0000\u0001\u001F", "\u0000\u0001\u001F"),
35+
Arguments.of("Hello 世界 🌍", "Hello 世界 🌍"));
3636
}
3737

3838
@ParameterizedTest
@@ -88,13 +88,13 @@ void valueBytes(byte[] input, String expectedJson) {
8888
private static Stream<Arguments> bytesValueProvider() {
8989
byte[] regularBytes = new byte[] {0, 1, 2, Byte.MAX_VALUE, Byte.MIN_VALUE};
9090
return Stream.of(
91-
Arguments.of(new byte[] {}, "\"\""),
92-
Arguments.of(regularBytes, '"' + Base64.getEncoder().encodeToString(regularBytes) + '"'));
91+
Arguments.of(new byte[] {}, ""),
92+
Arguments.of(regularBytes, Base64.getEncoder().encodeToString(regularBytes)));
9393
}
9494

9595
@Test
9696
void valueEmpty() {
97-
assertThat(Value.empty().asString()).isEqualTo("null");
97+
assertThat(Value.empty().asString()).isEqualTo("");
9898
}
9999

100100
@ParameterizedTest

api/all/src/test/java/io/opentelemetry/api/logs/ValueTest.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ void asString(Value<?> value, String expectedAsString) {
172172
private static Stream<Arguments> asStringArgs() {
173173
return Stream.of(
174174
// primitives
175-
arguments(Value.of("str"), "\"str\""),
175+
arguments(Value.of("str"), "str"),
176176
arguments(Value.of(true), "true"),
177177
arguments(Value.of(1), "1"),
178178
arguments(Value.of(1.1), "1.1"),
@@ -201,18 +201,15 @@ private static Stream<Arguments> asStringArgs() {
201201
"child", Value.of(Collections.singletonMap("grandchild", Value.of("str"))))),
202202
"{\"child\":{\"grandchild\":\"str\"}}"),
203203
// bytes
204-
arguments(
205-
Value.of("hello world".getBytes(StandardCharsets.UTF_8)), "\"aGVsbG8gd29ybGQ=\""));
204+
arguments(Value.of("hello world".getBytes(StandardCharsets.UTF_8)), "aGVsbG8gd29ybGQ="));
206205
}
207206

208207
@Test
209208
void valueByteAsString() {
210209
// TODO: add more test cases
211210
String str = "hello world";
212211
String base64Encoded = Value.of(str.getBytes(StandardCharsets.UTF_8)).asString();
213-
// Remove surrounding quotes from JSON string
214-
String base64Value = base64Encoded.substring(1, base64Encoded.length() - 1);
215-
byte[] decodedBytes = Base64.getDecoder().decode(base64Value);
212+
byte[] decodedBytes = Base64.getDecoder().decode(base64Encoded);
216213
assertThat(new String(decodedBytes, StandardCharsets.UTF_8)).isEqualTo(str);
217214
}
218215
}

exporters/logging/src/test/java/io/opentelemetry/exporter/logging/LoggingSpanExporterTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ void export() {
115115
.isEqualTo(
116116
"'testSpan1' : 12345678876543211234567887654321 8765432112345678 "
117117
+ "INTERNAL [tracer: tracer1:] "
118-
+ "{animal=\"cat\", bytes=ValueBytes{\"AQID\"}, empty=ValueEmpty{}, "
118+
+ "{animal=\"cat\", bytes=ValueBytes{AQID}, empty=ValueEmpty{}, "
119119
+ "heterogeneousArray=ValueArray{[\"string\",123]}, lives=9, "
120120
+ "map=KeyValueList{{\"nested\":\"value\"}}}");
121121
assertThat(logs.getEvents().get(1).getMessage())

exporters/logging/src/test/java/io/opentelemetry/exporter/logging/SystemOutLogRecordExporterTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ void format() {
4747
SystemOutLogRecordExporter.formatLog(output, log);
4848
assertThat(output.toString())
4949
.isEqualTo(
50-
"1970-08-07T10:00:00Z ERROR3 '\"message\"' : 00000000000000010000000000000002 0000000000000003 "
51-
+ "[scopeInfo: logTest:1.0] {amount=1, bytes=ValueBytes{\"AQID\"}, cheese=\"cheddar\", "
50+
"1970-08-07T10:00:00Z ERROR3 'message' : 00000000000000010000000000000002 0000000000000003 "
51+
+ "[scopeInfo: logTest:1.0] {amount=1, bytes=ValueBytes{AQID}, cheese=\"cheddar\", "
5252
+ "empty=ValueEmpty{}, heterogeneousArray=ValueArray{[\"string\",123]}, "
5353
+ "map=KeyValueList{{\"nested\":\"value\"}}}");
5454
}

exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverterTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,14 +352,14 @@ private static Stream<Arguments> labelValueSerializationArgs() {
352352
Arguments.of(
353353
Attributes.of(doubleArrayKey("key"), Arrays.asList(Double.MIN_VALUE, Double.MAX_VALUE)),
354354
"[4.9E-324,1.7976931348623157E308]"),
355-
Arguments.of(Attributes.of(valueKey("key"), Value.of(new byte[] {1, 2, 3})), "\"AQID\""),
355+
Arguments.of(Attributes.of(valueKey("key"), Value.of(new byte[] {1, 2, 3})), "AQID"),
356356
Arguments.of(
357357
Attributes.of(valueKey("key"), Value.of(KeyValue.of("nested", Value.of("value")))),
358358
"{\"nested\":\"value\"}"),
359359
Arguments.of(
360360
Attributes.of(valueKey("key"), Value.of(Value.of("string"), Value.of(123L))),
361361
"[\"string\",123]"),
362-
Arguments.of(Attributes.of(valueKey("key"), Value.empty()), "null"));
362+
Arguments.of(Attributes.of(valueKey("key"), Value.empty()), ""));
363363
}
364364

365365
static MetricData createSampleMetricData(

exporters/zipkin/src/main/java/io/opentelemetry/exporter/zipkin/EventDataToAnnotation.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import io.opentelemetry.api.common.Attributes;
1111
import io.opentelemetry.api.common.Value;
12+
import io.opentelemetry.api.common.ValueType;
1213
import io.opentelemetry.sdk.trace.data.EventData;
1314
import java.util.List;
1415

@@ -45,8 +46,31 @@ private static String toValue(Object o) {
4546
.stream().map(EventDataToAnnotation::toValue).collect(joining(",", "[", "]"));
4647
}
4748
if (o instanceof Value) {
48-
return ((Value<?>) o).asString();
49+
return toJsonValue((Value<?>) o);
4950
}
5051
return String.valueOf(o);
5152
}
53+
54+
// note: simple types (STRING, BOOLEAN, LONG, DOUBLE) won't actually come here
55+
// but handling here for completeness
56+
private static String toJsonValue(Value<?> value) {
57+
ValueType type = value.getType();
58+
switch (type) {
59+
case STRING:
60+
case BYTES:
61+
// For JSON encoding, strings and bytes need to be quoted
62+
return "\"" + value.asString() + "\"";
63+
case EMPTY:
64+
// For JSON encoding, empty values should be null
65+
return "null";
66+
case ARRAY:
67+
case KEY_VALUE_LIST:
68+
case BOOLEAN:
69+
case LONG:
70+
case DOUBLE:
71+
// Arrays, maps, and primitives are already valid JSON from asString()
72+
return value.asString();
73+
}
74+
throw new IllegalStateException("Unknown value type: " + type);
75+
}
5276
}

0 commit comments

Comments
 (0)