Experimental support for Log AnyValue body (#5880)
This commit is contained in:
parent
ccb2e04237
commit
efa46a5dcc
|
|
@ -41,7 +41,7 @@ final class LogMarshaler extends MarshalerWithSize {
|
||||||
KeyValueMarshaler[] attributeMarshalers =
|
KeyValueMarshaler[] attributeMarshalers =
|
||||||
KeyValueMarshaler.createRepeated(logRecordData.getAttributes());
|
KeyValueMarshaler.createRepeated(logRecordData.getAttributes());
|
||||||
|
|
||||||
// For now, map all the bodies to String AnyValue.
|
// TODO(jack-berg): handle AnyValue log body
|
||||||
StringAnyValueMarshaler anyValueMarshaler =
|
StringAnyValueMarshaler anyValueMarshaler =
|
||||||
new StringAnyValueMarshaler(MarshalerUtil.toBytes(logRecordData.getBody().asString()));
|
new StringAnyValueMarshaler(MarshalerUtil.toBytes(logRecordData.getBody().asString()));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,5 +12,7 @@ otelJava.moduleName.set("io.opentelemetry.extension.incubator")
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":api:all"))
|
api(project(":api:all"))
|
||||||
|
|
||||||
|
annotationProcessor("com.google.auto.value:auto-value")
|
||||||
|
|
||||||
testImplementation(project(":sdk:testing"))
|
testImplementation(project(":sdk:testing"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.extension.incubator.logs;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
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<ByteBuffer> 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 read only {@link ByteBuffer}. See {@link
|
||||||
|
* ByteBuffer#asReadOnlyBuffer()}.
|
||||||
|
* </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();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* 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 must not be null");
|
||||||
|
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() + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.extension.incubator.logs;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
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() + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
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<ByteBuffer> {
|
||||||
|
|
||||||
|
private final byte[] raw;
|
||||||
|
|
||||||
|
private AnyValueBytes(byte[] value) {
|
||||||
|
this.raw = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AnyValue<ByteBuffer> create(byte[] value) {
|
||||||
|
Objects.requireNonNull(value, "value must not be null");
|
||||||
|
return new AnyValueBytes(Arrays.copyOf(value, value.length));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnyValueType getType() {
|
||||||
|
return AnyValueType.BYTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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[raw.length * 2];
|
||||||
|
OtelEncodingUtils.bytesToBase16(raw, arr, raw.length);
|
||||||
|
return new String(arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "AnyValueBytes{" + asString() + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return (o instanceof AnyValueBytes) && Arrays.equals(this.raw, ((AnyValueBytes) o).raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Arrays.hashCode(raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.extension.incubator.logs;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
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.DOUBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Double getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String asString() {
|
||||||
|
return String.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.extension.incubator.logs;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
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() + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* 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 must not be null");
|
||||||
|
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 + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
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,
|
||||||
|
LONG,
|
||||||
|
DOUBLE,
|
||||||
|
ARRAY,
|
||||||
|
KEY_VALUE_LIST,
|
||||||
|
BYTES
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.extension.incubator.logs;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* 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 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 must not be null");
|
||||||
|
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(item -> item.getKey() + "=" + item.getAnyValue().asString())
|
||||||
|
.collect(joining(", ", "[", "]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,223 @@
|
||||||
|
/*
|
||||||
|
* 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.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
|
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);
|
||||||
|
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'}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@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) {
|
||||||
|
assertThat(value.asString()).isEqualTo(expectedAsString);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("DoubleBraceInitialization")
|
||||||
|
private static Stream<Arguments> asStringArgs() {
|
||||||
|
return Stream.of(
|
||||||
|
// primitives
|
||||||
|
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.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.of(
|
||||||
|
KeyAnyValue.of("key1", AnyValue.of("val1")),
|
||||||
|
KeyAnyValue.of("key2", AnyValue.of(2))),
|
||||||
|
"[key1=val1, key2=2]"),
|
||||||
|
// key value list from map
|
||||||
|
arguments(
|
||||||
|
AnyValue.of(
|
||||||
|
new LinkedHashMap<String, AnyValue<?>>() {
|
||||||
|
{
|
||||||
|
put("key1", AnyValue.of("val1"));
|
||||||
|
put("key2", AnyValue.of(2));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"[key1=val1, key2=2]"),
|
||||||
|
// map of map
|
||||||
|
arguments(
|
||||||
|
AnyValue.of(
|
||||||
|
Collections.singletonMap(
|
||||||
|
"child",
|
||||||
|
AnyValue.of(Collections.singletonMap("grandchild", AnyValue.of("str"))))),
|
||||||
|
"[child=[grandchild=str]]"),
|
||||||
|
// bytes
|
||||||
|
arguments(
|
||||||
|
AnyValue.of("hello world".getBytes(StandardCharsets.UTF_8)), "68656c6c6f20776f726c64"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void anyValueByteAsString() {
|
||||||
|
// TODO: add more test cases
|
||||||
|
String str = "hello world";
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ otelJava.moduleName.set("io.opentelemetry.sdk.logs")
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":api:all"))
|
api(project(":api:all"))
|
||||||
api(project(":sdk:common"))
|
api(project(":sdk:common"))
|
||||||
|
implementation(project(":extensions:incubator"))
|
||||||
|
|
||||||
implementation(project(":api:events"))
|
implementation(project(":api:events"))
|
||||||
|
|
||||||
|
|
@ -20,4 +21,5 @@ dependencies {
|
||||||
testImplementation(project(":sdk:testing"))
|
testImplementation(project(":sdk:testing"))
|
||||||
|
|
||||||
testImplementation("org.awaitility:awaitility")
|
testImplementation("org.awaitility:awaitility")
|
||||||
|
testImplementation("com.google.guava:guava")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,18 @@ import io.opentelemetry.api.logs.LogRecordBuilder;
|
||||||
import io.opentelemetry.api.logs.Severity;
|
import io.opentelemetry.api.logs.Severity;
|
||||||
import io.opentelemetry.api.trace.Span;
|
import io.opentelemetry.api.trace.Span;
|
||||||
import io.opentelemetry.context.Context;
|
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.common.InstrumentationScopeInfo;
|
||||||
import io.opentelemetry.sdk.internal.AttributesMap;
|
import io.opentelemetry.sdk.internal.AttributesMap;
|
||||||
import io.opentelemetry.sdk.logs.data.Body;
|
import io.opentelemetry.sdk.logs.data.Body;
|
||||||
|
import io.opentelemetry.sdk.logs.internal.AnyValueBody;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/** SDK implementation of {@link LogRecordBuilder}. */
|
/** SDK implementation of {@link LogRecordBuilder}. */
|
||||||
final class SdkLogRecordBuilder implements LogRecordBuilder {
|
final class SdkLogRecordBuilder implements ExtendedLogRecordBuilder {
|
||||||
|
|
||||||
private final LoggerSharedState loggerSharedState;
|
private final LoggerSharedState loggerSharedState;
|
||||||
private final LogLimits logLimits;
|
private final LogLimits logLimits;
|
||||||
|
|
@ -89,6 +92,12 @@ final class SdkLogRecordBuilder implements LogRecordBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LogRecordBuilder setBody(AnyValue<?> value) {
|
||||||
|
this.body = AnyValueBody.create(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> SdkLogRecordBuilder setAttribute(AttributeKey<T> key, T value) {
|
public <T> SdkLogRecordBuilder setAttribute(AttributeKey<T> key, T value) {
|
||||||
if (key == null || key.getKey().isEmpty() || value == null) {
|
if (key == null || key.getKey().isEmpty() || value == null) {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ public interface Body {
|
||||||
enum Type {
|
enum Type {
|
||||||
EMPTY,
|
EMPTY,
|
||||||
STRING
|
STRING
|
||||||
|
// TODO (jack-berg): Add ANY_VALUE type when API for setting body to AnyValue is stable
|
||||||
|
// ANY_VALUE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* 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.STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String asString() {
|
||||||
|
return value.asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnyValue<?> asAnyValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "AnyValueBody{" + asString() + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
/*
|
||||||
|
* 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.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;
|
||||||
|
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.of(1)).emit();
|
||||||
|
assertThat(exporter.getFinishedLogRecordItems())
|
||||||
|
.hasSize(1)
|
||||||
|
.satisfiesExactly(
|
||||||
|
logRecordData -> {
|
||||||
|
// 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()).asAnyValue())
|
||||||
|
.isEqualTo(AnyValue.of(1));
|
||||||
|
});
|
||||||
|
exporter.reset();
|
||||||
|
|
||||||
|
// ...or a byte array of raw data
|
||||||
|
extendedLogRecordBuilder(logger)
|
||||||
|
.setBody(AnyValue.of("hello world".getBytes(StandardCharsets.UTF_8)))
|
||||||
|
.emit();
|
||||||
|
assertThat(exporter.getFinishedLogRecordItems())
|
||||||
|
.hasSize(1)
|
||||||
|
.satisfiesExactly(
|
||||||
|
logRecordData -> {
|
||||||
|
// 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()).asAnyValue())
|
||||||
|
.isEqualTo(AnyValue.of("hello world".getBytes(StandardCharsets.UTF_8)));
|
||||||
|
});
|
||||||
|
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.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"));
|
||||||
|
}
|
||||||
|
}))))
|
||||||
|
.emit();
|
||||||
|
assertThat(exporter.getFinishedLogRecordItems())
|
||||||
|
.hasSize(1)
|
||||||
|
.satisfiesExactly(
|
||||||
|
logRecordData -> {
|
||||||
|
// 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(
|
||||||
|
"["
|
||||||
|
+ "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]"
|
||||||
|
+ "]");
|
||||||
|
assertThat(((AnyValueBody) logRecordData.getBody()).asAnyValue())
|
||||||
|
.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();
|
||||||
|
|
||||||
|
// ..or an array (optionally with heterogeneous types)
|
||||||
|
extendedLogRecordBuilder(logger)
|
||||||
|
.setBody(AnyValue.of(AnyValue.of("entry1"), AnyValue.of("entry2"), AnyValue.of(3)))
|
||||||
|
.emit();
|
||||||
|
assertThat(exporter.getFinishedLogRecordItems())
|
||||||
|
.hasSize(1)
|
||||||
|
.satisfiesExactly(
|
||||||
|
logRecordData -> {
|
||||||
|
// 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()).asAnyValue())
|
||||||
|
.isEqualTo(
|
||||||
|
AnyValue.of(AnyValue.of("entry1"), AnyValue.of("entry2"), AnyValue.of(3)));
|
||||||
|
});
|
||||||
|
exporter.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
ExtendedLogRecordBuilder extendedLogRecordBuilder(Logger logger) {
|
||||||
|
return (ExtendedLogRecordBuilder) logger.logRecordBuilder();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue