Skip to content

Commit f056298

Browse files
committed
Reboot the entity prototype
- Create Entity/EntityBuilder class in internal package - Update serialization code Need to discuss how to work with Resource going forward.
1 parent cc2844d commit f056298

5 files changed

Lines changed: 339 additions & 5 deletions

File tree

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.exporter.internal.otlp;
7+
8+
import io.opentelemetry.api.internal.StringUtils;
9+
import io.opentelemetry.exporter.internal.marshal.MarshalerUtil;
10+
import io.opentelemetry.exporter.internal.marshal.MarshalerWithSize;
11+
import io.opentelemetry.exporter.internal.marshal.Serializer;
12+
import io.opentelemetry.proto.common.v1.internal.EntityRef;
13+
import io.opentelemetry.sdk.resources.internal.Entity;
14+
import java.io.IOException;
15+
import java.nio.charset.StandardCharsets;
16+
import javax.annotation.Nullable;
17+
18+
/**
19+
* A Marshaler of {@link io.opentelemetry.sdk.resources.Entity}.
20+
*
21+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
22+
* at any time.
23+
*/
24+
public final class EntityRefMarshaler extends MarshalerWithSize {
25+
@Nullable private final byte[] schemaUrlUtf8;
26+
private final byte[] typeUtf8;
27+
private final byte[][] idKeysUtf8;
28+
private final byte[][] descriptionKeysUtf8;
29+
30+
@Override
31+
protected void writeTo(Serializer output) throws IOException {
32+
if (schemaUrlUtf8 != null) {
33+
output.writeString(EntityRef.SCHEMA_URL, schemaUrlUtf8);
34+
}
35+
output.writeString(EntityRef.TYPE, typeUtf8);
36+
output.writeRepeatedString(EntityRef.ID_KEYS, idKeysUtf8);
37+
output.writeRepeatedString(EntityRef.DESCRIPTION_KEYS, descriptionKeysUtf8);
38+
}
39+
40+
/** Consttructs an entity reference marshaler from a full entity. */
41+
public static EntityRefMarshaler createForEntity(Entity e) {
42+
byte[] schemaUrlUtf8 = null;
43+
if (!StringUtils.isNullOrEmpty(e.getSchemaUrl())) {
44+
schemaUrlUtf8 = e.getSchemaUrl().getBytes(StandardCharsets.UTF_8);
45+
}
46+
return new EntityRefMarshaler(
47+
schemaUrlUtf8,
48+
e.getType().getBytes(StandardCharsets.UTF_8),
49+
e.getIdentifyingAttributes().asMap().keySet().stream()
50+
.map(key -> key.getKey().getBytes(StandardCharsets.UTF_8))
51+
.toArray(byte[][]::new),
52+
e.getAttributes().asMap().keySet().stream()
53+
.map(key -> key.getKey().getBytes(StandardCharsets.UTF_8))
54+
.toArray(byte[][]::new));
55+
}
56+
57+
private EntityRefMarshaler(
58+
@Nullable byte[] schemaUrlUtf8,
59+
byte[] typeUtf8,
60+
byte[][] idKeysUtf8,
61+
byte[][] descriptionKeysUtf8) {
62+
super(calculateSize(schemaUrlUtf8, typeUtf8, idKeysUtf8, descriptionKeysUtf8));
63+
this.schemaUrlUtf8 = schemaUrlUtf8;
64+
this.typeUtf8 = typeUtf8;
65+
this.idKeysUtf8 = idKeysUtf8;
66+
this.descriptionKeysUtf8 = descriptionKeysUtf8;
67+
}
68+
69+
private static int calculateSize(
70+
@Nullable byte[] schemaUrlUtf8,
71+
byte[] typeUtf8,
72+
byte[][] idKeysUtf8,
73+
byte[][] descriptionKeysUtf8) {
74+
int size = 0;
75+
if (schemaUrlUtf8 != null) {
76+
size += MarshalerUtil.sizeBytes(EntityRef.SCHEMA_URL, schemaUrlUtf8);
77+
}
78+
size += MarshalerUtil.sizeBytes(EntityRef.TYPE, typeUtf8);
79+
MarshalerUtil.sizeRepeatedString(EntityRef.ID_KEYS, idKeysUtf8);
80+
MarshalerUtil.sizeRepeatedString(EntityRef.DESCRIPTION_KEYS, descriptionKeysUtf8);
81+
return size;
82+
}
83+
}

exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/ResourceMarshaler.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ public static ResourceMarshaler create(io.opentelemetry.sdk.resources.Resource r
3737

3838
RealResourceMarshaler realMarshaler =
3939
new RealResourceMarshaler(
40-
KeyValueMarshaler.createForAttributes(resource.getAttributes()));
40+
KeyValueMarshaler.createForAttributes(resource.getAttributes()),
41+
// TODO(jsuereth): This will support EntityRef in the future.
42+
new EntityRefMarshaler[] {});
4143

