> Source: https://www.ymotongpoo.com/works/otel-specs-ja/semconv/non-normative/groups/system/design-philosophy/


# システムセマンティック規約における計装の設計思想

システムセマンティック規約は、他のsemconvグループには見られない独特な二面性を抱えています。誰にでも当てはまる一般的な使用例を確実に押さえたい一方で、システムの健全性を監視することは非常に古くから行われてきた実践であり、既存の戦略も多岐にわたります。基本的な使用例をクロスプラットフォームな方法で押さえることはできますが、特定のプラットフォームに特化したユーザーを置き去りにしないようにしたいと考えています。クロスプラットフォームで普遍的ではない種類のデータに対して推奨事項が示されなければ、ユーザーは自分たちなりの計装の形を思い描くようになり、それこそがセマンティック規約が本来防ぐべき断片化を招くことになります。

以下の各節では、よくある計装の設計上の問いと、ワーキンググループとしてそれらにどう対応することにしたかを述べます。私たちの置かれている状況に固有の事情によって、一般的なセマンティック規約のガイダンスとは異なる判断をしている場合があり、そうした箇所は個別に明記します。

## 名前空間

関連する議論: [\#1161](https://github.com/open-telemetry/semantic-conventions/issues/1161)

システムセマンティック規約は、一般に次の名前空間を対象としています。

- `system`
- `process`
- `host`
- `memory`
- `network`
- `disk`
- `memory`
- `os`

メトリクスや属性の名前空間を決める際は、一般に次の考え方に基づきます。

**メトリクスや属性の名前空間は、計装の対象として想定しているオペレーティングシステムの概念に、論理的に対応づけられるべきです。**

最も分かりやすい例が、言語ランタイムのメトリクスと`process`名前空間のメトリクスです。これらのメトリクスの多くは非常に似ており、たいていの言語ランタイムは`cpu.time`や`memory.usage`など類似のメトリクスを何らかの形で提供しています。重複の排除を設計上の最優先事項とするなら、`process.cpu.time`や`process.memory.usage`は、そうしたメトリクスを生成しうる任意の言語ランタイムから単に参照されるべき、という結論になるはずです。しかし、ワーキンググループとしては、`process`名前空間とランタイム名前空間のメトリクスを分けておくことが重要だと考えています。`process`のメトリクスは**計装対象としてのOSレベルのプロセス**を表すことを意図しており、一方でランタイムのメトリクスは**計装対象としての言語ランタイム**を表すものだからです。

多くの場合これは、計装の目的をできるだけ明確にするという話にすぎませんが、異なる計装対象をまたいで定義を共有しようとすると、衝突が生じる可能性がある場合もあります。この結果を受け入れた具体例が`cpu.mode`です。[`*.cpu.state`という別々の属性のすべてのインスタンスを、共有の`cpu.mode`属性に統一する](https://github.com/open-telemetry/semantic-conventions/issues/1139)という判断がなされました。その結果、`cpu.mode`はルート定義において広範な列挙値を持つ必要があり、`cpu.mode`が使われる各`ref`ごとに特別な例外を設けています。`process.cpu.time`、`container.cpu.time`、`system.cpu.time`などで使われる`cpu.mode`は、列挙値全体のうち異なる部分集合を持つためです。私たちはグループとしてこのケースについては結果を受け入れることにしましたが、システムsemconv全体でこれを行いたいわけではありません。各名前空間にあまりに多くの例外的なケースが持ち込まれると、計装が汚染され、そもそも属性を共有していた目的が失われてしまうからです。

## 2つのクラスによる設計方針

関連する議論: [\#1403（該当コメント）](https://github.com/open-telemetry/semantic-conventions/issues/1403#issuecomment-2368815634)

システムsemconvの計装については、2つのペルソナを想定しています。ある計装があるとき、それがどちらのペルソナ向けかを判断し、その計装をどのように命名・扱うかを決めます。

### General class：計装を利用する誰にとっても簡単に手が届くようにしたい、汎用的でクロスプラットフォームな使用例

計装がGeneral Classに該当する場合、名前や例をできるだけ規範的にすることを目指します。この計装は、私たちがシステムセマンティック規約で本当に押さえたいと考える、最も重要な使用例を支えるものです。ダッシュボード、アラート、より広範なオブザーバビリティのセットアップに関するチュートリアルなどは、私たちがグループとして定めた[基本的な使用例][use cases doc]を扱うGeneral Classの計装を主に利用することになります。この計装については、いつ、どのように使うべきかを明確にしたいと考えています。General Classの計装は、**デフォルトで有効**にすることが推奨されます。

### Specialist class：すでに使い方を理解している専門家が、より詳細な情報を得るために有効化できる、より具体的な使用例

計装がSpecialist Classに該当する場合、想定する対象読者はすでにその概念に精通しており、自分が何を求めているか、なぜそれを求めているかを正確に理解している前提を置きます。Specialist Classの計装の目標は、非常に具体的で詳細な要件を持つユーザーが、独自の計装を考え出す手間をかけずに済むよう、セマンティック規約でその要件をきちんと満たすことです。それは、セマンティック規約がまさに解決しようとしている、計装が個別に分裂してしまう問題を再び招くことになるからです。Specialist Classの計装の扱いにおける主な違いは以下のとおりです。

1. 名前と結果として得られる値は、ユーザーが自分でその情報を探しに行ったときに期待するものへ直接対応づけられます。情報をどう使うべきか、どう分解すべきかについて規範的になることはほとんどありません。例えば、プロセスのcgroupを表すメトリクスの値は、ユーザーが`cat /proc/PID/cgroup`を実行した場合の結果と正確に一致します。
2. 特定のオペレーティングシステムに固有の計装については、その計装の名前にオペレーティングシステム名を含めます。詳しくは[名前におけるオペレーティングシステム](#オペレーティングシステム名の扱い)を参照してください。例えば、プロセスのcgroupを表すメトリクスは`process.linux.cgroup`となります。cgroupがLinuxカーネル固有の機能であるためです。

### 例

General Classの例:

- メモリ/CPUのusageおよびutilizationのメトリクス
- ディスクやネットワークに関する一般的なメトリクス
- システムやプロセスに関する普遍的な情報（名前、識別子、基本的な仕様など）

Specialist Classの例:

- 特定のLinux機能（procfs内のプロセス/システム固有の情報など。[/proc/meminfo](https://man7.org/linux/man-pages/man5/proc_meminfo.5.html)や[cgroups](https://man7.org/linux/man-pages/man7/cgroups.7.html)などを参照）
- 特定のWindows機能（プロセスに関する特殊な情報など。[Windowsハンドル](https://learn.microsoft.com/windows/win32/sysinfo/about-handles-and-objects)や[プロセスのワーキングセット](https://learn.microsoft.com/windows/win32/procthread/process-working-set)などを参照）
- オープンファイルディスクリプタやページフォルトなど、ニッチなプロセス情報

## 計装設計ガイド

新しい計装を設計する際は、次のステップにできるだけ忠実に従います。

### 計装クラスの選択

システムセマンティック規約において、ある計装がGeneralかSpecialistかを判断する際に最も重要な問いは以下のとおりです。

- クロスプラットフォームか？
- 私たちが最も重視する[使用例][use cases doc]をサポートしているか？サポートしているならGeneral Classとします。

この計装がGeneral Classとみなされるには、両方の問いに対する答えがおそらく「Yes」である必要があります。General Classの計装は最も広い対象読者に使われることを期待しているため、その計装が本当に必要かつ有用であることを、より厳しく検証する必要があります。

いずれかの問いに対する答えが「No」であれば、Specialist Classとみなす可能性が高くなります。

### 命名

General Classについては、プラットフォームに偏らず、一般的な概念を最も正確に表す名前を選びます。可能な限り単純さを重視します。これは最も広い対象読者が使う計装であり、理解しやすく、使い勝手が良いものにしたいからです。

Specialist Classについては、その文脈でその概念を表すために一般に使われている言葉に、最も直接的に一致する名前を選びます。この計装は任意項目であり、すでに自分が何を求めているかを正確に理解している人によって利用されることが多いため、名前をその定義にできるだけ近づけることを優先できます。プラットフォーム固有のSpecialist Classのメトリクスについて、同じメトリクス名がクロスプラットフォームな形で使われる可能性がほぼないのであれば、OSをサブ名前空間として（ルート名前空間ではなく）名前空間に含めます。詳しくは[この節](#オペレーティングシステム名の扱い)を参照してください。

### 値

General Classについては、計装の値がどうあるべきかについて規範的になることができます。General Classの計装が私たちの一般的な使用例に対するビジョンに最も近くなるようにしたいですし、専門家ではなく最も重要な基本情報だけを求めているユーザーが、標準搭載のsemconv計装を使ってできるだけ簡単にそれを取得できるようにしたいと考えています。つまり、汎用的な使用例に役立つのであれば、値を情報源から取得する際にその値を作り直すべきかどうかも含め、General Classの計装においては値がどうあるべきかについて判断を下すことが多くなります。

Specialist Classについては、規範的になることを避け、モデル化しようとしている概念にできるだけ近づけることを目指します。Specialist Classの計装は、すでにそれを理解している人によって有効化されることを想定しています。システムsemconvの文脈では、これはユーザーが以前は手作業や既存のOSツールを通じて収集していた情報を、OTLPとしてモデル化したいと考えるようなものかもしれません。

### 説明文

システムセマンティック規約でモデル化する概念は非常に複雑になることがあり、多くの一般的な実践者が必要とする以上に、オペレーティングシステムやコンピューティングの概念に関する深い知識を要する場合があります。その結果、モデル化している概念について詳細な説明を提供したくなります。しかし、知識のない読者にこれらの概念を教えるために必要な説明の量は、あまりに多くの文脈とニュアンスを要し、結果としてドキュメントを煩雑にし、本当に伝えるべき情報、つまり計装が何であるかだけでなく、どのように・なぜ特定の方法で計装するのかという詳細を見えにくくしてしまうと考えています。

つまり、システムセマンティック規約のドキュメントについては、**計装対象の概念について読者がある程度の基礎知識を持っていることを前提とします**。メトリクスや属性の`brief`フィールドと`note`フィールドは、計装の意図を理解するうえで欠かせない情報を伝えるために使うべきです。具体的には次のような情報です。

- 同じデータを異なるプラットフォームで計装した場合の違い
- 既存ツールから直接値を表示するのではなく、特定のデータに対して計算を行うことを推奨する場合
- 同じ概念を指す一般的な別の用語があるにもかかわらず、特定の名前や列挙値を選んだ場合

ルートのメトリクスや属性については、常に`brief`フィールドを持つことを目指します。`brief`フィールドは、そのメトリクスや属性が何であるかを説明するものであり、値の説明が単純な場合（例えば`procfs`のような一般的な情報源の値をそのまま表示するだけの場合）は、その値の説明を`brief`に含めることができます。値の計算方法についての説明や根拠が必要な場合は、その情報を`note`フィールドに移すべきです。列挙値については、属性全体に対して提供された`brief`があれば、それだけで各値の意図が自明であることが多いです。値についてすぐには明らかでない判断を下さなければならなかった場面では、`brief`を含めることができます。よくある例は、用語がプラットフォームによって異なり、すべての場面を表すために1つの用語を選ばなければならなかった場合です。この場合、`brief`を使って私たちの意図を明確にできます。

計装の意図を説明するためにある概念についての情報が必要な場合、その情報にはすべて権威あるドキュメント（Linuxの`man`ページやWin32のAPIドキュメントなど）への出典を付けなければなりません。既存の概念について、私たち自身の言葉で新しい説明を考え出すことは、この規約のドキュメント内では行いたくありません。

### ケーススタディ：`process.cgroup`

関連する議論: [\#1357](https://github.com/open-telemetry/semantic-conventions/issues/1357)、[\#1364（該当スレッド）](https://github.com/open-telemetry/semantic-conventions/pull/1364#discussion_r1730743509)

`hostmetricsreceiver`には、`process.cgroup`という名前のリソース属性があります。この属性をシステムセマンティック規約にどう取り込むべきでしょうか。

私たちの定義に基づけば、この属性はSpecialist Classに該当します。

- `cgroups`はLinux固有の機能である
- 私たちが押さえたいと考えているデフォルトの標準的な使用例の一部を直接構成するものではない

この属性については、名前を決める際に2つの重要な考慮点があります。

- この属性はSpecialist Classである
- Linux専用であり、他のOSに導入される可能性は低い（他の主要プラットフォームには、それぞれ独自の相当機能がある。WindowsのジョブオブジェクトやBSDのjailなど）

これは、この概念を指す際に専門家が文脈の中で使う言葉に一致する名前を選ぶべきだということを意味します。この概念を指すときの言い方は「`/proc/<pid>/cgroup`から収集した、プロセスのcgroup」となります。そこから`process.cgroup`という名前を出発点とします。さらに、この属性はLinux専用であり今後もそうであり続けると確信しているため、`process.linux.cgroup`という名前に落ち着きます。

このメトリクスはSpecialist Classに該当するため、値について規範的になりすぎないようにします。プロセスの`cgroup`を知る必要のあるユーザーは、すでにその解釈や使い方について十分な理解を持っていることが多く、このワーキンググループがあらゆる使われ方の可能性を想定しようとする価値はありません。この属性については、私たちの管轄が及ぶ範囲において、OSからの値、つまり`cat /proc/<pid>/cgroup`の直接の値をそのまま反映することが、はるかに単純です。特にcgroupについては、より専門化されたsemconvの計装が今後生まれる可能性が高く、特にコンテナランタイムやsystemdに特化した計装を支える形になるでしょう。cgroupを活用する専門的な計装を開発するワーキンググループにとっては、cgroup情報をより詳細にどう解釈し分解すべきかについて、より規範的であることの方が有用です。

## オペレーティングシステム名の扱い

関連する議論: [\#1255](https://github.com/open-telemetry/semantic-conventions/issues/1255)、[\#1364](https://github.com/open-telemetry/semantic-conventions/pull/1364#discussion_r1852465994)、[\#2984](https://github.com/open-telemetry/semantic-conventions/pull/2984#discussion_r2466369361)

オペレーティングシステムの監視は古くから行われている実践であり、プラットフォームごとに大きく異なるアプローチが数多く存在します。メモリ使用量のような一般的な統計情報であっても、特定のプラットフォームに特化した情報がその中に多く含まれており、それはそのプラットフォームを専門とする人にしか価値を持たない場合があります。

そのため、次の条件に当てはまる計装については、名前空間の一部にオペレーティングシステム名を含めることにしています。

1. 特定のオペレーティングシステムに固有である
2. 私たちが最も重要と考える一般的な使用例の一部として意図されていない

例えば、メトリクスや属性の名前として`process.linux`、`process.windows`、`process.posix`などが考えられます。ルートの`linux.*`、`windows.*`、`posix.*`という名前空間は持ちません。これは、[名前空間の節](#名前空間)で述べた原則、つまり属性やメトリクスのルート名前空間には計装対象を表させたいという原則を維持するためです。OSごとのルート名前空間があれば、`system`や`process`などの異なる計装対象がOSの名前空間の中でひどく絡み合ってしまい、意図した設計思想が損なわれてしまいます。

ただし、OS名を「ルート名前空間」レベルで避けるという話には、領域レベルでも同様に避けるという意味も含まれていることを明確にしておきます。OS名は、対象領域（`system.memory.linux.*`など）よりも後に置くべきであり、その前には置きません。これにより、ユーザーはまず対象領域（メモリ、CPU、ネットワークなど）で目的の情報を探し、必要であればそこからOS固有のバリエーションへと掘り下げていくことができます。

[use cases doc]: /works/otel-specs-ja/semconv/non-normative/groups/system/use-cases/

