From 363cd274d32f5dd85196390b8285bc17885b1f71 Mon Sep 17 00:00:00 2001 From: Jack Berg <jberg@newrelic.com> Date: Wed, 4 Oct 2023 17:30:29 -0500 Subject: [PATCH 1/6] Log AnyValue body prototype --- .../opentelemetry-sdk-logs.txt | 3 + .../otlp/logs/LogsRequestMarshalerTest.java | 15 +- extensions/incubator/build.gradle.kts | 2 + .../extension/incubator/logs/AnyValue.java | 50 +++++++ .../incubator/logs/AnyValueArray.java | 50 +++++++ .../incubator/logs/AnyValueBoolean.java | 39 ++++++ .../incubator/logs/AnyValueBytes.java | 47 +++++++ .../incubator/logs/AnyValueDouble.java | 39 ++++++ .../incubator/logs/AnyValueLong.java | 39 ++++++ .../incubator/logs/AnyValueString.java | 42 ++++++ .../incubator/logs/AnyValueType.java | 16 +++ .../logs/ExtendedLogRecordBuilder.java | 13 ++ .../extension/incubator/logs/KeyAnyValue.java | 17 +++ .../incubator/logs/KeyAnyValueImpl.java | 18 +++ .../incubator/logs/KeyAnyValueList.java | 62 +++++++++ .../incubator/logs/AnyValueTest.java | 84 ++++++++++++ sdk/logs/build.gradle.kts | 2 + .../sdk/logs/SdkLogRecordBuilder.java | 11 +- .../io/opentelemetry/sdk/logs/data/Body.java | 3 +- .../sdk/logs/internal/AnyValueBody.java | 38 ++++++ .../sdk/logs/AnyValueBodyTest.java | 128 ++++++++++++++++++ 21 files changed, 715 insertions(+), 3 deletions(-) create mode 100644 extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValue.java create mode 100644 extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueArray.java create mode 100644 extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBoolean.java create mode 100644 extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBytes.java create mode 100644 extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueDouble.java create mode 100644 extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueLong.java create mode 100644 extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueString.java create mode 100644 extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueType.java create mode 100644 extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/ExtendedLogRecordBuilder.java create mode 100644 extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValue.java create mode 100644 extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValueImpl.java create mode 100644 extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValueList.java create mode 100644 extensions/incubator/src/test/java/io/opentelemetry/extension/incubator/logs/AnyValueTest.java create mode 100644 sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/AnyValueBody.java create mode 100644 sdk/logs/src/test/java/io/opentelemetry/sdk/logs/AnyValueBodyTest.java diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt index 11010a0b08b..b4ad0cb56f7 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt @@ -1,4 +1,7 @@ Comparing source compatibility of against +*** MODIFIED ENUM: PUBLIC STATIC FINAL io.opentelemetry.sdk.logs.data.Body$Type (compatible) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW FIELD: PUBLIC(+) STATIC(+) FINAL(+) io.opentelemetry.sdk.logs.data.Body$Type ANY_VALUE *** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.logs.ReadWriteLogRecord (not serializable) === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.logs.ReadWriteLogRecord setAllAttributes(io.opentelemetry.api.common.Attributes) diff --git a/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LogsRequestMarshalerTest.java b/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LogsRequestMarshalerTest.java index 306cfe1ea14..59b60fe2ac7 100644 --- a/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LogsRequestMarshalerTest.java +++ b/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LogsRequestMarshalerTest.java @@ -24,6 +24,7 @@ import io.opentelemetry.proto.common.v1.AnyValue; import io.opentelemetry.proto.common.v1.InstrumentationScope; import io.opentelemetry.proto.common.v1.KeyValue; +import io.opentelemetry.proto.common.v1.KeyValueList; import io.opentelemetry.proto.logs.v1.LogRecord; import io.opentelemetry.proto.logs.v1.ResourceLogs; import io.opentelemetry.proto.logs.v1.ScopeLogs; @@ -153,7 +154,19 @@ void toProtoLogRecord_MinimalFields() { assertThat(logRecord.getSeverityText()).isBlank(); assertThat(logRecord.getSeverityNumber().getNumber()) .isEqualTo(Severity.UNDEFINED_SEVERITY_NUMBER.getSeverityNumber()); - assertThat(logRecord.getBody()).isEqualTo(AnyValue.newBuilder().setStringValue("").build()); + assertThat(logRecord.getBody()) + .isEqualTo( + AnyValue.newBuilder() + .setKvlistValue( + KeyValueList.newBuilder() + .addValues( + KeyValue.newBuilder() + .setKey("key") + .setValue(AnyValue.newBuilder().setStringValue("foo").build()) + .build()) + .build()) + .setStringValue("") + .build()); assertThat(logRecord.getAttributesList()).isEmpty(); assertThat(logRecord.getDroppedAttributesCount()).isZero(); assertThat(logRecord.getTimeUnixNano()).isEqualTo(12345); diff --git a/extensions/incubator/build.gradle.kts b/extensions/incubator/build.gradle.kts index 6acaf6cddb2..6e79f9361fd 100644 --- a/extensions/incubator/build.gradle.kts +++ b/extensions/incubator/build.gradle.kts @@ -12,5 +12,7 @@ otelJava.moduleName.set("io.opentelemetry.extension.incubator") dependencies { api(project(":api:all")) + annotationProcessor("com.google.auto.value:auto-value") + testImplementation(project(":sdk:testing")) } diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValue.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValue.java new file mode 100644 index 00000000000..78946fc704a --- /dev/null +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValue.java @@ -0,0 +1,50 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.extension.incubator.logs; + +import java.util.List; +import java.util.Map; + +public interface AnyValue<T> { + + static AnyValue<String> ofString(String value) { + return AnyValueString.create(value); + } + + static AnyValue<Boolean> ofBoolean(boolean value) { + return AnyValueBoolean.create(value); + } + + static AnyValue<Long> ofLong(long value) { + return AnyValueLong.create(value); + } + + static AnyValue<Double> ofDouble(double value) { + return AnyValueDouble.create(value); + } + + static AnyValue<byte[]> ofBytes(byte[] value) { + return AnyValueBytes.create(value); + } + + static AnyValue<List<AnyValue<?>>> ofArray(AnyValue<?>... value) { + return AnyValueArray.create(value); + } + + static AnyValue<List<KeyAnyValue>> ofKeyAnyValueArray(KeyAnyValue... value) { + return KeyAnyValueList.create(value); + } + + static AnyValue<List<KeyAnyValue>> ofMap(Map<String, AnyValue<?>> value) { + return KeyAnyValueList.createFromMap(value); + } + + AnyValueType getType(); + + T getValue(); + + String asString(); +} diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueArray.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueArray.java new file mode 100644 index 00000000000..551dbeb5405 --- /dev/null +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueArray.java @@ -0,0 +1,50 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.extension.incubator.logs; + +import static java.util.stream.Collectors.joining; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +final class AnyValueArray implements AnyValue<List<AnyValue<?>>> { + + private final List<AnyValue<?>> value; + + private AnyValueArray(List<AnyValue<?>> value) { + this.value = value; + } + + static AnyValue<List<AnyValue<?>>> create(AnyValue<?>... value) { + Objects.requireNonNull(value, "value"); + List<AnyValue<?>> list = new ArrayList<>(value.length); + list.addAll(Arrays.asList(value)); + return new AnyValueArray(Collections.unmodifiableList(list)); + } + + @Override + public AnyValueType getType() { + return AnyValueType.ARRAY; + } + + @Override + public List<AnyValue<?>> getValue() { + return value; + } + + @Override + public String asString() { + return value.stream().map(AnyValue::asString).collect(joining(", ", "[", "]")); + } + + @Override + public String toString() { + return "AnyValueArray{" + asString() + "}"; + } +} diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBoolean.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBoolean.java new file mode 100644 index 00000000000..fe3717a064f --- /dev/null +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBoolean.java @@ -0,0 +1,39 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.extension.incubator.logs; + +final class AnyValueBoolean implements AnyValue<Boolean> { + + private final boolean value; + + private AnyValueBoolean(boolean value) { + this.value = value; + } + + static AnyValue<Boolean> create(boolean value) { + return new AnyValueBoolean(value); + } + + @Override + public AnyValueType getType() { + return AnyValueType.BOOLEAN; + } + + @Override + public Boolean getValue() { + return value; + } + + @Override + public String asString() { + return String.valueOf(value); + } + + @Override + public String toString() { + return "AnyValueBoolean{" + asString() + "}"; + } +} diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBytes.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBytes.java new file mode 100644 index 00000000000..d2cb9a84ed5 --- /dev/null +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBytes.java @@ -0,0 +1,47 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.extension.incubator.logs; + +import io.opentelemetry.api.internal.OtelEncodingUtils; +import java.util.Arrays; +import java.util.Objects; + +final class AnyValueBytes implements AnyValue<byte[]> { + + private final byte[] value; + + private AnyValueBytes(byte[] value) { + this.value = value; + } + + static AnyValue<byte[]> create(byte[] value) { + Objects.requireNonNull(value, "value"); + return new AnyValueBytes(Arrays.copyOf(value, value.length)); + } + + @Override + public AnyValueType getType() { + return AnyValueType.BYTES; + } + + @Override + public byte[] getValue() { + return value; + } + + @Override + public String asString() { + // TODO: base64 would be better, but isn't available in android and java + char[] arr = new char[value.length * 2]; + OtelEncodingUtils.bytesToBase16(value, arr, value.length); + return new String(arr); + } + + @Override + public String toString() { + return "AnyValueBytes{" + asString() + "}"; + } +} diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueDouble.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueDouble.java new file mode 100644 index 00000000000..143014f08b2 --- /dev/null +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueDouble.java @@ -0,0 +1,39 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.extension.incubator.logs; + +final class AnyValueDouble implements AnyValue<Double> { + + private final double value; + + private AnyValueDouble(double value) { + this.value = value; + } + + static AnyValue<Double> create(double value) { + return new AnyValueDouble(value); + } + + @Override + public AnyValueType getType() { + return AnyValueType.LONG; + } + + @Override + public Double getValue() { + return value; + } + + @Override + public String asString() { + return String.valueOf(value); + } + + @Override + public String toString() { + return "AnyValueDouble{" + asString() + "}"; + } +} diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueLong.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueLong.java new file mode 100644 index 00000000000..c3c5d258ac3 --- /dev/null +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueLong.java @@ -0,0 +1,39 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.extension.incubator.logs; + +final class AnyValueLong implements AnyValue<Long> { + + private final long value; + + private AnyValueLong(long value) { + this.value = value; + } + + static AnyValue<Long> create(long value) { + return new AnyValueLong(value); + } + + @Override + public AnyValueType getType() { + return AnyValueType.LONG; + } + + @Override + public Long getValue() { + return value; + } + + @Override + public String asString() { + return String.valueOf(value); + } + + @Override + public String toString() { + return "AnyValueLong{" + asString() + "}"; + } +} diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueString.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueString.java new file mode 100644 index 00000000000..1980cec63a7 --- /dev/null +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueString.java @@ -0,0 +1,42 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.extension.incubator.logs; + +import java.util.Objects; + +final class AnyValueString implements AnyValue<String> { + + private final String value; + + private AnyValueString(String value) { + this.value = value; + } + + static AnyValue<String> create(String value) { + Objects.requireNonNull(value, "value"); + return new AnyValueString(value); + } + + @Override + public AnyValueType getType() { + return AnyValueType.STRING; + } + + @Override + public String getValue() { + return value; + } + + @Override + public String asString() { + return value; + } + + @Override + public String toString() { + return "AnyValueString{" + value + "}"; + } +} diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueType.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueType.java new file mode 100644 index 00000000000..3f07849fc06 --- /dev/null +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueType.java @@ -0,0 +1,16 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.extension.incubator.logs; + +public enum AnyValueType { + STRING, + BOOLEAN, + LONG, + DOUBLE, + ARRAY, + KEY_VALUE_LIST, + BYTES +} diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/ExtendedLogRecordBuilder.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/ExtendedLogRecordBuilder.java new file mode 100644 index 00000000000..3503b1651df --- /dev/null +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/ExtendedLogRecordBuilder.java @@ -0,0 +1,13 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.extension.incubator.logs; + +import io.opentelemetry.api.logs.LogRecordBuilder; + +public interface ExtendedLogRecordBuilder extends LogRecordBuilder { + + LogRecordBuilder setBody(AnyValue<?> body); +} diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValue.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValue.java new file mode 100644 index 00000000000..8b19b26867f --- /dev/null +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValue.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.extension.incubator.logs; + +public interface KeyAnyValue { + + static KeyAnyValue of(String key, AnyValue<?> value) { + return KeyAnyValueImpl.create(key, value); + } + + String getKey(); + + AnyValue<?> getAnyValue(); +} diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValueImpl.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValueImpl.java new file mode 100644 index 00000000000..516f46e0840 --- /dev/null +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValueImpl.java @@ -0,0 +1,18 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.extension.incubator.logs; + +import com.google.auto.value.AutoValue; + +@AutoValue +abstract class KeyAnyValueImpl implements KeyAnyValue { + + KeyAnyValueImpl() {} + + static KeyAnyValueImpl create(String key, AnyValue<?> value) { + return new AutoValue_KeyAnyValueImpl(key, value); + } +} diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValueList.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValueList.java new file mode 100644 index 00000000000..28f850641e2 --- /dev/null +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValueList.java @@ -0,0 +1,62 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.extension.incubator.logs; + +import static java.util.stream.Collectors.joining; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +final class KeyAnyValueList implements AnyValue<List<KeyAnyValue>> { + + private final List<KeyAnyValue> value; + + private KeyAnyValueList(List<KeyAnyValue> value) { + this.value = value; + } + + static AnyValue<List<KeyAnyValue>> create(KeyAnyValue... value) { + Objects.requireNonNull(value, "value"); + List<KeyAnyValue> list = new ArrayList<>(value.length); + list.addAll(Arrays.asList(value)); + return new KeyAnyValueList(Collections.unmodifiableList(list)); + } + + static AnyValue<List<KeyAnyValue>> createFromMap(Map<String, AnyValue<?>> value) { + Objects.requireNonNull(value, "value"); + KeyAnyValue[] array = + value.entrySet().stream() + .map(entry -> KeyAnyValue.of(entry.getKey(), entry.getValue())) + .toArray(KeyAnyValue[]::new); + return create(array); + } + + @Override + public AnyValueType getType() { + return AnyValueType.KEY_VALUE_LIST; + } + + @Override + public List<KeyAnyValue> getValue() { + return value; + } + + @Override + public String asString() { + return value.stream() + .map(entry -> entry.getKey() + "=" + entry.getAnyValue().asString()) + .collect(joining(", ", "[", "]")); + } + + @Override + public String toString() { + return "KeyAnyValueList{" + asString() + "}"; + } +} diff --git a/extensions/incubator/src/test/java/io/opentelemetry/extension/incubator/logs/AnyValueTest.java b/extensions/incubator/src/test/java/io/opentelemetry/extension/incubator/logs/AnyValueTest.java new file mode 100644 index 00000000000..333f40d0812 --- /dev/null +++ b/extensions/incubator/src/test/java/io/opentelemetry/extension/incubator/logs/AnyValueTest.java @@ -0,0 +1,84 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.extension.incubator.logs; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import io.opentelemetry.api.internal.OtelEncodingUtils; +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class AnyValueTest { + + @ParameterizedTest + @MethodSource("asStringArgs") + void asString(AnyValue<?> value, String expectedAsString) { + assertThat(value.asString()).isEqualTo(expectedAsString); + } + + @SuppressWarnings("DoubleBraceInitialization") + private static Stream<Arguments> asStringArgs() { + return Stream.of( + // primitives + arguments(AnyValue.ofString("str"), "str"), + arguments(AnyValue.ofBoolean(true), "true"), + arguments(AnyValue.ofLong(1), "1"), + arguments(AnyValue.ofDouble(1.1), "1.1"), + // heterogeneous array + arguments( + AnyValue.ofArray( + AnyValue.ofString("str"), + AnyValue.ofBoolean(true), + AnyValue.ofLong(1), + AnyValue.ofDouble(1.1)), + "[str, true, 1, 1.1]"), + // key value list from KeyAnyValue array + arguments( + AnyValue.ofKeyAnyValueArray( + KeyAnyValue.of("key1", AnyValue.ofString("val1")), + KeyAnyValue.of("key2", AnyValue.ofLong(2))), + "[key1=val1, key2=2]"), + // key value list from map + arguments( + AnyValue.ofMap( + new LinkedHashMap<String, AnyValue<?>>() { + { + put("key1", AnyValue.ofString("val1")); + put("key2", AnyValue.ofLong(2)); + } + }), + "[key1=val1, key2=2]"), + // map of map + arguments( + AnyValue.ofKeyAnyValueArray( + KeyAnyValue.of( + "child", + AnyValue.ofKeyAnyValueArray( + KeyAnyValue.of("grandchild", AnyValue.ofString("str"))))), + "[child=[grandchild=str]]"), + // bytes + arguments( + AnyValue.ofBytes("hello world".getBytes(StandardCharsets.UTF_8)), + "68656c6c6f20776f726c64")); + } + + @Test + void anyValueByteAsString() { + // TODO: add more test cases + String str = "hello world"; + String base16Encoded = AnyValue.ofBytes(str.getBytes(StandardCharsets.UTF_8)).asString(); + byte[] decodedBytes = OtelEncodingUtils.bytesFromBase16(base16Encoded, base16Encoded.length()); + assertThat(new String(decodedBytes, StandardCharsets.UTF_8)).isEqualTo(str); + } + + // TODO: test equals, hashcode, getType +} diff --git a/sdk/logs/build.gradle.kts b/sdk/logs/build.gradle.kts index 8ffe760b86f..6640c4ed0ee 100644 --- a/sdk/logs/build.gradle.kts +++ b/sdk/logs/build.gradle.kts @@ -12,6 +12,7 @@ otelJava.moduleName.set("io.opentelemetry.sdk.logs") dependencies { api(project(":api:all")) api(project(":sdk:common")) + implementation(project(":extensions:incubator")) implementation(project(":api:events")) @@ -20,4 +21,5 @@ dependencies { testImplementation(project(":sdk:testing")) testImplementation("org.awaitility:awaitility") + testImplementation("com.google.guava:guava") } diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java index 8ef8b2ae4b2..6bdd0407aa5 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java @@ -10,15 +10,18 @@ import io.opentelemetry.api.logs.Severity; import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Context; +import io.opentelemetry.extension.incubator.logs.AnyValue; +import io.opentelemetry.extension.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.internal.AttributesMap; import io.opentelemetry.sdk.logs.data.Body; +import io.opentelemetry.sdk.logs.internal.AnyValueBody; import java.time.Instant; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; /** SDK implementation of {@link LogRecordBuilder}. */ -final class SdkLogRecordBuilder implements LogRecordBuilder { +final class SdkLogRecordBuilder implements ExtendedLogRecordBuilder { private final LoggerSharedState loggerSharedState; private final LogLimits logLimits; @@ -89,6 +92,12 @@ public SdkLogRecordBuilder setBody(String body) { return this; } + @Override + public LogRecordBuilder setBody(AnyValue<?> value) { + this.body = AnyValueBody.create(value); + return this; + } + @Override public <T> SdkLogRecordBuilder setAttribute(AttributeKey<T> key, T value) { if (key == null || key.getKey().isEmpty() || value == null) { diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/data/Body.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/data/Body.java index a13ecc003fe..3258ae9c4f0 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/data/Body.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/data/Body.java @@ -21,7 +21,8 @@ public interface Body { /** An enum that represents all the possible value types for an {@code Body}. */ enum Type { EMPTY, - STRING + STRING, + ANY_VALUE } /** diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/AnyValueBody.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/AnyValueBody.java new file mode 100644 index 00000000000..4432c392a4d --- /dev/null +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/AnyValueBody.java @@ -0,0 +1,38 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.logs.internal; + +import io.opentelemetry.extension.incubator.logs.AnyValue; +import io.opentelemetry.sdk.logs.data.Body; +import javax.annotation.concurrent.Immutable; + +@Immutable +public final class AnyValueBody implements Body { + + private final AnyValue<?> value; + + private AnyValueBody(AnyValue<?> value) { + this.value = value; + } + + public static Body create(AnyValue<?> value) { + return new AnyValueBody(value); + } + + @Override + public Type getType() { + return Type.ANY_VALUE; + } + + @Override + public String asString() { + return value.asString(); + } + + public AnyValue<?> getAnyValue() { + return value; + } +} diff --git a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/AnyValueBodyTest.java b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/AnyValueBodyTest.java new file mode 100644 index 00000000000..5a922107177 --- /dev/null +++ b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/AnyValueBodyTest.java @@ -0,0 +1,128 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.logs; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; + +import io.opentelemetry.api.logs.Logger; +import io.opentelemetry.extension.incubator.logs.AnyValue; +import io.opentelemetry.extension.incubator.logs.ExtendedLogRecordBuilder; +import io.opentelemetry.extension.incubator.logs.KeyAnyValue; +import io.opentelemetry.sdk.logs.data.Body; +import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor; +import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter; +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; +import org.junit.jupiter.api.Test; + +class AnyValueBodyTest { + + @Test + @SuppressWarnings("DoubleBraceInitialization") + void anyValueBody() { + InMemoryLogRecordExporter exporter = InMemoryLogRecordExporter.create(); + SdkLoggerProvider provider = + SdkLoggerProvider.builder() + .addLogRecordProcessor(SimpleLogRecordProcessor.create(exporter)) + .build(); + Logger logger = provider.get(AnyValueBodyTest.class.getName()); + + // AnyValue can be a primitive type, like a string, long, double, boolean + extendedLogRecordBuilder(logger).setBody(AnyValue.ofLong(1)).emit(); + assertThat(exporter.getFinishedLogRecordItems()) + .hasSize(1) + .satisfiesExactly( + logRecordData -> { + assertThat(logRecordData.getBody().getType()).isEqualTo(Body.Type.ANY_VALUE); + assertThat(logRecordData.getBody().asString()).isEqualTo("1"); + }); + exporter.reset(); + + // ...or a byte array of raw data + extendedLogRecordBuilder(logger) + .setBody(AnyValue.ofBytes("hello world".getBytes(StandardCharsets.UTF_8))) + .emit(); + assertThat(exporter.getFinishedLogRecordItems()) + .hasSize(1) + .satisfiesExactly( + logRecordData -> { + assertThat(logRecordData.getBody().getType()).isEqualTo(Body.Type.ANY_VALUE); + assertThat(logRecordData.getBody().asString()).isEqualTo("68656c6c6f20776f726c64"); + }); + exporter.reset(); + + // But most commonly it will be used to represent complex structured like a map + extendedLogRecordBuilder(logger) + .setBody( + // The protocol data structure uses a repeated KeyValue to represent a map: + // https://github.com/open-telemetry/opentelemetry-proto/blob/ac3242b03157295e4ee9e616af53b81517b06559/opentelemetry/proto/common/v1/common.proto#L59 + // The comment says that keys aren't allowed to repeat themselves, and because its + // represented as a repeated KeyValue, we need to at least offer the ability to preserve + // order. + // Accepting a Map<String, AnyValue<?>> makes for a cleaner API, but ordering of the + // entries is lost. To accommodate use cases where ordering should be preserved we + // accept an array of key value pairs, but also a map based alternative (see the + // key_value_list_key entry). + AnyValue.ofKeyAnyValueArray( + KeyAnyValue.of("str_key", AnyValue.ofString("value")), + KeyAnyValue.of("bool_key", AnyValue.ofBoolean(true)), + KeyAnyValue.of("long_key", AnyValue.ofLong(1L)), + KeyAnyValue.of("double_key", AnyValue.ofDouble(1.1)), + KeyAnyValue.of( + "bytes_key", AnyValue.ofBytes("bytes".getBytes(StandardCharsets.UTF_8))), + KeyAnyValue.of( + "arr_key", + AnyValue.ofArray( + AnyValue.ofString("entry1"), AnyValue.ofLong(2), AnyValue.ofDouble(3.3))), + KeyAnyValue.of( + "key_value_list_key", + AnyValue.ofMap( + new LinkedHashMap<String, AnyValue<?>>() { + { + put("child_str_key1", AnyValue.ofString("child_value1")); + put("child_str_key2", AnyValue.ofString("child_value2")); + } + })))) + .emit(); + assertThat(exporter.getFinishedLogRecordItems()) + .hasSize(1) + .satisfiesExactly( + logRecordData -> { + assertThat(logRecordData.getBody().getType()).isEqualTo(Body.Type.ANY_VALUE); + assertThat(logRecordData.getBody().asString()) + .isEqualTo( + "[" + + "str_key=value, " + + "bool_key=true, " + + "long_key=1, " + + "double_key=1.1, " + + "bytes_key=6279746573, " + + "arr_key=[entry1, 2, 3.3], " + + "key_value_list_key=[child_str_key1=child_value1, child_str_key2=child_value2]" + + "]"); + }); + exporter.reset(); + + // ..or an array (optionally with heterogeneous types) + extendedLogRecordBuilder(logger) + .setBody( + AnyValue.ofArray( + AnyValue.ofString("entry1"), AnyValue.ofString("entry2"), AnyValue.ofLong(3))) + .emit(); + assertThat(exporter.getFinishedLogRecordItems()) + .hasSize(1) + .satisfiesExactly( + logRecordData -> { + assertThat(logRecordData.getBody().getType()).isEqualTo(Body.Type.ANY_VALUE); + assertThat(logRecordData.getBody().asString()).isEqualTo("[entry1, entry2, 3]"); + }); + exporter.reset(); + } + + ExtendedLogRecordBuilder extendedLogRecordBuilder(Logger logger) { + return (ExtendedLogRecordBuilder) logger.logRecordBuilder(); + } +} From 9b814235fa0185f8e113b84a1ee4530b4fe878cd Mon Sep 17 00:00:00 2001 From: Jack Berg <jberg@newrelic.com> Date: Wed, 4 Oct 2023 20:14:44 -0500 Subject: [PATCH 2/6] Improve API ergonomics --- .../internal/otlp/logs/LogMarshaler.java | 2 +- .../extension/incubator/logs/AnyValue.java | 16 ++++---- .../incubator/logs/AnyValueTest.java | 39 ++++++++----------- .../sdk/logs/AnyValueBodyTest.java | 30 +++++++------- 4 files changed, 39 insertions(+), 48 deletions(-) diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogMarshaler.java index bd2c6b3b8a2..7a68041e5d1 100644 --- a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogMarshaler.java +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogMarshaler.java @@ -41,7 +41,7 @@ static LogMarshaler create(LogRecordData logRecordData) { KeyValueMarshaler[] attributeMarshalers = KeyValueMarshaler.createRepeated(logRecordData.getAttributes()); - // For now, map all the bodies to String AnyValue. + // TODO(jack-berg): handle AnyValue log body StringAnyValueMarshaler anyValueMarshaler = new StringAnyValueMarshaler(MarshalerUtil.toBytes(logRecordData.getBody().asString())); diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValue.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValue.java index 78946fc704a..8dc9ed04b97 100644 --- a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValue.java +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValue.java @@ -10,35 +10,35 @@ public interface AnyValue<T> { - static AnyValue<String> ofString(String value) { + static AnyValue<String> of(String value) { return AnyValueString.create(value); } - static AnyValue<Boolean> ofBoolean(boolean value) { + static AnyValue<Boolean> of(boolean value) { return AnyValueBoolean.create(value); } - static AnyValue<Long> ofLong(long value) { + static AnyValue<Long> of(long value) { return AnyValueLong.create(value); } - static AnyValue<Double> ofDouble(double value) { + static AnyValue<Double> of(double value) { return AnyValueDouble.create(value); } - static AnyValue<byte[]> ofBytes(byte[] value) { + static AnyValue<byte[]> of(byte[] value) { return AnyValueBytes.create(value); } - static AnyValue<List<AnyValue<?>>> ofArray(AnyValue<?>... value) { + static AnyValue<List<AnyValue<?>>> of(AnyValue<?>... value) { return AnyValueArray.create(value); } - static AnyValue<List<KeyAnyValue>> ofKeyAnyValueArray(KeyAnyValue... value) { + static AnyValue<List<KeyAnyValue>> of(KeyAnyValue... value) { return KeyAnyValueList.create(value); } - static AnyValue<List<KeyAnyValue>> ofMap(Map<String, AnyValue<?>> value) { + static AnyValue<List<KeyAnyValue>> of(Map<String, AnyValue<?>> value) { return KeyAnyValueList.createFromMap(value); } diff --git a/extensions/incubator/src/test/java/io/opentelemetry/extension/incubator/logs/AnyValueTest.java b/extensions/incubator/src/test/java/io/opentelemetry/extension/incubator/logs/AnyValueTest.java index 333f40d0812..1c8bf63a575 100644 --- a/extensions/incubator/src/test/java/io/opentelemetry/extension/incubator/logs/AnyValueTest.java +++ b/extensions/incubator/src/test/java/io/opentelemetry/extension/incubator/logs/AnyValueTest.java @@ -10,6 +10,7 @@ import io.opentelemetry.api.internal.OtelEncodingUtils; import java.nio.charset.StandardCharsets; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.stream.Stream; import org.junit.jupiter.api.Test; @@ -29,53 +30,47 @@ void asString(AnyValue<?> value, String expectedAsString) { private static Stream<Arguments> asStringArgs() { return Stream.of( // primitives - arguments(AnyValue.ofString("str"), "str"), - arguments(AnyValue.ofBoolean(true), "true"), - arguments(AnyValue.ofLong(1), "1"), - arguments(AnyValue.ofDouble(1.1), "1.1"), + arguments(AnyValue.of("str"), "str"), + arguments(AnyValue.of(true), "true"), + arguments(AnyValue.of(1), "1"), + arguments(AnyValue.of(1.1), "1.1"), // heterogeneous array arguments( - AnyValue.ofArray( - AnyValue.ofString("str"), - AnyValue.ofBoolean(true), - AnyValue.ofLong(1), - AnyValue.ofDouble(1.1)), + AnyValue.of(AnyValue.of("str"), AnyValue.of(true), AnyValue.of(1), AnyValue.of(1.1)), "[str, true, 1, 1.1]"), // key value list from KeyAnyValue array arguments( - AnyValue.ofKeyAnyValueArray( - KeyAnyValue.of("key1", AnyValue.ofString("val1")), - KeyAnyValue.of("key2", AnyValue.ofLong(2))), + AnyValue.of( + KeyAnyValue.of("key1", AnyValue.of("val1")), + KeyAnyValue.of("key2", AnyValue.of(2))), "[key1=val1, key2=2]"), // key value list from map arguments( - AnyValue.ofMap( + AnyValue.of( new LinkedHashMap<String, AnyValue<?>>() { { - put("key1", AnyValue.ofString("val1")); - put("key2", AnyValue.ofLong(2)); + put("key1", AnyValue.of("val1")); + put("key2", AnyValue.of(2)); } }), "[key1=val1, key2=2]"), // map of map arguments( - AnyValue.ofKeyAnyValueArray( - KeyAnyValue.of( + AnyValue.of( + Collections.singletonMap( "child", - AnyValue.ofKeyAnyValueArray( - KeyAnyValue.of("grandchild", AnyValue.ofString("str"))))), + AnyValue.of(Collections.singletonMap("grandchild", AnyValue.of("str"))))), "[child=[grandchild=str]]"), // bytes arguments( - AnyValue.ofBytes("hello world".getBytes(StandardCharsets.UTF_8)), - "68656c6c6f20776f726c64")); + AnyValue.of("hello world".getBytes(StandardCharsets.UTF_8)), "68656c6c6f20776f726c64")); } @Test void anyValueByteAsString() { // TODO: add more test cases String str = "hello world"; - String base16Encoded = AnyValue.ofBytes(str.getBytes(StandardCharsets.UTF_8)).asString(); + String base16Encoded = AnyValue.of(str.getBytes(StandardCharsets.UTF_8)).asString(); byte[] decodedBytes = OtelEncodingUtils.bytesFromBase16(base16Encoded, base16Encoded.length()); assertThat(new String(decodedBytes, StandardCharsets.UTF_8)).isEqualTo(str); } diff --git a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/AnyValueBodyTest.java b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/AnyValueBodyTest.java index 5a922107177..09486fdaa06 100644 --- a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/AnyValueBodyTest.java +++ b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/AnyValueBodyTest.java @@ -31,7 +31,7 @@ void anyValueBody() { Logger logger = provider.get(AnyValueBodyTest.class.getName()); // AnyValue can be a primitive type, like a string, long, double, boolean - extendedLogRecordBuilder(logger).setBody(AnyValue.ofLong(1)).emit(); + extendedLogRecordBuilder(logger).setBody(AnyValue.of(1)).emit(); assertThat(exporter.getFinishedLogRecordItems()) .hasSize(1) .satisfiesExactly( @@ -43,7 +43,7 @@ void anyValueBody() { // ...or a byte array of raw data extendedLogRecordBuilder(logger) - .setBody(AnyValue.ofBytes("hello world".getBytes(StandardCharsets.UTF_8))) + .setBody(AnyValue.of("hello world".getBytes(StandardCharsets.UTF_8))) .emit(); assertThat(exporter.getFinishedLogRecordItems()) .hasSize(1) @@ -66,24 +66,22 @@ void anyValueBody() { // entries is lost. To accommodate use cases where ordering should be preserved we // accept an array of key value pairs, but also a map based alternative (see the // key_value_list_key entry). - AnyValue.ofKeyAnyValueArray( - KeyAnyValue.of("str_key", AnyValue.ofString("value")), - KeyAnyValue.of("bool_key", AnyValue.ofBoolean(true)), - KeyAnyValue.of("long_key", AnyValue.ofLong(1L)), - KeyAnyValue.of("double_key", AnyValue.ofDouble(1.1)), - KeyAnyValue.of( - "bytes_key", AnyValue.ofBytes("bytes".getBytes(StandardCharsets.UTF_8))), + AnyValue.of( + KeyAnyValue.of("str_key", AnyValue.of("value")), + KeyAnyValue.of("bool_key", AnyValue.of(true)), + KeyAnyValue.of("long_key", AnyValue.of(1L)), + KeyAnyValue.of("double_key", AnyValue.of(1.1)), + KeyAnyValue.of("bytes_key", AnyValue.of("bytes".getBytes(StandardCharsets.UTF_8))), KeyAnyValue.of( "arr_key", - AnyValue.ofArray( - AnyValue.ofString("entry1"), AnyValue.ofLong(2), AnyValue.ofDouble(3.3))), + AnyValue.of(AnyValue.of("entry1"), AnyValue.of(2), AnyValue.of(3.3))), KeyAnyValue.of( "key_value_list_key", - AnyValue.ofMap( + AnyValue.of( new LinkedHashMap<String, AnyValue<?>>() { { - put("child_str_key1", AnyValue.ofString("child_value1")); - put("child_str_key2", AnyValue.ofString("child_value2")); + put("child_str_key1", AnyValue.of("child_value1")); + put("child_str_key2", AnyValue.of("child_value2")); } })))) .emit(); @@ -108,9 +106,7 @@ void anyValueBody() { // ..or an array (optionally with heterogeneous types) extendedLogRecordBuilder(logger) - .setBody( - AnyValue.ofArray( - AnyValue.ofString("entry1"), AnyValue.ofString("entry2"), AnyValue.ofLong(3))) + .setBody(AnyValue.of(AnyValue.of("entry1"), AnyValue.of("entry2"), AnyValue.of(3))) .emit(); assertThat(exporter.getFinishedLogRecordItems()) .hasSize(1) From 057ebbcb6c728c0c0913ba2d21942bd26b060892 Mon Sep 17 00:00:00 2001 From: Jack Berg <jberg@newrelic.com> Date: Fri, 13 Oct 2023 15:54:57 -0500 Subject: [PATCH 3/6] Clean up to prepare for review --- .../kotlin/otel.java-conventions.gradle.kts | 5 +- .../opentelemetry-sdk-common.txt | 11 +- .../opentelemetry-sdk-logs.txt | 3 - .../opentelemetry-sdk-metrics.txt | 21 +-- .../opentelemetry-sdk-testing.txt | 12 +- .../extension/incubator/logs/AnyValue.java | 54 +++++++ .../incubator/logs/AnyValueArray.java | 15 +- .../incubator/logs/AnyValueBoolean.java | 15 ++ .../incubator/logs/AnyValueBytes.java | 20 ++- .../incubator/logs/AnyValueDouble.java | 17 ++- .../incubator/logs/AnyValueLong.java | 15 ++ .../incubator/logs/AnyValueString.java | 15 +- .../incubator/logs/AnyValueType.java | 5 + .../logs/ExtendedLogRecordBuilder.java | 2 + .../extension/incubator/logs/KeyAnyValue.java | 8 + .../incubator/logs/KeyAnyValueList.java | 17 ++- .../incubator/logs/AnyValueTest.java | 141 +++++++++++++++++- .../sdk/logs/internal/AnyValueBody.java | 5 + .../sdk/logs/AnyValueBodyTest.java | 29 ++++ 19 files changed, 353 insertions(+), 57 deletions(-) diff --git a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts index 43791644017..4d601c406dc 100644 --- a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts @@ -74,10 +74,7 @@ tasks { // https://groups.google.com/forum/#!topic/bazel-discuss/_R3A9TJSoPM "-Xlint:-processing", // We suppress the "options" warning because it prevents compilation on modern JDKs - "-Xlint:-options", - - // Fail build on any warning - "-Werror", + "-Xlint:-options" ), ) } diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-common.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-common.txt index cc95503822e..df26146497b 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-common.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-common.txt @@ -1,11 +1,2 @@ Comparing source compatibility of against -+++ NEW ENUM: PUBLIC(+) FINAL(+) io.opentelemetry.sdk.common.export.MemoryMode (compatible) - +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. - +++ NEW INTERFACE: java.lang.constant.Constable - +++ NEW INTERFACE: java.lang.Comparable - +++ NEW INTERFACE: java.io.Serializable - +++ NEW SUPERCLASS: java.lang.Enum - +++ NEW FIELD: PUBLIC(+) STATIC(+) FINAL(+) io.opentelemetry.sdk.common.export.MemoryMode REUSABLE_DATA - +++ NEW FIELD: PUBLIC(+) STATIC(+) FINAL(+) io.opentelemetry.sdk.common.export.MemoryMode IMMUTABLE_DATA - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.common.export.MemoryMode valueOf(java.lang.String) - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.common.export.MemoryMode[] values() +No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt index b4ad0cb56f7..fac1cdbcecc 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt @@ -2,6 +2,3 @@ Comparing source compatibility of against *** MODIFIED ENUM: PUBLIC STATIC FINAL io.opentelemetry.sdk.logs.data.Body$Type (compatible) === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 +++ NEW FIELD: PUBLIC(+) STATIC(+) FINAL(+) io.opentelemetry.sdk.logs.data.Body$Type ANY_VALUE -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.logs.ReadWriteLogRecord (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.logs.ReadWriteLogRecord setAllAttributes(io.opentelemetry.api.common.Attributes) diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt index 6cf519e2823..df26146497b 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt @@ -1,21 +1,2 @@ Comparing source compatibility of against -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.export.CollectionRegistration (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) java.util.Collection<io.opentelemetry.sdk.metrics.data.MetricData> collectAllMetrics() - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.export.CollectionRegistration noop() -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.export.MetricExporter (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.common.export.MemoryMode getMemoryMode() -+++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.export.MetricProducer (not serializable) - +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. - +++ NEW SUPERCLASS: java.lang.Object - +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) java.util.Collection<io.opentelemetry.sdk.metrics.data.MetricData> produce(io.opentelemetry.sdk.resources.Resource) -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.export.MetricReader (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.common.export.MemoryMode getMemoryMode() -*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.metrics.export.PeriodicMetricReader (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.common.export.MemoryMode getMemoryMode() -*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder registerMetricProducer(io.opentelemetry.sdk.metrics.export.MetricProducer) +No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt index 24ed11618d8..df26146497b 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt @@ -1,12 +1,2 @@ Comparing source compatibility of against -*** MODIFIED CLASS: PUBLIC io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.testing.exporter.InMemoryMetricReaderBuilder builder() - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.common.export.MemoryMode getMemoryMode() -+++ NEW CLASS: PUBLIC(+) FINAL(+) io.opentelemetry.sdk.testing.exporter.InMemoryMetricReaderBuilder (not serializable) - +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. - +++ NEW SUPERCLASS: java.lang.Object - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader build() - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.exporter.InMemoryMetricReaderBuilder setAggregationTemporalitySelector(io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector) - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.exporter.InMemoryMetricReaderBuilder setDefaultAggregationSelector(io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector) - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.exporter.InMemoryMetricReaderBuilder setMemoryMode(io.opentelemetry.sdk.common.export.MemoryMode) +No changes. \ No newline at end of file diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValue.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValue.java index 8dc9ed04b97..a509c48aef4 100644 --- a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValue.java +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValue.java @@ -8,43 +8,97 @@ import java.util.List; import java.util.Map; +/** + * AnyValue mirrors the proto <a + * href="https://github.com/open-telemetry/opentelemetry-proto/blob/ac3242b03157295e4ee9e616af53b81517b06559/opentelemetry/proto/common/v1/common.proto#L28">AnyValue</a> + * message type, and is used to model any type. + * + * <p>It can be used to represent: + * + * <ul> + * <li>Primitive values via {@link #of(long)}, {@link #of(String)}, {@link #of(boolean)}, {@link + * #of(double)}. + * <li>String-keyed maps (i.e. associative arrays, dictionaries) via {@link #of(KeyAnyValue...)}, + * {@link #of(Map)}. Note, because map values are type {@link AnyValue}, maps can be nested + * within other maps. + * <li>Arrays (heterogeneous or homogenous) via {@link #of(AnyValue[])}. Note, because array + * values are type {@link AnyValue}, arrays can contain primitives, complex types like maps or + * arrays, or any combination. + * <li>Raw bytes via {@link #of(byte[])} + * </ul> + * + * @param <T> the type. See {@link #getValue()} for description of types. + */ public interface AnyValue<T> { + /** Returns an {@link AnyValue} for the {@link String} value. */ static AnyValue<String> of(String value) { return AnyValueString.create(value); } + /** Returns an {@link AnyValue} for the {@code boolean} value. */ static AnyValue<Boolean> of(boolean value) { return AnyValueBoolean.create(value); } + /** Returns an {@link AnyValue} for the {@code long} value. */ static AnyValue<Long> of(long value) { return AnyValueLong.create(value); } + /** Returns an {@link AnyValue} for the {@code double} value. */ static AnyValue<Double> of(double value) { return AnyValueDouble.create(value); } + /** Returns an {@link AnyValue} for the {@code byte[]} value. */ static AnyValue<byte[]> of(byte[] value) { return AnyValueBytes.create(value); } + /** Returns an {@link AnyValue} for the array of {@link AnyValue} values. */ static AnyValue<List<AnyValue<?>>> of(AnyValue<?>... value) { return AnyValueArray.create(value); } + /** + * Returns an {@link AnyValue} for the array of {@link KeyAnyValue} values. {@link + * KeyAnyValue#getKey()} values should not repeat - duplicates may be dropped. + */ static AnyValue<List<KeyAnyValue>> of(KeyAnyValue... value) { return KeyAnyValueList.create(value); } + /** Returns an {@link AnyValue} for the {@link Map} of key, {@link AnyValue}. */ static AnyValue<List<KeyAnyValue>> of(Map<String, AnyValue<?>> value) { return KeyAnyValueList.createFromMap(value); } + /** Returns the type of this {@link AnyValue}. Useful for building switch statements. */ AnyValueType getType(); + /** + * Returns the value for this {@link AnyValue}. + * + * <p>The return type varies by {@link #getType()} as described below: + * + * <ul> + * <li>{@link AnyValueType#STRING} returns {@link String} + * <li>{@link AnyValueType#BOOLEAN} returns {@code boolean} + * <li>{@link AnyValueType#LONG} returns {@code long} + * <li>{@link AnyValueType#DOUBLE} returns {@code double} + * <li>{@link AnyValueType#ARRAY} returns {@link List} of {@link AnyValue} + * <li>{@link AnyValueType#KEY_VALUE_LIST} returns {@link List} of {@link KeyAnyValue} + * <li>{@link AnyValueType#BYTES} returns {@code byte[]} + * </ul> + */ T getValue(); + /** + * Return a string encoding of this {@link AnyValue}. This is intended to be a fallback serialized + * representation in case there is no suitable encoding that can utilize {@link #getType()} / + * {@link #getValue()} to serialize specific types. + */ + // TODO(jack-berg): Should this be a JSON encoding? String asString(); } diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueArray.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueArray.java index 551dbeb5405..dd96a60793d 100644 --- a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueArray.java +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueArray.java @@ -22,7 +22,7 @@ private AnyValueArray(List<AnyValue<?>> value) { } static AnyValue<List<AnyValue<?>>> create(AnyValue<?>... value) { - Objects.requireNonNull(value, "value"); + Objects.requireNonNull(value, "value must not be null"); List<AnyValue<?>> list = new ArrayList<>(value.length); list.addAll(Arrays.asList(value)); return new AnyValueArray(Collections.unmodifiableList(list)); @@ -47,4 +47,17 @@ public String asString() { public String toString() { return "AnyValueArray{" + asString() + "}"; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + return (o instanceof AnyValue) && Objects.equals(this.value, ((AnyValue<?>) o).getValue()); + } + + @Override + public int hashCode() { + return value.hashCode(); + } } diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBoolean.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBoolean.java index fe3717a064f..5fa862b777f 100644 --- a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBoolean.java +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBoolean.java @@ -5,6 +5,8 @@ package io.opentelemetry.extension.incubator.logs; +import java.util.Objects; + final class AnyValueBoolean implements AnyValue<Boolean> { private final boolean value; @@ -36,4 +38,17 @@ public String asString() { public String toString() { return "AnyValueBoolean{" + asString() + "}"; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + return (o instanceof AnyValue) && Objects.equals(this.value, ((AnyValue<?>) o).getValue()); + } + + @Override + public int hashCode() { + return Boolean.hashCode(value); + } } diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBytes.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBytes.java index d2cb9a84ed5..589cce83f9e 100644 --- a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBytes.java +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBytes.java @@ -18,7 +18,7 @@ private AnyValueBytes(byte[] value) { } static AnyValue<byte[]> create(byte[] value) { - Objects.requireNonNull(value, "value"); + Objects.requireNonNull(value, "value must not be null"); return new AnyValueBytes(Arrays.copyOf(value, value.length)); } @@ -34,7 +34,8 @@ public byte[] getValue() { @Override public String asString() { - // TODO: base64 would be better, but isn't available in android and java + // TODO: base64 would be better, but isn't available in android and java. Can we vendor in a + // base64 implementation? char[] arr = new char[value.length * 2]; OtelEncodingUtils.bytesToBase16(value, arr, value.length); return new String(arr); @@ -44,4 +45,19 @@ public String asString() { public String toString() { return "AnyValueBytes{" + asString() + "}"; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + return (o instanceof AnyValue) + && ((AnyValue<?>) o).getType() == AnyValueType.BYTES + && Arrays.equals(this.value, (byte[]) ((AnyValue<?>) o).getValue()); + } + + @Override + public int hashCode() { + return Arrays.hashCode(value); + } } diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueDouble.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueDouble.java index 143014f08b2..4e2cdccf33b 100644 --- a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueDouble.java +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueDouble.java @@ -5,6 +5,8 @@ package io.opentelemetry.extension.incubator.logs; +import java.util.Objects; + final class AnyValueDouble implements AnyValue<Double> { private final double value; @@ -19,7 +21,7 @@ static AnyValue<Double> create(double value) { @Override public AnyValueType getType() { - return AnyValueType.LONG; + return AnyValueType.DOUBLE; } @Override @@ -36,4 +38,17 @@ public String asString() { public String toString() { return "AnyValueDouble{" + asString() + "}"; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + return (o instanceof AnyValue) && Objects.equals(this.value, ((AnyValue<?>) o).getValue()); + } + + @Override + public int hashCode() { + return Double.hashCode(value); + } } diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueLong.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueLong.java index c3c5d258ac3..558a08376ee 100644 --- a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueLong.java +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueLong.java @@ -5,6 +5,8 @@ package io.opentelemetry.extension.incubator.logs; +import java.util.Objects; + final class AnyValueLong implements AnyValue<Long> { private final long value; @@ -36,4 +38,17 @@ public String asString() { public String toString() { return "AnyValueLong{" + asString() + "}"; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + return (o instanceof AnyValue) && Objects.equals(this.value, ((AnyValue<?>) o).getValue()); + } + + @Override + public int hashCode() { + return Long.hashCode(value); + } } diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueString.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueString.java index 1980cec63a7..6a7b0a1c8e2 100644 --- a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueString.java +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueString.java @@ -16,7 +16,7 @@ private AnyValueString(String value) { } static AnyValue<String> create(String value) { - Objects.requireNonNull(value, "value"); + Objects.requireNonNull(value, "value must not be null"); return new AnyValueString(value); } @@ -39,4 +39,17 @@ public String asString() { public String toString() { return "AnyValueString{" + value + "}"; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + return (o instanceof AnyValue) && Objects.equals(this.value, ((AnyValue<?>) o).getValue()); + } + + @Override + public int hashCode() { + return value.hashCode(); + } } diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueType.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueType.java index 3f07849fc06..f683cc61ea5 100644 --- a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueType.java +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueType.java @@ -5,6 +5,11 @@ package io.opentelemetry.extension.incubator.logs; +/** + * AnyValue type options, mirroring <a + * href="https://github.com/open-telemetry/opentelemetry-proto/blob/ac3242b03157295e4ee9e616af53b81517b06559/opentelemetry/proto/common/v1/common.proto#L31">AnyValue#value + * options</a>. + */ public enum AnyValueType { STRING, BOOLEAN, diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/ExtendedLogRecordBuilder.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/ExtendedLogRecordBuilder.java index 3503b1651df..b1ca789c1f6 100644 --- a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/ExtendedLogRecordBuilder.java +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/ExtendedLogRecordBuilder.java @@ -7,7 +7,9 @@ import io.opentelemetry.api.logs.LogRecordBuilder; +/** Extended {@link LogRecordBuilder} with experimental APIs. */ public interface ExtendedLogRecordBuilder extends LogRecordBuilder { + /** Set the body {@link AnyValue}. */ LogRecordBuilder setBody(AnyValue<?> body); } diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValue.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValue.java index 8b19b26867f..6aeb5eab6ab 100644 --- a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValue.java +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValue.java @@ -5,13 +5,21 @@ package io.opentelemetry.extension.incubator.logs; +/** + * Key-value pair of {@link String} key and {@link AnyValue} value. + * + * @see AnyValue#of(KeyAnyValue...) + */ public interface KeyAnyValue { + /** Returns a {@link KeyAnyValue} for the given {@code key} and {@code value}. */ static KeyAnyValue of(String key, AnyValue<?> value) { return KeyAnyValueImpl.create(key, value); } + /** Returns the key. */ String getKey(); + /** Returns the value. */ AnyValue<?> getAnyValue(); } diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValueList.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValueList.java index 28f850641e2..501a7a66ac0 100644 --- a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValueList.java +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValueList.java @@ -23,14 +23,14 @@ private KeyAnyValueList(List<KeyAnyValue> value) { } static AnyValue<List<KeyAnyValue>> create(KeyAnyValue... value) { - Objects.requireNonNull(value, "value"); + Objects.requireNonNull(value, "value must not be null"); List<KeyAnyValue> list = new ArrayList<>(value.length); list.addAll(Arrays.asList(value)); return new KeyAnyValueList(Collections.unmodifiableList(list)); } static AnyValue<List<KeyAnyValue>> createFromMap(Map<String, AnyValue<?>> value) { - Objects.requireNonNull(value, "value"); + Objects.requireNonNull(value, "value must not be null"); KeyAnyValue[] array = value.entrySet().stream() .map(entry -> KeyAnyValue.of(entry.getKey(), entry.getValue())) @@ -59,4 +59,17 @@ public String asString() { public String toString() { return "KeyAnyValueList{" + asString() + "}"; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + return (o instanceof AnyValue) && Objects.equals(this.value, ((AnyValue<?>) o).getValue()); + } + + @Override + public int hashCode() { + return value.hashCode(); + } } diff --git a/extensions/incubator/src/test/java/io/opentelemetry/extension/incubator/logs/AnyValueTest.java b/extensions/incubator/src/test/java/io/opentelemetry/extension/incubator/logs/AnyValueTest.java index 1c8bf63a575..47b2d1c4d58 100644 --- a/extensions/incubator/src/test/java/io/opentelemetry/extension/incubator/logs/AnyValueTest.java +++ b/extensions/incubator/src/test/java/io/opentelemetry/extension/incubator/logs/AnyValueTest.java @@ -6,12 +6,15 @@ package io.opentelemetry.extension.incubator.logs; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.params.provider.Arguments.arguments; import io.opentelemetry.api.internal.OtelEncodingUtils; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; +import java.util.Map; import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -20,6 +23,142 @@ class AnyValueTest { + @Test + void anyValue_OfString() { + assertThat(AnyValue.of("foo")) + .satisfies( + anyValue -> { + assertThat(anyValue.getType()).isEqualTo(AnyValueType.STRING); + assertThat(anyValue.getValue()).isEqualTo("foo"); + assertThat(anyValue).hasSameHashCodeAs(AnyValue.of("foo")); + }); + } + + @Test + void anyValue_OfBoolean() { + assertThat(AnyValue.of(true)) + .satisfies( + anyValue -> { + assertThat(anyValue.getType()).isEqualTo(AnyValueType.BOOLEAN); + assertThat(anyValue.getValue()).isEqualTo(true); + assertThat(anyValue).hasSameHashCodeAs(AnyValue.of(true)); + }); + } + + @Test + void anyValue_OfLong() { + assertThat(AnyValue.of(1L)) + .satisfies( + anyValue -> { + assertThat(anyValue.getType()).isEqualTo(AnyValueType.LONG); + assertThat(anyValue.getValue()).isEqualTo(1L); + assertThat(anyValue).hasSameHashCodeAs(AnyValue.of(1L)); + }); + } + + @Test + void anyValue_OfDouble() { + assertThat(AnyValue.of(1.1)) + .satisfies( + anyValue -> { + assertThat(anyValue.getType()).isEqualTo(AnyValueType.DOUBLE); + assertThat(anyValue.getValue()).isEqualTo(1.1); + assertThat(anyValue).hasSameHashCodeAs(AnyValue.of(1.1)); + }); + } + + @Test + void anyValue_OfByteArray() { + assertThat(AnyValue.of(new byte[] {'a', 'b'})) + .satisfies( + anyValue -> { + assertThat(anyValue.getType()).isEqualTo(AnyValueType.BYTES); + assertThat(anyValue.getValue()).isEqualTo(new byte[] {'a', 'b'}); + assertThat(anyValue).hasSameHashCodeAs(AnyValue.of(new byte[] {'a', 'b'})); + }); + } + + @Test + void anyValue_OfAnyValueArray() { + assertThat(AnyValue.of(AnyValue.of(true), AnyValue.of(1L))) + .satisfies( + anyValue -> { + assertThat(anyValue.getType()).isEqualTo(AnyValueType.ARRAY); + assertThat(anyValue.getValue()) + .isEqualTo(Arrays.asList(AnyValue.of(true), AnyValue.of(1L))); + assertThat(anyValue) + .hasSameHashCodeAs(AnyValue.of(AnyValue.of(true), AnyValue.of(1L))); + }); + } + + @Test + @SuppressWarnings("DoubleBraceInitialization") + void anyValue_OfKeyValueList() { + assertThat( + AnyValue.of( + KeyAnyValue.of("bool", AnyValue.of(true)), KeyAnyValue.of("long", AnyValue.of(1L)))) + .satisfies( + anyValue -> { + assertThat(anyValue.getType()).isEqualTo(AnyValueType.KEY_VALUE_LIST); + assertThat(anyValue.getValue()) + .isEqualTo( + Arrays.asList( + KeyAnyValue.of("bool", AnyValue.of(true)), + KeyAnyValue.of("long", AnyValue.of(1L)))); + assertThat(anyValue) + .hasSameHashCodeAs( + AnyValue.of( + KeyAnyValue.of("bool", AnyValue.of(true)), + KeyAnyValue.of("long", AnyValue.of(1L)))); + }); + + assertThat( + AnyValue.of( + new LinkedHashMap<String, AnyValue<?>>() { + { + put("bool", AnyValue.of(true)); + put("long", AnyValue.of(1L)); + } + })) + .satisfies( + anyValue -> { + assertThat(anyValue.getType()).isEqualTo(AnyValueType.KEY_VALUE_LIST); + assertThat(anyValue.getValue()) + .isEqualTo( + Arrays.asList( + KeyAnyValue.of("bool", AnyValue.of(true)), + KeyAnyValue.of("long", AnyValue.of(1L)))); + assertThat(anyValue) + .hasSameHashCodeAs( + AnyValue.of( + new LinkedHashMap<String, AnyValue<?>>() { + { + put("bool", AnyValue.of(true)); + put("long", AnyValue.of(1L)); + } + })); + }); + } + + @Test + void anyValue_NullsNotAllowed() { + assertThatThrownBy(() -> AnyValue.of((String) null)) + .isInstanceOf(NullPointerException.class) + .hasMessageContaining("value must not be null"); + assertThatThrownBy(() -> AnyValue.of((byte[]) null)) + .isInstanceOf(NullPointerException.class) + .hasMessageContaining("value must not be null"); + assertThatThrownBy(() -> AnyValue.of((AnyValue<?>[]) null)) + .isInstanceOf(NullPointerException.class) + .hasMessageContaining("value must not be null"); + assertThatThrownBy(() -> AnyValue.of((KeyAnyValue[]) null)) + .isInstanceOf(NullPointerException.class) + .hasMessageContaining("value must not be null"); + assertThatThrownBy(() -> AnyValue.of((Map<String, AnyValue<?>>) null)) + .isInstanceOf(NullPointerException.class) + .hasMessageContaining("value must not be null"); + } + @ParameterizedTest @MethodSource("asStringArgs") void asString(AnyValue<?> value, String expectedAsString) { @@ -74,6 +213,4 @@ void anyValueByteAsString() { byte[] decodedBytes = OtelEncodingUtils.bytesFromBase16(base16Encoded, base16Encoded.length()); assertThat(new String(decodedBytes, StandardCharsets.UTF_8)).isEqualTo(str); } - - // TODO: test equals, hashcode, getType } diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/AnyValueBody.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/AnyValueBody.java index 4432c392a4d..f756b8ac10a 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/AnyValueBody.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/AnyValueBody.java @@ -35,4 +35,9 @@ public String asString() { public AnyValue<?> getAnyValue() { return value; } + + @Override + public String toString() { + return "AnyValueBody{" + asString() + "}"; + } } diff --git a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/AnyValueBodyTest.java b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/AnyValueBodyTest.java index 09486fdaa06..be02c79f656 100644 --- a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/AnyValueBodyTest.java +++ b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/AnyValueBodyTest.java @@ -13,6 +13,7 @@ import io.opentelemetry.extension.incubator.logs.KeyAnyValue; import io.opentelemetry.sdk.logs.data.Body; import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor; +import io.opentelemetry.sdk.logs.internal.AnyValueBody; import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter; import java.nio.charset.StandardCharsets; import java.util.LinkedHashMap; @@ -38,6 +39,8 @@ void anyValueBody() { logRecordData -> { assertThat(logRecordData.getBody().getType()).isEqualTo(Body.Type.ANY_VALUE); assertThat(logRecordData.getBody().asString()).isEqualTo("1"); + assertThat(((AnyValueBody) logRecordData.getBody()).getAnyValue()) + .isEqualTo(AnyValue.of(1)); }); exporter.reset(); @@ -51,6 +54,8 @@ void anyValueBody() { logRecordData -> { assertThat(logRecordData.getBody().getType()).isEqualTo(Body.Type.ANY_VALUE); assertThat(logRecordData.getBody().asString()).isEqualTo("68656c6c6f20776f726c64"); + assertThat(((AnyValueBody) logRecordData.getBody()).getAnyValue()) + .isEqualTo(AnyValue.of("hello world".getBytes(StandardCharsets.UTF_8))); }); exporter.reset(); @@ -101,6 +106,27 @@ void anyValueBody() { + "arr_key=[entry1, 2, 3.3], " + "key_value_list_key=[child_str_key1=child_value1, child_str_key2=child_value2]" + "]"); + assertThat(((AnyValueBody) logRecordData.getBody()).getAnyValue()) + .isEqualTo( + AnyValue.of( + KeyAnyValue.of("str_key", AnyValue.of("value")), + KeyAnyValue.of("bool_key", AnyValue.of(true)), + KeyAnyValue.of("long_key", AnyValue.of(1L)), + KeyAnyValue.of("double_key", AnyValue.of(1.1)), + KeyAnyValue.of( + "bytes_key", AnyValue.of("bytes".getBytes(StandardCharsets.UTF_8))), + KeyAnyValue.of( + "arr_key", + AnyValue.of(AnyValue.of("entry1"), AnyValue.of(2), AnyValue.of(3.3))), + KeyAnyValue.of( + "key_value_list_key", + AnyValue.of( + new LinkedHashMap<String, AnyValue<?>>() { + { + put("child_str_key1", AnyValue.of("child_value1")); + put("child_str_key2", AnyValue.of("child_value2")); + } + })))); }); exporter.reset(); @@ -114,6 +140,9 @@ void anyValueBody() { logRecordData -> { assertThat(logRecordData.getBody().getType()).isEqualTo(Body.Type.ANY_VALUE); assertThat(logRecordData.getBody().asString()).isEqualTo("[entry1, entry2, 3]"); + assertThat(((AnyValueBody) logRecordData.getBody()).getAnyValue()) + .isEqualTo( + AnyValue.of(AnyValue.of("entry1"), AnyValue.of("entry2"), AnyValue.of(3))); }); exporter.reset(); } From 943c3fadcc5699acda02f2a2c4f4c8cbfe2d07e1 Mon Sep 17 00:00:00 2001 From: Jack Berg <jberg@newrelic.com> Date: Fri, 13 Oct 2023 15:56:40 -0500 Subject: [PATCH 4/6] japicmp --- docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt index 3013553d6ea..fac1cdbcecc 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt @@ -1,2 +1,4 @@ -Comparing source compatibility of against -No changes. +Comparing source compatibility of against +*** MODIFIED ENUM: PUBLIC STATIC FINAL io.opentelemetry.sdk.logs.data.Body$Type (compatible) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW FIELD: PUBLIC(+) STATIC(+) FINAL(+) io.opentelemetry.sdk.logs.data.Body$Type ANY_VALUE From 07a51c23c16e6b86bbe834678f40fc34ca28432e Mon Sep 17 00:00:00 2001 From: Jack Berg <jberg@newrelic.com> Date: Wed, 18 Oct 2023 09:46:08 -0500 Subject: [PATCH 5/6] PR feedback --- .../kotlin/otel.java-conventions.gradle.kts | 5 +++- .../otlp/logs/LogsRequestMarshalerTest.java | 15 +----------- .../extension/incubator/logs/AnyValue.java | 6 +++-- .../incubator/logs/AnyValueBytes.java | 23 +++++++++---------- .../incubator/logs/KeyAnyValueList.java | 2 +- .../incubator/logs/AnyValueTest.java | 9 +++++++- 6 files changed, 29 insertions(+), 31 deletions(-) diff --git a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts index 4d601c406dc..43791644017 100644 --- a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts @@ -74,7 +74,10 @@ tasks { // https://groups.google.com/forum/#!topic/bazel-discuss/_R3A9TJSoPM "-Xlint:-processing", // We suppress the "options" warning because it prevents compilation on modern JDKs - "-Xlint:-options" + "-Xlint:-options", + + // Fail build on any warning + "-Werror", ), ) } diff --git a/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LogsRequestMarshalerTest.java b/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LogsRequestMarshalerTest.java index 59b60fe2ac7..306cfe1ea14 100644 --- a/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LogsRequestMarshalerTest.java +++ b/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LogsRequestMarshalerTest.java @@ -24,7 +24,6 @@ import io.opentelemetry.proto.common.v1.AnyValue; import io.opentelemetry.proto.common.v1.InstrumentationScope; import io.opentelemetry.proto.common.v1.KeyValue; -import io.opentelemetry.proto.common.v1.KeyValueList; import io.opentelemetry.proto.logs.v1.LogRecord; import io.opentelemetry.proto.logs.v1.ResourceLogs; import io.opentelemetry.proto.logs.v1.ScopeLogs; @@ -154,19 +153,7 @@ void toProtoLogRecord_MinimalFields() { assertThat(logRecord.getSeverityText()).isBlank(); assertThat(logRecord.getSeverityNumber().getNumber()) .isEqualTo(Severity.UNDEFINED_SEVERITY_NUMBER.getSeverityNumber()); - assertThat(logRecord.getBody()) - .isEqualTo( - AnyValue.newBuilder() - .setKvlistValue( - KeyValueList.newBuilder() - .addValues( - KeyValue.newBuilder() - .setKey("key") - .setValue(AnyValue.newBuilder().setStringValue("foo").build()) - .build()) - .build()) - .setStringValue("") - .build()); + assertThat(logRecord.getBody()).isEqualTo(AnyValue.newBuilder().setStringValue("").build()); assertThat(logRecord.getAttributesList()).isEmpty(); assertThat(logRecord.getDroppedAttributesCount()).isZero(); assertThat(logRecord.getTimeUnixNano()).isEqualTo(12345); diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValue.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValue.java index a509c48aef4..6f250e10da7 100644 --- a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValue.java +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValue.java @@ -5,6 +5,7 @@ package io.opentelemetry.extension.incubator.logs; +import java.nio.ByteBuffer; import java.util.List; import java.util.Map; @@ -52,7 +53,7 @@ static AnyValue<Double> of(double value) { } /** Returns an {@link AnyValue} for the {@code byte[]} value. */ - static AnyValue<byte[]> of(byte[] value) { + static AnyValue<ByteBuffer> of(byte[] value) { return AnyValueBytes.create(value); } @@ -89,7 +90,8 @@ static AnyValue<List<KeyAnyValue>> of(Map<String, AnyValue<?>> value) { * <li>{@link AnyValueType#DOUBLE} returns {@code double} * <li>{@link AnyValueType#ARRAY} returns {@link List} of {@link AnyValue} * <li>{@link AnyValueType#KEY_VALUE_LIST} returns {@link List} of {@link KeyAnyValue} - * <li>{@link AnyValueType#BYTES} returns {@code byte[]} + * <li>{@link AnyValueType#BYTES} returns read only {@link ByteBuffer}. See {@link + * ByteBuffer#asReadOnlyBuffer()}. * </ul> */ T getValue(); diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBytes.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBytes.java index 589cce83f9e..1677a3313e8 100644 --- a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBytes.java +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/AnyValueBytes.java @@ -6,18 +6,19 @@ package io.opentelemetry.extension.incubator.logs; import io.opentelemetry.api.internal.OtelEncodingUtils; +import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Objects; -final class AnyValueBytes implements AnyValue<byte[]> { +final class AnyValueBytes implements AnyValue<ByteBuffer> { - private final byte[] value; + private final byte[] raw; private AnyValueBytes(byte[] value) { - this.value = value; + this.raw = value; } - static AnyValue<byte[]> create(byte[] value) { + static AnyValue<ByteBuffer> create(byte[] value) { Objects.requireNonNull(value, "value must not be null"); return new AnyValueBytes(Arrays.copyOf(value, value.length)); } @@ -28,16 +29,16 @@ public AnyValueType getType() { } @Override - public byte[] getValue() { - return value; + public ByteBuffer getValue() { + return ByteBuffer.wrap(raw).asReadOnlyBuffer(); } @Override public String asString() { // TODO: base64 would be better, but isn't available in android and java. Can we vendor in a // base64 implementation? - char[] arr = new char[value.length * 2]; - OtelEncodingUtils.bytesToBase16(value, arr, value.length); + char[] arr = new char[raw.length * 2]; + OtelEncodingUtils.bytesToBase16(raw, arr, raw.length); return new String(arr); } @@ -51,13 +52,11 @@ public boolean equals(Object o) { if (this == o) { return true; } - return (o instanceof AnyValue) - && ((AnyValue<?>) o).getType() == AnyValueType.BYTES - && Arrays.equals(this.value, (byte[]) ((AnyValue<?>) o).getValue()); + return (o instanceof AnyValueBytes) && Arrays.equals(this.raw, ((AnyValueBytes) o).raw); } @Override public int hashCode() { - return Arrays.hashCode(value); + return Arrays.hashCode(raw); } } diff --git a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValueList.java b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValueList.java index 501a7a66ac0..427ed4cb13a 100644 --- a/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValueList.java +++ b/extensions/incubator/src/main/java/io/opentelemetry/extension/incubator/logs/KeyAnyValueList.java @@ -51,7 +51,7 @@ public List<KeyAnyValue> getValue() { @Override public String asString() { return value.stream() - .map(entry -> entry.getKey() + "=" + entry.getAnyValue().asString()) + .map(item -> item.getKey() + "=" + item.getAnyValue().asString()) .collect(joining(", ", "[", "]")); } diff --git a/extensions/incubator/src/test/java/io/opentelemetry/extension/incubator/logs/AnyValueTest.java b/extensions/incubator/src/test/java/io/opentelemetry/extension/incubator/logs/AnyValueTest.java index 47b2d1c4d58..842cd505c61 100644 --- a/extensions/incubator/src/test/java/io/opentelemetry/extension/incubator/logs/AnyValueTest.java +++ b/extensions/incubator/src/test/java/io/opentelemetry/extension/incubator/logs/AnyValueTest.java @@ -10,6 +10,8 @@ import static org.junit.jupiter.params.provider.Arguments.arguments; import io.opentelemetry.api.internal.OtelEncodingUtils; +import java.nio.ByteBuffer; +import java.nio.ReadOnlyBufferException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; @@ -73,7 +75,12 @@ void anyValue_OfByteArray() { .satisfies( anyValue -> { assertThat(anyValue.getType()).isEqualTo(AnyValueType.BYTES); - assertThat(anyValue.getValue()).isEqualTo(new byte[] {'a', 'b'}); + ByteBuffer value = anyValue.getValue(); + // AnyValueBytes returns read only view of ByteBuffer + assertThatThrownBy(value::array).isInstanceOf(ReadOnlyBufferException.class); + byte[] bytes = new byte[value.remaining()]; + value.get(bytes); + assertThat(bytes).isEqualTo(new byte[] {'a', 'b'}); assertThat(anyValue).hasSameHashCodeAs(AnyValue.of(new byte[] {'a', 'b'})); }); } From 72daf329df67400eb4ef14b7cf1f8474b1894d68 Mon Sep 17 00:00:00 2001 From: Jack Berg <jberg@newrelic.com> Date: Tue, 31 Oct 2023 13:28:42 -0500 Subject: [PATCH 6/6] Remove Body#ANY_VALUE --- .../opentelemetry-sdk-logs.txt | 4 +--- .../io/opentelemetry/sdk/logs/data/Body.java | 5 +++-- .../sdk/logs/internal/AnyValueBody.java | 4 ++-- .../sdk/logs/AnyValueBodyTest.java | 21 +++++++++++-------- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt index fac1cdbcecc..df26146497b 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt @@ -1,4 +1,2 @@ Comparing source compatibility of against -*** MODIFIED ENUM: PUBLIC STATIC FINAL io.opentelemetry.sdk.logs.data.Body$Type (compatible) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW FIELD: PUBLIC(+) STATIC(+) FINAL(+) io.opentelemetry.sdk.logs.data.Body$Type ANY_VALUE +No changes. \ No newline at end of file diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/data/Body.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/data/Body.java index 3258ae9c4f0..2dc7957de91 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/data/Body.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/data/Body.java @@ -21,8 +21,9 @@ public interface Body { /** An enum that represents all the possible value types for an {@code Body}. */ enum Type { EMPTY, - STRING, - ANY_VALUE + STRING + // TODO (jack-berg): Add ANY_VALUE type when API for setting body to AnyValue is stable + // ANY_VALUE } /** diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/AnyValueBody.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/AnyValueBody.java index f756b8ac10a..7a1a9f2138f 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/AnyValueBody.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/AnyValueBody.java @@ -24,7 +24,7 @@ public static Body create(AnyValue<?> value) { @Override public Type getType() { - return Type.ANY_VALUE; + return Type.STRING; } @Override @@ -32,7 +32,7 @@ public String asString() { return value.asString(); } - public AnyValue<?> getAnyValue() { + public AnyValue<?> asAnyValue() { return value; } diff --git a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/AnyValueBodyTest.java b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/AnyValueBodyTest.java index be02c79f656..e793dc513d0 100644 --- a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/AnyValueBodyTest.java +++ b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/AnyValueBodyTest.java @@ -11,7 +11,6 @@ import io.opentelemetry.extension.incubator.logs.AnyValue; import io.opentelemetry.extension.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.extension.incubator.logs.KeyAnyValue; -import io.opentelemetry.sdk.logs.data.Body; import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor; import io.opentelemetry.sdk.logs.internal.AnyValueBody; import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter; @@ -37,9 +36,10 @@ void anyValueBody() { .hasSize(1) .satisfiesExactly( logRecordData -> { - assertThat(logRecordData.getBody().getType()).isEqualTo(Body.Type.ANY_VALUE); + // TODO (jack-berg): add assertion when ANY_VALUE is added to Body.Type + // assertThat(logRecordData.getBody().getType()).isEqualTo(Body.Type.ANY_VALUE); assertThat(logRecordData.getBody().asString()).isEqualTo("1"); - assertThat(((AnyValueBody) logRecordData.getBody()).getAnyValue()) + assertThat(((AnyValueBody) logRecordData.getBody()).asAnyValue()) .isEqualTo(AnyValue.of(1)); }); exporter.reset(); @@ -52,9 +52,10 @@ void anyValueBody() { .hasSize(1) .satisfiesExactly( logRecordData -> { - assertThat(logRecordData.getBody().getType()).isEqualTo(Body.Type.ANY_VALUE); + // TODO (jack-berg): add assertion when ANY_VALUE is added to Body.Type + // assertThat(logRecordData.getBody().getType()).isEqualTo(Body.Type.ANY_VALUE); assertThat(logRecordData.getBody().asString()).isEqualTo("68656c6c6f20776f726c64"); - assertThat(((AnyValueBody) logRecordData.getBody()).getAnyValue()) + assertThat(((AnyValueBody) logRecordData.getBody()).asAnyValue()) .isEqualTo(AnyValue.of("hello world".getBytes(StandardCharsets.UTF_8))); }); exporter.reset(); @@ -94,7 +95,8 @@ void anyValueBody() { .hasSize(1) .satisfiesExactly( logRecordData -> { - assertThat(logRecordData.getBody().getType()).isEqualTo(Body.Type.ANY_VALUE); + // TODO (jack-berg): add assertion when ANY_VALUE is added to Body.Type + // assertThat(logRecordData.getBody().getType()).isEqualTo(Body.Type.ANY_VALUE); assertThat(logRecordData.getBody().asString()) .isEqualTo( "[" @@ -106,7 +108,7 @@ void anyValueBody() { + "arr_key=[entry1, 2, 3.3], " + "key_value_list_key=[child_str_key1=child_value1, child_str_key2=child_value2]" + "]"); - assertThat(((AnyValueBody) logRecordData.getBody()).getAnyValue()) + assertThat(((AnyValueBody) logRecordData.getBody()).asAnyValue()) .isEqualTo( AnyValue.of( KeyAnyValue.of("str_key", AnyValue.of("value")), @@ -138,9 +140,10 @@ void anyValueBody() { .hasSize(1) .satisfiesExactly( logRecordData -> { - assertThat(logRecordData.getBody().getType()).isEqualTo(Body.Type.ANY_VALUE); + // TODO (jack-berg): add assertion when ANY_VALUE is added to Body.Type + // assertThat(logRecordData.getBody().getType()).isEqualTo(Body.Type.ANY_VALUE); assertThat(logRecordData.getBody().asString()).isEqualTo("[entry1, entry2, 3]"); - assertThat(((AnyValueBody) logRecordData.getBody()).getAnyValue()) + assertThat(((AnyValueBody) logRecordData.getBody()).asAnyValue()) .isEqualTo( AnyValue.of(AnyValue.of("entry1"), AnyValue.of("entry2"), AnyValue.of(3))); });