OpenTelemetry超入門

# 概要

この記事は、OpenTelemetryについて全く知らない人に向けた入門記事です。前半でOpenTelemetryとは何か解説し、後半でデモアプリケーションを動かしながら、理解を深めていきます。

![ot_logo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/438013bc-96db-f1e8-e743-b01d7d3d3583.png)

:::note info
この記事の執筆時点のOpenTelemetryの最新バージョンは、1.8.0です。参照する公式ドキュメントや動作確認するデモアプリケーションのバージョンも1.8.0です。
:::

## OpenTelemetryとは

OpenTelemetryは、マイクロサービスアーキテクチャーで分散されたサービス（アプリケーション）の健全性や性能を示す「テレメトリーデータ」を生成、収集、管理、エクスポートするためのOSSです。

大規模な分散システムでは、健全性や性能を把握することが極めて困難なため、迅速なトラブルシューティングのためにはテレメトリーデータの収集が求められます。分散されたサービス（アプリケーション）からテレメトリーデータを収集するには、それらのアプリケーションが異なるプログラミング言語で実装していたとしても、それらに組み込むことができる、標準化された（ベンダーに依存しない）APIやSDKなどが必要です。それを提供するのがOpenTelemetryです。

OpenTelemetryの目的は、あくまでもテレメトリーデータを簡単に生成、収集、管理、エクスポートできるようにすることであり、テレメトリーデータの保存と可視化はJaegerやPrometheusのようなOSSまたは商用製品に任せるように、役割分担されています。

