Skip to content
Draft
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f056298
Reboot the entity prototype
jsuereth Jun 18, 2025
5460d5e
Add base entity class, merge logic and test
jsuereth Jun 19, 2025
ce0e66d
Move Entity and EntityBuilder to be pure interfaces
jsuereth Jun 19, 2025
cb74286
Add more merge helper methods and tests
jsuereth Jun 19, 2025
3369be3
Added test for merging raw attributes
jsuereth Jun 19, 2025
2c4aaeb
Cleanups and reverting silly changes
jsuereth Jun 19, 2025
8af2cd1
fix minor style issue
jsuereth Jun 19, 2025
da525bd
Actually wire entities into resource
jsuereth Jun 20, 2025
b119561
Fix typo
jsuereth Jun 20, 2025
ddf096d
Remove all publicly accessible references to Entity from stable packages
jsuereth Jun 20, 2025
9c222dd
Add incubating EntityProvider API for specification work
jsuereth Jun 20, 2025
2871ff0
Fix refactoring that broke reflection.
jsuereth Jun 21, 2025
34b6f60
Fix schema url and test, now that we proved it works
jsuereth Jun 21, 2025
0711350
Initial cut at moving Entity SDK to have correpsonding API.
jsuereth Jul 5, 2025
ebf9557
Further simplify the API and SDK for entities
jsuereth Jul 5, 2025
73ef00a
Wire ResourceProvider in end-to-end test with new incubating Extended…
jsuereth Jul 6, 2025
8d4288e
Fix javadoc issues
jsuereth Jul 6, 2025
f22ec8d
Update exporters/otlp/common/src/main/java/io/opentelemetry/exporter/…
jsuereth Jul 10, 2025
d220005
Update sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProv…
jsuereth Jul 10, 2025
f9dcb52
Fixes from review.
jsuereth Jul 10, 2025
18afb60
Remove methods we don't need yet.
jsuereth Jul 10, 2025
7ace665
Remove redundant visibility.
jsuereth Jul 10, 2025
d5219d1
More cleanups from review.
jsuereth Jul 10, 2025
cba0606
Simplify prototype of SdkResource.
jsuereth Jul 10, 2025
a38a544
Fix typo.
jsuereth Jul 10, 2025
d01f5a3
More cleanups from review.
jsuereth Jul 10, 2025
2560c51
Add more tests for ResourceProvider.
jsuereth Jul 11, 2025
bf72978
Fix stylecheck.
jsuereth Jul 11, 2025
af3bbd9
More checkstyle fixes.
jsuereth Jul 11, 2025
2809916
Rename prototype to more closely match OTEP 4316.
jsuereth Jul 11, 2025
912ba24
Finish remove implementation and listeners.
jsuereth Jul 11, 2025
634c4a3
Remove assertjs helpers, update javadoc.
jsuereth Jul 11, 2025
5c126c9
Fix entity javadoc.
jsuereth Jul 11, 2025
7a7c426
Fix tests.
jsuereth Jul 11, 2025
8ad0e5f
Tweak incubator SDK to support asynchronous configuratio and use Enti…
jsuereth Aug 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.incubator.entities;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import java.util.function.Consumer;

/**
* A builder of an Entity that allows to add identifying or descriptive {@link Attributes}, as well
* as type and schema_url.
*
* <p>Entity represents an object of interest associated with produced telemetry: traces, metrics or
* logs.
*
* <p>For example, telemetry produced using OpenTelemetry SDK is normally associated with a Service
* entity. Similarly, OpenTelemetry defines system metrics for a host. The Host is the entity we
* want to associate metrics with in this case.
*
* <p>Entities may be also associated with produced telemetry indirectly. For example a service that
* produces telemetry is also related with a process in which the service runs, so we say that the
* Service entity is related to the Process entity. The process normally also runs on a host, so we
* say that the Process entity is related to the Host entity.
*/
public interface EntityBuilder {
/**
* Assign an OpenTelemetry schema URL to the resulting Entity.
*
* @param schemaUrl The URL of the OpenTelemetry schema being used to create this Entity.
* @return this
*/
EntityBuilder setSchemaUrl(String schemaUrl);

/**
* Modify the descriptive attributes of this Entity.
*
* @param f A {@link Consumer} which builds the descriptive attributes.
* @return this
*/
EntityBuilder withDescription(Consumer<AttributesBuilder> f);

/**
* Modify the identifying attributes of this Entity.
*
* @param f A {@link Consumer} which builds the identifying attributes.
* @return this
*/
EntityBuilder withId(Consumer<AttributesBuilder> f);

/** Emits the current entity. */
void emit();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe call this attach() since we have Resource#attacheEntity()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is anticipation of having an EntityBuilder that just emits Entity without attaching to active resource.

I debated two things:

