# Entity Event

> Source: https://www.ymotongpoo.com/works/otel-specs-ja/spec/entities/entity-events/


**ステータス**: [Development](../../document-status/)

## 概要

Entity Eventは、構造化されたログイベントとしてEntity情報を伝える手段を提供します。この方法は、Entityをリソースデータの一部として定義する方法（[Entityデータモデル](../data-model/)を参照）を補完するものです。

Entity Eventは、OpenTelemetryの[ログデータモデル](/works/otel-specs-ja/spec/logs/data-model/)を使った構造化イベントとして、具体的には`EventName`と属性の構造を持つ[Event](/works/otel-specs-ja/spec/logs/data-model/#events)として表現されます。

## Entity Eventを使うべき場面

Entity Eventは、特に以下のような場合に有用です。

1. **関連するテレメトリーがない場合**: そのEntityにテレメトリーシグナルが関連付けられていない場合、あるいはテレメトリーがEntityデータ自体より重要度が低い場合。

2. **記述情報が複雑な場合**: Entityの記述情報が単純なリソース属性値では表現しきれない場合。リソース属性値は単純な文字列であることが想定されていますが、Entityの記述はマップや配列のような複雑な値（Kubernetes ConfigMapの内容、複雑なクラウドメタデータ、ネストしたタグなど）を含むことができます。

3. **Entityの関係が必要な場合**: Entity情報に他のEntityへの関係を含める必要がある場合。リソースデータには関係の情報を含められません。

4. **ライフサイクルの追跡が必要な場合**: Entityのライフサイクルイベント（作成、状態変化、削除）を、テレメトリーシグナルとは独立して明示的に追跡する必要がある場合。

Entity Eventは、Entityに関連付けられたテレメトリーシグナルと併用して、追加のコンテキストや関係の情報を提供できます。

## イベント種別

Entity情報は、以下のイベント種別を通じて伝えられます。

1. **Entity State Event** (`entity.state`): Entityが作成されたとき、その属性が変化したとき、あるいはEntityがまだ存在していることを示すために定期的に送出されます。

2. **Entity Delete Event** (`entity.delete`): Entityが削除されたときに送出されます。

### Entity State Event

Entity State Eventは、Entityが作成されたとき、その記述属性が変化したとき、あるいはEntityがまだ存在していることを示すために定期的に送出されます。

**イベント名**: `entity.state`

**必須の属性**:

| 属性 | 型 | 説明 |
| --------- | ---- | ----------- |
| `entity.type` | string | Entityの種別を定義します。Entityの生存期間中に変わってはなりません（MUST NOT）。例: "service"、"host"、"k8s.pod"。 |
| `entity.id` | map<string, string> | Entityを識別する属性です。Entityの生存期間中に変わってはなりません（MUST NOT）。このマップは少なくとも1つの属性を含まなければなりません（MUST）。キーと値は文字列でなければなりません（MUST）。属性名についてはOpenTelemetryの[セマンティック規約](https://github.com/open-telemetry/semantic-conventions)に従うべきです（SHOULD）。 |

**任意の属性**:

| 属性 | 型 | 説明 |
| --------- | ---- | ----------- |
| `entity.description` | map<string, AnyValue> | Entityの記述的な（識別に使わない）属性です。これらの属性はEntityの識別の一部ではありません。各Entity State Eventは、そのEntityの記述の現在の完全な状態を含みます。存在しない場合は、空のマップとして扱わなければなりません（MUST）。[AnyValue](/works/otel-specs-ja/spec/common/#anyvalue)の定義に従い、スカラー値、配列、ネストしたマップを含められます。属性についてはOpenTelemetryの[セマンティック規約](https://github.com/open-telemetry/semantic-conventions)に従うべきです（SHOULD）。 |
| `entity.relationships` | array of maps | 他のEntityへの関係です。各関係は、`type`（string、関係を記述する）、`entity.type`（string、関連するEntityの種別）、`entity.id`（map<string, string>、関連するEntityの識別属性）を含むマップです。存在しない場合は、空の配列として扱わなければなりません（MUST）。 |
| `entity.report.interval` | int64 (秒) | このEntityの報告間隔です。存在する場合は非負の値でなければなりません（MUST）。存在しない場合、報告間隔は不明です。`0`という値は、定期的な状態イベントが一切送出されないことを示します。正の値は、定期的な状態イベントが送出される間隔を示します。受信側は、この値を使って次のイベントが到着すべき時刻を判断し、イベントが届かなくなったらそのEntityが消滅したと推測できます。 |

**Timestampフィールド**:

LogRecordの`Timestamp`フィールドは、このイベントが生成・送出された時刻を表します。

**イベントの送出**:

実装は、Entityの記述属性が変化するたびに、また`entity.report.interval`の値に基づいて定期的に、Entity State Eventを送出すべきです（SHOULD）。実装は、Entityが削除されたときにもEntity Delete Eventを送出すべきです（SHOULD）。

**今後の検討事項**:

各Entity State Eventは、そのEntityの現在の完全な状態を含みます。将来スケーラビリティの問題が生じた場合、完全な状態ではなく変更点のみを伝える「パッチ」イベントの仕組みを、仕様として導入する可能性があります。

### Entity Delete Event

Entity Delete Eventは、特定のEntityが消滅したことを示します。

**イベント名**: `entity.delete`

**必須の属性**:

| 属性 | 型 | 説明 |
| --------- | ---- | ----------- |
| `entity.type` | string | 削除対象のEntityの種別です。 |
| `entity.id` | map<string, string> | 削除対象のEntityを識別する属性です。 |

**任意の属性**:

| 属性 | 型 | 説明 |
| --------- | ---- | ----------- |
| `entity.delete.reason` | string | Entityが削除された理由です。例: "terminated"、"expired"、"evicted"、"user_requested"、"scaled_down"。 |

**Timestampフィールド**:

LogRecordの`Timestamp`フィールドは、そのEntityが削除された時刻を表します。

**配信の保証**:

Entityが消滅した際にEntity Delete Eventが必ず送信されるとは限りません。Entityシグナルの受信側は、Entity State Eventの報告が届かなくなったEntityを期限切れとして扱うことで、この状況に対処できるようにしなければなりません（MUST）。この期限切れの仕組みは、以前に報告された`entity.report.interval`フィールドに基づきます。受信側はこの値を使って次のEntity State Eventが到着すべき時刻を計算し、イベントが期限内に到着しない場合、Entity Delete Eventが観測されなくてもそのEntityが消滅したとみなせます。

受信側は、Entity Delete Eventが順序通りに到着しない場合（例えば最後のEntity State Eventより先に到着する場合）にも対応できるようにしなければなりません（MUST）。この場合、各Entity State Eventはそのときの完全な状態を表し、以前に削除されたEntityの記録を更新するために使えるため、受信側は状態の更新をそのまま適用すべきです（SHOULD）。

## Entityの関係

Entityの関係は、Entity同士がどのように結びついているかを記述します。関係は、Entity State Event内に関係記述子の配列として埋め込まれます。

### 関係の構造

`entity.relationships`配列内の各関係は、以下を含むマップです。

**必須のフィールド**:

| フィールド | 型 | 説明 |
| ----- | ---- | ----------- |
| `relationship.type` | string | 関係の種別です。その関係の意味を記述します（例: "scheduled_on"、"contains"、"depends_on"）。[標準の関係種別](#標準の関係種別)を参照してください。 |
| `entity.type` | string | 対象Entityの種別です。 |
| `entity.id` | map<string, string> | 対象Entityの識別属性です。 |

**関係の方向**:

関係には方向があります。`source --[type]--> target`という形式で、以下のとおりです。

- **source**は、Entity State Eventを送出しているEntityです。
- **target**は、関係記述子の中で参照されているEntityです。

### 標準の関係種別

関係種別は開かれた列挙です。標準の関係種別はOpenTelemetryセマンティック規約の中で定義されるべきです（SHOULD）。ドメイン固有の関係を表すためにカスタムの関係種別を定義してもよい（MAY）ものとします。

例えば、`scheduled_on`という関係種別は、あるワークロードがインフラストラクチャ上でスケジューリングされていること（例えば、Kubernetes PodがNode上でスケジューリングされていること）を表すために使えます。

### 関係の配置

`entity.relationships`配列内にどちらのEntityが関係を保持するかを選ぶ際、実装は、生存期間が**より短い**、または**入れ替わりの頻度がより高い**Entity種別に関係を配置することを優先すべきです（SHOULD）。これにより、送出が必要になるEntity State Eventの総数を最小限に抑えられます。

**理由**: 関係はEntity State Eventに埋め込まれているため、あるEntityの関係が変化するたびに新しい状態イベントを送出しなければなりません。より安定したEntity側に関係を配置すると、生存期間の短いEntityが作成・破棄されるたびに頻繁な状態イベントの送出が必要になってしまいます。

**例**:

- **推奨**: `k8s.pod -> part_of -> k8s.replicaset`（関係をPodに配置する）
  - **こうではなく**: `k8s.replicaset -> contains -> k8s.pod`（関係をReplicaSetに配置する）
  - **理由**: Podは頻繁に入れ替わります。関係をPodに配置すれば、Podが作成・破棄されたときに新しいPodの状態イベントだけを送出すれば済みます。関係をReplicaSetに配置した場合、Podの作成・破棄が起きるたびに、含まれるすべてのPodの一覧を更新した新しいReplicaSetの状態イベントが必要になります。

- **推奨**: `container -> part_of -> k8s.pod`（関係をコンテナに配置する）
  - **こうではなく**: `k8s.pod -> contains -> container`（関係をPodに配置する）
  - **理由**: コンテナは独立して再起動することがあるため、関係をコンテナに配置すればPodの状態イベントの数を減らせます。

- **推奨**: `process -> runs_on -> host`（関係をプロセスに配置する）
  - **こうではなく**: `host -> hosts -> process`（関係をホストに配置する）
  - **理由**: プロセスは頻繁に開始・終了する一方、ホストは長期間存続します。

両方のEntityの生存期間が同程度である場合は、どちらの方向でも構いません。セマンティック規約は、一般的なEntity種別についての関係の配置に関するガイダンスを提供すべきです（SHOULD）。

### 関係のライフサイクル

**関係の作成**:
`entity.relationships`配列に新しい関係を含めたEntity State Eventを送出します。

**関係の更新**:
現在の状態を反映するよう更新された`entity.relationships`配列を持つ新しいEntity State Eventを送出します。

**関係の削除**:
`entity.relationships`配列からその関係を取り除いた新しいEntity State Eventを送出します。

**暗黙的な削除**:
あるEntityが削除される（Entity Delete Eventが送出される）と、そのEntityが関わるすべての関係は暗黙的に削除されます。バックエンドはこれに応じて処理すべきです（SHOULD）。

## 例

以下の例は、Entity Eventの論理的な表現を示します。これらは実際のOTLPワイヤーフォーマットの表現ではなく、イベントの意味構造を示すためのものです。

### Kubernetes Podのエンティティ状態

Kubernetes Podが作成されたとき、またはその属性が変化したとき、以下のようになります。

```
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
```

### サービスとプロセスの関係

プロセスのスクレイピングを行う（`hostmetricsreceiver`など）ホストレベルのOTel Collectorが、`process`というEntity State Eventを送出します。これはOSからプロセスのメタデータを読み取ります（Linuxでは`/proc`経由）。

```
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
```

`service.instance`というEntityは、OTel SDKによって送出されます。SDKは、自身のサービスの識別情報と、自身が実行されているプロセスの両方を把握している、信頼できる情報源です。`service.instance`は`process`への`runs_on`という関係を所有します。また、サービスインスタンスは自身が属する論理的なサービスよりも生存期間が短いため、`service`への`part_of`という関係も所有します。

```
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
```

`service`というEntityは論理的なサービスを表します。これも`service.instance`のEntity Eventと合わせて、OTel SDKによって送出されます。

```
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の削除

Podが終了したとき、以下のようになります。

```
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
```