ちなみに、[OpenTelemetryの公式サイト](https://opentelemetry.io/docs/what-is-opentelemetry/)では、以下のようにOpenTelemetryとは「Observability framework」（オブザーバビリティーフレームワーク）である説明しています。

> OpenTelemetry is an Observability framework and toolkit designed to create and manage telemetry data such as traces, metrics, and logs. 

## オブザーバビリティーとは

オブザーバビリティー（可観測性）とは、システムやアプリケーションの内部の状態や挙動を把握し、問題（例えば、リクエストに対する処理の遅延など）を追跡および解決するための能力を指します。オブザーバビリティーを向上させる目的は、システム全体の健全性を維持し、問題を早期に検出して迅速に対処することです。これにより、システムの信頼性や可用性を向上させることができます。オブザーバビリティーを実現するためには、次に説明するテレメトリーデータが必要になります。

## テレメトリーデータとは

OpenTelemetryにおけるテレメトリーデータとは、以下の3つを意味します。

 - ログ：主にトラブルシューティングを容易にすることを目的に記録するメッセージ（テキスト形式の情報）です。ログには、このメッセージとともに、ログ生成処理が実行された時刻やログレベルなどが含まれます。ログの形式には、プレーンテキスト、構造化、非構造化の3種類がありますが、プレーンテキストが最も一般的です。
 <!-- 
 発生したイベントを各時点で記録したテキストです。コードブロックが実行されると、そのイベントが発生した日時やイベントに関する「ペイロード」を記録したログエントリーが生成されます。ログの形式には、プレーンテキスト、構造化、非構造化の3種類があります。プレーンテキストが最も一般的ですが、近年では、追加のメタデータを含み照会が容易な構造化ログも増えています。これに対して、非構造化ログは解析が困難です。ログは、通常、アプリケーションの動作を正確に示す情報源として使われます。システムで問題が発生したときに確認したり、開発者がコードの問題を解決して実行状況を検証するときに使用したりします。分散システムでの障害は、多くの場合、下層での問題(根本原因)が積み重なって発生しますが、ログを調べれば、特定のコードブロックが実行されたときの詳細を確認できます。
```
I, [2021-02-23T13:26:23.505892 #22473]  INFO -- : [6459ffe1-ea53-4044-aaa3-bf902868f730] Started GET "/" for ::1 at 2021-02-23 13:26:23 -0800
```
-->
 - メトリクス：一定の期間にわたって測定された何らかの数値です。日時、イベント名、イベント値などの属性が記録されます。ログとは異なり、形式は構造化が基本であるため、照会が容易です。保存にも適しており、大量のメトリクスを長期間保存して、システムの過去の傾向を把握することもできます。メトリクスを監視して、問題の兆候を検出した場合にアラートするようなこともできます。
<!--メトリクスは、一定の期間にわたって測定された数値です。タイムスタンプ、イベント名、イベント値などの属性が記録されます。ログとは異なり、形式は構造化が基本であるため、照会が容易です。保存にも適しており、大量のメトリクスを長期間保存して、システムの過去の傾向を把握することもできます。大規模なデータセットを作成したり、データを定期的に収集したりして、ビジネス上の疑問の答えを導き出すのにも最適です。メトリクスは長期にわたって記録することが多く、そうしたデータは、今日の複雑なシステムで問題を分析して対応するために欠かせません。メトリクスを集約したり、問題の初期兆候を示す可能性のあるメトリクスを監視したりして、アラートの精度を高めることもできます。
```json
{
  "name": "hello-greetings",
  "context": {
    "trace_id": "0x5b8aa5a2d2c872e8321cf37308d69df2",
    "span_id": "0x5fb397be34d26b51"
  },
  "parent_id": "0x051581bf3cb55c13",
  "start_time": "2022-04-29T18:52:58.114304Z",
  "end_time": "2022-04-29T22:52:58.114561Z",
  "attributes": {
    "http.route": "some_route2"
  },
  "events": [
    {
      "name": "hey there!",
      "timestamp": "2022-04-29T18:52:58.114561Z",
      "attributes": {
        "event_attributes": 1
      }
    },
    {
      "name": "bye now!",
      "timestamp": "2022-04-29T18:52:58.114585Z",
      "attributes": {
        "event_attributes": 1
      }
    }
  ]
}
```
-->
 - トレース：分散システムにおけるリクエストの処理経路を表すデータです。分散システムでリクエストが処理される際には、さまざまな処理（外部APIの呼び出し、DBからのデータの取得、など）が実行されます。これらの各処理を表すデータは「スパン」と呼ばれ、操作の過程でトレースID、スパンID、操作名、開始/終了の日時、イベントなどが含まれます。このスパンにより構成されるトレースを監視・追跡すること（「分散トレーシング」と呼ばれる）により、エラー、遅延、リソースの可用性などの問題の原因となっているコードブロックを特定できます。
<!--トレースは、分散システムにおけるリクエストの処理経路を表すデータです。分散システムでリクエストが処理される際には、さまざまな処理（外部APIの呼び出し、DBからのデータの取得、など）が実行されます。これらの各処理は「スパン」と呼ばれ、操作の過程でトレースID、スパンID、操作名、開始/終了のタイムスタンプ、イベント、その他のインデックス化データなどの情報がエンコードされます。この分散トレーシングと呼ばれる手法でトレースを確認することにより、実行パス全体を追跡して、エラー、遅延、リソースの可用性などの問題の原因となっているコードブロックを特定できます。トレースでメトリクスを生成することもできます。特に、RED (比率、エラー、期間)と呼ばれる形式のメトリクスを生成できます。さらに重要な点として、トラブルシューティングの際に、どのログやメトリクスがその問題と深く関係しているかを示すコンテキストをトレースから取得できます。
```json
{
  "name": "hello-greetings",
  "context": {
    "trace_id": "0x5b8aa5a2d2c872e8321cf37308d69df2",
    "span_id": "0x5fb397be34d26b51"
  },
  "parent_id": "0x051581bf3cb55c13",
  "start_time": "2022-04-29T18:52:58.114304Z",
  "end_time": "2022-04-29T22:52:58.114561Z",
  "attributes": {
    "http.route": "some_route2"
  },
  "events": [
    {
      "name": "hey there!",
      "timestamp": "2022-04-29T18:52:58.114561Z",
      "attributes": {
        "event_attributes": 1
      }
    },
    {
      "name": "bye now!",
      "timestamp": "2022-04-29T18:52:58.114585Z",
      "attributes": {
        "event_attributes": 1
      }
    }
  ]
}
```
-->

## OpenTelemetryのアーキテクチャーと構成要素

以下の図は、後述するデモアプリケーションにおけるテレメトリーデータのフローを表した図ですが、OpenTelemetryのアーキテクチャーと構成要素を大雑把に示していると考えられます。

![スクリーンショット 2024-03-12 161808.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/7b0bc6af-53fb-3b3c-fb80-8dd0ef4ad2ac.png)

最上部の「OpenTelemetry Demo」の「Microservice」は、様々のプログラミング言語で実装された複数のマイクロサービスです。これらのサービスに、実装するプログラミング言語に対応したOpenTelemetryのSDKが組み込まれています。そのSDKにより生成されたテレメトリーデータがOpenTelemetry Collectorの構成要素であるReceiverにHTTPやgRPCで送られ、それをProcessorが処理します。処理されたデータは、ExporterによりPrometeusやJaegerにHTTPやgRPCで送信されます。

# 動作検証

OpenTelemetryプロジェクトでは、デモアプリケーション「OpenTelemetry Demo」を公開しているので、これを動かしながら、OpenTelemetryの理解を深めていきましょう。

https://github.com/open-telemetry/opentelemetry-demo

以下のように、GitHubからクローンして、`opentelemetry-demo`ディレクトリに移動します。

```
$ git clone https://github.com/open-telemetry/opentelemetry-demo.git
$ cd opentelemetry-demo/
```

## デモの前提条件

OpenTelemetry Demoを起動する前提条件は、以下になっています。

 - Docker
 - Docker Compose v2.0.0以上
 - アプリケーション用に6GBのRAM

## デモのアーキテクチャー

OpenTelemetry Demoは、gRPCとHTTPを介して互いに対話する、異なるプログラミング言語で書かれたマイクロサービスと、ユーザートラフィックを偽造するためにLocustを使用する負荷ジェネレータで構成されています。このデモのアーキテクチャーは以下のようになっています。

![スクリーンショット 2024-03-05 094319.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/32339bdf-c653-e3d1-2df2-35f832dd2b5b.png)

詳細は、ドキュメントを確認してください。

https://opentelemetry.io/docs/demo/architecture/

## デモを動かしてみよう

では実際にOpenTelemetry Demoを動かしてみましょう。以下のように、`docker compose up`コマンドで起動しますが、

```
$ docker compose up --force-recreate --remove-orphans --detach
```

私の環境ではKafkaが起動せず、エラーになります。

```
[+] Running 18/18
 ✔ Container grafana                  Running                                                                                                                                                                                                                                                            0.0s 
 ✔ Container ffs-postgres             Healthy                                                                                                                                                                                                                                                            0.0s 
 ✔ Container jaeger                   Running                                                                                                                                                                                                                                                            0.0s 
 ✔ Container redis-cart               Running                                                                                                                                                                                                                                                            0.0s 
 ✔ Container prometheus               Running                                                                                                                                                                                                                                                            0.0s 
 ✘ Container kafka                    Error                                                                                                                                                                                                                                                              0.0s 
 ✔ Container opensearch               Running                                                                                                                                                                                                                                                            0.0s 
 ✔ Container feature-flag-service     Running                                                                                                                                                                                                                                                            0.0s 
 ✔ Container otel-col                 Running                                                                                                                                                                                                                                                            0.0s 
 ✔ Container ad-service               Running                                                                                                                                                                                                                                                            0.0s 
 ✔ Container currency-service         Running                                                                                                                                                                                                                                                            0.0s 
 ✔ Container email-service            Running                                                                                                                                                                                                                                                            0.0s 
 ✔ Container product-catalog-service  Running                                                                                                                                                                                                                                                            0.0s 
 ✔ Container shipping-service         Running                                                                                                                                                                                                                                                            0.0s 
 ✔ Container cart-service             Running                                                                                                                                                                                                                                                            0.0s 
 ✔ Container quote-service            Running                                                                                                                                                                                                                                                            0.0s 
 ✔ Container payment-service          Running                                                                                                                                                                                                                                                            0.0s 
 ✔ Container recommendation-service   Running                                                                                                                                                                                                                                                            0.0s 
dependency failed to start: container kafka is unhealthy
```

原因は未調査ですが、とりあえず単独で起動させれば、よさそうです。

```
$ docker start kafka
```

ただ、上のログで分かるように起動するサービスも多すぎますし、メモリーも大量に消費するので、もう少し小さな構成にできると、ありがたいです。ということで、調べてみると、カレントディレクトリーに`docker-compose.yml`の他に`docker-compose.minimal.yml`というファイルがあることに気づきました。

差分を見ると、Feature Flagsとそれが使用するPostgreSQLなどのコンテナー、アカウントサービスやテスト用のコンテナーなどが`docker-compose.minimal.yml`には含まれていないことが分かります。また、各コンテナーのメモリーの使用量が大幅に制限されていることも読み取れます。

![Screenshot from 2024-03-07 09-59-14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/479e1dd4-2c0e-7214-e320-ab8962bb868d.png)

前述のデモのアーキテクチャーの図では、赤枠で囲まれたサービスが含まれていません。

![スクリーンショット 2024-03-05 094319.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/8925ba97-1282-3e19-b411-ec00f25ba9dc.png)

前回起動したものをいったん終了させて、

```
$ docker compose down
```

このファイルを指定して、起動してみましょう。

```
$ docker compose -f docker-compose.minimal.yml up --remove-orphans --detach
```

```
[+] Running 19/19
 ✔ Container redis-cart               Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container jaeger                   Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container opensearch               Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container grafana                  Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container prometheus               Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container otel-col                 Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container email-service            Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container payment-service          Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container ad-service               Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container quote-service            Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container cart-service             Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container currency-service         Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container shipping-service         Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container product-catalog-service  Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container recommendation-service   Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container checkout-service         Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container frontend                 Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container load-generator           Running                                                                                                                                                                                                                                                    0.0s 
 ✔ Container frontend-proxy           Started  
```

今度は正常に起動したようです。これにより、以下のアプリケーションにアクセスできるようです。

| アプリケーション | URL | アプリケーションの概要 | 最小構成（※） |
|:-----------|:------------|:------------|:------------:|
| Webストア       | http://localhost:8080/        | オンラインストアのサンプルアプリ。様々なマイクロサービスと連携して、機能を実現している。         | 〇       |
| Grafana     | http://localhost:8080/grafana/      | OpenTelemetryがテレメトリーデータを視覚化し、システム状態の分析するためのアプリ。       | 〇       |
| Feature Flags       | http://localhost:8080/feature/        | 様々なマイクロサービスの特定の機能を意図的に異常動作させて、一定の確率でエラーを発生させるかように切り替えるためのUI。         | ×       |
| Locust（ロードジェネレーター）         | http://localhost:8080/loadgen/          | UIのある負荷テストツール。現実的なユーザーの買い物の流れを模したリクエストをフロントエンドに送り続けるサービス。           | 〇       |
| Jaeger       | http://localhost:8080/jaeger/ui/       | OpenTelemetryが生成したテレメトリーデータを収集し、それを可視化、分析するUI。      | 〇       |

※最小構成の`docker-compose.minimal.yml`に含まれるかどうか。

### Webストア

まずは[Webストア](http://localhost:8080/)にアクセスしてみましょう。トップページは以下のようになっています。「Go Shopping」をクリックして、適当に買い物をしてみましょう。

![Screenshot from 2024-02-20 15-34-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/c9372542-7504-2915-7e13-b5f05a47c2f5.png)

適当に商品を選択し、

![Screenshot from 2024-03-06 13-42-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/19b4655a-b971-a49e-3798-6710c1ecb2a1.png)

購入してみます。

![Screenshot from 2024-03-06 13-42-46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/d23e45b8-b29a-6480-f9bd-07afa2e01175.png)

購入が完了しました。非常に単純なオンラインストアアプリですね。

![Screenshot from 2024-03-06 13-43-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/63da9522-52ae-ea35-65a4-aad43024ef69.png)

これで様々サービスへリクエストが送信されたことになります。

### Jaeger

次に[JaegerのUI](http://localhost:8080/jaeger/ui/)にアクセスします。以下のような画面が表示されるので、「Service」に「frontend」を選択して、検索してみましょう。

![Screenshot from 2024-02-20 15-44-52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/c6284a12-69dc-08ff-9fc9-6558771a29f3.png)

リクエストの送信時刻と処理時間を可視化したものが表示されます。

![Screenshot from 2024-03-06 14-00-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/b3099a51-18fc-2bf8-234b-d86b636f6515.png)

この中の●の1つをクリックしてみると、以下のように内部でカートサービスなどのサービスにリクエストしていることが分かります。

![Screenshot from 2024-03-06 14-00-32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/ae40759b-f5ae-865a-fdb3-07b065994c62.png)

System ArchitectureタブのDAGタブをクリックします。これを見ると、サービス間でどのようにリクエストが行われているかが分かります。矢印に付与されている数値は、各サービス間のトレース数を表しています。これは、あるサービスから別のサービスにトレースがどれだけの数で移動しているかを示しています。つまり、トレース数が多い矢印は、サービス間の通信がより頻繁に行われていると言えます。

![Screenshot from 2024-03-05 10-03-06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/693f89eb-03a0-1d92-7d1a-56c8681764a1.png)

Monitorタブをクリックすると、全体や各URLにおけるレイテンシーやエラー率が確認できます。

![Screenshot from 2024-03-06 14-01-24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/f69a5e25-0f63-215c-4b8c-f39de53354ec.png)

### Grafana

[Grafana](http://localhost:8080/grafana/)にもアクセスてみましょう。

![Screenshot from 2024-02-20 15-41-40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/ce967b07-5d07-9087-caad-0fd90dfac846.png)

「Create your first dashboard」でダッシュボードを作成すると、様々なメトリクスを可視化することができます。

![Screenshot from 2024-03-06 17-33-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/dd5af48b-673d-5ad7-cee9-f5b60873ed45.png)

### Locust

ロードジェネレーターである[Locust](http://localhost:8080/loadgen/)のUIにもアクセスしてみましょう。

![Screenshot from 2024-02-20 15-44-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/f6832ad9-d46a-3c18-41a0-b8a2947ee6cb.png)

デフォルトでは10ユーザーがオンラインストアアプリにアクセスしているようなリクエストが自動的に転送されているようです。

STOPボタンをクリックして、このリクエストの転送をやめてみます（負荷を無くしてみます）。しばらく時間をおいてグラフを見てみると、いくつかのメトリクスが急降下してそのまま上昇しないことがわかります（※以下のグラフでは3時間ほど放置しています）。

![Screenshot from 2024-03-06 21-10-48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/4d0c0f3a-462e-7671-d35f-f4d5c386c6a9.png)

では負荷を最初の10倍にしてみましょう。

![Screenshot from 2024-03-06 21-14-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/19afba40-f9fd-29dc-0434-dece48a24950.png)

これにより、いくつかのメトリクスが急上昇します。

![Screenshot from 2024-03-06 21-28-29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/5f6f0b0a-5b92-95cc-aea3-8888a27c0063.png)

### Feature Flags

ちなみにFeature Flagsが起動している場合、アクセスすると以下のような画面が表示されます。

![Screenshot from 2024-02-20 15-43-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/a9e8d356-6ca5-a30c-4529-d40e64cedf99.png)

特定のサービスに意図的にエラーを発生させることで、どのような違いが出るか確認することができます。

# ソースコード解析

各アプリケーションのソースコードにはどのようなOpenTelemetryのコードが追加されているのでしょうか？ソースコードを解析してみましょう。

デモのアーキテクチャーの図を見てわかるように、サービスは様々なプログラミング言語で実装されているので、自分が見やすい言語のサービスのソースコードを読むことができます。

ここでは、Pythonで実装されたレコメンデーションサービスのソースコードをチェックしてみます。このサービスは、ユーザーが閲覧した商品に基づいて、ユーザーに推奨される商品のリストを取得する役割を果たします。

まずはレコメンデーションサービスのディレクトリーに移動します。

```shell-session
$ cd src/recommendationservice/
```

`tree`コマンドでディレクトリー構成を見てみましょう。

```shell-session
$ tree
```

```
.
├── Dockerfile
├── README.md
├── logger.py
├── metrics.py
├── recommendation_server.py
└── requirements.txt
```

6ファイルだけで、非常にシンプルですね。`requirements.txt`を見ると、OpenTelemetryのライブラリーが依存関係にあるのが分かります。

```txt
opentelemetry-distro==0.43b0
opentelemetry-exporter-otlp-proto-grpc==1.22.0
```

どのような実装になっているのでしょうか。メインの処理が実装されていると思われる`recommendation_server.py`を覗いてみましょう。

まず、OpenTelemetryでテレメトリーデータ（トレース、メトリクス、ログ）を生成するためのモジュールをインポートしています。

```python
from opentelemetry import trace, metrics
from opentelemetry._logs import set_logger_provider
```

これらのモジュールを使って、トレース、メトリクス、ログを生成するためのコードを見てみましょう。

### トレースを生成するためのコード

トレースを生成するためのOpenTelemetryの`Tracer`オブジェクトは、以下のコードで初期化するようです。

```python
    tracer = trace.get_tracer_provider().get_tracer(service_name)
```

スパンは、以下のようにOpenTelemetryの`Tracer`オブジェクトの`start_as_current_span()`メソッドを使って生成します。`with`文とともに生成すれば、`with`文のコードブロックが終了する時点でスパンも終了します。

```python
    with tracer.start_as_current_span("get_product_list") as span:
```

`get_product_list`は、推奨する商品リストを取得するためのスパンの名前です。

### メトリクスを生成するためのコード

メトリクスを生成するオブジェクトは、以下のコードで初期化していますが、

```python
    meter = metrics.get_meter_provider().get_meter(service_name)
    rec_svc_metrics = init_metrics(meter)
```

この`init_metrics()`は以下のような実装になっており、`meter.create_counter()`で`Counter`オブジェクトを生成し、

```python
def init_metrics(meter):

    # Recommendations counter
    app_recommendations_counter = meter.create_counter(
        'app_recommendations_counter', unit='recommendations', 
        description="Counts the total number of given recommendations"
    )

    rec_svc_metrics = {
        "app_recommendations_counter": app_recommendations_counter,
    }

    return rec_svc_metrics
```

サービスの呼び出し1回当たりの推奨商品数の合計をメトリクスとして保存するようです。

```python
        rec_svc_metrics["app_recommendations_counter"].add(len(prod_list), 
            {'recommendation.type': 'catalog'})
```

なお、CPUやメモリーの消費量、GCの回数などのメトリクスは自動的に取得できるようになっています。そのためのコードをアプリケーションに追加する必要はありません。

### ログを生成するためのコード

ログを生成するオブジェクトの初期化は、以下のようになっています。

```python
    # Initialize Logs
    logger_provider = LoggerProvider(
        resource=Resource.create(
            {
                'service.name': service_name,
            }
        ),
    )
    set_logger_provider(logger_provider)
    log_exporter = OTLPLogExporter(insecure=True)
    logger_provider.add_log_record_processor(BatchLogRecordProcessor(log_exporter))
    handler = LoggingHandler(level=logging.NOTSET, logger_provider=logger_provider)

    # Attach OTLP handler to logger
    logger = logging.getLogger('main')
    logger.addHandler(handler)
```

初期化が完了したら、以下のようにメッセージを含むログをレベルに応じて生成することができます。

```python
        logger.info(f"Receive ListRecommendations for product ids:{prod_list}")
```

今回はPythonで実装されたサービスを確認してみましたが、他のプログラミング言語のソースコードにも同様の実装になっていると思います。

# 感想

OpenTelemetryを利用することで、複雑な分散システムでもプログラミング言語に依存せず標準化されたテレメトリーデータを生成できる点は非常に有用だと思いました。テレメトリーデータは分散システムの健全性や性能を把握するだけでなく、機械学習を活用して障害の予兆の検出やリソースの削減などにも利用できそうです。ただし、実装を誤っていると、かえって混乱を招きトラブルシューティングの時間を遅くしたり、不要な作業を発生させてしまうかもしれません。

# 参考

 - [OpenTelemetry公式サイト](https://opentelemetry.io/)
 - [OpenTelemetry Demo Documentation](https://opentelemetry.io/docs/demo/)
 - [GitHub - OpenTelemetryプロジェクト](https://github.com/open-telemetry)