# Entity Events

LLMS index: [llms.txt](/llms.txt)

---

**Status**: [Development](../document-status.md)


## Overview

Entity events provide a way to communicate entity information as structured log events.
This approach is complementary to defining entities as part of Resource data (see [Entity Data Model](./data-model.md)).

Entity events are represented as structured events using the OpenTelemetry [Logs Data Model](../logs/data-model.md),
specifically as [Events](../logs/data-model.md#events) with a defined `EventName` and attribute structure.

## When to Use Entity Events

Entity events are particularly useful when:

1. **No Associated Telemetry**: The entity has no telemetry signals associated with it, or
   the telemetry is less important than the entity data itself.

2. **Complex Descriptive Information**: Entity descriptive information is too complex for
   simple resource attribute values. The resource attribute values are expected to be simple
   strings, but entity descriptions can contain complex values like maps and arrays
   (e.g., Kubernetes ConfigMap content, complex cloud metadata, nested tags).

3. **Entity Relationships**: The entity information needs to include relationships to other
   entities. Resource data cannot contain relationship information.

4. **Lifecycle Tracking**: There is a need to explicitly track entity lifecycle events
   (creation, state changes, deletion) independently from telemetry signals.

Entity events can be used alongside telemetry signals associated with entities to provide
additional context and relationship information.

## Event Types

Entity information is communicated through the following event types:

1. **Entity State Event** (`entity.state`): Emitted when an entity is created, when its
   attributes change, or periodically to indicate the entity still exists.

2. **Entity Delete Event** (`entity.delete`): Emitted when an entity is removed.

### Entity State Event

The Entity State Event is emitted when an entity is created, when its descriptive attributes
change, or periodically to indicate the entity still exists.

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

**Required Attributes**:

| Attribute | Type | Description |
| --------- | ---- | ----------- |
| `entity.type` | string | Defines the type of the entity. MUST not change during the lifetime of the entity. For example: "service", "host", "k8s.pod". |
| `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. |

**Optional Attributes**:

| Attribute | Type | Description |
| --------- | ---- | ----------- |
| `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. When absent, MUST be treated as an empty map. 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. |
| `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). When absent, MUST be treated as an empty array. |
| `entity.report.interval` | int64 (seconds) | The reporting period for this entity. MUST be a non-negative value when present. When absent, the reporting period is unknown. A value of `0` indicates that no periodic state events will be sent. A positive value indicates the interval at which periodic state events will be emitted. Can be used by receivers to determine when to expect the next event and infer that an entity is gone if events stop arriving. |

**Timestamp Field**:

The `Timestamp` field of the LogRecord represents the time when this event was generated and sent.

**Event Emission**:

Implementations SHOULD emit Entity State events whenever entity descriptive attributes change,
and periodically based on the `entity.report.interval` value to indicate the entity still exists.
Implementations SHOULD also emit Entity Delete events when entities are removed.

**Future Considerations**:

Each Entity State event contains the complete current state of the entity. If scalability
issues arise in the future, the specification may introduce a "patch" event mechanism to
communicate only the changes rather than the full state.

### Entity Delete Event

The Entity Delete Event indicates that a particular entity is gone.

**Event Name**: `entity.delete`

**Required Attributes**:

| Attribute | Type | Description |
| --------- | ---- | ----------- |
| `entity.type` | string | The type of the entity being deleted. |
| `entity.id` | map<string, string> | Attributes that identify the entity being deleted. |

**Optional Attributes**:

| Attribute | Type | Description |
| --------- | ---- | ----------- |
| `entity.delete.reason` | string | The reason for entity deletion. Examples: "terminated", "expired", "evicted", "user_requested", "scaled_down". |

**Timestamp Field**:

The `Timestamp` field of the LogRecord represents the time when the entity was deleted.

**Delivery Guarantees**:

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

Recipients MUST also be prepared to receive an Entity Delete event out of order, for example,
before the last Entity State event. In this case, recipients SHOULD apply state updates
regardless, as each Entity State event represents the full current state of the entity and
can be used to update a previously deleted entity record.

## Entity Relationships

Entity relationships describe how entities are connected to each other. Relationships are
embedded within Entity State events as an array of relationship descriptors.

### Relationship Structure

Each relationship in the `entity.relationships` array is a map containing:

**Required Fields**:

| Field | Type | Description |
| ----- | ---- | ----------- |
| `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). |
| `entity.type` | string | The type of the target entity. |
| `entity.id` | map<string, string> | The identifying attributes of the target entity. |

**Relationship Direction**:

Relationships have direction: `source --[type]--> target`, where:

- The **source** is the entity emitting the Entity State event
- The **target** is referenced in the relationship descriptor

### Standard Relationship Types

Relationship types form an open enumeration. Standard relationship types SHOULD be defined
in OpenTelemetry semantic conventions. Custom relationship types MAY be defined to represent
domain-specific relationships.

For example, a `scheduled_on` relationship type could be used to express that a workload
is scheduled on infrastructure (e.g., a Kubernetes Pod scheduled on a Node).

### Relationship Placement

When choosing which entity should contain a relationship in its `entity.relationships` array,
implementations SHOULD prefer placing relationships on the entity type with the **shorter
lifespan** or **higher churn rate**. This minimizes the total number of Entity State events
that need to be sent.

