Skip to content

Commit a0a740c

Browse files
committed
address the concerns about event volume and size:
1. State Changes vs Periodic Reports (addresses event volume) 2. Aggregated Reporting (addresses event volume and relationship churn) 3. Relationship Placement Guidance (addresses relationship array size) Other changes: - Changed entity.id to map<string, string> (was map<string, AnyValue>) - Renamed entity.interval to report.interval for clarity - Made entity.description a required field (was optional)
1 parent cf33967 commit a0a740c

File tree

1 file changed

+188
-107
lines changed

1 file changed

+188
-107
lines changed

specification/entities/entity-events.md

Lines changed: 188 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ weight: 3
1414

1515
- [Overview](#overview)
1616
- [When to Use Entity Events](#when-to-use-entity-events)
17-
- [Entity Events Data Model](#entity-events-data-model)
17+
- [Event Types](#event-types)
1818
* [Entity State Event](#entity-state-event)
1919
* [Entity Delete Event](#entity-delete-event)
20+
* [Aggregated Reporting](#aggregated-reporting)
2021
- [Entity Relationships](#entity-relationships)
2122
* [Relationship Structure](#relationship-structure)
2223
* [Standard Relationship Types](#standard-relationship-types)
24+
* [Relationship Placement](#relationship-placement)
2325
* [Relationship Lifecycle](#relationship-lifecycle)
2426
- [Examples](#examples)
2527
* [Kubernetes Pod Entity State](#kubernetes-pod-entity-state)
@@ -58,14 +60,21 @@ Entity events are particularly useful when:
5860
Entity events can be used alongside telemetry signals associated with entities to provide
5961
additional context and relationship information.
6062

61-
## Entity Events Data Model
63+
## Event Types
6264

63-
Entity events use the OpenTelemetry Logs Data Model with specific conventions for the
64-
`EventName` and `Attributes` fields.
65+
Entity information is communicated through the following event types:
66+
67+
1. **Entity State Event** (`entity.state`): Emitted when an entity is created, when its
68+
attributes change, or periodically to indicate the entity still exists. The presence or
69+
absence of the `entity.updated` attribute distinguishes between state changes and
70+
periodic reports.
71+
72+
2. **Entity Delete Event** (`entity.delete`): Emitted when an entity is removed.
6573

6674
### Entity State Event
6775

68-
The Entity State Event stores information about the state of an entity at a particular moment in time.
76+
The Entity State Event is emitted when an entity is created, when its descriptive attributes
77+
change, or periodically to indicate the entity still exists.
6978

7079
**Event Name**: `entity.state`
7180

@@ -74,39 +83,36 @@ The Entity State Event stores information about the state of an entity at a part
7483
| Attribute | Type | Description |
7584
| --------- | ---- | ----------- |
7685
| `entity.type` | string | Defines the type of the entity. MUST not change during the lifetime of the entity. For example: "service", "host", "k8s.pod". |
77-
| `entity.id` | map<string, AnyValue> | Attributes that identify the entity. MUST not change during the lifetime of the entity. The map MUST contain at least one attribute. Follows OpenTelemetry [attribute definition](../common/README.md#attribute). |
86+
| `entity.id` | map<string, string> | Attributes that identify the entity. MUST not change during the lifetime of the entity. The map MUST contain at least one attribute. Keys and values MUST be strings. SHOULD follow OpenTelemetry [semantic conventions](https://github.com/open-telemetry/semantic-conventions) for attribute names. |
87+
| `entity.description` | map<string, AnyValue> | Descriptive (non-identifying) attributes of the entity. These attributes are not part of the entity's identity. Each Entity State event contains the complete current state of the entity's description. Follows [AnyValue](../common/README.md#anyvalue) definition: can contain scalar values, arrays, or nested maps. SHOULD follow OpenTelemetry [semantic conventions](https://github.com/open-telemetry/semantic-conventions) for attributes. |
88+
| `entity.relationships` | array of maps | Relationships to other entities. Each relationship is a map containing: `type` (string, describes the relationship), `entity.type` (string, the type of the related entity), and `entity.id` (map<string, string>, identifying attributes of the related entity). |
89+
| `report.interval` | int64 (milliseconds) | The reporting interval for this entity. MUST be a non-negative value. A value of `0` indicates that periodic reporting is disabled and only state changes will be reported. A positive value indicates the interval at which periodic state events (without `entity.updated`) will be emitted. Can be used by receivers to infer that a no longer reported entity is gone, even if the Entity Delete event was not observed. |
7890

7991
**Optional Attributes**:
8092

8193
| Attribute | Type | Description |
8294
| --------- | ---- | ----------- |
83-
| `entity.description` | map<string, AnyValue> | Descriptive (non-identifying) attributes of the entity. MAY change over the lifetime of the entity. These attributes are not part of the entity's identity. Each Entity State event with a non-empty `entity.description` completely replaces the previously reported description. Follows [AnyValue](../common/README.md#anyvalue) definition: can contain scalar values, arrays, or nested maps. SHOULD follow OpenTelemetry [semantic conventions](https://github.com/open-telemetry/semantic-conventions) for attributes. |
84-
| `entity.interval` | int64 (milliseconds) | Defines the reporting period, i.e., how frequently information about this entity is reported via Entity State events even if the entity does not change. MUST be a positive value. A value of `0` indicates that no periodic reporting is expected. The next expected Entity State event for this entity is expected at (Timestamp + Interval) time. Can be used by receivers to infer that a no longer reported entity is gone, even if the Entity Delete event was not observed. |
85-
| `entity.relationships` | array of map<string, AnyValue> | Array of relationships that this entity has with other entities. MAY change over the lifetime of the entity. Each Entity State event with a non-empty `entity.relationships` completely replaces the previously reported relationships. See [Entity Relationships](#entity-relationships) for details. |
95+
| `entity.updated` | string | The timestamp when the entity's state last changed, in ISO 8601 format. When present, indicates this is a state change event. When absent, indicates this is a periodic report with no changes since the last report. When multiple changes occur between reports (aggregated reporting), this represents the timestamp of the most recent change. |
8696

8797
**Timestamp Field**:
8898

89-
The `Timestamp` field of the LogRecord represents the time when the entity state described
90-
by this event became effective. This is the time measured by the origin clock.
91-
92-
**State Mutations**:
99+
The `Timestamp` field of the LogRecord represents the time when this event was generated and sent.
100+
For immediate reporting without aggregation, this will typically match `entity.updated`. For
101+
aggregated reporting, this represents when the event was sent, while `entity.updated` represents
102+
when the most recent state change occurred.
93103

94-
An entity mutates (changes) when one or more of its descriptive attributes changes, or when
95-
its relationships change. A new descriptive attribute may be added, an existing descriptive
96-
attribute may be deleted, or a value of an existing descriptive attribute may be changed.
97-
All these changes represent valid mutations of an entity over time. When these mutations
98-
happen, the identity of the entity does not change.
104+
**State Changes vs Periodic Reports**:
99105

100-
When the entity's state changes, sources SHOULD emit a new Entity State event with a fresh
101-
timestamp and the complete current state of all fields.
106+
- When `entity.updated` is **present**, this is a state change event indicating the entity
107+
was created or its attributes were modified. The value of `entity.updated` indicates when
108+
the most recent change occurred.
109+
- When `entity.updated` is **absent**, this is a periodic report indicating the entity still
110+
exists but has not changed since the last report.
102111

103-
**Periodic Reporting**:
104-
105-
Entity event producers SHOULD periodically emit Entity State events even if the entity does
106-
not change. In this case, the `entity.type`, `entity.id`, `entity.description`, and
107-
`entity.relationships` fields will remain the same, but a fresh `Timestamp` will be recorded.
108-
Producing such events allows the system to be resilient to event losses and serves as a
109-
liveliness indicator.
112+
Implementations SHOULD emit state change events whenever entity descriptive attributes change,
113+
periodic state events based on the `report.interval` value, and delete events when entities
114+
are removed. Implementations MAY provide configuration options to allow users to disable
115+
state change events or periodic state events independently.
110116

111117
### Entity Delete Event
112118

@@ -119,7 +125,7 @@ The Entity Delete Event indicates that a particular entity is gone.
119125
| Attribute | Type | Description |
120126
| --------- | ---- | ----------- |
121127
| `entity.type` | string | The type of the entity being deleted. |
122-
| `entity.id` | map<string, AnyValue> | Attributes that identify the entity being deleted. |
128+
| `entity.id` | map<string, string> | Attributes that identify the entity being deleted. |
123129

124130
**Optional Attributes**:
125131

@@ -129,18 +135,42 @@ The Entity Delete Event indicates that a particular entity is gone.
129135

130136
**Timestamp Field**:
131137

132-
The `Timestamp` field of the LogRecord represents the time when the entity was deleted,
133-
measured by the origin clock.
138+
The `Timestamp` field of the LogRecord represents the time when the entity was deleted.
134139

135140
**Delivery Guarantees**:
136141

137142
Transmitting Entity Delete events is not guaranteed when an entity is gone. Recipients of
138143
entity signals MUST be prepared to handle this situation by expiring entities that are no
139144
longer seeing Entity State events reported. The expiration mechanism is based on the
140-
previously reported `entity.interval` field. Recipients can use this value to compute when
145+
previously reported `report.interval` field. Recipients can use this value to compute when
141146
to expect the next Entity State event and, if the event does not arrive in a timely manner
142147
(plus some slack), consider the entity to be gone even if the Entity Delete event was not observed.
143148

149+
### Aggregated Reporting
150+
151+
Implementations MAY provide aggregated reporting as an optional capability to reduce the amount
152+
of data sent over the wire. When aggregated reporting is enabled:
153+
154+
- Multiple rapid changes to the same entity are collapsed into a single Entity State event
155+
- Only the **latest state** of the entity is reported
156+
- Intermediate state changes are not preserved
157+
- The `entity.updated` attribute contains the timestamp of the **most recent change** that
158+
occurred during the aggregation period
159+
- The LogRecord `Timestamp` represents when the aggregated event was sent
160+
161+
**Example**:
162+
163+
If a Pod's phase changes from "Pending" → "Running" → "Running" (with label update) between
164+
reports, only one event is sent with:
165+
166+
- The final state (Running with updated labels)
167+
- `entity.updated` = timestamp of the label update (the most recent change)
168+
- LogRecord `Timestamp` = when the aggregated event was sent
169+
170+
Aggregated reporting reduces data volume at the cost of losing visibility into intermediate
171+
states. When provided, implementations SHOULD document the aggregated reporting capability
172+
and allow configuration of the aggregation behavior.
173+
144174
## Entity Relationships
145175

146176
Entity relationships describe how entities are connected to each other. Relationships are
@@ -156,7 +186,7 @@ Each relationship in the `entity.relationships` array is a map containing:
156186
| ----- | ---- | ----------- |
157187
| `relationship.type` | string | The type of relationship. Describes the semantic meaning of the relationship (e.g., "scheduled_on", "contains", "depends_on"). See [Standard Relationship Types](#standard-relationship-types). |
158188
| `entity.type` | string | The type of the target entity. |
159-
| `entity.id` | map<string, AnyValue> | The identifying attributes of the target entity. |
189+
| `entity.id` | map<string, string> | The identifying attributes of the target entity. |
160190

161191
**Optional Fields**:
162192

@@ -189,6 +219,39 @@ Relationship types form an open enumeration. Custom relationship types MAY be de
189219
represent domain-specific relationships. Semantic conventions SHOULD define standard
190220
relationship types for common entity types.
191221

222+
### Relationship Placement
223+
224+
When choosing which entity should contain a relationship in its `entity.relationships` array,
225+
implementations SHOULD prefer placing relationships on the entity type with the **shorter
226+
lifespan** or **higher churn rate**. This minimizes the total number of Entity State events
227+
that need to be sent.
228+
229+
**Rationale**: Since relationships are embedded in Entity State events, every time an entity's
230+
relationships change, a new state event must be emitted. Placing relationships on the more
231+
stable entity would require frequent state event emissions whenever the shorter-lived entities
232+
are created or destroyed.
233+
234+
**Examples**:
235+
236+
- **Prefer**: `k8s.pod -> part_of -> k8s.replicaset` (relationship on the pod)
237+
- **Rather than**: `k8s.replicaset -> contains -> k8s.pod` (relationship on the replicaset)
238+
- **Reason**: Pods churn frequently. With relationships on pods, only new pod state events
239+
are sent when pods are created/destroyed. If relationships were on the replicaset, every
240+
pod creation/destruction would require a new replicaset state event with an updated list
241+
of all contained pods.
242+
243+
- **Prefer**: `container -> part_of -> k8s.pod` (relationship on the container)
244+
- **Rather than**: `k8s.pod -> contains -> container` (relationship on the pod)
245+
- **Reason**: Containers may restart independently, so placing the relationship on the
246+
container reduces the number of pod state events.
247+
248+
- **Prefer**: `process -> runs_on -> host` (relationship on the process)
249+
- **Rather than**: `host -> hosts -> process` (relationship on the host)
250+
- **Reason**: Processes start and stop frequently, while hosts are long-lived.
251+
252+
When both entities have similar lifespans, either direction is acceptable. Semantic conventions
253+
SHOULD provide guidance on relationship placement for common entity types.
254+
192255
### Relationship Lifecycle
193256

194257
**Creating Relationships**:
@@ -206,86 +269,104 @@ entity is the source are implicitly deleted. Backends SHOULD handle this accordi
206269

207270
## Examples
208271

272+
The following examples show the logical representation of entity events. These are NOT
273+
actual OTLP wire format representations, but rather illustrate the semantic structure
274+
of the events.
275+
209276
### Kubernetes Pod Entity State
210277

211-
```json
212-
{
213-
"timestamp": "2026-01-12T10:30:00.000000000Z",
214-
"eventName": "entity.state",
215-
"resource": {
216-
"attributes": {
217-
"k8s.cluster.name": "prod-cluster"
218-
}
219-
},
220-
"attributes": {
221-
"entity.type": "k8s.pod",
222-
"entity.id": {
223-
"k8s.pod.uid": "abc-123-def-456"
224-
},
225-
"entity.description": {
226-
"k8s.pod.name": "nginx-deployment-66b6c",
227-
"k8s.namespace.name": "default",
228-
"k8s.pod.labels": {
229-
"app": "nginx",
230-
"version": "1.21",
231-
"tier": "frontend"
232-
},
233-
"k8s.pod.phase": "Running",
234-
},
235-
"entity.interval": 60000,
236-
"entity.relationships": [
237-
{
238-
"relationship.type": "scheduled_on",
239-
"entity.type": "k8s.node",
240-
"entity.id": {
241-
"k8s.node.uid": "node-001"
242-
}
243-
},
244-
{
245-
"relationship.type": "contains",
246-
"entity.type": "container",
247-
"entity.id": {
248-
"container.id": "container-456"
249-
}
250-
},
251-
{
252-
"relationship.type": "contains",
253-
"entity.type": "container",
254-
"entity.id": {
255-
"container.id": "container-789"
256-
}
257-
},
258-
{
259-
"relationship.type": "part_of",
260-
"entity.type": "k8s.replicaset",
261-
"entity.id": {
262-
"k8s.replicaset.uid": "rs-456"
263-
}
264-
}
265-
]
266-
}
267-
}
278+
When a Kubernetes Pod is created or its attributes change:
279+
280+
```
281+
LogRecord:
282+
Timestamp: 2026-01-12T10:30:00.000000000Z
283+
EventName: entity.state
284+
Resource:
285+
k8s.cluster.name: prod-cluster
286+
Attributes:
287+
entity.type: k8s.pod
288+
entity.id:
289+
k8s.pod.uid: abc-123-def-456
290+
entity.description:
291+
k8s.pod.name: nginx-deployment-66b6c
292+
k8s.pod.labels:
293+
app: nginx
294+
version: "1.21"
295+
tier: frontend
296+
k8s.pod.phase: Running
297+
entity.updated: 2026-01-12T10:30:00.000000000Z
298+
report.interval: 60000
299+
entity.relationships:
300+
- relationship.type: scheduled_on
301+
entity.type: k8s.node
302+
entity.id:
303+
k8s.node.uid: node-001
304+
- relationship.type: part_of
305+
entity.type: k8s.replicaset
306+
entity.id:
307+
k8s.replicaset.uid: rs-456
268308
```
269309

270-
### Entity Delete Event
310+
### Periodic Entity Report
311+
312+
Periodic report for the same Pod with no changes:
313+
314+
```
315+
LogRecord:
316+
Timestamp: 2026-01-12T10:31:00.000000000Z
317+
EventName: entity.state
318+
Resource:
319+
k8s.cluster.name: prod-cluster
320+
Attributes:
321+
entity.type: k8s.pod
322+
entity.id:
323+
k8s.pod.uid: abc-123-def-456
324+
entity.description:
325+
k8s.pod.name: nginx-deployment-66b6c
326+
k8s.pod.labels:
327+
app: nginx
328+
version: "1.21"
329+
k8s.pod.phase: Running
330+
report.interval: 60000
331+
```
332+
333+
### Aggregated Entity Update
334+
335+
When multiple changes are aggregated and sent in a single event:
336+
337+
```
338+
LogRecord:
339+
Timestamp: 2026-01-12T10:31:00.000000000Z
340+
EventName: entity.state
341+
Resource:
342+
k8s.cluster.name: prod-cluster
343+
Attributes:
344+
entity.type: k8s.pod
345+
entity.id:
346+
k8s.pod.uid: abc-123-def-456
347+
entity.description:
348+
k8s.pod.name: nginx-deployment-66b6c
349+
k8s.pod.labels:
350+
app: nginx
351+
version: "1.21"
352+
k8s.pod.phase: Running
353+
entity.updated: 2026-01-12T10:30:45.000000000Z
354+
report.interval: 60000
355+
```
356+
357+
### Entity Delete
271358

272359
When the Pod is terminated:
273360

274-
```json
275-
{
276-
"timestamp": "2026-01-12T11:00:00.000000000Z",
277-
"eventName": "entity.delete",
278-
"resource": {
279-
"attributes": {
280-
"k8s.cluster.name": "prod-cluster"
281-
}
282-
},
283-
"attributes": {
284-
"entity.type": "k8s.pod",
285-
"entity.id": {
286-
"k8s.pod.uid": "abc-123-def-456"
287-
},
288-
"entity.delete.reason": "terminated"
289-
}
290-
}
361+
```
362+
LogRecord:
363+
Timestamp: 2026-01-12T11:00:00.000000000Z
364+
EventName: entity.delete
365+
Resource:
366+
k8s.cluster.name: prod-cluster
367+
Attributes:
368+
entity.type: k8s.pod
369+
entity.id:
370+
k8s.pod.uid: abc-123-def-456
371+
entity.delete.reason: terminated
291372
```

0 commit comments

Comments
 (0)