Haskell のモニタリング

Haskell の監視について学ぶ。

Instana による Haskell アプリケーションの監視およびトレース機能については、 Instana Haskell Trace SDK のGitHubページに詳細が記載されています。 API のドキュメントは Hackage で公開されています。

何が Haskell Trace SDKであり、何がそうでないか

Instana Haskell Trace SDK は、その他の大半の言語でサポートする方法では、自動インスツルメンテーション/トレースをサポートしていません。 その代わりに、このSDKでは、 Java 向けの Instana Trace SDK と同様に、手動でスパンを作成することができます。 Haskell Trace SDK は、スパンを作成するための便利な API を提供するほか、実動コードのパフォーマンスを妨げないように効率的に、Instana エージェントとの接続の確立とエージェントへのスパンの送信も行います。 最後になりますが、ランタイム・メトリックを収集して Instana に報告します。

インストール

Instana Haskell Trace SDK をアプリケーションで使用するには、instana-haskell-trace-sdk を依存関係に追加します (例えば、cabal ファイルの build-depends セクションに追加します)。 スタック を使用している場合は、SDK (および使用するバージョン番号) を stack.yaml ファイルの extra-deps セクションに追加する必要がある場合があります。

extra-deps:
- instana-haskell-trace-sdk-0.7.0.0

使用するスタックリゾルバーによっては、extra-deps に aeson-extra 以下を追加する必要がある場合もあります:

- aeson-extra-0.4.1.3@sha256:8ad8e4d28e46280ea98af2e94dcaaf524c396c9d91b1eba867961efc6e7f923f,2997

使用法

サンプルアプリ:Monad Shopをご覧ください。 Instana Haskell Trace SDKの実際の動作を確認できます。

初期化

この SDK を使用する前に、通常はアプリケーションの始動時に 1 回初期化する必要があります。

import qualified Instana.SDK.SDK as InstanaSDK

main :: IO ()
main = do
  -- ... initialize things ...

  -- initialize Instana
  instana <- InstanaSDK.initInstana

  -- ... initialize more things

エージェントに送信されるスパンを作成するための追加のすべての呼び出しに、InstanaSDK.initInstana によって返された値 instana :: Instana.SDK.InstanaContext が必要です。 SDK は、initInstana 呼び出しを受信するとすぐに、(別個のスレッドで非同期に) エージェントへの接続を試行します。

SDK は、環境変数を使用して構成することも、構成パラメーターを初期化関数に渡してコードで直接構成することも、またはその両方を行って構成することもできます。

プログラマチックに構成パラメーターを渡したい場合は、initInstana の代わりに initConfiguredInstana を使用してください。

import qualified Instana.SDK.SDK as InstanaSDK

main :: IO ()
main = do

  -- Example snippet for using the Instana SDK and providing a configuration
  -- (agent host, port, ...) directly in code. You only need to specify the
  -- configuration values you are interested in and can omit everything else
  -- (see https://www.yesodweb.com/book/settings-types).
  let
    config =
      InstanaSDK.defaultConfig
        { InstanaSDK.agentHost = Just "127.0.0.1"
        , InstanaSDK.agentPort = Just 42699
        , InstanaSDK.serviceName = Just "A Great Hakell Service"
        , InstanaSDK.forceTransmissionAfter = Just 1000
        , InstanaSDK.forceTransmissionStartingAt = Just 500
        , InstanaSDK.maxBufferedSpans = Just 1000
        }
  instana <- InstanaSDK.initConfiguredInstana config

構成レコードの作成時に省略されるか、 Nothingに設定される構成パラメーターの場合、SDK は環境変数 (以下を参照) にフォールバックしてから、デフォルト値にフォールバックします。

withInstana および withConfiguredInstanaと呼ばれる、初期化関数の ブラケット・スタイル のバリアントもあります。

import qualified Instana.SDK.SDK as InstanaSDK

main :: IO ()
main = do
  InstanaSDK.withInstana runApp

runApp :: InstanaContext -> IO ()
runApp instana = do
  -- do your thing here :-)

または、ブラケット記法と構成レコードを使用する場合、次のようになります。

import qualified Instana.SDK.SDK as InstanaSDK

main :: IO ()
main = do
  let
    config =
      InstanaSDK.defaultConfig
        { InstanaSDK.agentHost = Just "127.0.0.1"
        , InstanaSDK.agentPort = Just 42699
        , InstanaSDK.serviceName = Just "A Great Hakell Service"
        , InstanaSDK.forceTransmissionAfter = Just 1000
        , InstanaSDK.forceTransmissionStartingAt = Just 500
        , InstanaSDK.maxBufferedSpans = Just 1000
        }

  InstanaSDK.withConfiguredInstana config runApp

