# Entityデータモデル

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


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

Entityは、生成されたテレメトリー（トレース、メトリクス、プロファイル、ログ）に関連付けられる、関心の対象となるオブジェクトを表します。

例えば、OpenTelemetry SDKを使って生成されたテレメトリーは、通常`service`というEntityに関連付けられます。同様に、OpenTelemetryはホストのシステムメトリクスを定義しており、この場合メトリクスを関連付けたいEntityは`host`です。

Entityは、生成されたテレメトリーに間接的に関連付けられることもあります。例えば、テレメトリーを生成するサービスは、そのサービスが実行されているプロセスとも関係があるため、`service`というEntityは`process`というEntityに関連していると言えます。プロセスは通常ホスト上でも実行されるため、`process`というEntityは`host`というEntityに関連していると言えます。

> [!NOTE]
> Entityの関係のモデリングは、今後の仕様策定作業の中で洗練されていく予定です。

以下のデータモデルは、Entityを記録する際の物理的な形式やエンコーディングとは関係なく、Entityの論理モデルを定義します。

| フィールド | 型 | 説明 |
| ----- | ---- | ----------- |
| Type | string | Entityの種別を定義します。Entityの生存期間中に変わってはなりません（MUST NOT）。例: "service"や"host"。このフィールドは必須であり、有効なEntityでは空であってはなりません（MUST NOT）。 |
| ID | map<string, attribute value> | Entityを識別する属性です。<p>Entityの生存期間中に変わってはなりません（MUST NOT）。IDは少なくとも1つの属性を含む必要があります。<p>OpenTelemetryの[属性の定義](../../common/#attribute)に従います。属性についてはOpenTelemetryの[セマンティック規約](https://github.com/open-telemetry/semantic-conventions)に従うべきです（SHOULD）。 |
| Description | map<string, attribute value> | Entityの記述的な（識別に使わない）属性です。<p>Entityの生存期間中に変わってもよく（MAY）、空でもよいものとします。これらの属性はEntityの識別の一部ではありません。<p>OpenTelemetryの[属性の定義](../../common/#attribute)に従います。属性についてはOpenTelemetryの[セマンティック規約](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/README.md)に従うべきです（SHOULD）。 |

## 必要十分な識別情報

一般に、テレメトリープロデューサーがIDを構成するために利用できるEntityの属性は複数あります。利用可能な属性のうち、EntityのIDには、そのEntityを一意に識別するために十分な最小限の属性の集合を含めるべきです。例えば、ホスト上のプロセスは（`process.pid`、`process.start_time`）という属性で一意に識別できます。これに加えて`process.executable.name`のような属性をIDに追加することは不要であり、必要十分な識別情報の原則に反します。

## 再現可能な識別情報

Entityの識別属性は、そのEntityを観測する側が繰り返し取得できる値であるべきです（SHOULD）。例えば、`process`というEntityは、その識別情報がプロセス自身（SDK経由など）から生成されたか、同じホスト上で稼働するOpenTelemetry Collectorから生成されたか、あるいはそのプロセスを記述する他の何らかのシステムから生成されたかにかかわらず、同一の識別情報を持つべきです（同一のプロセスとして認識されるべきです）。

> [!TIP]
> 複数の観測者にわたって再現可能な識別属性を実現する方法は数多くあります。多くの成功しているシステムは、中央のレジストリや知識ストアから識別情報を配布する方式に依っていますが、OpenTelemetryはあらゆる可能なシナリオに対応する必要があります。

## 識別属性

OpenTelemetryセマンティック規約は、定義済みのすべてのEntity種別について識別属性キーの集合を定義しなければなりません（MUST）。

識別属性の名前は、他のEntity種別との衝突を避けるため、そのEntity種別を接頭辞として使うべきです（SHOULD）。例えば、`k8s.node`というEntityは識別属性として`k8s.node.uid`を使います。

Entityが複数の観測者によって送出されうる場合、以下の規則が適用されます。

* 同じEntityを報告する2つの独立した観測者は、すべての識別属性について同一の値を提供できなければなりません（MUST）。

* ある観測者が1つ以上の識別属性を確実に取得できない場合、その観測者はそのEntity種別を使ってテレメトリーを送出してはなりません（MUST NOT）。代わりに、次のいずれかを行うべきです（SHOULD）。
  1. 完全な属性集合を提供できる観測者に委ね、その観測者を*信頼できる情報源*として扱う。
  2. 確実に補完できる識別属性の集合を持つ*別の*Entity種別を送出する。

これにより、観測者間でEntityの識別情報が一貫し、曖昧にならないことが保証されます。

## ResourceとEntity

OpenTelemetryのシグナル（メトリクス、ログ、トレース、プロファイル）は、`k8s.cluster`、`k8s.node`、`host`、`container`など、それぞれが特定のインフラストラクチャやランタイムのコンポーネントを表す1つ以上のEntityを添付できます。

以前は、Resourceデータモデルはシグナルごとにフラット化された属性に依存していました。Entityの導入により、テレメトリーは同一のシグナル内で複数の異なる、しかし互いに関連するコンポーネントを、それぞれ独自の識別情報と追加のメタデータを持つ形で表現できるようになります。EntityはResourceモデルと同じ属性のプールを利用します。これにより、データのより効率的なエンコーディングと転送が可能になるとともに、既存のResource属性との後方互換性も保たれます。

### 属性参照モデル

Entityは、テレメトリーシグナルの`resource`セクション内で定義できます。Entityの識別属性および記述属性は、Resource内で定義された共有属性を参照します。例えば、OTLPでは、Entityは自身のキーバリューペアを直接持ちません。代わりに、OTLP 1.xとの後方互換性を保つため、`resource.attributes`内のキーを参照します。

この方式は、属性が特定の構造に縛られず、異なるEntityをまたいで柔軟に参照できる属性のフラット化をサポートするために設計されています。このモデルは以下を提供します。

- Entityを共有属性で識別・記述する方法。
- データの重複や不整合を避ける能力。
- エンコーディングと転送のためのより効率的な表現。

### 共有される記述属性の配置

属性のフラット化により、複数のEntityが同じ属性キーを参照しつつ、Entityごとに異なる値を持つことが可能になります。このような状況では、以下の規則が適用されます。

複数のEntityが、値が競合しうる同じ記述属性キーを共有している場合、その属性は論理的に**いずれか1つ**のEntityにのみ属さなければなりません（MUST）。他のすべてのEntityはそれを参照すべきではありません（SHOULD NOT）。その属性は、テレメトリーシグナルに関連付けられたEntityに、トポロジーグラフ上で最も近い、**最も具体的な**Entityによって参照されなければなりません（MUST）。

**例:**

あるシグナルが`k8s.cluster`と`k8s.node`の両方のEntityを含み、両者が異なる値を持ちうる記述属性`cloud.availability_zone`を持つ場合、より具体的なEntityである`k8s.node`のEntity**だけ**がこのキーを参照できます。

他のEntity（例えば`k8s.cluster`）は、所有関係の全体像が把握できる別のテレメトリーチャネル（例えばEntity Event）でこの属性を報告できます。

## Entityの統合

Entityは、種別が同一であり、識別属性が完全に一致し、かつ`schema_url`が同一である場合に限り統合してもよい（MAY）ものとします。これは、両方のEntityが同一の識別属性キーを持ち、各キーについて値も一致していなければならないことを意味します。

統合可能性を確認するアルゴリズムの例を示します。

```
can_merge(current_entity, new_entity) {
  current_entity.type == new_entity.type &&
  current_entity.schema_url == new_entity.schema_url &&
  has_same_attributes(current_entity.identity, new_entity.identity)
}
```

Entityを統合する際、Descriptionに含まれるすべての属性は統合され、一方のEntityが「プライマリ」として扱われ、属性の値が競合する場合は「プライマリ」側のEntityの値が採用されます。

統合を行うアルゴリズムの例を示します。

```
merge(current_entity, new_entity) {
  if can_merge(current_entity, new_entity) {
    for attribute in new_entity.description {
      // New entity descriptions take precedence.
      current_entity.description.insert(attribute)
    }
  }
}
```

注: Entity同士の`schema_url`が異なる場合、統合を試みる前に（可能であれば）同じスキーマバージョンに変換すべきです（SHOULD）。ここで定義した統合アルゴリズムは、Entity同士が既に同じスキーマバージョンにあることを前提としています。

## Entityの例

_このセクションは非規範的であり、データモデルを示す目的でのみ記載しています。_

以下に、Entityの例と、それぞれが持つ典型的な識別属性、および関連付けられる可能性のある記述属性の例をいくつか示します。

_注: これらの例はセマンティック規約から逸脱している場合があります（MAY）。_

<table>
   <tr>
    <td><strong>Entity</strong>
    </td>
    <td><strong>Entity種別</strong>
    </td>
    <td><strong>識別属性</strong>
    </td>
    <td><strong>記述属性</strong>
    </td>
   </tr>
   <tr>
    <td>Container
    </td>
    <td><pre>container</pre>
    </td>
    <td>container.id
    </td>
    <td>container.image.id<br/>
        container.image.name<br/>
        container.image.tag.{key}<br/>
        container.label.{key}<br/>
        container.name<br/>
        container.runtime<br/>
        oci.manifest.digest<br/>
        container.command<br/>
    </td>
   </tr>
   <tr>
    <td>Host
    </td>
    <td><pre>host</pre>
    </td>
    <td>host.id
    </td>
    <td>host.arch<br/>
        host.name<br/>
        host.type<br/>
        host.image.id<br/>
        host.image.name<br/>
        host.image.version<br/>
        host.type
    </td>
   </tr>
   <tr>
    <td>Kubernetes Node
    </td>
    <td><pre>k8s.node</pre>
    </td>
    <td>k8s.node.uid
    </td>
    <td>k8s.node.name
    </td>
   </tr>
   <tr>
    <td>Kubernetes Pod
    </td>
    <td><pre>k8s.pod</pre>
    </td>
    <td>k8s.pod.uid
    </td>
    <td>k8s.pod.name<br/>
        k8s.pod.label.{key}<br/>
        k8s.pod.annotation.{key}<br/>
    </td>
   </tr>
   <tr>
    <td>Service Instance
    </td>
    <td><pre>service.instance</pre>
    </td>
    <td>service.instance.id<br/>
        service.name<br/>
        service.namespace
    </td>
    <td>service.version
    </td>
   </tr>
</table>