**Rationale**: Since relationships are embedded in Entity State events, every time an entity's
relationships change, a new state event must be emitted. Placing relationships on the more
stable entity would require frequent state event emissions whenever the shorter-lived entities
are created or destroyed.

**Examples**:

- **Prefer**: `k8s.pod -> part_of -> k8s.replicaset` (relationship on the pod)
  - **Rather than**: `k8s.replicaset -> contains -> k8s.pod` (relationship on the replicaset)
  - **Reason**: Pods churn frequently. With relationships on pods, only new pod state events
    are sent when pods are created/destroyed. If relationships were on the replicaset, every
    pod creation/destruction would require a new replicaset state event with an updated list
    of all contained pods.

- **Prefer**: `container -> part_of -> k8s.pod` (relationship on the container)
  - **Rather than**: `k8s.pod -> contains -> container` (relationship on the pod)
  - **Reason**: Containers may restart independently, so placing the relationship on the
    container reduces the number of pod state events.

- **Prefer**: `process -> runs_on -> host` (relationship on the process)
  - **Rather than**: `host -> hosts -> process` (relationship on the host)
  - **Reason**: Processes start and stop frequently, while hosts are long-lived.

When both entities have similar lifespans, either direction is acceptable. Semantic conventions
SHOULD provide guidance on relationship placement for common entity types.

### Relationship Lifecycle

**Creating Relationships**:
Emit an Entity State event with the new relationship included in the `entity.relationships` array.

**Updating Relationships**:
Emit a new Entity State event with the updated `entity.relationships` array reflecting the current state.

**Deleting Relationships**:
Emit a new Entity State event with the relationship removed from the `entity.relationships` array.

**Implicit Deletion**:
When an entity is deleted (Entity Delete event is emitted), all relationships where that
entity is involved are implicitly deleted. Backends SHOULD handle this accordingly.

## Examples

The following examples show the logical representation of entity events. These are NOT
actual OTLP wire format representations, but rather illustrate the semantic structure
of the events.

### Kubernetes Pod Entity State

When a Kubernetes Pod is created or its attributes change:

```
LogRecord:
  Timestamp: 2026-01-12T10:30:00.000000000Z
  EventName: entity.state
  Resource:
    k8s.cluster.name: prod-cluster
  Attributes:
    entity.type: k8s.pod
    entity.id:
      k8s.pod.uid: abc-123-def-456
    entity.description:
      k8s.pod.name: nginx-deployment-66b6c
      k8s.pod.labels:
        app: nginx
        version: "1.21"
        tier: frontend
      k8s.pod.phase: Running
    entity.report.interval: 60
    entity.relationships:
      - relationship.type: scheduled_on
        entity.type: k8s.node
        entity.id:
          k8s.node.uid: node-001
      - relationship.type: part_of
        entity.type: k8s.replicaset
        entity.id:
          k8s.replicaset.uid: rs-456
```

### Service and Process Relationship

A host-level OTel Collector with process scraping (e.g. `hostmetricsreceiver`) emits a
`process` entity state event. It reads process metadata from the OS (via `/proc` on Linux).

```
LogRecord:
  Timestamp: 2026-01-12T11:00:00.000000000Z
  EventName: entity.state
  Resource:
    host.id: host-abc-123
    host.name: prod-host-01
  Attributes:
    entity.type: process
    entity.id:
      process.pid: 4821
      process.start_time: 1736928900
    entity.description:
      process.executable.name: payment-service
      process.executable.path: /usr/bin/payment-service
      process.command_line: /usr/bin/payment-service --port 8080
    entity.report.interval: 300
```

The `service.instance` entity is emitted by an OTel SDK, which is the authoritative source:
it knows both its own service identity and the process it runs in. `service.instance` owns
the `runs_on` relationship to `process`. It also owns the `part_of` relationship to `service`,
since service instances are shorter-lived than the logical service they belong to.

```
LogRecord:
  Timestamp: 2026-01-12T11:00:00.000000000Z
  EventName: entity.state
  Resource:
    host.id: host-abc-123
    host.name: prod-host-01
  Attributes:
    entity.type: service.instance
    entity.id:
      service.name: payment-service
      service.namespace: prod
      service.instance.id: payment-service-prod-abc123
    entity.description:
      service.version: "2.3.1"
    entity.report.interval: 300
    entity.relationships:
      - relationship.type: runs_on
        entity.type: process
        entity.id:
          process.pid: 4821
          process.start_time: 1736928900
      - relationship.type: part_of
        entity.type: service
        entity.id:
          service.name: payment-service
          service.namespace: prod
```

The `service` entity represents the logical service. It is also emitted by the OTel SDK 
alongside the `service.instance` entity event.

```
LogRecord:
  Timestamp: 2026-01-12T11:00:00.000000000Z
  EventName: entity.state
  Resource:
    host.id: host-abc-123
    host.name: prod-host-01
  Attributes:
    entity.type: service
    entity.id:
      service.name: payment-service
      service.namespace: prod
    entity.report.interval: 300
```

### Entity Delete

When the Pod is terminated:

```
LogRecord:
  Timestamp: 2026-01-12T11:00:00.000000000Z
  EventName: entity.delete
  Resource:
    k8s.cluster.name: prod-cluster
  Attributes:
    entity.type: k8s.pod
    entity.id:
      k8s.pod.uid: abc-123-def-456
    entity.delete.reason: terminated
```