  • Having EntityBuilder have two methods: emit and attach where this PR would only have attach.
  • Having the Resource method you call to get the builder be named attach or emit.

I was planning to talk about this awkwardness in the next Entities SIG, but agree with only "attach" then this should be "attach".

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.incubator.entities;

import io.opentelemetry.api.OpenTelemetry;

/** Extension to {@link OpenTelemetry} that adds {@link ResourceProvider}. */
public interface ExtendedOpenTelemetry extends OpenTelemetry {
/** Returns the {@link ResourceProvider} for this {@link OpenTelemetry}. */
default ResourceProvider getResourceProvider() {
return ResourceProvider.noop();

Check warning on line 14 in api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/ExtendedOpenTelemetry.java

View check run for this annotation

Codecov / codecov/patch

api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/ExtendedOpenTelemetry.java#L14

Added line #L14 was not covered by tests
}

/** Returns the {@link Resource} that telemetry from this {@link OpenTelemetry} uses. */
default Resource getResource() {
return getResourceProvider().getResource();

Check warning on line 19 in api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/ExtendedOpenTelemetry.java

View check run for this annotation

Codecov / codecov/patch

api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/ExtendedOpenTelemetry.java#L19

Added line #L19 was not covered by tests
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.incubator.entities;

import io.opentelemetry.api.common.AttributesBuilder;
import java.util.function.Consumer;

final class NoopEntityBuilder implements EntityBuilder {

Check warning on line 11 in api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopEntityBuilder.java

View check run for this annotation

Codecov / codecov/patch

api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopEntityBuilder.java#L11

Added line #L11 was not covered by tests
@Override
public EntityBuilder setSchemaUrl(String schemaUrl) {
return this;

Check warning on line 14 in api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopEntityBuilder.java

View check run for this annotation

Codecov / codecov/patch

api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopEntityBuilder.java#L14

Added line #L14 was not covered by tests
}

@Override
public EntityBuilder withDescription(Consumer<AttributesBuilder> f) {
return this;

Check warning on line 19 in api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopEntityBuilder.java

View check run for this annotation

Codecov / codecov/patch

api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopEntityBuilder.java#L19

Added line #L19 was not covered by tests
}

@Override
public EntityBuilder withId(Consumer<AttributesBuilder> f) {
return this;

Check warning on line 24 in api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopEntityBuilder.java

View check run for this annotation

Codecov / codecov/patch

api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopEntityBuilder.java#L24

Added line #L24 was not covered by tests
}

@Override
public void emit() {}

Check warning on line 28 in api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopEntityBuilder.java

View check run for this annotation

Codecov / codecov/patch

api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopEntityBuilder.java#L28

Added line #L28 was not covered by tests
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.incubator.entities;

final class NoopResource implements Resource {

Check warning on line 8 in api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopResource.java

View check run for this annotation

Codecov / codecov/patch

api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopResource.java#L8

Added line #L8 was not covered by tests

@Override
public boolean removeEntity(String entityType) {
return false;

Check warning on line 12 in api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopResource.java

View check run for this annotation

Codecov / codecov/patch

api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopResource.java#L12

Added line #L12 was not covered by tests
}

@Override
public EntityBuilder attachEntity(String entityType) {
return new NoopEntityBuilder();

Check warning on line 17 in api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopResource.java

View check run for this annotation

Codecov / codecov/patch

api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopResource.java#L17

Added line #L17 was not covered by tests
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.incubator.entities;

final class NoopResourceProvider implements ResourceProvider {

Check warning on line 8 in api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopResourceProvider.java

View check run for this annotation

Codecov / codecov/patch

api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopResourceProvider.java#L8

Added line #L8 was not covered by tests

@Override
public Resource getResource() {
return new NoopResource();

Check warning on line 12 in api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopResourceProvider.java

View check run for this annotation

Codecov / codecov/patch

api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/NoopResourceProvider.java#L12

Added line #L12 was not covered by tests
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.incubator.entities;

/** The active resource for which Telemetry is being generated. */
public interface Resource {
/**
* Removes an entity from this resource.
*
* @param entityType the type of entity to remove.
* @return true if entity was found and removed.
*/
public boolean removeEntity(String entityType);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So can multiple instrumentations compete to add or remove an entity with the same type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. This API is here to explore the browser-SIGs needs where session has changed and needs to be replaced.

The current addOrUpdate method does NOT replace an entity if the ID has changed, you must manually remove it.

This is the "footgun" we're worried about in Entities SIG and need to explore.


/**
* Attaches an entity to the current {@link Resource}.
*
* @param entityType The type of the entity.
* @return A builder that can construct an entity.
*/
public EntityBuilder attachEntity(String entityType);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.incubator.entities;

/**
* A registry for interacting with {@link Resource}s. The name <i>Provider</i> is for consistency
* with other languages and it is <b>NOT</b> loaded using reflection.
*
* @see Resource
*/
public interface ResourceProvider {
/**
* Returns a no-op {@link ResourceProvider} which only creates no-op {@link Resource}s which do
* not record nor are emitted.
*/
static ResourceProvider noop() {
return new NoopResourceProvider();

Check warning on line 20 in api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/ResourceProvider.java

View check run for this annotation

Codecov / codecov/patch

api/incubator/src/main/java/io/opentelemetry/api/incubator/entities/ResourceProvider.java#L20

Added line #L20 was not covered by tests
}

/** Returns the active {@link Resource} for which Telemetry is reported. */
Resource getResource();
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
Comparing source compatibility of opentelemetry-sdk-common-1.52.0-SNAPSHOT.jar against opentelemetry-sdk-common-1.51.0.jar
No changes.
*** MODIFIED CLASS: PUBLIC ABSTRACT io.opentelemetry.sdk.resources.Resource (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.resources.Resource create(io.opentelemetry.api.common.Attributes, java.lang.String, java.util.Collection<io.opentelemetry.sdk.resources.internal.Entity>)
*** MODIFIED METHOD: PUBLIC NON_ABSTRACT (<- ABSTRACT) io.opentelemetry.api.common.Attributes getAttributes()
10 changes: 9 additions & 1 deletion docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
Comparing source compatibility of opentelemetry-sdk-testing-1.52.0-SNAPSHOT.jar against opentelemetry-sdk-testing-1.51.0.jar
No changes.
+++ NEW CLASS: PUBLIC(+) io.opentelemetry.sdk.testing.assertj.EntityAssert (not serializable)
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.assertj.EntityAssert hasDescriptionSatisfying(org.assertj.core.api.ThrowingConsumer<io.opentelemetry.api.common.Attributes>)
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.assertj.EntityAssert hasIdSatisfying(org.assertj.core.api.ThrowingConsumer<io.opentelemetry.api.common.Attributes>)
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.assertj.EntityAssert hasSchemaUrl(java.lang.String)
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.assertj.EntityAssert hasType(java.lang.String)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.testing.assertj.EntityAssert assertThat(io.opentelemetry.sdk.resources.internal.Entity)
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.exporter.internal.otlp;

import io.opentelemetry.api.internal.StringUtils;
import io.opentelemetry.exporter.internal.marshal.MarshalerUtil;
import io.opentelemetry.exporter.internal.marshal.MarshalerWithSize;
import io.opentelemetry.exporter.internal.marshal.Serializer;
import io.opentelemetry.proto.common.v1.internal.EntityRef;
import io.opentelemetry.sdk.resources.internal.Entity;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import javax.annotation.Nullable;

/**
* A Marshaler of {@link io.opentelemetry.sdk.resources.Entity}.
*
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
public final class EntityRefMarshaler extends MarshalerWithSize {
@Nullable private final byte[] schemaUrlUtf8;
private final byte[] typeUtf8;
private final byte[][] idKeysUtf8;
private final byte[][] descriptionKeysUtf8;

@Override
protected void writeTo(Serializer output) throws IOException {
if (schemaUrlUtf8 != null) {
output.writeString(EntityRef.SCHEMA_URL, schemaUrlUtf8);
}
output.writeString(EntityRef.TYPE, typeUtf8);
output.writeRepeatedString(EntityRef.ID_KEYS, idKeysUtf8);
output.writeRepeatedString(EntityRef.DESCRIPTION_KEYS, descriptionKeysUtf8);
}

/** Consttructs an entity reference marshaler from a full entity. */
public static EntityRefMarshaler createForEntity(Entity e) {
byte[] schemaUrlUtf8 = null;
if (!StringUtils.isNullOrEmpty(e.getSchemaUrl())) {
schemaUrlUtf8 = e.getSchemaUrl().getBytes(StandardCharsets.UTF_8);
}
return new EntityRefMarshaler(
schemaUrlUtf8,
e.getType().getBytes(StandardCharsets.UTF_8),
e.getId().asMap().keySet().stream()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eventually we'll want to make this more efficient, avoiding stream() and implementing a stateless version of this, which refers to a version which minimizes / avoids memory allocations using a variety of techniques centered around object reuse.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. This reminds me of the "dictonary" / "register an attribute set" API discussion from the spec meeting.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if it wasn't apparent for now - This marshaller is only used ONCE per resource instance. So unless resource is being updated frequently (which we do not expect), then this cost is not paid often, if more than once.

.map(key -> key.getKey().getBytes(StandardCharsets.UTF_8))
.toArray(byte[][]::new),
e.getDescription().asMap().keySet().stream()
.map(key -> key.getKey().getBytes(StandardCharsets.UTF_8))
.toArray(byte[][]::new));
}

private EntityRefMarshaler(
@Nullable byte[] schemaUrlUtf8,
byte[] typeUtf8,
byte[][] idKeysUtf8,
byte[][] descriptionKeysUtf8) {
super(calculateSize(schemaUrlUtf8, typeUtf8, idKeysUtf8, descriptionKeysUtf8));
this.schemaUrlUtf8 = schemaUrlUtf8;
this.typeUtf8 = typeUtf8;
this.idKeysUtf8 = idKeysUtf8;
this.descriptionKeysUtf8 = descriptionKeysUtf8;
}

private static int calculateSize(
@Nullable byte[] schemaUrlUtf8,
byte[] typeUtf8,
byte[][] idKeysUtf8,
byte[][] descriptionKeysUtf8) {
int size = 0;
if (schemaUrlUtf8 != null) {
size += MarshalerUtil.sizeBytes(EntityRef.SCHEMA_URL, schemaUrlUtf8);
}
size += MarshalerUtil.sizeBytes(EntityRef.TYPE, typeUtf8);
MarshalerUtil.sizeRepeatedString(EntityRef.ID_KEYS, idKeysUtf8);
MarshalerUtil.sizeRepeatedString(EntityRef.DESCRIPTION_KEYS, descriptionKeysUtf8);
return size;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.opentelemetry.exporter.internal.marshal.MarshalerWithSize;
import io.opentelemetry.exporter.internal.marshal.Serializer;
import io.opentelemetry.proto.resource.v1.internal.Resource;
import io.opentelemetry.sdk.resources.internal.EntityUtil;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
Expand Down Expand Up @@ -37,7 +38,10 @@ public static ResourceMarshaler create(io.opentelemetry.sdk.resources.Resource r

RealResourceMarshaler realMarshaler =
new RealResourceMarshaler(
KeyValueMarshaler.createForAttributes(resource.getAttributes()));
KeyValueMarshaler.createForAttributes(resource.getAttributes()),
EntityUtil.getEntities(resource).stream()
.map(EntityRefMarshaler::createForEntity)
.toArray(MarshalerWithSize[]::new));

ByteArrayOutputStream binaryBos =
new ByteArrayOutputStream(realMarshaler.getBinarySerializedSize());
Expand Down Expand Up @@ -70,19 +74,26 @@ public void writeTo(Serializer output) throws IOException {

private static final class RealResourceMarshaler extends MarshalerWithSize {
private final KeyValueMarshaler[] attributes;
private final MarshalerWithSize[] entityRefs;

private RealResourceMarshaler(KeyValueMarshaler[] attributes) {
super(calculateSize(attributes));
private RealResourceMarshaler(KeyValueMarshaler[] attributes, MarshalerWithSize[] entityRefs) {
super(calculateSize(attributes, entityRefs));
this.attributes = attributes;
this.entityRefs = entityRefs;
}

@Override
protected void writeTo(Serializer output) throws IOException {
output.serializeRepeatedMessage(Resource.ATTRIBUTES, attributes);
output.serializeRepeatedMessage(Resource.ENTITY_REFS, entityRefs);
}

private static int calculateSize(KeyValueMarshaler[] attributeMarshalers) {
return MarshalerUtil.sizeRepeatedMessage(Resource.ATTRIBUTES, attributeMarshalers);
private static int calculateSize(
KeyValueMarshaler[] attributeMarshalers, MarshalerWithSize[] entityRefs) {
int size = 0;
size += MarshalerUtil.sizeRepeatedMessage(Resource.ATTRIBUTES, attributeMarshalers);
size += size += MarshalerUtil.sizeRepeatedMessage(Resource.ENTITY_REFS, entityRefs);
return size;
}
}
}
Loading
Loading