runApp :: InstanaContext -> IO ()
runApp instana = do
  -- do your thing here :-)

スパンの作成

HTTP エントリーの自動トレース

SDK を WAI ミドルウェア・プラグインとして使用して、WAI アプリケーションのすべての着信 HTTP 要求に対して、SDK がエントリー・スパンを自動的に作成するようにすることができます。 Instana トレース・ヘッダーと W3C トレース・コンテキスト・ヘッダーは、プラグインによって自動的に考慮されます。 出口スパンは、 withHttpExit または startHttpExit/completeExit 関数を使用して手動で作成する必要があることに注意してください (以下を参照してください)。

ミドルウェア・プラグインは、HTTP 応答に追加の HTTP 応答ヘッダー (Server-Timing) も追加します。 このヘッダーは、Web サイト・モニター・バックエンド相関を有効にします。

import qualified Instana.Wai.Middleware.Entry as InstanaWaiMiddleware

main = do
  Warp.run 3000 $ InstanaWaiMiddleware.traceHttpEntries instana $ app

ブラケット記法 (高水準 API)

with で始まるすべての関数は (他のパラメーターの中でも) 入出力アクションを受け入れます。 SDK は、スパンを事前に開始してから、指定された入出力アクションを実行し、その後でスパンを完了します。 この記法の使用は、自分でスパンを開始して完了する必要がある低水準 API よりも推奨されます。

  • withRootEntry: トレースのルートである (親スパンがない) エントリー・スパンを作成します。
  • withEntry: 親スパンを持つエントリー・スパンを作成します。
  • withHttpEntry: Instana トレース・ヘッダーまたは W3C トレース・コンテキスト・ヘッダーについて着信 HTTP 要求を検査し、エントリー・スパンを作成する便利な関数。 正しいメタデータをスパンに自動的に追加します。 Instana WAI ミドルウェア・プラグインの使用時に着信 HTTP 要求を処理する必要がないことに注目してください (上記を参照)。
  • withHttpEntry_: より汎用性の高いタイプのシグニチャーを使用するものの、機能が少ない withHttpEntry のバリアント。 withHttpEntryの場合と同様に、着信ヘッダー (Instana ヘッダーまたは W3C トレース・コンテキスト) からのトレースが自動的に続行されますが、HTTP 応答の状況コードはキャプチャーされず、Web サイト・モニターのバックエンド相関 (Server-Timing) の応答ヘッダーも追加されません。 可能な場合は、この関数の代わりに withHttpEntry を使用することをお勧めします。 または、withHttpEntry_ ブロックの内部で postProcessHttpResponse を呼び出して、上記の 2 つの欠落した機能に対応することもできます。 Instana WAI ミドルウェア・プラグインの使用時に着信 HTTP 要求を処理する必要がないことに注目してください。
  • withExit: エグジット・スパンを作成します。 エグジット・スパンには親としてエントリー・スパンが必要であるため、withRootEntry または withEntry の呼び出しの中でのみ呼び出すことができます。
  • withHttpExit: 特定の HTTP クライアント要求のエグジット・スパンを作成します。 正しいメタデータをスパンに自動的に追加するため、発信 HTTP 要求のトレース時に withExit よりも優先されます。 また、Instana トレース HTTP ヘッダーと W3C トレース・コンテキスト HTTP ヘッダー HTTP を要求に追加して、トレース・コンテキストをダウンストリームに伝搬します。
  • postProcessHttpResponse: HTTP エントリーの応答を処理します。 この関数は、HTTP エントリー・スパンがまだアクティブである間に呼び出す必要があります。 withHttpEntry_ ブロック内、または startHttpEntry から completeEntry の間で使用できます。 この関数は 2 つのことを実現します。すなわち、応答から HTTP 状況コードをキャプチャーし、現在アクティブなスパンに注釈として追加します。 また、Web サイトのバックエンド相関のモニターを可能にする HTTP 応答に、追加の HTTP 応答ヘッダー (サーバー・タイミング) を追加します。 クライアント・コードでは、これを直接呼び出す必要はほとんどありません。 代わりに、両方を自動的に実行する withHttpEntry を使用して着信 HTTP 要求をキャプチャーします。
  • addAnnotation: 現在のスパンにタグを追加します。

