From 4fdc2a2707da102fb5a2b074c84a941497aab7b1 Mon Sep 17 00:00:00 2001 From: Jack Berg Date: Fri, 3 May 2024 15:29:32 -0500 Subject: [PATCH 1/2] Add LowAllocationLogRequestMarshaler --- .../otlp/AnyValueStatelessMarshaler.java | 110 +++++++++++++ .../otlp/ArrayAnyValueStatelessMarshaler.java | 80 ++-------- ...ributeArrayAnyValueStatelessMarshaler.java | 89 +++++++++++ .../AttributeKeyValueStatelessMarshaler.java | 145 +++++++++++++++++ .../otlp/BytesAnyValueStatelessMarshaler.java | 36 +++++ ...eyValueListAnyValueStatelessMarshaler.java | 38 +++++ .../otlp/KeyValueStatelessMarshaler.java | 128 +++------------ .../otlp/StringAnyValueMarshaler.java | 8 +- ...umentationScopeLogsStatelessMarshaler.java | 63 ++++++++ .../otlp/logs/LogStatelessMarshaler.java | 147 ++++++++++++++++++ .../LowAllocationLogsRequestMarshaler.java | 107 +++++++++++++ .../logs/ResourceLogsStatelessMarshaler.java | 80 ++++++++++ .../traces/SpanEventStatelessMarshaler.java | 9 +- .../traces/SpanLinkStatelessMarshaler.java | 9 +- .../otlp/traces/SpanStatelessMarshaler.java | 12 +- .../internal/otlp/AnyValueMarshalerTest.java | 32 +++- .../otlp/logs/LogsRequestMarshalerTest.java | 57 ++++++- .../LowAllocationLogRequestMarshalerTest.java | 142 +++++++++++++++++ 18 files changed, 1097 insertions(+), 195 deletions(-) create mode 100644 exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AnyValueStatelessMarshaler.java create mode 100644 exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AttributeArrayAnyValueStatelessMarshaler.java create mode 100644 exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AttributeKeyValueStatelessMarshaler.java create mode 100644 exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/BytesAnyValueStatelessMarshaler.java create mode 100644 exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/KeyValueListAnyValueStatelessMarshaler.java create mode 100644 exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/InstrumentationScopeLogsStatelessMarshaler.java create mode 100644 exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogStatelessMarshaler.java create mode 100644 exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LowAllocationLogsRequestMarshaler.java create mode 100644 exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/ResourceLogsStatelessMarshaler.java create mode 100644 exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LowAllocationLogRequestMarshalerTest.java diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AnyValueStatelessMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AnyValueStatelessMarshaler.java new file mode 100644 index 00000000000..f9cbb05589f --- /dev/null +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AnyValueStatelessMarshaler.java @@ -0,0 +1,110 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.otlp; + +import io.opentelemetry.api.incubator.logs.AnyValue; +import io.opentelemetry.api.incubator.logs.KeyAnyValue; +import io.opentelemetry.exporter.internal.marshal.MarshalerContext; +import io.opentelemetry.exporter.internal.marshal.Serializer; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshaler; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshalerUtil; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.List; + +/** + * A Marshaler of key value pairs. See {@link AnyValueMarshaler}. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class AnyValueStatelessMarshaler implements StatelessMarshaler> { + + public static final AnyValueStatelessMarshaler INSTANCE = new AnyValueStatelessMarshaler(); + + private AnyValueStatelessMarshaler() {} + + @SuppressWarnings("unchecked") + @Override + public void writeTo(Serializer output, AnyValue value, MarshalerContext context) + throws IOException { + switch (value.getType()) { + case STRING: + StringAnyValueStatelessMarshaler.INSTANCE.writeTo( + output, (String) value.getValue(), context); + return; + case BOOLEAN: + BoolAnyValueStatelessMarshaler.INSTANCE.writeTo( + output, (Boolean) value.getValue(), context); + return; + case LONG: + IntAnyValueStatelessMarshaler.INSTANCE.writeTo(output, (Long) value.getValue(), context); + return; + case DOUBLE: + DoubleAnyValueStatelessMarshaler.INSTANCE.writeTo( + output, (Double) value.getValue(), context); + return; + case ARRAY: + output.serializeMessageWithContext( + io.opentelemetry.proto.common.v1.internal.AnyValue.ARRAY_VALUE, + (List>) value.getValue(), + ArrayAnyValueStatelessMarshaler.INSTANCE, + context); + return; + case KEY_VALUE_LIST: + output.serializeMessageWithContext( + io.opentelemetry.proto.common.v1.internal.AnyValue.KVLIST_VALUE, + (List) value.getValue(), + KeyValueListAnyValueStatelessMarshaler.INSTANCE, + context); + return; + case BYTES: + BytesAnyValueStatelessMarshaler.INSTANCE.writeTo( + output, (ByteBuffer) value.getValue(), context); + return; + } + // Error prone ensures the switch statement is complete, otherwise only can happen with + // unaligned versions which are not supported. + throw new IllegalArgumentException("Unsupported value type."); + } + + @SuppressWarnings("unchecked") + @Override + public int getBinarySerializedSize(AnyValue value, MarshalerContext context) { + switch (value.getType()) { + case STRING: + return StringAnyValueStatelessMarshaler.INSTANCE.getBinarySerializedSize( + (String) value.getValue(), context); + case BOOLEAN: + return BoolAnyValueStatelessMarshaler.INSTANCE.getBinarySerializedSize( + (Boolean) value.getValue(), context); + case LONG: + return IntAnyValueStatelessMarshaler.INSTANCE.getBinarySerializedSize( + (Long) value.getValue(), context); + case DOUBLE: + return DoubleAnyValueStatelessMarshaler.INSTANCE.getBinarySerializedSize( + (Double) value.getValue(), context); + case ARRAY: + return StatelessMarshalerUtil.sizeMessageWithContext( + io.opentelemetry.proto.common.v1.internal.AnyValue.ARRAY_VALUE, + (List>) value.getValue(), + ArrayAnyValueStatelessMarshaler.INSTANCE, + context); + case KEY_VALUE_LIST: + return StatelessMarshalerUtil.sizeMessageWithContext( + io.opentelemetry.proto.common.v1.internal.AnyValue.KVLIST_VALUE, + (List) value.getValue(), + KeyValueListAnyValueStatelessMarshaler.INSTANCE, + context); + case BYTES: + return BytesAnyValueStatelessMarshaler.INSTANCE.getBinarySerializedSize( + (ByteBuffer) value.getValue(), context); + } + // Error prone ensures the switch statement is complete, otherwise only can happen with + // unaligned versions which are not supported. + throw new IllegalArgumentException("Unsupported value type."); + } +} diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/ArrayAnyValueStatelessMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/ArrayAnyValueStatelessMarshaler.java index 87e2bd3eb7a..a558953514c 100644 --- a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/ArrayAnyValueStatelessMarshaler.java +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/ArrayAnyValueStatelessMarshaler.java @@ -5,84 +5,32 @@ package io.opentelemetry.exporter.internal.otlp; -import io.opentelemetry.api.common.AttributeType; +import io.opentelemetry.api.incubator.logs.AnyValue; import io.opentelemetry.exporter.internal.marshal.MarshalerContext; import io.opentelemetry.exporter.internal.marshal.Serializer; -import io.opentelemetry.exporter.internal.marshal.StatelessMarshaler2; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshaler; import io.opentelemetry.exporter.internal.marshal.StatelessMarshalerUtil; import io.opentelemetry.proto.common.v1.internal.ArrayValue; import java.io.IOException; import java.util.List; -/** See {@link ArrayAnyValueMarshaler}. */ -// TODO: add support for List> -final class ArrayAnyValueStatelessMarshaler - implements StatelessMarshaler2> { - static final ArrayAnyValueStatelessMarshaler INSTANCE = - new ArrayAnyValueStatelessMarshaler<>(); +/** A Marshaler of key value pairs. See {@link ArrayAnyValueMarshaler}. */ +final class ArrayAnyValueStatelessMarshaler implements StatelessMarshaler>> { + + static final ArrayAnyValueStatelessMarshaler INSTANCE = new ArrayAnyValueStatelessMarshaler(); + + private ArrayAnyValueStatelessMarshaler() {} - @SuppressWarnings("unchecked") @Override - public void writeTo(Serializer output, AttributeType type, List list, MarshalerContext context) + public void writeTo(Serializer output, List> value, MarshalerContext context) throws IOException { - switch (type) { - case STRING_ARRAY: - output.serializeRepeatedMessageWithContext( - ArrayValue.VALUES, - (List) list, - StringAnyValueStatelessMarshaler.INSTANCE, - context); - return; - case LONG_ARRAY: - output.serializeRepeatedMessageWithContext( - ArrayValue.VALUES, (List) list, IntAnyValueStatelessMarshaler.INSTANCE, context); - return; - case BOOLEAN_ARRAY: - output.serializeRepeatedMessageWithContext( - ArrayValue.VALUES, - (List) list, - BoolAnyValueStatelessMarshaler.INSTANCE, - context); - return; - case DOUBLE_ARRAY: - output.serializeRepeatedMessageWithContext( - ArrayValue.VALUES, - (List) list, - DoubleAnyValueStatelessMarshaler.INSTANCE, - context); - return; - default: - throw new IllegalArgumentException("Unsupported attribute type."); - } + output.serializeRepeatedMessageWithContext( + ArrayValue.VALUES, value, AnyValueStatelessMarshaler.INSTANCE, context); } - @SuppressWarnings("unchecked") @Override - public int getBinarySerializedSize(AttributeType type, List list, MarshalerContext context) { - switch (type) { - case STRING_ARRAY: - return StatelessMarshalerUtil.sizeRepeatedMessageWithContext( - ArrayValue.VALUES, - (List) list, - StringAnyValueStatelessMarshaler.INSTANCE, - context); - case LONG_ARRAY: - return StatelessMarshalerUtil.sizeRepeatedMessageWithContext( - ArrayValue.VALUES, (List) list, IntAnyValueStatelessMarshaler.INSTANCE, context); - case BOOLEAN_ARRAY: - return StatelessMarshalerUtil.sizeRepeatedMessageWithContext( - ArrayValue.VALUES, - (List) list, - BoolAnyValueStatelessMarshaler.INSTANCE, - context); - case DOUBLE_ARRAY: - return StatelessMarshalerUtil.sizeRepeatedMessageWithContext( - ArrayValue.VALUES, - (List) list, - DoubleAnyValueStatelessMarshaler.INSTANCE, - context); - default: - throw new IllegalArgumentException("Unsupported attribute type."); - } + public int getBinarySerializedSize(List> value, MarshalerContext context) { + return StatelessMarshalerUtil.sizeRepeatedMessageWithContext( + ArrayValue.VALUES, value, AnyValueStatelessMarshaler.INSTANCE, context); } } diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AttributeArrayAnyValueStatelessMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AttributeArrayAnyValueStatelessMarshaler.java new file mode 100644 index 00000000000..db92ca1e7dc --- /dev/null +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AttributeArrayAnyValueStatelessMarshaler.java @@ -0,0 +1,89 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.otlp; + +import io.opentelemetry.api.common.AttributeType; +import io.opentelemetry.exporter.internal.marshal.MarshalerContext; +import io.opentelemetry.exporter.internal.marshal.Serializer; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshaler2; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshalerUtil; +import io.opentelemetry.proto.common.v1.internal.ArrayValue; +import java.io.IOException; +import java.util.List; + +/** See {@link ArrayAnyValueMarshaler}. */ +final class AttributeArrayAnyValueStatelessMarshaler + implements StatelessMarshaler2> { + static final AttributeArrayAnyValueStatelessMarshaler INSTANCE = + new AttributeArrayAnyValueStatelessMarshaler<>(); + + private AttributeArrayAnyValueStatelessMarshaler() {} + + @SuppressWarnings("unchecked") + @Override + public void writeTo(Serializer output, AttributeType type, List list, MarshalerContext context) + throws IOException { + switch (type) { + case STRING_ARRAY: + output.serializeRepeatedMessageWithContext( + ArrayValue.VALUES, + (List) list, + StringAnyValueStatelessMarshaler.INSTANCE, + context); + return; + case LONG_ARRAY: + output.serializeRepeatedMessageWithContext( + ArrayValue.VALUES, (List) list, IntAnyValueStatelessMarshaler.INSTANCE, context); + return; + case BOOLEAN_ARRAY: + output.serializeRepeatedMessageWithContext( + ArrayValue.VALUES, + (List) list, + BoolAnyValueStatelessMarshaler.INSTANCE, + context); + return; + case DOUBLE_ARRAY: + output.serializeRepeatedMessageWithContext( + ArrayValue.VALUES, + (List) list, + DoubleAnyValueStatelessMarshaler.INSTANCE, + context); + return; + default: + throw new IllegalArgumentException("Unsupported attribute type."); + } + } + + @SuppressWarnings("unchecked") + @Override + public int getBinarySerializedSize(AttributeType type, List list, MarshalerContext context) { + switch (type) { + case STRING_ARRAY: + return StatelessMarshalerUtil.sizeRepeatedMessageWithContext( + ArrayValue.VALUES, + (List) list, + StringAnyValueStatelessMarshaler.INSTANCE, + context); + case LONG_ARRAY: + return StatelessMarshalerUtil.sizeRepeatedMessageWithContext( + ArrayValue.VALUES, (List) list, IntAnyValueStatelessMarshaler.INSTANCE, context); + case BOOLEAN_ARRAY: + return StatelessMarshalerUtil.sizeRepeatedMessageWithContext( + ArrayValue.VALUES, + (List) list, + BoolAnyValueStatelessMarshaler.INSTANCE, + context); + case DOUBLE_ARRAY: + return StatelessMarshalerUtil.sizeRepeatedMessageWithContext( + ArrayValue.VALUES, + (List) list, + DoubleAnyValueStatelessMarshaler.INSTANCE, + context); + default: + throw new IllegalArgumentException("Unsupported attribute type."); + } + } +} diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AttributeKeyValueStatelessMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AttributeKeyValueStatelessMarshaler.java new file mode 100644 index 00000000000..3fb1f7c25f6 --- /dev/null +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/AttributeKeyValueStatelessMarshaler.java @@ -0,0 +1,145 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.otlp; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.AttributeType; +import io.opentelemetry.api.internal.InternalAttributeKeyImpl; +import io.opentelemetry.exporter.internal.marshal.MarshalerContext; +import io.opentelemetry.exporter.internal.marshal.MarshalerUtil; +import io.opentelemetry.exporter.internal.marshal.Serializer; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshaler2; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshalerUtil; +import io.opentelemetry.proto.common.v1.internal.AnyValue; +import io.opentelemetry.proto.common.v1.internal.KeyValue; +import java.io.IOException; +import java.util.List; + +/** + * A Marshaler of key value pairs. See {@link KeyValueMarshaler}. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class AttributeKeyValueStatelessMarshaler + implements StatelessMarshaler2, Object> { + public static final AttributeKeyValueStatelessMarshaler INSTANCE = + new AttributeKeyValueStatelessMarshaler(); + private static final byte[] EMPTY_BYTES = new byte[0]; + + private AttributeKeyValueStatelessMarshaler() {} + + @Override + public void writeTo( + Serializer output, AttributeKey attributeKey, Object value, MarshalerContext context) + throws IOException { + if (attributeKey.getKey().isEmpty()) { + output.serializeString(KeyValue.KEY, EMPTY_BYTES); + } else if (attributeKey instanceof InternalAttributeKeyImpl) { + byte[] keyUtf8 = ((InternalAttributeKeyImpl) attributeKey).getKeyUtf8(); + output.serializeString(KeyValue.KEY, keyUtf8); + } else { + output.serializeStringWithContext(KeyValue.KEY, attributeKey.getKey(), context); + } + output.serializeMessageWithContext( + KeyValue.VALUE, attributeKey, value, ValueStatelessMarshaler.INSTANCE, context); + } + + @Override + public int getBinarySerializedSize( + AttributeKey attributeKey, Object value, MarshalerContext context) { + int size = 0; + if (!attributeKey.getKey().isEmpty()) { + if (attributeKey instanceof InternalAttributeKeyImpl) { + byte[] keyUtf8 = ((InternalAttributeKeyImpl) attributeKey).getKeyUtf8(); + size += MarshalerUtil.sizeBytes(KeyValue.KEY, keyUtf8); + } else { + return StatelessMarshalerUtil.sizeStringWithContext( + KeyValue.KEY, attributeKey.getKey(), context); + } + } + size += + StatelessMarshalerUtil.sizeMessageWithContext( + KeyValue.VALUE, attributeKey, value, ValueStatelessMarshaler.INSTANCE, context); + + return size; + } + + private static class ValueStatelessMarshaler + implements StatelessMarshaler2, Object> { + static final ValueStatelessMarshaler INSTANCE = new ValueStatelessMarshaler(); + + @SuppressWarnings("unchecked") + @Override + public int getBinarySerializedSize( + AttributeKey attributeKey, Object value, MarshalerContext context) { + AttributeType attributeType = attributeKey.getType(); + switch (attributeType) { + case STRING: + return StringAnyValueStatelessMarshaler.INSTANCE.getBinarySerializedSize( + (String) value, context); + case LONG: + return IntAnyValueStatelessMarshaler.INSTANCE.getBinarySerializedSize( + (Long) value, context); + case BOOLEAN: + return BoolAnyValueStatelessMarshaler.INSTANCE.getBinarySerializedSize( + (Boolean) value, context); + case DOUBLE: + return DoubleAnyValueStatelessMarshaler.INSTANCE.getBinarySerializedSize( + (Double) value, context); + case STRING_ARRAY: + case LONG_ARRAY: + case BOOLEAN_ARRAY: + case DOUBLE_ARRAY: + return StatelessMarshalerUtil.sizeMessageWithContext( + AnyValue.ARRAY_VALUE, + attributeType, + (List) value, + AttributeArrayAnyValueStatelessMarshaler.INSTANCE, + context); + } + // Error prone ensures the switch statement is complete, otherwise only can happen with + // unaligned versions which are not supported. + throw new IllegalArgumentException("Unsupported attribute type."); + } + + @SuppressWarnings("unchecked") + @Override + public void writeTo( + Serializer output, AttributeKey attributeKey, Object value, MarshalerContext context) + throws IOException { + AttributeType attributeType = attributeKey.getType(); + switch (attributeType) { + case STRING: + StringAnyValueStatelessMarshaler.INSTANCE.writeTo(output, (String) value, context); + return; + case LONG: + IntAnyValueStatelessMarshaler.INSTANCE.writeTo(output, (Long) value, context); + return; + case BOOLEAN: + BoolAnyValueStatelessMarshaler.INSTANCE.writeTo(output, (Boolean) value, context); + return; + case DOUBLE: + DoubleAnyValueStatelessMarshaler.INSTANCE.writeTo(output, (Double) value, context); + return; + case STRING_ARRAY: + case LONG_ARRAY: + case BOOLEAN_ARRAY: + case DOUBLE_ARRAY: + output.serializeMessageWithContext( + AnyValue.ARRAY_VALUE, + attributeType, + (List) value, + AttributeArrayAnyValueStatelessMarshaler.INSTANCE, + context); + return; + } + // Error prone ensures the switch statement is complete, otherwise only can happen with + // unaligned versions which are not supported. + throw new IllegalArgumentException("Unsupported attribute type."); + } + } +} diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/BytesAnyValueStatelessMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/BytesAnyValueStatelessMarshaler.java new file mode 100644 index 00000000000..767ff8b8176 --- /dev/null +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/BytesAnyValueStatelessMarshaler.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.otlp; + +import io.opentelemetry.exporter.internal.marshal.CodedOutputStream; +import io.opentelemetry.exporter.internal.marshal.MarshalerContext; +import io.opentelemetry.exporter.internal.marshal.Serializer; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshaler; +import io.opentelemetry.proto.common.v1.internal.AnyValue; +import java.io.IOException; +import java.nio.ByteBuffer; + +/** See {@link BytesAnyValueMarshaler}. */ +final class BytesAnyValueStatelessMarshaler implements StatelessMarshaler { + static final BytesAnyValueStatelessMarshaler INSTANCE = new BytesAnyValueStatelessMarshaler(); + + private BytesAnyValueStatelessMarshaler() {} + + @Override + public void writeTo(Serializer output, ByteBuffer value, MarshalerContext context) + throws IOException { + byte[] bytes = context.getData(byte[].class); + output.writeBytes(AnyValue.BYTES_VALUE, bytes); + } + + @Override + public int getBinarySerializedSize(ByteBuffer value, MarshalerContext context) { + byte[] bytes = new byte[value.remaining()]; + value.get(bytes); + context.addData(bytes); + return AnyValue.BYTES_VALUE.getTagSize() + CodedOutputStream.computeByteArraySizeNoTag(bytes); + } +} diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/KeyValueListAnyValueStatelessMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/KeyValueListAnyValueStatelessMarshaler.java new file mode 100644 index 00000000000..854eb2cea6e --- /dev/null +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/KeyValueListAnyValueStatelessMarshaler.java @@ -0,0 +1,38 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.otlp; + +import io.opentelemetry.api.incubator.logs.KeyAnyValue; +import io.opentelemetry.exporter.internal.marshal.MarshalerContext; +import io.opentelemetry.exporter.internal.marshal.Serializer; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshaler; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshalerUtil; +import io.opentelemetry.proto.common.v1.internal.KeyValueList; +import java.io.IOException; +import java.util.List; + +/** A Marshaler of key value pairs. See {@link KeyValueListAnyValueMarshaler}. */ +final class KeyValueListAnyValueStatelessMarshaler + implements StatelessMarshaler> { + + static final KeyValueListAnyValueStatelessMarshaler INSTANCE = + new KeyValueListAnyValueStatelessMarshaler(); + + private KeyValueListAnyValueStatelessMarshaler() {} + + @Override + public void writeTo(Serializer output, List value, MarshalerContext context) + throws IOException { + output.serializeRepeatedMessageWithContext( + KeyValueList.VALUES, value, KeyValueStatelessMarshaler.INSTANCE, context); + } + + @Override + public int getBinarySerializedSize(List value, MarshalerContext context) { + return StatelessMarshalerUtil.sizeRepeatedMessageWithContext( + KeyValueList.VALUES, value, KeyValueStatelessMarshaler.INSTANCE, context); + } +} diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/KeyValueStatelessMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/KeyValueStatelessMarshaler.java index 5714912d2d0..0f14f0d354c 100644 --- a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/KeyValueStatelessMarshaler.java +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/KeyValueStatelessMarshaler.java @@ -5,138 +5,46 @@ package io.opentelemetry.exporter.internal.otlp; -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.AttributeType; -import io.opentelemetry.api.internal.InternalAttributeKeyImpl; +import io.opentelemetry.api.incubator.logs.KeyAnyValue; import io.opentelemetry.exporter.internal.marshal.MarshalerContext; -import io.opentelemetry.exporter.internal.marshal.MarshalerUtil; import io.opentelemetry.exporter.internal.marshal.Serializer; -import io.opentelemetry.exporter.internal.marshal.StatelessMarshaler2; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshaler; import io.opentelemetry.exporter.internal.marshal.StatelessMarshalerUtil; -import io.opentelemetry.proto.common.v1.internal.AnyValue; import io.opentelemetry.proto.common.v1.internal.KeyValue; import java.io.IOException; -import java.util.List; -/** - * A Marshaler of key value pairs. See {@link KeyValueMarshaler}. - * - *

