OpenTelemetry 対応監視ツールへのテレメトリ・データの送信
OpenTelemetryOpens in a new tab (OTel) は、テレメトリ・データを生成、エクスポート、収集するための、オープン・ソースのフレームワークおよびツールキットです。
このバージョンの InterSystems IRIS は、サポートされているシステムにおいて、OpenTelemetry SDK を活用し、テレメトリ・データを OpenTelemetry ProtocolOpens in a new tab シグナルとして HTTP (OTLP/HTTP) を経由して OpenTelemetry CollectorOpens in a new tab または互換性のあるその他の監視ツールにエクスポートおよび送信できるようにします。
このバージョンの InterSystems IRIS では、この機能は macOS、Windows、および AIX システムには提供されていません。
InterSystems IRIS を構成して以下のタイプのシグナルを送信できます。
-
メトリック — InterSystems IRIS の /api/monitor API で収集するように構成した計測値。
-
ログ — InterSystems IRIS がシステム・メッセージ・ログまたは監査データベースに記録するイベント。
-
トレース — 要求がアプリケーション内をどのように移動するかに関する情報。
これらのシグナルが OTel 内でどのように機能するかについて詳しくは、OTel ドキュメントの メトリックOpens in a new tab、ログOpens in a new tab、およびトレースOpens in a new tabのページを参照してください。
InterSystems IRIS は、これらのシグナルをユーザが指定したエンドポイントにプッシュします。この指定方法については "ターゲット・エンドポイントの構成" を参照してください。InterSystems IRIS は、メトリックやログを、共通の構成パラメータに基づいて定期的に送信します。あるいは、各タイプのシグナルの送信を個別に有効化および構成することもできます。詳細は、後述の対応するセクションで説明します。
ターゲット・エンドポイントの構成
InterSystems IRIS インスタンスが OTLP/HTTP シグナルを送信するエンドポイントを指定するには、インスタンスのホスト・システム上の環境変数 OTEL_EXPORTER_OTLP_ENDPOINT を、目的のアドレスに設定します。環境変数の設定手順については、オペレーティング・システムのドキュメントを参照してください。
OTLP/HTTP の送信を有効にして OTEL_EXPORTER_OTLP_ENDPOINT 環境変数を設定しない場合、インスタンスは OpenTelemetry CollectorOpens in a new tab の OTLP/HTTP レシーバの既定のエンドポイント (http://localhost:4318) にシグナルを送信します。
メトリックの送信
InterSystems IRIS は、/api/monitor API が収集したすべてのメトリック・イベント (カスタム・アプリケーションのメトリックを含む) を、指定した OTLP/HTTP エンドポイントに定期的に送信します。
メトリック・イベントを送信するようにインスタンスを構成するには、以下の手順を実行します。
-
収集したいすべてのメトリックを収集するようにインスタンスを構成します。相互運用プロダクションのメトリックを収集する場合は、これらを手動で有効にする必要があります。カスタム・アプリケーションのメトリックを収集するようにインスタンスを構成することもできます。
-
インスタンスの開始時にメトリックの送信を開始するよう、インスタンスの OpenTelemetry エクスポータを構成します。これは、以下のいずれかの方法で実行できます。
-
管理ポータルで、[システム管理]→[構成]→[追加の設定]→[モニタ] に移動し、[Enable OTel Metrics] を選択してから、[保存] を選択します。
-
Config.MonitorOpens in a new tab クラスを変更するか (クラス・リファレンスの説明を参照)、直接 CPF ファイルを編集し、OTELMetrics パラメータを変更します。
-
-
必要に応じて、エクスポータがメトリックを送信する頻度を変更します。既定では、エクスポータは 10 秒ごとにシグナルを送信します。この間隔は、以下のいずれかの方法で変更できます。
-
管理ポータルで、[システム管理]→[構成]→[追加の設定]→[モニタ] に移動します。[OTel Exporter Interval] フィールドを目的の間隔 (秒) に変更し、[保存] を選択します。
-
Config.MonitorOpens in a new tab クラスを変更するか (クラス・リファレンスの説明を参照)、直接 CPF ファイルを編集し、OTELInterval パラメータを変更します。
-
-
前述の手順で CPF パラメータを変更してインスタンスを構成した場合は、インスタンスを再起動して変更を有効にします。
ログの送信
InterSystems IRIS は、構造化ログ・ファイルに含まれるログ・イベントと同じカテゴリのログ・イベント (つまり、システム・メッセージ・ログ (messages.log) または監査データベースに記録されるイベント) に対して OTLP/HTTP を送信できます。InterSystems IRIS は、指定した OTLP/HTTP エンドポイントに、定期的に構造化ログ・イベントを送信します。
ログ・イベントを送信するようにインスタンスを構成するには、以下の手順を実行します。
-
インスタンスの開始時にログ・イベントの送信を開始するよう、インスタンスの OpenTelemetry エクスポータを構成します。これは、以下のいずれかの方法で実行できます。
-
管理ポータルで、[システム管理]→[構成]→[追加の設定]→[モニタ] に移動し、[Enable OTel Logs] を選択してから、[保存] を選択します。
-
Config.MonitorOpens in a new tab クラスを変更するか (クラス・リファレンスの説明を参照)、直接 CPF ファイルを編集し、OTELLogs パラメータを変更します。
-
-
必要に応じて、インスタンスの OpenTelemetry エクスポータによって送信されるログ・レベルの最小深刻度レベルを構成します。深刻度レベルは構造化ログで使用しているものと同じです。既定の深刻度レベルのしきい値は WARN です。このレベルの場合、エクスポータは WARN、SEVERE、および FATAL レベルのログ・イベントを送信します。DEBUG2、DEBUG、および INFO レベルのログ・イベントは送信しません。
最小深刻度レベルは、以下のいずれかの方法で変更できます。
-
管理ポータルで、[システム管理]→[構成]→[追加の設定]→[モニタ] に移動します。[OTel Log Level] ドロップダウン・メニューから深刻度レベルの目的のしきい値を選択します。[保存] を選択します。
-
Config.MonitorOpens in a new tab クラスを変更するか (クラス・リファレンスの説明を参照)、直接 CPF ファイルを編集し、OTELLogLevel パラメータを変更します。
-
-
必要に応じて、エクスポータがログ・イベントを送信する頻度を変更します。既定では、エクスポータは 10 秒ごとにシグナルを送信します。
この間隔は、以下のいずれかの方法で変更できます。
-
管理ポータルで、[システム管理]→[構成]→[追加の設定]→[モニタ] に移動します。[OTel Exporter Interval] フィールドを目的の間隔 (秒) に変更し、[保存] を選択します。
-
Config.MonitorOpens in a new tab クラスを変更するか (クラス・リファレンスの説明を参照)、直接 CPF ファイルを編集し、OTELInterval パラメータを変更します。
-
-
前述の手順で CPF パラメータを変更してインスタンスを構成した場合は、インスタンスを再起動して変更を有効にします。
トレースの送信
トレースは、要求がアプリケーション内を移動する様子を記録します。これは、要求に対する応答の一部としてアプリケーションが実行する作業の構成単位を表す、1 つ以上の入れ子可能なスパンで構成されます。トレースを記録するには、アプリケーションのコードに、スパンを生成し、情報を入力して、それらを継続的なトレースの一部としてコンテキスト化するインストルメントを含める必要があります。トレースに関する OpenTelemetry の仕様の詳細は、OpenTelemetry のドキュメントOpens in a new tabを参照してください。
InterSystems IRIS では、OTel 仕様に準拠したトレースを生成するようアプリケーション・コードをインストルメント化するための簡単な API を、%Trace パッケージで提供しています。アプリケーション内でこのインストルメントがトレースを生成するようになると、InterSystems IRIS はこれを指定した OTLP/HTTP エンドポイントに送信します。
%Trace API を使用してトレースを生成するようにアプリケーション・コードを編集する方法は以下のとおりです。
-
アプリケーションのトレーサ・プロバイダOpens in a new tabとして機能する %Trace.TracerProvider クラスのインスタンスを作成します。
このクラスのコンストラクタは、オプションの引数として、TracerProvider オブジェクトの ResourceAttributes プロパティを設定するために使用する配列を受け入れます。アプリケーションに関するグローバル属性を指定する場合は、以下の例のように、目的のキーと値のペアを含む配列を定義し、それをコンストラクタ・メソッドに参照渡しします。
set attributes("service.name") = "test_service" set attributes("service.version") = "2.0" set tracerProv = ##class(%Trace.TracerProvider).%New(.attributes)
attributes = {} attributes["service.name"] = "test_service" attributes["service.version"] = "2.0" attrArray = iris.arrayref(attributes) tracerProv = iris.cls('%Trace.TracerProvider')._New(.attrArray)
-
ほとんどの場合、アプリケーションのライフ・サイクル全体を通して、ネームスペース全体のインストルメンテーション・コードで共有できるように、開始時に単一の TraceProvider オブジェクトをインスタンス化することをお勧めします。そのためには、以下のように、新しく作成した TraceProvider オブジェクトを、%Trace.Provider クラスの SetTracerProvider() メソッドに提供します。
do ##class(%Trace.Provider).SetTracerProvider(tracerProv)
iris.cls('%Trace.Provider').SetTracerProvider(tracerProv)
インストルメンテーション・コード内で TraceProvider オブジェクトにアクセスする必要がある場合 (この後の手順で説明します) は、補完的な GetTracerProvider() メソッドを使用してこれを呼び出します。
set tracerProv = ##class(%Trace.Provider).GetTracerProvider()
tracerProv = iris.cls('%Trace.Provider').GetTracerProvider()
-
アプリケーション・コードをインストルメント化するには、まず、TracerProvider オブジェクトの GetTracer() メソッドを使用してトレーサをインスタンス化します (Tracer オブジェクトは、トレースの実際のスパンを生成します)。
GetTracer() は、Name と Version の 2 つの引数を受け取ります。これらの引数を使用して、トレースするアプリケーションまたはアプリケーション・コンポーネントを一意に識別し、そのバージョン番号を指定します。以下に例を示します。
set tracer = tracerProv.GetTracer("service.orderprocessor", "2.0.2")
tracer = tracerProv.GetTracer("service.orderprocessor", "2.0.2")
-
Tracer オブジェクトの StartSpan() メソッドを使用して、ルート・スパンを開始します。StartSpan() は、スパンのプロパティを定義するために、以下の引数を順に受け取ります。
-
Name — スパンの名前フィールドの設定に使用される文字列。
-
Parent — (オプション) 新しいスパンの親スパンとして指定するスパンを識別する %Trace.Context オブジェクト。Parent を指定せず、事前にアクティブなスパンを指定 (この後の手順で説明します) していない場合、このメソッドはスパンをルート・スパンとして初期化し、一意のトレース ID を付与します。
-
Spankind — (オプション)スパンを、OpenTelemetry 仕様で認識されるスパンの種類Opens in a new tabのいずれかとして識別する文字列。Spankind を指定しない場合、スパンは既定で Internal として分類されます。
-
AttributesOpens in a new tab — (オプション)参照渡しされる、キーと値のペアの配列。
-
StartTime — (オプション)スパンの開始時刻を $ZTIMESTAMP 形式で記録するタイムスタンプ。指定されていない場合、StartTime は現在の時刻に設定されます。
例えば、小売の取引を処理するルート・スパンを初期化するコードは、以下のようになります。
set rootAttr("customer.id") = customer.ID set rootAttr("product.id") = product.ID set rootSpan = tracer.StartSpan("order", , "Server", .rootAttr)
rootspan = {} rootAttr("customer.id") = customer.ID rootAttr("product.id") = product.ID rootAttrArray = iris.arrayref(rootAttr) set rootSpan = tracer.StartSpan("order", , "Server", .rootAttrArray)
-
-
必要に応じて、このルート・スパン内に階層的に子スパンを入れ子にします。シンプルな実装では、目的の親を ActiveSpan として識別する %Trace.Context オブジェクトを作成した後、その Context オブジェクトを StartSpan() の Parent 引数として提供することで、新しいスパンの親スパンを手動で指定できます。
ただし、この手動アプローチを使用して語彙スコープをまたいでコンテキストを管理しようとするのは現実的ではありません。そのため、%Trace API はコンテキストを管理するための動的なスコーピング・メカニズムを提供します。
分散されたトレースのコンテキストを動的に管理するには、以下の手順を実行します。
-
以下のように、Tracer オブジェクトの SetActiveSpan() メソッドを使用して、親スパンを "有効な" スパンとして指定します。
set rootScope = tracer.SetActiveSpan(rootSpan)
rootScope = tracer.SetActiveSpan(rootSpan)
SetActiveSpan() は、%Trace.Scope クラスのインスタンスを返します。この Scope オブジェクトは、対応するスパンが有効であることを示すレコードとして機能します。
-
以下のように、Parent を指定せずに StartSpan() を呼び出すことで、新しいスパンを開始します。
set childSpan1 = tracer.StartSpan("order_payproc")
childSpan1 = tracer.StartSpan("order_payproc")
有効なスパンの Scope オブジェクトがメモリ内に残っている限り、他の Parent が指定されていなければ、StartSpan() は既定で有効なスパンの子として新しいスパンを初期化します。
-
スパンをさらに入れ子にするには、子スパンで SetActiveSpan() を呼び出し、それを新しい有効なスパンとして指定して、新しい Scope オブジェクトを生成します。有効なスパンは、特定の時点でメモリ内に存在している最新の Scope オブジェクトによって識別されます。したがって、新しい有効なスパンに対して Scope オブジェクトを生成すると、StartSpan() は既定で、その子として新しいスパンを初期化します。
前の例を続けると、以下のコードは、新しいスパン childSpan2 を childSpan1 (これ自体が rootSpan の子です) の子として開始します。
set child1Scope = tracer.SetActiveSpan(childSpan1) set childSpan2 = tracer.StartSpan("order_payproc_addnewcard")
child1Scope = tracer.SetActiveSpan(childSpan1) childSpan2 = tracer.StartSpan("order_payproc_addnewcard")
-
現在有効なスパンの Scope オブジェクトを破棄すると、以前有効だったスパンが再度 StartSpan() の既定の親になります (その Scope オブジェクトも破棄していない場合)。前の例を続けると、以下のコードは、新しいスパン childSpan3 を rootSpan の子かつ childSpan1 の兄弟として開始します。
kill child1Scope set childSpan3 = tracer.StartSpan("order_sendconfirm", , "Server")
iris.execute('kill child1Scope) childSpan3 = tracer.StartSpan("order_sendconfirm", , "Server")
-
-
必要に応じて、スパンに関する情報を定義します。このためには、以下の方法があります。
-
Span オブジェクトの AddEvent() メソッドを使用してスパン・イベントOpens in a new tabを追加する。
-
Span オブジェクトの AddLink() メソッドを使用してスパン・リンクOpens in a new tabを追加する。
-
Span オブジェクトの Context プロパティ (%Trace.SpanContext のインスタンス) の TraceFlags プロパティと TraceState プロパティを変更し、それぞれスパンのトレース・フラグとトレース状態を変更します。
Note:TraceFlags プロパティは、エクスポート用にスパンをサンプリングするかどうかを指定するビットを提供します。条件付きでこのビットの値を設定するロジックを定義することにより、アプリケーション内でトレースのサンプリング・アルゴリズムを定義できます。
前の例を続けると、以下のコードは、"paymentdeclined" イベントと、paymentProcLivenessSpan という架空のスパンへのリンクで "order_payproc" スパン (childSpan1) を拡張します。
set eventAttr("declined.reason")="Unknown error occurred." do childSpan1.AddEvent("paymentdeclined", .eventAttr) do childSpan1.AddLink(paymentProcLivenessSpan.Context)
eventAttr = {} eventAttr("declined.reason") = "Unknown error occurred." eventAttrArray = iris.arrayref(eventAttr) childSpan1.AddEvent("paymentdeclined", .eventAttrArray) childSpan1.AddLink(paymentProcLivenessSpan.Context)
-
-
スパンを終了する前に、以下の例のように、Span オブジェクトの SetStatus() メソッドを使用して、スパンのステータスを更新します。
do childSpan1.SetStatus("Ok")
childSpan1.SetStatus("Ok")
SetStatus() は、OpenTelemetry 仕様で認識される 3 つのスパン・ステータスOpens in a new tabに対応する 3 つの可能な値を持つことのできる 1 つの引数 (文字列) を受け入れます。
-
Span オブジェクトの End() メソッドを使用して、各スパンを終了します。End() は、オプションの引数として $TIMESTAMP 形式のタイムスタンプを受け入れます。タイムスタンプが指定されている場合、End() はその時刻をそのスパンの終了時刻として記録します。指定されていない場合は、End() は、以下の例のように、スパンの終了時刻を現在の時刻に設定します。
do childSpan1.End()
childSpan1.End()
スパンの Context プロパティの TraceFlags の ‘sampled’ ビットが適切に設定されていれば、InterSystems IRIS は、スパンの終了時に OpenTelemetry SDK を呼び出して、そのスパンをエクスポートします。
-
SetActiveSpan() を使用して、(前の手順で推奨されているように) 語彙スコープをまたいで入れ子になったスパンを管理する場合は、有効なスパンが終了するごとに、そのスパンの Scope オブジェクトを破棄します。前の例を続けると、以下のコードは、rootSpan で囲まれたトレースを終了し、新しいトレースを記録するためにアプリケーションを準備します。
do rootSpan.SetStatus("Ok") do rootSpan.End() kill rootScope
rootSpan.SetStatus("Ok") rootSpan.End() iris.execute('kill rootScope')
-
必要に応じて、編集したコードをインポートしてリコンパイルし、アプリケーションのトレースを有効にします。
トレースの無効化
アプリケーションをインストルメント化した後にそのトレースを無効化するには、以下の手順を実行します。
-
アプリケーションが使用するすべてのトレーサ・プロバイダを、(%Trace.TracerProvider ではなく) %Trace.NoopTracerProvider のインスタンスとして初期化するようにコードを編集します。前述のインストルメンテーション手順で示した例に戻ると、無効化するには、tracerProv オブジェクトを設定するコード行を、以下のように変更する必要があります。
set tracerProv = ##class(%Trace.NoopTracerProvider).%New(.attributes)
tracerProv = iris.cls('%Trace.NoopTracerProvider')._New(.attrArray)
tracerProv がネームスペース全体に対応するトレーサ・プロバイダとして設定されている場合は、それ以上編集の必要はありません。
-
必要に応じて、編集したコードをインポートしてリコンパイルし、アプリケーションのトレースを無効にします。
エラー処理とリカバリ
OTLP/HTTP エンドポイントでシグナルを受信していた OpenTelemetry 対応のツールが予期しないシステム・エラーにより使用できなくなった場合、InterSystems IRIS インスタンスの OpenTelemetry エクスポータは、システム・メッセージ・ログ (messages.log) にエラーを記録します。その後、インスタンスからのシグナルの送信を停止します。
SYS.Monitor.OTelOpens in a new tab クラスは、OTLP/HTTP エンドポイントでの通信がリストアされたかどうかをテストするのに役立つ、TestLogs()Opens in a new tab、TestMetrics()Opens in a new tab、および TestTraces()Opens in a new tab というメソッドを提供します。
エラーの原因を解決し、OTLP/HTTP 接続をリストアしたら、以下のようにしてシグナルの送信を再開できます。
-
インスタンスでターミナル・セッションを開き、%SYS ネームスペースに移動します。
-
メトリックとログの送信を再開するには、以下のコマンドを実行します。
do ##class(SYS.Monitor.OTel).Start()
-
トレースの送信を再開するには、以下のコマンドを実行します。
do ##class(SYS.Monitor.OTel).EnableTraces()