低水準 API/明示的な開始と完了

  • startRootEntry: トレースの先頭である (親スパンがない) エントリー・スパンを開始します。 いずれかの時点で completeEntry を呼び出す必要があります。
  • startEntry: エントリー・スパンを開始します。 いずれかの時点で completeEntry を呼び出す必要があります。
  • startHttpEntry: 着信 HTTP 要求のエントリー・スパンを開始します。 正しいメタデータをスパンに自動的に追加します。 WAI ミドルウェア・プラグインの使用時に着信 HTTP 要求を処理する必要はありません (上記を参照)。 いずれかの時点で completeEntry を呼び出す必要があります。
  • startExit: エグジット・スパンを開始します。 いずれかの時点で completeExit を呼び出す必要があります。
  • startHttpExit: 発信 HTTP 要求のエグジット・スパンを開始します。 正しいメタデータをスパンに自動的に追加するため、発信 HTTP 要求のトレース時に startExit よりも優先されます。 また、HTTP ヘッダーを要求に追加して、トレース・コンテキストをダウンストリームに伝搬します。 いずれかの時点で completeExit を呼び出す必要があります。
  • completeEntry: エントリー・スパンをファイナライズします。 スパンは、Instana エージェントに送信するために SDK のスパン・バッファーに入れられます。
  • completeExit: エグジット・スパンをファイナライズします。 スパンは、Instana エージェントに送信するために SDK のスパン・バッファーに入れられます。

ベスト・プラクティス

Instana のカスタム・トレースに関する資料を読んでいることを確認してください。この資料には、Instana トレースをコードに統合するための便利な情報が多数含まれています。特に、(InstanaSDK.addTagおよびInstanaSDK.addTagAtを介して) スパンに追加できるメタデータについて説明しています。

Instana は、いわゆる登録されたスパンと SDK スパンとを区別します。 登録されたスパンは通常、自動トレースによって作成され、Instana の処理パイプラインで登録されているスパンごとに特殊な処理があります。 これに対して、SDK スパンは、トレース SDK (Haskell トレース SDK や、他のランタイム・プラットフォーム用のその他の類似 SDK のような) を使用して作成されるタイプのスパンです。 SDK スパンは、Instana の処理パイプラインによってより汎用性の高い方法で処理されます。

この SDK を使用して作成されるほぼすべてのスパンが SDK スパンであることに注意してください。 この SDK が登録されたスパンを作成する例外は次の 2 つのみです。

  • HTTP/WAI エントリー (サーバー) スパン
  • HTTP エグジット (クライアント) スパン

このSDKには、これらの登録済みスパンを作成するための特別な関数(withHttpEntrywithHttpExit および対応する低レベル関数 startHttpEntrystartHttpExit)が用意されています( 「ブラケットのスタイル 」および「 低レベル」 を参照: API )。

環境変数を使用した構成

前のセクションで説明したように、SDKをプログラムで設定する代わりに、環境変数を使って設定することも可能です:

  • INSTANA_AGENT_HOST: 接続先の Instana エージェントの IP またはホスト。 デフォルト: 127.0.0.1。
  • INSTANA_AGENT_PORT: 接続先の Instana エージェントのポート。 デフォルト: 42699。
  • INSTANA_SERVICE_NAME: Instana でデフォルトのサービス名をオーバーライドします。
  • INSTANA_FORCE_TRANSMISSION_STARTING_AFTER: 通常、スパンはエージェントに送信される前にバッファーに入れられます。 この設定により、バッファーに入れられたすべてのスパンが所定のミリ秒数後に強制的に送信されます。 デフォルト: 1000。
  • INSTANA_FORCE_TRANSMISSION_STARTING_AT: この設定により、所定のスパン数がバッファーに入れられたら、バッファーに入れられたすべてのスパンが強制的に送信されます。
  • INSTANA_MAX_BUFFERED_SPANS: バッファーに入れるスパン数を制限します。 制限に達すると、スパンが除去されます。 この設定は、過剰なスパン数がバッファーに入らないようにする、メモリー・リークに対する安全機能です。 INSTANA_FORCE_TRANSMISSION_STARTING_AT より大きくなければなりません。
  • INSTANA_LOG_LEVEL: 「デバッグログの設定」のセクションを参照してください。
  • INSTANA_LOG_LEVEL_STDOUT: 「デバッグログの設定」のセクションを参照してください。
  • INSTANA_OVERRIDE_HSLOGGER_ROOT_HANDLER: 「デバッグログの設定」のセクションを参照してください。