This class is internal and is hence not for public use. Its APIs are unstable and can change - * at any time. - */ -public final class KeyValueStatelessMarshaler - implements StatelessMarshaler2, Object> { - public static final KeyValueStatelessMarshaler INSTANCE = new KeyValueStatelessMarshaler(); +/** A Marshaler of key value pairs. See {@link AnyValueMarshaler}. */ +final class KeyValueStatelessMarshaler implements StatelessMarshaler { + + static final KeyValueStatelessMarshaler INSTANCE = new KeyValueStatelessMarshaler(); private static final byte[] EMPTY_BYTES = new byte[0]; + private KeyValueStatelessMarshaler() {} + @Override - public void writeTo( - Serializer output, AttributeKey attributeKey, Object value, MarshalerContext context) + public void writeTo(Serializer output, KeyAnyValue value, MarshalerContext context) throws IOException { - if (attributeKey.getKey().isEmpty()) { + String key = value.getKey(); + if (key.isEmpty()) { output.serializeString(KeyValue.KEY, EMPTY_BYTES); - } else if (attributeKey instanceof InternalAttributeKeyImpl) { - byte[] keyUtf8 = ((InternalAttributeKeyImpl) attributeKey).getKeyUtf8(); - output.serializeString(KeyValue.KEY, keyUtf8); } else { - output.serializeStringWithContext(KeyValue.KEY, attributeKey.getKey(), context); + output.serializeStringWithContext(KeyValue.KEY, key, context); } output.serializeMessageWithContext( - KeyValue.VALUE, attributeKey, value, ValueStatelessMarshaler.INSTANCE, context); + KeyValue.VALUE, value.getAnyValue(), AnyValueStatelessMarshaler.INSTANCE, context); } @Override - public int getBinarySerializedSize( - AttributeKey attributeKey, Object value, MarshalerContext context) { + public int getBinarySerializedSize(KeyAnyValue value, MarshalerContext context) { int size = 0; - if (!attributeKey.getKey().isEmpty()) { - if (attributeKey instanceof InternalAttributeKeyImpl) { - byte[] keyUtf8 = ((InternalAttributeKeyImpl) attributeKey).getKeyUtf8(); - size += MarshalerUtil.sizeBytes(KeyValue.KEY, keyUtf8); - } else { - return StatelessMarshalerUtil.sizeStringWithContext( - KeyValue.KEY, attributeKey.getKey(), context); - } + String key = value.getKey(); + if (!key.isEmpty()) { + size += StatelessMarshalerUtil.sizeStringWithContext(KeyValue.KEY, key, context); } size += StatelessMarshalerUtil.sizeMessageWithContext( - KeyValue.VALUE, attributeKey, value, ValueStatelessMarshaler.INSTANCE, context); + KeyValue.VALUE, value.getAnyValue(), AnyValueStatelessMarshaler.INSTANCE, context); return size; } - - private static class ValueStatelessMarshaler - implements StatelessMarshaler2, Object> { - static final ValueStatelessMarshaler INSTANCE = new ValueStatelessMarshaler(); - - @SuppressWarnings("unchecked") - @Override - public int getBinarySerializedSize( - AttributeKey attributeKey, Object value, MarshalerContext context) { - AttributeType attributeType = attributeKey.getType(); - switch (attributeType) { - case STRING: - return StringAnyValueStatelessMarshaler.INSTANCE.getBinarySerializedSize( - (String) value, context); - case LONG: - return IntAnyValueStatelessMarshaler.INSTANCE.getBinarySerializedSize( - (Long) value, context); - case BOOLEAN: - return BoolAnyValueStatelessMarshaler.INSTANCE.getBinarySerializedSize( - (Boolean) value, context); - case DOUBLE: - return DoubleAnyValueStatelessMarshaler.INSTANCE.getBinarySerializedSize( - (Double) value, context); - case STRING_ARRAY: - case LONG_ARRAY: - case BOOLEAN_ARRAY: - case DOUBLE_ARRAY: - return StatelessMarshalerUtil.sizeMessageWithContext( - AnyValue.ARRAY_VALUE, - attributeType, - (List) value, - ArrayAnyValueStatelessMarshaler.INSTANCE, - context); - } - // Error prone ensures the switch statement is complete, otherwise only can happen with - // unaligned versions which are not supported. - throw new IllegalArgumentException("Unsupported attribute type."); - } - - @SuppressWarnings("unchecked") - @Override - public void writeTo( - Serializer output, AttributeKey attributeKey, Object value, MarshalerContext context) - throws IOException { - AttributeType attributeType = attributeKey.getType(); - switch (attributeType) { - case STRING: - StringAnyValueStatelessMarshaler.INSTANCE.writeTo(output, (String) value, context); - return; - case LONG: - IntAnyValueStatelessMarshaler.INSTANCE.writeTo(output, (Long) value, context); - return; - case BOOLEAN: - BoolAnyValueStatelessMarshaler.INSTANCE.writeTo(output, (Boolean) value, context); - return; - case DOUBLE: - DoubleAnyValueStatelessMarshaler.INSTANCE.writeTo(output, (Double) value, context); - return; - case STRING_ARRAY: - case LONG_ARRAY: - case BOOLEAN_ARRAY: - case DOUBLE_ARRAY: - output.serializeMessageWithContext( - AnyValue.ARRAY_VALUE, - attributeType, - (List) value, - ArrayAnyValueStatelessMarshaler.INSTANCE, - context); - return; - } - // Error prone ensures the switch statement is complete, otherwise only can happen with - // unaligned versions which are not supported. - throw new IllegalArgumentException("Unsupported attribute type."); - } - } } diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/StringAnyValueMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/StringAnyValueMarshaler.java index e62c55d2da1..cc7bf4527c6 100644 --- a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/StringAnyValueMarshaler.java +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/StringAnyValueMarshaler.java @@ -33,12 +33,16 @@ static MarshalerWithSize create(String value) { @Override public void writeTo(Serializer output) throws IOException { - // Do not call serialize* method because we always have to write the message tag even if the - // value is empty since it's a oneof. + if (valueUtf8.length == 0) { + return; + } output.writeString(AnyValue.STRING_VALUE, valueUtf8); } private static int calculateSize(byte[] valueUtf8) { + if (valueUtf8.length == 0) { + return 0; + } return AnyValue.STRING_VALUE.getTagSize() + CodedOutputStream.computeByteArraySizeNoTag(valueUtf8); } diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/InstrumentationScopeLogsStatelessMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/InstrumentationScopeLogsStatelessMarshaler.java new file mode 100644 index 00000000000..60789604b3e --- /dev/null +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/InstrumentationScopeLogsStatelessMarshaler.java @@ -0,0 +1,63 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.otlp.logs; + +import io.opentelemetry.exporter.internal.marshal.MarshalerContext; +import io.opentelemetry.exporter.internal.marshal.MarshalerUtil; +import io.opentelemetry.exporter.internal.marshal.Serializer; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshaler2; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshalerUtil; +import io.opentelemetry.exporter.internal.otlp.InstrumentationScopeMarshaler; +import io.opentelemetry.proto.logs.v1.internal.ScopeLogs; +import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.logs.data.LogRecordData; +import java.io.IOException; +import java.util.List; + +/** See {@link InstrumentationScopeLogsMarshaler}. */ +final class InstrumentationScopeLogsStatelessMarshaler + implements StatelessMarshaler2> { + static final InstrumentationScopeLogsStatelessMarshaler INSTANCE = + new InstrumentationScopeLogsStatelessMarshaler(); + + @Override + public void writeTo( + Serializer output, + InstrumentationScopeInfo instrumentationScope, + List logs, + MarshalerContext context) + throws IOException { + InstrumentationScopeMarshaler instrumentationScopeMarshaler = + context.getData(InstrumentationScopeMarshaler.class); + + output.serializeMessage(ScopeLogs.SCOPE, instrumentationScopeMarshaler); + output.serializeRepeatedMessageWithContext( + ScopeLogs.LOG_RECORDS, logs, LogStatelessMarshaler.INSTANCE, context); + output.serializeStringWithContext( + ScopeLogs.SCHEMA_URL, instrumentationScope.getSchemaUrl(), context); + } + + @Override + public int getBinarySerializedSize( + InstrumentationScopeInfo instrumentationScope, + List logs, + MarshalerContext context) { + InstrumentationScopeMarshaler instrumentationScopeMarshaler = + InstrumentationScopeMarshaler.create(instrumentationScope); + context.addData(instrumentationScopeMarshaler); + + int size = 0; + size += MarshalerUtil.sizeMessage(ScopeLogs.SCOPE, instrumentationScopeMarshaler); + size += + StatelessMarshalerUtil.sizeRepeatedMessageWithContext( + ScopeLogs.LOG_RECORDS, logs, LogStatelessMarshaler.INSTANCE, context); + size += + StatelessMarshalerUtil.sizeStringWithContext( + ScopeLogs.SCHEMA_URL, instrumentationScope.getSchemaUrl(), context); + + return size; + } +} diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogStatelessMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogStatelessMarshaler.java new file mode 100644 index 00000000000..68d524b4341 --- /dev/null +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogStatelessMarshaler.java @@ -0,0 +1,147 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.otlp.logs; + +import static io.opentelemetry.exporter.internal.otlp.logs.LogMarshaler.toProtoSeverityNumber; + +import io.opentelemetry.api.incubator.logs.AnyValue; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.SpanId; +import io.opentelemetry.api.trace.TraceId; +import io.opentelemetry.exporter.internal.marshal.MarshalerContext; +import io.opentelemetry.exporter.internal.marshal.MarshalerUtil; +import io.opentelemetry.exporter.internal.marshal.Serializer; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshaler; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshalerUtil; +import io.opentelemetry.exporter.internal.otlp.AnyValueStatelessMarshaler; +import io.opentelemetry.exporter.internal.otlp.AttributeKeyValueStatelessMarshaler; +import io.opentelemetry.proto.logs.v1.internal.LogRecord; +import io.opentelemetry.sdk.logs.data.Body; +import io.opentelemetry.sdk.logs.data.LogRecordData; +import io.opentelemetry.sdk.logs.internal.AnyValueBody; +import java.io.IOException; + +/** See {@link LogMarshaler}. */ +final class LogStatelessMarshaler implements StatelessMarshaler { + private static final String INVALID_TRACE_ID = TraceId.getInvalid(); + private static final String INVALID_SPAN_ID = SpanId.getInvalid(); + static final LogStatelessMarshaler INSTANCE = new LogStatelessMarshaler(); + + @Override + public void writeTo(Serializer output, LogRecordData log, MarshalerContext context) + throws IOException { + output.serializeFixed64(LogRecord.TIME_UNIX_NANO, log.getTimestampEpochNanos()); + output.serializeFixed64( + LogRecord.OBSERVED_TIME_UNIX_NANO, log.getObservedTimestampEpochNanos()); + output.serializeEnum(LogRecord.SEVERITY_NUMBER, toProtoSeverityNumber(log.getSeverity())); + output.serializeStringWithContext(LogRecord.SEVERITY_TEXT, log.getSeverityText(), context); + output.serializeMessageWithContext( + LogRecord.BODY, log.getBody(), BodyMarshaler.INSTANCE, context); + output.serializeRepeatedMessageWithContext( + LogRecord.ATTRIBUTES, + log.getAttributes(), + AttributeKeyValueStatelessMarshaler.INSTANCE, + context); + int droppedAttributesCount = log.getTotalAttributeCount() - log.getAttributes().size(); + output.serializeUInt32(LogRecord.DROPPED_ATTRIBUTES_COUNT, droppedAttributesCount); + + SpanContext spanContext = log.getSpanContext(); + output.serializeFixed32(LogRecord.FLAGS, spanContext.getTraceFlags().asByte()); + if (!spanContext.getTraceId().equals(INVALID_TRACE_ID)) { + output.serializeTraceId(LogRecord.TRACE_ID, spanContext.getTraceId(), context); + } + if (!spanContext.getSpanId().equals(INVALID_SPAN_ID)) { + output.serializeSpanId(LogRecord.SPAN_ID, spanContext.getSpanId(), context); + } + } + + @Override + public int getBinarySerializedSize(LogRecordData log, MarshalerContext context) { + int size = 0; + + size += MarshalerUtil.sizeFixed64(LogRecord.TIME_UNIX_NANO, log.getTimestampEpochNanos()); + size += + MarshalerUtil.sizeFixed64( + LogRecord.OBSERVED_TIME_UNIX_NANO, log.getObservedTimestampEpochNanos()); + size += + MarshalerUtil.sizeEnum(LogRecord.SEVERITY_NUMBER, toProtoSeverityNumber(log.getSeverity())); + size += + StatelessMarshalerUtil.sizeStringWithContext( + LogRecord.SEVERITY_TEXT, log.getSeverityText(), context); + size += + StatelessMarshalerUtil.sizeMessageWithContext( + LogRecord.BODY, log.getBody(), BodyMarshaler.INSTANCE, context); + size += + StatelessMarshalerUtil.sizeRepeatedMessageWithContext( + LogRecord.ATTRIBUTES, + log.getAttributes(), + AttributeKeyValueStatelessMarshaler.INSTANCE, + context); + int droppedAttributesCount = log.getTotalAttributeCount() - log.getAttributes().size(); + size += MarshalerUtil.sizeUInt32(LogRecord.DROPPED_ATTRIBUTES_COUNT, droppedAttributesCount); + + SpanContext spanContext = log.getSpanContext(); + size += MarshalerUtil.sizeFixed32(LogRecord.FLAGS, spanContext.getTraceFlags().asByte()); + if (!spanContext.getTraceId().equals(INVALID_TRACE_ID)) { + size += MarshalerUtil.sizeTraceId(LogRecord.TRACE_ID, spanContext.getTraceId()); + } + if (!spanContext.getSpanId().equals(INVALID_SPAN_ID)) { + size += MarshalerUtil.sizeSpanId(LogRecord.SPAN_ID, spanContext.getSpanId()); + } + + return size; + } + + private static class BodyMarshaler implements StatelessMarshaler { + + private static final BodyMarshaler INSTANCE = new BodyMarshaler(); + private static final AnyValue EMPTY_BODY = AnyValue.of(""); + + private BodyMarshaler() {} + + @Override + public void writeTo(Serializer output, Body value, MarshalerContext context) + throws IOException { + AnyValue anyValue; + if (value instanceof AnyValueBody) { + anyValue = ((AnyValueBody) value).asAnyValue(); + } else { + switch (value.getType()) { + case STRING: + anyValue = context.getData(AnyValue.class); + break; + case EMPTY: + anyValue = EMPTY_BODY; + break; + default: + throw new IllegalStateException("Unsupported Body type: " + value.getType()); + } + } + AnyValueStatelessMarshaler.INSTANCE.writeTo(output, anyValue, context); + } + + @Override + public int getBinarySerializedSize(Body value, MarshalerContext context) { + AnyValue anyValue; + if (value instanceof AnyValueBody) { + anyValue = ((AnyValueBody) value).asAnyValue(); + } else { + switch (value.getType()) { + case STRING: + anyValue = AnyValue.of(value.asString()); + context.addData(anyValue); + break; + case EMPTY: + anyValue = EMPTY_BODY; + break; + default: + throw new IllegalStateException("Unsupported Body type: " + value.getType()); + } + } + return AnyValueStatelessMarshaler.INSTANCE.getBinarySerializedSize(anyValue, context); + } + } +} diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LowAllocationLogsRequestMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LowAllocationLogsRequestMarshaler.java new file mode 100644 index 00000000000..f3bee53f061 --- /dev/null +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LowAllocationLogsRequestMarshaler.java @@ -0,0 +1,107 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.otlp.logs; + +import io.opentelemetry.exporter.internal.marshal.Marshaler; +import io.opentelemetry.exporter.internal.marshal.MarshalerContext; +import io.opentelemetry.exporter.internal.marshal.Serializer; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshalerUtil; +import io.opentelemetry.proto.collector.logs.v1.internal.ExportLogsServiceRequest; +import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.logs.data.LogRecordData; +import io.opentelemetry.sdk.resources.Resource; +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * {@link Marshaler} to convert SDK {@link LogRecordData} to OTLP ExportLogsServiceRequest. See + * {@link LogsRequestMarshaler}. + * + *

Example usage: + * + *

{@code
+ * void marshal(LowAllocationLogRequestMarshaler requestMarshaler, OutputStream output,
+ *     List logDataList) throws IOException {
+ *   requestMarshaler.initialize(logDataList);
+ *   try {
+ *     requestMarshaler.writeBinaryTo(output);
+ *   } finally {
+ *     requestMarshaler.reset();
+ *   }
+ * }
+ * }
+ * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class LowAllocationLogsRequestMarshaler extends Marshaler { + private static final MarshalerContext.Key RESOURCE_LOG_SIZE_CALCULATOR_KEY = + MarshalerContext.key(); + private static final MarshalerContext.Key RESOURCE_LOG_WRITER_KEY = MarshalerContext.key(); + + private final MarshalerContext context = new MarshalerContext(); + + @SuppressWarnings("NullAway") + private Map>> resourceAndScopeMap; + + private int size; + + public void initialize(Collection logDataList) { + resourceAndScopeMap = groupByResourceAndScope(context, logDataList); + size = calculateSize(context, resourceAndScopeMap); + } + + public void reset() { + context.reset(); + } + + @Override + public int getBinarySerializedSize() { + return size; + } + + @Override + public void writeTo(Serializer output) throws IOException { + // serializing can be retried, reset the indexes, so we could call writeTo multiple times + context.resetReadIndex(); + output.serializeRepeatedMessageWithContext( + ExportLogsServiceRequest.RESOURCE_LOGS, + resourceAndScopeMap, + ResourceLogsStatelessMarshaler.INSTANCE, + context, + RESOURCE_LOG_WRITER_KEY); + } + + private static int calculateSize( + MarshalerContext context, + Map>> resourceAndScopeMap) { + return StatelessMarshalerUtil.sizeRepeatedMessageWithContext( + ExportLogsServiceRequest.RESOURCE_LOGS, + resourceAndScopeMap, + ResourceLogsStatelessMarshaler.INSTANCE, + context, + RESOURCE_LOG_SIZE_CALCULATOR_KEY); + } + + private static Map>> + groupByResourceAndScope(MarshalerContext context, Collection logDataList) { + + if (logDataList.isEmpty()) { + return Collections.emptyMap(); + } + + return StatelessMarshalerUtil.groupByResourceAndScope( + logDataList, + // TODO(anuraaga): Replace with an internal SdkData type of interface that exposes these + // two. + LogRecordData::getResource, + LogRecordData::getInstrumentationScopeInfo, + context); + } +} diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/ResourceLogsStatelessMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/ResourceLogsStatelessMarshaler.java new file mode 100644 index 00000000000..ba128c880c7 --- /dev/null +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/ResourceLogsStatelessMarshaler.java @@ -0,0 +1,80 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.otlp.logs; + +import io.opentelemetry.exporter.internal.marshal.MarshalerContext; +import io.opentelemetry.exporter.internal.marshal.MarshalerUtil; +import io.opentelemetry.exporter.internal.marshal.Serializer; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshaler2; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshalerUtil; +import io.opentelemetry.exporter.internal.otlp.ResourceMarshaler; +import io.opentelemetry.proto.logs.v1.internal.ResourceLogs; +import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.logs.data.LogRecordData; +import io.opentelemetry.sdk.resources.Resource; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * A Marshaler of ResourceLogs. See {@link ResourceLogsMarshaler}. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class ResourceLogsStatelessMarshaler + implements StatelessMarshaler2>> { + static final ResourceLogsStatelessMarshaler INSTANCE = new ResourceLogsStatelessMarshaler(); + private static final MarshalerContext.Key SCOPE_LOG_WRITER_KEY = MarshalerContext.key(); + private static final MarshalerContext.Key SCOPE_LOG_SIZE_CALCULATOR_KEY = MarshalerContext.key(); + + @Override + public void writeTo( + Serializer output, + Resource resource, + Map> scopeMap, + MarshalerContext context) + throws IOException { + ResourceMarshaler resourceMarshaler = context.getData(ResourceMarshaler.class); + output.serializeMessage(ResourceLogs.RESOURCE, resourceMarshaler); + + output.serializeRepeatedMessageWithContext( + ResourceLogs.SCOPE_LOGS, + scopeMap, + InstrumentationScopeLogsStatelessMarshaler.INSTANCE, + context, + SCOPE_LOG_WRITER_KEY); + + output.serializeStringWithContext(ResourceLogs.SCHEMA_URL, resource.getSchemaUrl(), context); + } + + @Override + public int getBinarySerializedSize( + Resource resource, + Map> scopeMap, + MarshalerContext context) { + + int size = 0; + + ResourceMarshaler resourceMarshaler = ResourceMarshaler.create(resource); + context.addData(resourceMarshaler); + size += MarshalerUtil.sizeMessage(ResourceLogs.RESOURCE, resourceMarshaler); + + size += + StatelessMarshalerUtil.sizeRepeatedMessageWithContext( + ResourceLogs.SCOPE_LOGS, + scopeMap, + InstrumentationScopeLogsStatelessMarshaler.INSTANCE, + context, + SCOPE_LOG_SIZE_CALCULATOR_KEY); + + size += + StatelessMarshalerUtil.sizeStringWithContext( + ResourceLogs.SCHEMA_URL, resource.getSchemaUrl(), context); + + return size; + } +} diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/traces/SpanEventStatelessMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/traces/SpanEventStatelessMarshaler.java index ef09f065749..9915d3d6757 100644 --- a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/traces/SpanEventStatelessMarshaler.java +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/traces/SpanEventStatelessMarshaler.java @@ -10,7 +10,7 @@ import io.opentelemetry.exporter.internal.marshal.Serializer; import io.opentelemetry.exporter.internal.marshal.StatelessMarshaler; import io.opentelemetry.exporter.internal.marshal.StatelessMarshalerUtil; -import io.opentelemetry.exporter.internal.otlp.KeyValueStatelessMarshaler; +import io.opentelemetry.exporter.internal.otlp.AttributeKeyValueStatelessMarshaler; import io.opentelemetry.proto.trace.v1.internal.Span; import io.opentelemetry.sdk.trace.data.EventData; import java.io.IOException; @@ -25,7 +25,10 @@ public void writeTo(Serializer output, EventData event, MarshalerContext context output.serializeFixed64(Span.Event.TIME_UNIX_NANO, event.getEpochNanos()); output.serializeStringWithContext(Span.Event.NAME, event.getName(), context); output.serializeRepeatedMessageWithContext( - Span.Event.ATTRIBUTES, event.getAttributes(), KeyValueStatelessMarshaler.INSTANCE, context); + Span.Event.ATTRIBUTES, + event.getAttributes(), + AttributeKeyValueStatelessMarshaler.INSTANCE, + context); int droppedAttributesCount = event.getTotalAttributeCount() - event.getAttributes().size(); output.serializeUInt32(Span.Event.DROPPED_ATTRIBUTES_COUNT, droppedAttributesCount); } @@ -39,7 +42,7 @@ public int getBinarySerializedSize(EventData event, MarshalerContext context) { StatelessMarshalerUtil.sizeRepeatedMessageWithContext( Span.Event.ATTRIBUTES, event.getAttributes(), - KeyValueStatelessMarshaler.INSTANCE, + AttributeKeyValueStatelessMarshaler.INSTANCE, context); int droppedAttributesCount = event.getTotalAttributeCount() - event.getAttributes().size(); size += MarshalerUtil.sizeUInt32(Span.Event.DROPPED_ATTRIBUTES_COUNT, droppedAttributesCount); diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/traces/SpanLinkStatelessMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/traces/SpanLinkStatelessMarshaler.java index 2b711d8e96c..aafca37aa03 100644 --- a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/traces/SpanLinkStatelessMarshaler.java +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/traces/SpanLinkStatelessMarshaler.java @@ -12,7 +12,7 @@ import io.opentelemetry.exporter.internal.marshal.Serializer; import io.opentelemetry.exporter.internal.marshal.StatelessMarshaler; import io.opentelemetry.exporter.internal.marshal.StatelessMarshalerUtil; -import io.opentelemetry.exporter.internal.otlp.KeyValueStatelessMarshaler; +import io.opentelemetry.exporter.internal.otlp.AttributeKeyValueStatelessMarshaler; import io.opentelemetry.proto.trace.v1.internal.Span; import io.opentelemetry.sdk.trace.data.LinkData; import java.io.IOException; @@ -28,7 +28,10 @@ public void writeTo(Serializer output, LinkData link, MarshalerContext context) output.serializeSpanId(Span.Link.SPAN_ID, link.getSpanContext().getSpanId(), context); output.serializeString(Span.Link.TRACE_STATE, context.getData(byte[].class)); output.serializeRepeatedMessageWithContext( - Span.Link.ATTRIBUTES, link.getAttributes(), KeyValueStatelessMarshaler.INSTANCE, context); + Span.Link.ATTRIBUTES, + link.getAttributes(), + AttributeKeyValueStatelessMarshaler.INSTANCE, + context); int droppedAttributesCount = link.getTotalAttributeCount() - link.getAttributes().size(); output.serializeUInt32(Span.Link.DROPPED_ATTRIBUTES_COUNT, droppedAttributesCount); output.serializeFixed32( @@ -50,7 +53,7 @@ public int getBinarySerializedSize(LinkData link, MarshalerContext context) { StatelessMarshalerUtil.sizeRepeatedMessageWithContext( Span.Link.ATTRIBUTES, link.getAttributes(), - KeyValueStatelessMarshaler.INSTANCE, + AttributeKeyValueStatelessMarshaler.INSTANCE, context); int droppedAttributesCount = link.getTotalAttributeCount() - link.getAttributes().size(); size += MarshalerUtil.sizeUInt32(Span.Link.DROPPED_ATTRIBUTES_COUNT, droppedAttributesCount); diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/traces/SpanStatelessMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/traces/SpanStatelessMarshaler.java index 80703cf628d..d47f9dda9ad 100644 --- a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/traces/SpanStatelessMarshaler.java +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/traces/SpanStatelessMarshaler.java @@ -13,7 +13,7 @@ import io.opentelemetry.exporter.internal.marshal.Serializer; import io.opentelemetry.exporter.internal.marshal.StatelessMarshaler; import io.opentelemetry.exporter.internal.marshal.StatelessMarshalerUtil; -import io.opentelemetry.exporter.internal.otlp.KeyValueStatelessMarshaler; +import io.opentelemetry.exporter.internal.otlp.AttributeKeyValueStatelessMarshaler; import io.opentelemetry.proto.trace.v1.internal.Span; import io.opentelemetry.sdk.trace.data.SpanData; import java.io.IOException; @@ -41,7 +41,10 @@ public void writeTo(Serializer output, SpanData span, MarshalerContext context) output.serializeFixed64(Span.END_TIME_UNIX_NANO, span.getEndEpochNanos()); output.serializeRepeatedMessageWithContext( - Span.ATTRIBUTES, span.getAttributes(), KeyValueStatelessMarshaler.INSTANCE, context); + Span.ATTRIBUTES, + span.getAttributes(), + AttributeKeyValueStatelessMarshaler.INSTANCE, + context); int droppedAttributesCount = span.getTotalAttributeCount() - span.getAttributes().size(); output.serializeUInt32(Span.DROPPED_ATTRIBUTES_COUNT, droppedAttributesCount); @@ -86,7 +89,10 @@ public int getBinarySerializedSize(SpanData span, MarshalerContext context) { size += StatelessMarshalerUtil.sizeRepeatedMessageWithContext( - Span.ATTRIBUTES, span.getAttributes(), KeyValueStatelessMarshaler.INSTANCE, context); + Span.ATTRIBUTES, + span.getAttributes(), + AttributeKeyValueStatelessMarshaler.INSTANCE, + context); int droppedAttributesCount = span.getTotalAttributeCount() - span.getAttributes().size(); size += MarshalerUtil.sizeUInt32(Span.DROPPED_ATTRIBUTES_COUNT, droppedAttributesCount); diff --git a/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/AnyValueMarshalerTest.java b/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/AnyValueMarshalerTest.java index 9514249defb..cd56af8fd84 100644 --- a/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/AnyValueMarshalerTest.java +++ b/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/AnyValueMarshalerTest.java @@ -15,7 +15,10 @@ import com.google.protobuf.util.JsonFormat; import io.opentelemetry.api.incubator.logs.KeyAnyValue; import io.opentelemetry.exporter.internal.marshal.Marshaler; +import io.opentelemetry.exporter.internal.marshal.MarshalerContext; import io.opentelemetry.exporter.internal.marshal.MarshalerWithSize; +import io.opentelemetry.exporter.internal.marshal.Serializer; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshaler; import io.opentelemetry.proto.common.v1.AnyValue; import io.opentelemetry.proto.common.v1.ArrayValue; import io.opentelemetry.proto.common.v1.KeyValue; @@ -35,13 +38,22 @@ class AnyValueMarshalerTest { @ParameterizedTest @MethodSource("serializeAnyValueArgs") - void anyValueString( + void anyValueString_StatefulMarshaler( io.opentelemetry.api.incubator.logs.AnyValue anyValue, AnyValue expectedSerializedValue) { MarshalerWithSize marshaler = AnyValueMarshaler.create(anyValue); AnyValue serializedValue = parse(AnyValue.getDefaultInstance(), marshaler); assertThat(serializedValue).isEqualTo(expectedSerializedValue); } + @ParameterizedTest + @MethodSource("serializeAnyValueArgs") + void anyValueString_StatelessMarshaler( + io.opentelemetry.api.incubator.logs.AnyValue anyValue, AnyValue expectedSerializedValue) { + Marshaler marshaler = createMarshaler(AnyValueStatelessMarshaler.INSTANCE, anyValue); + AnyValue serializedValue = parse(AnyValue.getDefaultInstance(), marshaler); + assertThat(serializedValue).isEqualTo(expectedSerializedValue); + } + private static Stream serializeAnyValueArgs() { return Stream.of( // primitives @@ -167,4 +179,22 @@ private static String toJson(Marshaler marshaler) { } return new String(bos.toByteArray(), StandardCharsets.UTF_8); } + + private static Marshaler createMarshaler(StatelessMarshaler marshaler, T data) { + return new Marshaler() { + private final MarshalerContext context = new MarshalerContext(); + private final int size = marshaler.getBinarySerializedSize(data, context); + + @Override + public int getBinarySerializedSize() { + return size; + } + + @Override + protected void writeTo(Serializer output) throws IOException { + context.resetReadIndex(); + marshaler.writeTo(output, data, context); + } + }; + } } 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..9c01ddc7aed 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 @@ -21,6 +21,9 @@ import io.opentelemetry.api.trace.TraceId; import io.opentelemetry.api.trace.TraceState; import io.opentelemetry.exporter.internal.marshal.Marshaler; +import io.opentelemetry.exporter.internal.marshal.MarshalerContext; +import io.opentelemetry.exporter.internal.marshal.Serializer; +import io.opentelemetry.exporter.internal.marshal.StatelessMarshaler; import io.opentelemetry.proto.common.v1.AnyValue; import io.opentelemetry.proto.common.v1.InstrumentationScope; import io.opentelemetry.proto.common.v1.KeyValue; @@ -28,6 +31,7 @@ import io.opentelemetry.proto.logs.v1.ResourceLogs; import io.opentelemetry.proto.logs.v1.ScopeLogs; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.testing.logs.TestLogRecordData; import java.io.ByteArrayOutputStream; @@ -39,6 +43,8 @@ import java.util.Locale; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; class LogsRequestMarshalerTest { private static final byte[] TRACE_ID_BYTES = @@ -95,12 +101,13 @@ void toProtoResourceLogs() { .build()); } - @Test - void toProtoLogRecord() { + @ParameterizedTest + @EnumSource(MarshalerSource.class) + void toProtoLogRecord(MarshalerSource marshalerSource) { LogRecord logRecord = parse( LogRecord.getDefaultInstance(), - LogMarshaler.create( + marshalerSource.create( TestLogRecordData.builder() .setResource( Resource.create(Attributes.builder().put("testKey", "testValue").build())) @@ -133,12 +140,13 @@ void toProtoLogRecord() { assertThat(logRecord.getObservedTimeUnixNano()).isEqualTo(6789); } - @Test - void toProtoLogRecord_MinimalFields() { + @ParameterizedTest + @EnumSource(MarshalerSource.class) + void toProtoLogRecord_MinimalFields(MarshalerSource marshalerSource) { LogRecord logRecord = parse( LogRecord.getDefaultInstance(), - LogMarshaler.create( + marshalerSource.create( TestLogRecordData.builder() .setResource( Resource.create(Attributes.builder().put("testKey", "testValue").build())) @@ -153,7 +161,7 @@ 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().build()); assertThat(logRecord.getAttributesList()).isEmpty(); assertThat(logRecord.getDroppedAttributesCount()).isZero(); assertThat(logRecord.getTimeUnixNano()).isEqualTo(12345); @@ -239,4 +247,39 @@ private static String toJson(Marshaler marshaler) { } return new String(bos.toByteArray(), StandardCharsets.UTF_8); } + + private static Marshaler createMarshaler(StatelessMarshaler marshaler, T data) { + return new Marshaler() { + private final MarshalerContext context = new MarshalerContext(); + private final int size = marshaler.getBinarySerializedSize(data, context); + + @Override + public int getBinarySerializedSize() { + return size; + } + + @Override + protected void writeTo(Serializer output) throws IOException { + context.resetReadIndex(); + marshaler.writeTo(output, data, context); + } + }; + } + + private enum MarshalerSource { + MARSHALER { + @Override + Marshaler create(LogRecordData logData) { + return LogMarshaler.create(logData); + } + }, + LOW_ALLOCATION_MARSHALER { + @Override + Marshaler create(LogRecordData logData) { + return createMarshaler(LogStatelessMarshaler.INSTANCE, logData); + } + }; + + abstract Marshaler create(LogRecordData logData); + } } diff --git a/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LowAllocationLogRequestMarshalerTest.java b/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LowAllocationLogRequestMarshalerTest.java new file mode 100644 index 00000000000..4890e02dd66 --- /dev/null +++ b/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LowAllocationLogRequestMarshalerTest.java @@ -0,0 +1,142 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.otlp.logs; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.TraceFlags; +import io.opentelemetry.api.trace.TraceState; +import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.logs.data.LogRecordData; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.testing.logs.TestLogRecordData; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; + +class LowAllocationLogRequestMarshalerTest { + + private static final AttributeKey KEY_BOOL = AttributeKey.booleanKey("key_bool"); + private static final AttributeKey KEY_STRING = AttributeKey.stringKey("key_string"); + private static final AttributeKey KEY_INT = AttributeKey.longKey("key_int"); + private static final AttributeKey KEY_DOUBLE = AttributeKey.doubleKey("key_double"); + private static final AttributeKey> KEY_STRING_ARRAY = + AttributeKey.stringArrayKey("key_string_array"); + private static final AttributeKey> KEY_LONG_ARRAY = + AttributeKey.longArrayKey("key_long_array"); + private static final AttributeKey> KEY_DOUBLE_ARRAY = + AttributeKey.doubleArrayKey("key_double_array"); + private static final AttributeKey> KEY_BOOLEAN_ARRAY = + AttributeKey.booleanArrayKey("key_boolean_array"); + private static final String BODY = "Hello world from this log..."; + + private static final Resource RESOURCE = + Resource.create( + Attributes.builder() + .put(KEY_BOOL, true) + .put(KEY_STRING, "string") + .put(KEY_INT, 100L) + .put(KEY_DOUBLE, 100.3) + .put(KEY_STRING_ARRAY, Arrays.asList("string", "string")) + .put(KEY_LONG_ARRAY, Arrays.asList(12L, 23L)) + .put(KEY_DOUBLE_ARRAY, Arrays.asList(12.3, 23.1)) + .put(KEY_BOOLEAN_ARRAY, Arrays.asList(true, false)) + .build()); + + private static final InstrumentationScopeInfo INSTRUMENTATION_SCOPE_INFO = + InstrumentationScopeInfo.create("name"); + private static final String TRACE_ID = "7b2e170db4df2d593ddb4ddf2ddf2d59"; + private static final String SPAN_ID = "170d3ddb4d23e81f"; + private static final SpanContext SPAN_CONTEXT = + SpanContext.create(TRACE_ID, SPAN_ID, TraceFlags.getSampled(), TraceState.getDefault()); + + private final List logRecordDataList = createLogRecordDataList(); + + private static List createLogRecordDataList() { + List logRecordDataList = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + logRecordDataList.add(createLogRecordData()); + } + return logRecordDataList; + } + + private static LogRecordData createLogRecordData() { + return TestLogRecordData.builder() + .setResource(RESOURCE) + .setInstrumentationScopeInfo(INSTRUMENTATION_SCOPE_INFO) + .setBody(BODY) + .setSeverity(Severity.INFO) + .setSeverityText("INFO") + .setSpanContext(SPAN_CONTEXT) + .setAttributes( + Attributes.builder() + .put(KEY_BOOL, true) + .put(KEY_STRING, "string") + .put(KEY_INT, 100L) + .put(KEY_DOUBLE, 100.3) + .build()) + .setTotalAttributeCount(2) + .setTimestamp(12345, TimeUnit.NANOSECONDS) + .setObservedTimestamp(6789, TimeUnit.NANOSECONDS) + .build(); + } + + @Test + void validateOutput() throws Exception { + byte[] result; + { + LogsRequestMarshaler requestMarshaler = LogsRequestMarshaler.create(logRecordDataList); + ByteArrayOutputStream customOutput = + new ByteArrayOutputStream(requestMarshaler.getBinarySerializedSize()); + requestMarshaler.writeBinaryTo(customOutput); + result = customOutput.toByteArray(); + } + + byte[] lowAllocationResult; + { + LowAllocationLogsRequestMarshaler requestMarshaler = new LowAllocationLogsRequestMarshaler(); + requestMarshaler.initialize(logRecordDataList); + ByteArrayOutputStream customOutput = + new ByteArrayOutputStream(requestMarshaler.getBinarySerializedSize()); + requestMarshaler.writeBinaryTo(customOutput); + lowAllocationResult = customOutput.toByteArray(); + } + + assertThat(lowAllocationResult).isEqualTo(result); + } + + @Test + void validateJsonOutput() throws Exception { + String result; + { + LogsRequestMarshaler requestMarshaler = LogsRequestMarshaler.create(logRecordDataList); + ByteArrayOutputStream customOutput = + new ByteArrayOutputStream(requestMarshaler.getBinarySerializedSize()); + requestMarshaler.writeJsonTo(customOutput); + result = new String(customOutput.toByteArray(), StandardCharsets.UTF_8); + } + + String lowAllocationResult; + { + LowAllocationLogsRequestMarshaler requestMarshaler = new LowAllocationLogsRequestMarshaler(); + requestMarshaler.initialize(logRecordDataList); + ByteArrayOutputStream customOutput = + new ByteArrayOutputStream(requestMarshaler.getBinarySerializedSize()); + requestMarshaler.writeJsonTo(customOutput); + lowAllocationResult = new String(customOutput.toByteArray(), StandardCharsets.UTF_8); + } + + assertThat(lowAllocationResult).isEqualTo(result); + } +} From 613e31ca20128481b8f0d3265f15ee03bcca9a05 Mon Sep 17 00:00:00 2001 From: Jack Berg Date: Fri, 3 May 2024 15:44:44 -0500 Subject: [PATCH 2/2] Add benchmark --- .../otlp/LogsRequestMarshalerBenchmark.java | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 exporters/otlp/common/src/jmh/java/io/opentelemetry/exporter/internal/otlp/LogsRequestMarshalerBenchmark.java diff --git a/exporters/otlp/common/src/jmh/java/io/opentelemetry/exporter/internal/otlp/LogsRequestMarshalerBenchmark.java b/exporters/otlp/common/src/jmh/java/io/opentelemetry/exporter/internal/otlp/LogsRequestMarshalerBenchmark.java new file mode 100644 index 00000000000..289f6f77845 --- /dev/null +++ b/exporters/otlp/common/src/jmh/java/io/opentelemetry/exporter/internal/otlp/LogsRequestMarshalerBenchmark.java @@ -0,0 +1,148 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.otlp; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.incubator.events.EventLogger; +import io.opentelemetry.api.incubator.logs.AnyValue; +import io.opentelemetry.api.logs.Logger; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.exporter.internal.otlp.logs.LogsRequestMarshaler; +import io.opentelemetry.exporter.internal.otlp.logs.LowAllocationLogsRequestMarshaler; +import io.opentelemetry.sdk.logs.SdkLoggerProvider; +import io.opentelemetry.sdk.logs.data.LogRecordData; +import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor; +import io.opentelemetry.sdk.logs.internal.SdkEventLoggerProvider; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Warmup; + +@BenchmarkMode({Mode.AverageTime}) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 10, time = 1) +@Fork(1) +public class LogsRequestMarshalerBenchmark { + + private static final Collection LOGS; + private static final LowAllocationLogsRequestMarshaler MARSHALER = + new LowAllocationLogsRequestMarshaler(); + private static final TestOutputStream OUTPUT = new TestOutputStream(); + + static { + InMemoryLogRecordExporter logRecordExporter = InMemoryLogRecordExporter.create(); + SdkLoggerProvider loggerProvider = + SdkLoggerProvider.builder() + .setResource( + Resource.create( + Attributes.builder() + .put(AttributeKey.booleanKey("key_bool"), true) + .put(AttributeKey.stringKey("key_string"), "string") + .put(AttributeKey.longKey("key_int"), 100L) + .put(AttributeKey.doubleKey("key_double"), 100.3) + .put( + AttributeKey.stringArrayKey("key_string_array"), + Arrays.asList("string", "string")) + .put(AttributeKey.longArrayKey("key_long_array"), Arrays.asList(12L, 23L)) + .put( + AttributeKey.doubleArrayKey("key_double_array"), + Arrays.asList(12.3, 23.1)) + .put( + AttributeKey.booleanArrayKey("key_boolean_array"), + Arrays.asList(true, false)) + .build())) + .addLogRecordProcessor(SimpleLogRecordProcessor.create(logRecordExporter)) + .build(); + + Logger logger1 = loggerProvider.get("logger"); + logger1 + .logRecordBuilder() + .setBody("Hello world from this log...") + .setAllAttributes( + Attributes.builder() + .put("key_bool", true) + .put("key_String", "string") + .put("key_int", 100L) + .put("key_double", 100.3) + .build()) + .setSeverity(Severity.INFO) + .setSeverityText("INFO") + .emit(); + + SdkEventLoggerProvider eventLoggerProvider = SdkEventLoggerProvider.create(loggerProvider); + EventLogger eventLogger = eventLoggerProvider.get("event-logger"); + eventLogger + .builder("namespace.my-event-name") + // Helper methods to set primitive types + .put("stringKey", "value") + .put("longKey", 1L) + .put("doubleKey", 1.0) + .put("boolKey", true) + // Helper methods to set primitive array types + .put("stringArrKey", "value1", "value2") + .put("longArrKey", 1L, 2L) + .put("doubleArrKey", 1.0, 2.0) + .put("boolArrKey", true, false) + // Set AnyValue types to encode complex data + .put( + "anyValueKey", AnyValue.of(Collections.singletonMap("childKey1", AnyValue.of("value")))) + .emit(); + + LOGS = logRecordExporter.getFinishedLogRecordItems(); + } + + @Benchmark + public int marshalStateful() throws IOException { + LogsRequestMarshaler marshaler = LogsRequestMarshaler.create(LOGS); + OUTPUT.reset(); + marshaler.writeBinaryTo(OUTPUT); + return OUTPUT.getCount(); + } + + @Benchmark + public int marshalStatefulJson() throws IOException { + LogsRequestMarshaler marshaler = LogsRequestMarshaler.create(LOGS); + OUTPUT.reset(); + marshaler.writeJsonTo(OUTPUT); + return OUTPUT.getCount(); + } + + @Benchmark + public int marshalStateless() throws IOException { + MARSHALER.initialize(LOGS); + try { + OUTPUT.reset(); + MARSHALER.writeBinaryTo(OUTPUT); + return OUTPUT.getCount(); + } finally { + MARSHALER.reset(); + } + } + + @Benchmark + public int marshalStatelessJson() throws IOException { + MARSHALER.initialize(LOGS); + try { + OUTPUT.reset(); + MARSHALER.writeJsonTo(OUTPUT); + return OUTPUT.getCount(); + } finally { + MARSHALER.reset(); + } + } +}