Datadog API を使用した Node.js カスタム インスツルメンテーション
対応するライブラリインスツルメンテーションを使用しない場合( ライブラリの互換性参照)、手動でコードをインスツルメントする必要があります。
dd-trace ライブラリの機能性を拡張したり、アプリケーションのインスツルメントをより精確に制御するのに役立つ方法がライブラリにあります。
ddtrace ライブラリの機能性を拡張したり、アプリケーションのインスツルメントをより精確に制御するのに役立つ方法がライブラリにあります。
内蔵のインスツルメンテーションおよびカスタムインスツルメンテーションは、有意義なオペレーションに関連するスパンを作成します。
タグを追加することで、意味のあるデータを含めるためにアクティブスパンにアクセスすることができます。
const span = tracer.scope().active()
詳しくは Scope の API 詳細をご覧ください。
タグはスパンで setTag または addTags メソッドを使用してスパンに追加できます。サポートされている値のタイプは、文字列、数値、オブジェクトです。
// foo:bar タグを追加します
span.setTag('foo', 'bar')
// user_id:5 タグを追加します
span.setTag('user_id', 5)
// obj.first:foo および obj.second:bar タグを追加します
span.setTag('obj', { first: 'foo', second: 'bar' })
// foo:bar および baz:qux タグを追加します
span.addTags({
foo: 'bar',
baz: 'qux'
})
タグは、トレーサーで直接構成することにより、すべてのスパンに追加できます。これは、カンマ区切りの DD_TAGS 環境変数を使用するか、トレーサーの初期化で tags オプションを使用して実行できます。
// DD_TAGS=foo:bar,baz:qux と同等
tracer.init({
tags: {
foo: 'bar',
baz: 'qux'
}
})
// すべてのスパンにこれらのタグが追加されます
一部の Datadog インテグレーションでは、スパンフックをサポートしています。スパンフックを使用すると、スパンが完了する直前に更新できます。これは、タグを変更したり、コードからアクセスできないスパンにタグを追加する場合に役立ちます。
// tracer.init() の直後のエントリポイントの上部
tracer.use('express', {
// リクエストスパンが終了する直前にフックが実行されます
hooks: {
request: (span, req, res) => {
span.setTag('customer.id', req.query.customer_id)
}
}
})
詳しくは、個々のプラグインの API 詳細をご覧ください。
エラー オブジェクトに対応した特別な error タグを使うと、エラーをスパンに追加できます。これにより、エラーは 3 つのタグに分割されます: error.type、error.message、error.stack。
try {
getIngredients()
} catch (e) {
span.setTag('error', e)
}
tracer.trace() または tracer.wrap() を使用している場合、これはエラーがスローされたときに自動的に行われます。
スパンの作成
dd-trace ライブラリは、tracer.init() で多くのライブラリとフレームワークに対応するスパンを自動生成します。しかし、使用しているコードを可視化したい場合はスパンの利用が便利です。
Web リクエスト (例: /make-sandwich) 内で、getIngredients() や assembleSandwich() など、測定に役立つさまざまなオペレーションを行うことができます。
Synchronous コードは、tracer.trace() でトレースできます。これにより、コールバックが戻ったときに自動的にスパンが終了し、スローされたエラーが自動的にキャプチャされます。
app.get('/make-sandwich', (req, res) => {
const sandwich = tracer.trace('sandwich.make', { resource: 'resource_name' }, () => {
const ingredients = tracer.trace('get_ingredients', { resource: 'resource_name' }, () => {
return getIngredients()
})
return tracer.trace('assemble_sandwich', { resource: 'resource_name' }, () => {
assembleSandwich(ingredients)
})
})
res.end(sandwich)
})
詳しくは tracer.trace() の API 詳細をご覧ください。
Promise は tracer.trace() でトレースできます。これにより、返された Promise が解決したときに自動的にスパンが終了し、拒否エラーが自動的にキャプチャされます。
const getIngredients = () => {
return new Promise((resolve, reject) => {
resolve('Salami');
});
};
app.get('/make-sandwich', (req, res) => {
return tracer.trace('sandwich.make', { resource: 'resource_name' }, () => {
return tracer.trace('get_ingredients', { resource: 'resource_name' }, () => getIngredients())
.then((ingredients) => {
return tracer.trace('assemble_sandwich', { resource: 'resource_name' }, () => {
return assembleSandwich(ingredients)
})
})
}).then(sandwich => res.end(sandwich))
})
詳しくは tracer.trace() の API 詳細をご覧ください。
Async/await は tracer.trace() でトレースできます。これにより、返された Promise が解決したときに自動的にスパンが終了し、拒否エラーが自動的にキャプチャされます。
app.get('/make-sandwich', async (req, res) => {
const sandwich = await tracer.trace('sandwich.make', { resource: 'resource_name' }, async () => {
const ingredients = await tracer.trace('get_ingredients', { resource: 'resource_name' }, () => {
return getIngredients()
})
return tracer.trace('assemble_sandwich', { resource: 'resource_name' }, () => {
return assembleSandwich(ingredients)
})
})
res.end(sandwich)
})
詳しくは tracer.trace() の API 詳細をご覧ください。
コードを変更せずに既存の関数をラップすることができます。これは、コードを自分で制御できない関数をトレースするのに役立ちます。これは、最後の引数がコールバックの代わりにラップする関数であることを除いて、 tracer.trace() と同じ引数を取る tracer.wrap() で実行できます。
// 関数が定義された後
getIngredients = tracer.wrap('get_ingredients', { resource: 'resource_name' }, getIngredients)
assembleSandwich = tracer.wrap('assemble_sandwich', { resource: 'resource_name' }, assembleSandwich)
// ルートが定義されている場所
app.get('/make-sandwich', (req, res) => {
const sandwich = tracer.trace('sandwich.make', { resource: 'resource_name' }, () => {
const ingredients = getIngredients()
return assembleSandwich(ingredients)
})
res.end(sandwich)
})
詳しくは tracer.trace() の API 詳細をご覧ください。
リクエストのフィルタリング
アプリケーションのいくつかのリクエストはインスツルメンテーションすべきでない場合があります。よくあるケースは、ヘルスチェックやその他の合成トラフィックでしょう。これらのリクエストは http プラグインの blocklist や allowlist オプションで無視することができます。
// tracer.init() の直後のエントリポイントの上部
tracer.use('http', {
blocklist: ['/health', '/ping']
})
この構成は、必要に応じてクライアントとサーバーで分割することができます。例:
tracer.use('http', {
server: {
blocklist: ['/ping']
}
})
さらに、Agent が Datadog に送信しないように、リソース名に基づいてトレースを除外することができます。この他、セキュリティや Agent の細かい設定については、セキュリティページや不要なリソースの無視で確認することができます。
dd-trace-api
ddtrace-api はプレビューです!
dd-trace-api パッケージはプレビュー段階であり、必要な API 呼び出しをすべて含まない場合があります。より完全な機能が必要な場合は、前のセクションで説明した API を使用してください。
以下の手順は、プレビュー版の ddtrace-api パッケージで試してみたい場合にのみ必要です。
dd-trace-api パッケージ は、Datadog APM の Node.js 向けカスタム インスツルメンテーションで使用できる、安定した公開 API を提供します。このパッケージが実装しているのは API インターフェースだけで、スパンを生成して Datadog に送信するための基盤機能は含まれていません。
インターフェース (dd-trace-api) と実装 (dd-trace) を分離することで、次のような利点があります:
- You can rely on an API that changes less frequently and more predictably for your custom instrumentation
- If you only use automatic instrumentation, you can ignore API changes entirely
- シングル ステップとカスタムの両方のインスツルメンテーションを実装している場合でも、
dd-trace パッケージの複数コピーに依存せずに済みます。
dd-trace-api を使用するには:
アプリに dd-trace と dd-trace-api のライブラリをインストールします。注: dd-trace はシングル ステップ インスツルメンテーションで自動的にインストールされますが、dd-trace-api はアプリに手動でインストールする必要があります。
npm install dd-trace dd-trace-api
dd-trace を使用して Node.js アプリケーションにインスツルメンテーションを追加します。シングル ステップ インスツルメンテーションを利用している場合は、この手順は不要です。
node --require dd-trace/init app.js
ここまで設定できたら、前のセクションの例と同じ要領でカスタム インスツルメンテーションを書けます。ただし、require するのは dd-trace ではなく dd-trace-api です。
例:
const tracer = require('dd-trace-api')
const express = require('express')
const app = express()
app.get('/make-sandwich', (req, res) => {
const sandwich = tracer.trace('sandwich.make', { resource: 'resource_name' }, () => {
const ingredients = tracer.trace('get_ingredients', { resource: 'resource_name' }, () => {
return getIngredients()
})
return tracer.trace('assemble_sandwich', { resource: 'resource_name' }, () => {
assembleSandwich(ingredients)
})
})
res.end(sandwich)
})
サポートされている API コールの全一覧は、そのパッケージの API 定義 を参照してください。
その他の参考資料