Web開発
RailsでOpenTelemetryログを設定する方法
How we configured OpenTelemetry logs in Rails (sixpatterns.com)
要約
この記事では、ベンダーロックインを避けつつRailsプロジェクトにオブザーバビリティを追加するために、OpenTelemetryログを構成する方法を説明します。OpenTelemetryは、ログ、メトリクス、トレースの3種類のテレメトリデータを標準的なOTLPフォーマットで生成し、バックエンドの切り替えを容易にします。筆者らは、OpenTelemetry Ruby SDKを使用して、Collectorを介さずに直接Grafana Cloudへログをエクスポートする設定を行い、その過程で遭遇した問題点と、SDKへのアップストリーム修正貢献について共有しています。
全文翻訳
最近、単一ベンダーへのロックインを避けつつ、Railsプロジェクトにオブザーバビリティを追加する必要がありました。OpenTelemetryが自然な選択でした。これは、ログ、メトリクス、トレースと呼ばれる3種類のテレメトリデータを生成します。これらのシグナルはそれぞれ独立しているため、他のシグナルなしで1つを採用することができます。これらのシグナルはすべて、OTLP(OpenTelemetry Protocol)として知られる標準的でベンダーに依存しない形式で転送されます。この形式は業界標準であるため、アプリケーションコードを書き直すことなく、将来的にバックエンド(Datadog、New Relic、Grafana Cloudなど)を切り替えることができます。
この記事では、OpenTelemetry Ruby SDKを設定して、ログを直接ベンダーであるGrafana Cloudにエクスポートする方法を説明します。また、途中で遭遇したいくつかの問題と、アップストリームに貢献した修正についても議論します。
ベンダーへの直接エクスポート
標準的なOpenTelemetryのデプロイメントでは、アプリケーションの横でOpenTelemetry Collectorを実行します。Collectorは、通常はサイドカーコンテナまたは同じホスト上のサービスである別のプロセスです。アプリケーションはローカルネットワーク経由でテレメトリをCollectorに送信し、Collectorはデータをバッチ処理してベンダーに転送します。
インフラストラクチャをシンプルに保つため、Collectorをバイパスし、Ruby SDKから直接Grafana Cloudにログをエクスポートしました。Scout APMのロギングgemも同様のアプローチを使用しており、アプリから直接ログを送信します。ログの量は少ないため、SDKの組み込みバッチ処理で十分です。バッファリング、サンプリング、またはアプリ外でのスクラビングが必要な場合は、Collectorが依然としてより良い選択肢です。
ログ用のRuby SDKの設定
Gemfileに以下のgemを追加します。
```ruby
gem "opentelemetry-sdk"
gem "opentelemetry-logs-sdk"
gem "opentelemetry-exporter-otlp"
gem "opentelemetry-exporter-otlp-logs"
gem "opentelemetry-instrumentation-all"
gem "opentelemetry-instrumentation-logger"
```
OpenTelemetryは非常にモジュール化されているため、各gemは特定の責任を担います。
- opentelemetry-sdk: コアのOpenTelemetryフレームワーク(トレースと設定のエントリポイント)。
- opentelemetry-logs-sdk: ロギングシグナル(トレースとは別)のサポートを追加します。
- opentelemetry-exporter-otlp: OTLP経由でネットワーク上にトレースをエクスポートします。
- opentelemetry-exporter-otlp-logs: OTLP経由でネットワーク上にログをエクスポートします。
- opentelemetry-instrumentation-all: Rails、Rack、Active Recordを含むインストルメンテーションgemをバンドルします。
- opentelemetry-instrumentation-logger: 標準のRuby Loggerにフックし、ログメッセージをOpenTelemetryログレコードにします。
これらのgemをインストールしたら、エクスポーターはデータをどこに送信するかを知る必要があります。ベンダーのエンドポイントと認証トークンを環境変数として設定します(SDKは標準のOTLPエクスポーター環境変数を自動的に検出します)。
```bash
OTEL_EXPORTER_OTLP_ENDPOINT="https://your-vendor.com/otlp"
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic <your-token>"
```
最後に、SDKを初期化してデータ収集とエクスポートを開始する必要があります。イニシャライザーを作成します(config/initializers/opentelemetry.rb)。
```ruby
return if ENV["OTEL_EXPORTER_OTLP_ENDPOINT"].blank?
OpenTelemetry::SDK.configure do |c|
c.service_name = "our-rails-app"
c.use_all
end
```
ここで、`c.use_all` は、`opentelemetry-instrumentation-all` および `opentelemetry-instrumentation-logger` にバンドルされているものを含む、要求されたすべてのインストルメンテーションを有効にします。
ローカルでのテスト
SDKが設定されたので、`OTEL_LOGS_EXPORTER=console` を設定してテストできます。これにより、ベンダーに送信する代わりにターミナルにログレコードが出力されます。これは、デプロイする前にセットアップが機能することを確認する最も簡単な方法です。
Ruby SDKで見つかった問題と修正
Grafana Cloudにログをエクスポートした際、Ruby SDKが他の言語SDKやOpenTelemetry仕様とは異なる動作をする2つの問題が見つかりました。
1. エクスポーターがベースパスをドロップした
一部のベンダーバックエンドでは、Grafana Cloudの場合のように `/otlp` のような特定のベースパスを持つエンドポイントにOTLPデータを送信する必要がありますが、エクスポーターはシグナルパスを追加する際にそれをドロップしました。
- エンドポイント: `https://your-vendor.com/otlp`
- 期待されるもの: `https://your-vendor.com/otlp/v1/logs`
- 実際のもの: `https://your-vendor.com/v1/logs`
この問題はissue #2157で報告し、PR #2158で修正され、`opentelemetry-exporter-otlp-logs` v0.5.1でリリースされました。
2. HTTP 204レスポンスの処理
Grafana Cloudはログを取り込んだ後、204 No Contentを返しますが、エクスポーターは200 OKのみを成功として扱っていました。実際にはエクスポートは成功していましたが、アプリはそれぞれを失敗としてログに記録していました。この問題はissue #2043で提起し、PR #2044で修正され、`opentelemetry-exporter-otlp-logs` v0.4.0でリリースされました。
次のステップ: 構造化ロギング
ログが正常にエクスポートされるようになったので、次の論理的なステップは構造化ロギングです。これはここでカバーするには多すぎますが、`rails_semantic_logger` gemは素晴らしい出発点であり、将来の記事でその完全なOpenTelemetryセットアップをカバーするかもしれません!