Insightでのトレース

GraalVM Insightを使用して、標準のOpenTracing APIを介して、スムーズで宣言的な非定型トレースを実装できます。トレースを実行中のアプリケーションに追加し、その場でカスタマイズして、不正行為のインシデントを調査するために必要な正しい情報を抽出できます。

次の例は、GraalVM Insightを使用したトレース機能を示しています。開始するには、Node.js用のJaegerのクライアント側インストゥルメンテーション・ライブラリをインストールします:

graalvm/bin/npm install jaeger-client@3.17.1

これで、tracerオブジェクトを介してインストゥルメントagent.jsjaeger-clientモジュールによって提供されるOpenTracing APIを使用できます(使用可能になったら、このガイドの後半で説明します):

let initialize = function(tracer) {
    var counter = 0;

    insight.on('enter', function(ctx, frame) {
        const args = frame.args;
        if ('request' !== frame.type || args.length !== 2 || typeof args[0] !== 'object' || typeof args[1] !== 'object') {
            return;
        }
        const req = args[0];
        const res = args[1];
        const span = tracer.startSpan("request");
        span.setTag("span.kind", "server");
        span.setTag("http.url", req.url);
        span.setTag("http.method", req.method);
        res.id = ++counter;
        res.span = span;
        console.log(`agent: handling #${res.id} request for ${req.url}`);
    }, {
        roots: true,
        rootNameFilter: 'emit',
        at: {
            sourcePath: '.*events.js'
        }
    });

    insight.on('return', function(ctx, frame) {
        var res = frame['this'];
        if (res.span) {
            res.span.finish();
            console.log(`agent: finished #${res.id} request`);
        } else {
            //Caused, for example, by Tracer itself connecting to the Jaeger server
        }
    }, {
        roots: true,
        rootNameFilter: 'end',
        at: {
            sourcePath: '.*_http_outgoing.js'
        }
    });
    console.log('agent: ready');
};

システムは、HTTPリクエストへのレスポンスを初期化して終了するために使用するemit('request', ...)およびres.end()関数にフックします。resオブジェクトは動的JavaScriptオブジェクトであるため、ソースevents.jsemit関数のenterハンドラにidおよびspan属性を追加できます。その後、end関数のreturnハンドラでこれらの属性を使用できます。

GraalVM Insightでは、frame変数とそのフィールドにアクセスできます。そのため、インストゥルメントはreq.urlまたはreq.methodの値を読み取り、OpenTracingサーバーにspan.setTag値として提供できます。

このインストゥルメントでは、それを適切なタイミングで有効にできるかどうかだけが問題です。InsightのNode.jsアプリケーションへの組込みに関する項を参照して、管理サーバーの作成方法と、必要に応じてトレース・スクリプト(OpenTracingベースのスクリプトを含む)を動的に適用する方法を確認します。このガイドでは、よりシンプルなものが使用されます。

jaegerオブジェクトが指定されたときにインストゥルメントを有効にします:

let initializeJaeger = function (ctx, frame) {
    insight.off('enter', initializeJaeger);

    let jaeger = frame.jaeger;

    var initTracer = jaeger.initTracer;
    console.log('agent: Jaeger tracer obtained');

    // See schema https://github.com/jaegertracing/jaeger-client-node/blob/master/src/configuration.js#L37
    var config = {
      serviceName: 'insight-demo',
      reporter: {
        // Provide the traces endpoint. This forces the client to connect directly to the Collector and send
        // spans over HTTP
        collectorEndpoint: 'http://localhost:14268/api/traces',
        // Provide username and password if authentication is enabled in the Collector
        // username: '',
        // password: '',
      },
      sampler: {
          type : 'const',
          param : 1
      }
    };
    var options = {
      tags: {
        'insight-demo.version': '1.1.2',
      },
    //  metrics: metrics,
      logger: console,
      sampler: {
          type : 'const',
          param : 1
      }
    };

    var tracer = initTracer(config, options);
    initialize(tracer);
};

insight.on('return', initializeJaeger, {
  roots: true,
  rootNameFilter: 'jaegerAvailable'
});

このインストゥルメントは、メイン・サーバー・スクリプトからのヘルプを必要とします。server.jsjaeger-clientモジュールを取得し、jaegerAvailable関数を介してエージェントに渡すようにします。次に、一般的なHTTPサーバーを作成します。server.jsの内容は次のとおりです:

function jaegerAvailable(jaeger) {
    console.log("Providing Jaeger object to the agent");
}
jaegerAvailable(require("jaeger-client"));

const http = require("http");
const srv = http.createServer((req, res) => {
    console.log(`server: obtained request ${res.id}`);
    setTimeout(() => {
        res.write(`OK# ${res.id}`);
        console.log(`server: replied to request ${res.id}`);
        res.end();
    }, 5);
});
srv.listen(8080);

これら2つのファイルを使用すると、ノード・アプリケーションおよびエージェントを起動できます。ただし、まずJaegerサーバーを起動します:

docker run -d --name jaeger \
-e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
-p 5775:5775/udp   -p 6831:6831/udp   -p 6832:6832/udp \
-p 5778:5778   -p 16686:16686   -p 14268:14268   -p 9411:9411 \
jaegertracing/all-in-one:latest

graalvm/bin/node --insight=agent.js server.js
Providing Jaeger object to the agent
agent: Jaeger tracer obtained
Initializing Jaeger Tracer with RemoteReporter and ConstSampler(always)
agent: ready

これで、http://localhost:16686/で入手可能なJaeger UIに接続し、サーバーに負荷をかけることができるようになりました:

ab -c 10 -n 10000 http://localhost:8080/

サーバーは、リクエストの処理中にコンソールの詳細情報を出力し、Jaeger UIではトレースが表示されます:

Jaeger UI

これで、トレースを使用したプレーンなNode.jsアプリケーションを拡張する方法のガイドは完了です。トレースは独自のagent.jsファイルに分離されたままであり、開始時に(ここで説明)、または必要に応じて動的に適用できます。

次に読むもの

Insightの詳細および難しいタスクについては、Insightのマニュアルを参照してください。