4244
ByteArrayOutputStream binaryBos =
4345
new ByteArrayOutputStream(realMarshaler.getBinarySerializedSize());
@@ -70,19 +72,26 @@ public void writeTo(Serializer output) throws IOException {
7072

7173
private static final class RealResourceMarshaler extends MarshalerWithSize {
7274
private final KeyValueMarshaler[] attributes;
75+
private final MarshalerWithSize[] entityRefs;
7376

74-
private RealResourceMarshaler(KeyValueMarshaler[] attributes) {
75-
super(calculateSize(attributes));
77+
private RealResourceMarshaler(KeyValueMarshaler[] attributes, MarshalerWithSize[] entityRefs) {
78+
super(calculateSize(attributes, entityRefs));
7679
this.attributes = attributes;
80+
this.entityRefs = entityRefs;
7781
}
7882

7983
@Override
8084
protected void writeTo(Serializer output) throws IOException {
8185
output.serializeRepeatedMessage(Resource.ATTRIBUTES, attributes);
86+
output.serializeRepeatedMessage(Resource.ENTITY_REFS, entityRefs);
8287
}
8388

84-
private static int calculateSize(KeyValueMarshaler[] attributeMarshalers) {
85-
return MarshalerUtil.sizeRepeatedMessage(Resource.ATTRIBUTES, attributeMarshalers);
89+
private static int calculateSize(
90+
KeyValueMarshaler[] attributeMarshalers, MarshalerWithSize[] entityRefs) {
91+
int size = 0;
92+
size += MarshalerUtil.sizeRepeatedMessage(Resource.ATTRIBUTES, attributeMarshalers);
93+
size += size += MarshalerUtil.sizeRepeatedMessage(Resource.ENTITY_REFS, entityRefs);
94+
return size;
8695
}
8796
}
8897
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.exporter.internal.otlp;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
10+
import com.google.protobuf.InvalidProtocolBufferException;
11+
import com.google.protobuf.Message;
12+
import com.google.protobuf.util.JsonFormat;
13+
import io.opentelemetry.exporter.internal.marshal.Marshaler;
14+
import io.opentelemetry.proto.common.v1.EntityRef;
15+
import io.opentelemetry.sdk.resources.internal.Entity;
16+
import java.io.ByteArrayOutputStream;
17+
import java.io.IOException;
18+
import java.io.UncheckedIOException;
19+
import java.nio.charset.StandardCharsets;
20+
import org.junit.jupiter.api.Test;
21+
22+
class EntityRefMarshalerTest {
23+
@Test
24+
void toEntityRefs() {
25+
Entity e =
26+
Entity.builder("test")
27+
.setSchemaUrl("test-url")
28+
.withDescriptive(attr -> attr.put("desc.key", "desc.value"))
29+
.withIdentifying(attr -> attr.put("id.key", "id.value"))
30+
.build();
31+
EntityRef proto = parse(EntityRef.getDefaultInstance(), EntityRefMarshaler.createForEntity(e));
32+
assertThat(proto.getType()).isEqualTo("test");
33+
assertThat(proto.getSchemaUrl()).isEqualTo("test-url");
34+
assertThat(proto.getIdKeysList()).containsExactly("id.key");
35+
assertThat(proto.getDescriptionKeysList()).containsExactly("desc.key");
36+
}
37+
38+
@SuppressWarnings("unchecked")
39+
private static <T extends Message> T parse(T prototype, Marshaler marshaler) {
40+
byte[] serialized = toByteArray(marshaler);
41+
T result;
42+
try {
43+
result = (T) prototype.newBuilderForType().mergeFrom(serialized).build();
44+
} catch (InvalidProtocolBufferException e) {
45+
throw new UncheckedIOException(e);
46+
}
47+
// Our marshaler should produce the exact same length of serialized output (for example, field
48+
// default values are not outputted), so we check that here. The output itself may have slightly
49+
// different ordering, mostly due to the way we don't output oneof values in field order all the
50+
// tieme. If the lengths are equal and the resulting protos are equal, the marshaling is
51+
// guaranteed to be valid.
52+
assertThat(result.getSerializedSize()).isEqualTo(serialized.length);
53+
54+
// Compare JSON
55+
String json = toJson(marshaler);
56+
Message.Builder builder = prototype.newBuilderForType();
57+
try {
58+
JsonFormat.parser().merge(json, builder);
59+
} catch (InvalidProtocolBufferException e) {
60+
throw new UncheckedIOException(e);
61+
}
62+
assertThat(builder.build()).isEqualTo(result);
63+
64+
return result;
65+
}
66+
67+
private static byte[] toByteArray(Marshaler marshaler) {
68+
ByteArrayOutputStream bos = new ByteArrayOutputStream();
69+
try {
70+
marshaler.writeBinaryTo(bos);
71+
} catch (IOException e) {
72+
throw new UncheckedIOException(e);
73+
}
74+
return bos.toByteArray();
75+
}
76+
77+
private static String toJson(Marshaler marshaler) {
78+
ByteArrayOutputStream bos = new ByteArrayOutputStream();
79+
try {
80+
marshaler.writeJsonTo(bos);
81+
} catch (IOException e) {
82+
throw new UncheckedIOException(e);
83+
}
84+
return new String(bos.toByteArray(), StandardCharsets.UTF_8);
85+
}
86+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.sdk.resources.internal;
7+
8+
import com.google.auto.value.AutoValue;
9+
import io.opentelemetry.api.common.Attributes;
10+
import javax.annotation.Nullable;
11+
import javax.annotation.concurrent.Immutable;
12+
13+
/**
14+
* Entity represents an object of interest associated with produced telemetry: traces, metrics or
15+
* logs.
16+
*
17+
* <p>For example, telemetry produced using OpenTelemetry SDK is normally associated with a Service
18+
* entity. Similarly, OpenTelemetry defines system metrics for a host. The Host is the entity we
19+
* want to associate metrics with in this case.
20+
*
21+
* <p>Entities may be also associated with produced telemetry indirectly. For example a service that
22+
* produces telemetry is also related with a process in which the service runs, so we say that the
23+
* Service entity is related to the Process entity. The process normally also runs on a host, so we
24+
* say that the Process entity is related to the Host entity.
25+
*
26+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
27+
* at any time.
28+
*/
29+
@Immutable
30+
@AutoValue
31+
public abstract class Entity {
32+
/**
33+
* Returns the entity type string of this entity. Must not be null.
34+
*
35+
* @return the entity type.
36+
*/
37+
public abstract String getType();
38+
39+
/**
40+
* Returns a map of attributes that identify the entity.
41+
*
42+
* @return a map of attributes.
43+
*/
44+
public abstract Attributes getIdentifyingAttributes();
45+
46+
/**
47+
* Returns a map of attributes that describe the entity.
48+
*
49+
* @return a map of attributes.
50+
*/
51+
public abstract Attributes getAttributes();
52+
53+
/**
54+
* Returns the URL of the OpenTelemetry schema used by this resource. May be null if this entity
55+
* does not abide by schema conventions (i.e. is custom).
56+
*
57+
* @return An OpenTelemetry schema URL.
58+
* @since 1.4.0
59+
*/
60+
@Nullable
61+
public abstract String getSchemaUrl();
62+
63+
static final Entity create(
64+
String entityType,
65+
Attributes identifying,
66+
Attributes descriptive,
67+
@Nullable String schemaUrl) {
68+
return new AutoValue_Entity(entityType, identifying, descriptive, schemaUrl);
69+
}
70+
71+
public final EntityBuilder toBuilder() {
72+
return new EntityBuilder(this);
73+
}
74+
75+
public static final EntityBuilder builder(String entityType) {
76+
return new EntityBuilder(entityType);
77+
}
78+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.sdk.resources.internal;
7+
8+
import io.opentelemetry.api.common.Attributes;
9+
import io.opentelemetry.api.common.AttributesBuilder;
10+
import java.util.function.Consumer;
11+
import javax.annotation.Nullable;
12+
13+
/**
14+
* A builder of {@link Entity} that allows to add identifying or descriptive {@link Attributes}, as
15+
* well as type and schema_url.
16+
*
17+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
18+
* at any time.
19+
*/
20+
public class EntityBuilder {
21+
private final String entityType;
22+
private final AttributesBuilder attributesBuilder;
23+
private final AttributesBuilder identifyingBuilder;
24+
@Nullable private String schemaUrl;
25+
26+
EntityBuilder(String entityType) {
27+
this.entityType = entityType;
28+
this.attributesBuilder = Attributes.builder();
29+
this.identifyingBuilder = Attributes.builder();
30+
}
31+
32+
EntityBuilder(Entity seed) {
33+
this.entityType = seed.getType();
34+
this.schemaUrl = seed.getSchemaUrl();
35+
this.identifyingBuilder = seed.getIdentifyingAttributes().toBuilder();
36+
this.attributesBuilder = seed.getAttributes().toBuilder();
37+
}
38+
39+
/**
40+
* Assign an OpenTelemetry schema URL to the resulting Entity.
41+
*
42+
* @param schemaUrl The URL of the OpenTelemetry schema being used to create this Entity.
43+
* @return this
44+
*/
45+
public EntityBuilder setSchemaUrl(String schemaUrl) {
46+
this.schemaUrl = schemaUrl;
47+
return this;
48+
}
49+
50+
/**
51+
* Modify the descriptive attributes of this Entity.
52+
*
53+
* @param f A thunk which manipulates descriptive attributes.
54+
* @return this
55+
*/
56+
public EntityBuilder withDescriptive(Consumer<AttributesBuilder> f) {
57+
f.accept(this.attributesBuilder);
58+
return this;
59+
}
60+
61+
/**
62+
* Modify the identifying attributes of this Entity.
63+
*
64+
* @param f A thunk which manipulates identifying attributes.
65+
* @return this
66+
*/
67+
public EntityBuilder withIdentifying(Consumer<AttributesBuilder> f) {
68+
f.accept(this.identifyingBuilder);
69+
return this;
70+
}
71+
72+
/** Create the {@link Entity} from this. */
73+
public Entity build() {
74+
// TODO - Better Checks, e.g. identifying attributes are non-zero.
75+
return Entity.create(
76+
entityType, identifyingBuilder.build(), attributesBuilder.build(), schemaUrl);
77+
}
78+
}

0 commit comments

Comments
 (0)