次の方法で共有


.NET および .NET Core でのカスタム メトリックの収集

Azure Monitor Application Insights の .NET および .NET Core SDK には、カスタム メトリックを収集する 2 つの異なるメソッド (TrackMetric()GetMetric()) があります。 これら 2 つのメソッドの主な違いは、ローカル集計です。 TrackMetric() メソッドには事前集計がありません。 GetMetric() メソッドには事前集計があります。 集計を使用することをお勧めします。そのため、TrackMetric() はカスタム メトリックを収集するための推奨される方法ではなくなりました。 この記事では、GetMetric() メソッドの使用方法と、そのしくみの背後にある論理的根拠についていくつか説明します。

Note

以下のドキュメントは、Application Insights クラシック API に関するものです。 Application Insights の長期的な計画は、OpenTelemetry を使用してデータを収集することです。 詳細については、「.NET、Node.js、Python、Java アプリケーション用の Azure Monitor OpenTelemetry を有効にする」と「Microsoft の OpenTelemetry ロードマップ」を参照してください。 移行ガイダンスは、.NETNode.jsPython に利用可能です。

事前集計 API と非事前集計 API

TrackMetric() メソッドでは、メトリックを示す未加工のテレメトリを送信します。 値ごとに単一のテレメトリ項目を送信することは、非効率的です。 また、すべての TrackMetric(item) がテレメトリ初期化子とプロセッサの完全な SDK パイプラインを通過するため、TrackMetric() メソッドはパフォーマンスの点でも非効率的です。

TrackMetric() とは異なり、GetMetric() ではローカル事前集計が自動的に処理され、1 分の一定間隔で集計されたサマリー メトリックのみが送信されます。 一部のカスタム メトリックを秒またはミリ秒単位で厳密に監視する必要がある場合、1 分ごとに監視するだけのストレージとネットワーク トラフィックのコストのみが発生している間にそのようにすることができます。 また、この動作により、集計されたメトリックに対して送信する必要があるテレメトリ項目の合計数が大幅に減少するため、調整が発生するリスクが大幅に軽減されます。

Application Insights では、TrackMetric()GetMetric() を介して収集されたカスタム メトリックはサンプリングの対象にはなりません。 重要なメトリックをサンプリングすると、これらのメトリックに基づいて作成した可能性のあるアラートの信頼性が低下する場合があるシナリオにつながることがあります。 カスタム メトリックをサンプリングしないようにすることで、一般に、アラートのしきい値違反が発生した場合にアラートが起動するという確信を得ることができます。 カスタム メトリックはサンプリングされないため、潜在的な懸念事項がいくつかあります。

1 秒ごとに、あるいはさらに細かい間隔でメトリックの傾向を追跡すると、次のようになることがあります。

  • データ ストレージのコストが増加する。 Azure Monitor に送信するデータの量に関連したコストが発生します 送信するデータの量が多いほど、監視の全体的なコストが高くなります。
  • ネットワーク トラフィックまたはパフォーマンスのオーバーヘッドが増加します。 一部のシナリオでは、このオーバーヘッドにより、金銭的にもアプリケーション パフォーマンスの面でもコストがかかる可能性があります。
  • インジェスト調整のリスク アプリで短期間に高いレートのテレメトリを送信すると、Azure Monitor によってデータ ポイントが削除 ("スロットル") されます。

調整が懸念事項になるのは、それによってアラートが起動しなくなる場合があるためです。 アラートをトリガーする条件がローカルで発生し、送信されるデータが多すぎるためにインジェスト エンドポイントで削除される可能性があります。 独自のローカル集計ロジックを実装していない限り、.NET および .NET Core では TrackMetric() を使用しないことをお勧めします。 すべてのインスタンスを追跡しようとしたときに、特定の期間にわたってイベントが発生した場合は、TrackEvent() の方が適していることがわかります。 カスタム メトリックとは異なり、カスタム イベントはサンプリングの対象となることに注意してください。 独自のローカル事前集計を記述しなくても TrackMetric() を引き続き使用することはできます。 ただし、その場合は落とし穴に注意してください。

つまり、事前集計を行い、すべての Track() 呼び出しからの値を蓄積し、1 分ごとにサマリーや集計を送信するという理由から、GetMetric() をお勧めします。 GetMetric() メソッドにより、すべての関連情報を引き続き収集しながら、送信するデータ ポイントを少なくすることによって、コストとパフォーマンスのオーバーヘッドを大幅に削減できます。

注意

GetMetric() メソッドがあるのは、.NET および .NET Core SDK のみです。 Java を使用している場合は、Micrometer を使用したカスタム メトリックの送信に関するセクションを参照してください。 JavaScript と Node.js の場合は、引き続き TrackMetric() を使用しますが、前のセクションで概説した注意事項に留意してください。 Python の場合は、OpenCensus.stats を使用してカスタム メトリックを送信できますが、メトリックの実装は異なります。

GetMetric の使用を開始する

この例では、基本的な .NET Core 3.1 ワーカー サービス アプリケーションを使用します。 これらの例で使用されたテスト環境をレプリケートする場合は、ワーカー サービスの監視に関する記事の手順 1 から 6 に従います。 次の手順では、基本的な worker サービス プロジェクト テンプレートに Application Insights を追加します。 この概念は、Web アプリやコンソール アプリを含め、SDK を使用できる一般的なアプリケーションに適用されます。

メトリックの送信

worker.cs ファイルの内容を次のコードに置き換えます。

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.ApplicationInsights;

namespace WorkerService3
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        private TelemetryClient _telemetryClient;

        public Worker(ILogger<Worker> logger, TelemetryClient tc)
        {
            _logger = logger;
            _telemetryClient = tc;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {   // The following line demonstrates usages of GetMetric API.
            // Here "computersSold", a custom metric name, is being tracked with a value of 42 every second.
            while (!stoppingToken.IsCancellationRequested)
            {
                _telemetryClient.GetMetric("ComputersSold").TrackValue(42);

                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(1000, stoppingToken);
            }
        }
    }
}

サンプル コードを実行すると、Visual Studio 出力ウィンドウにテレメトリが送信されることなく、while ループが繰り返し実行されていることがわかります。 単一のテレメトリ項目が約 60 秒のマークで送信され、このテストでは次のようになります。

Application Insights Telemetry: {"name":"Microsoft.ApplicationInsights.Dev.00000000-0000-0000-0000-000000000000.Metric", "time":"2019-12-28T00:54:19.0000000Z",
"ikey":"00000000-0000-0000-0000-000000000000",
"tags":{"ai.application.ver":"1.0.0.0",
"ai.cloud.roleInstance":"Test-Computer-Name",
"ai.internal.sdkVersion":"m-agg2c:2.12.0-21496",
"ai.internal.nodeName":"Test-Computer-Name"},
"data":{"baseType":"MetricData",
"baseData":{"ver":2,"metrics":[{"name":"ComputersSold",
"kind":"Aggregation",
"value":1722,
"count":41,
"min":42,
"max":42,
"stdDev":0}],
"properties":{"_MS.AggregationIntervalMs":"42000",
"DeveloperMode":"true"}}}}

この単一のテレメトリ項目は、41 の個別のメトリック測定の集計を表します。 同じ値を何度も送信していたため、標準偏差 (stDev) は 0 で、最大 (max) と最小 (min) の値は同一になっています。 value プロパティは、集計されたすべての個別の値の合計を表します。

注意

GetMetric メソッドでは、最後の値 (例: gauge) の追跡や、ヒストグラムまたは分布の追跡はサポートされていません。

ログ (Analytics) エクスペリエンスで Application Insights リソースを調べると、個別のテレメトリ項目は次のスクリーンショットのようになります。

Log Analytics のクエリ ビューを示すスクリーンショット。

注意

取り込み後に未加工のテレメトリ項目に明示的な sum プロパティやフィールドは含まれませんが、ここでは 1 つ作成します。 この場合、valuevalueSum の両方のプロパティは同じことを表します。

ログベースとカスタム メトリックの両方として、ポータルの [メトリック] セクションでカスタム メトリック テレメトリにアクセスすることもできます。 次のスクリーンショットはログベースのメトリックの例です。

メトリックス エクスプローラー ビューを示すスクリーンショット。

高スループット使用率のメトリック参照をキャッシュする

場合によっては、メトリック値が頻繁に観測されることがあります。 たとえば、1 秒あたり 500 要求を処理する高スループット サービスでは、要求ごとに 20 個のテレメトリ メトリックの生成が必要になる場合があります。 結果は、1 秒あたり 10,000 の値を追跡することを意味します。 このような高スループットのシナリオでは、ユーザーは、一部の参照を回避して SDK を補助することが必要になる場合があります。

たとえば、前の例では、ComputersSold というメトリックのハンドルを参照し、観測値 42 を追跡しました。 代わりに、複数のトラック呼び出しに対してハンドルがキャッシュされる可能性があります。

//...

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            // This is where the cache is stored to handle faster lookup
            Metric computersSold = _telemetryClient.GetMetric("ComputersSold");
            while (!stoppingToken.IsCancellationRequested)
            {

                computersSold.TrackValue(42);

                computersSold.TrackValue(142);

                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(50, stoppingToken);
            }
        }

前の例では、メトリック ハンドルをキャッシュするだけでなく、Task.Delay も 50 ミリ秒に短縮され、ループがより頻繁に実行されるようになりました。 その結果、TrackValue() が 772 回呼び出されました。

多次元メトリック

前のセクションの例は、0 次元メトリックを示しています。 メトリックは多次元にすることもできます。 現在、最大で 10 個のディメンションがサポートされています。

1 次元メトリックを作成する方法の例を次に示します。

//...

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            // This is an example of a metric with a single dimension.
            // FormFactor is the name of the dimension.
            Metric computersSold= _telemetryClient.GetMetric("ComputersSold", "FormFactor");

            while (!stoppingToken.IsCancellationRequested)
            {
                // The number of arguments (dimension values)
                // must match the number of dimensions specified while GetMetric.
                // Laptop, Tablet, etc are values for the dimension "FormFactor"
                computersSold.TrackValue(42, "Laptop");
                computersSold.TrackValue(20, "Tablet");
                computersSold.TrackValue(126, "Desktop");


                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(50, stoppingToken);
            }
        }

このサンプル コードを少なくとも 60 秒実行すると、3 つの異なるテレメトリ項目が Azure に送信されます。 それぞれの項目は、3 つのフォーム ファクターのいずれかの集計を表します。 先ほどと同じように、ログ (Analytics) ビューでさらに確認することができます。

多次元メトリックの Log Analytics ビューを示すスクリーンショット。

メトリックス エクスプローラーでは、次のようになります。

カスタム メトリックを示すスクリーンショット。

新しいカスタム ディメンションでメトリックを分割したり、メトリック ビューでカスタム ディメンションを表示したりできないことがわかります。

分割のサポートを示すスクリーンショット。

既定では、メトリック エクスプローラー内の多次元メトリックは、Application Insights リソースで有効になっていません。

多次元メトリックを有効にする

Application Insights リソースに対して多次元メトリックを有効にするには、[使用量と推定コスト]>[カスタム メトリック]>[カスタム メトリック ディメンションに関するアラートを有効にします]>[OK] の順に選択します。 詳細については、「カスタム メトリックのディメンションと事前集計」を参照してください。

この変更を行って新しい多次元テレメトリを送信した後、[分割を適用] を選択できます。

Note

ポータルで機能をオンにした後に新しく送信されたメトリックにのみ、ディメンションが格納されます。

分割の適用を示すスクリーンショット。

FormFactor ディメンションのメトリック集計を表示します。

フォーム ファクターを示すスクリーンショット。

3 個を超えるディメンションがある場合に MetricIdentifier を使用する

現在、10 個のディメンションがサポートされています。 3 個を超えるディメンションでは MetricIdentifier を使用する必要があります。

// Add "using Microsoft.ApplicationInsights.Metrics;" to use MetricIdentifier
// MetricIdentifier id = new MetricIdentifier("[metricNamespace]","[metricId],"[dim1]","[dim2]","[dim3]","[dim4]","[dim5]");
MetricIdentifier id = new MetricIdentifier("CustomMetricNamespace","ComputerSold", "FormFactor", "GraphicsCard", "MemorySpeed", "BatteryCapacity", "StorageCapacity");
Metric computersSold  = _telemetryClient.GetMetric(id);
computersSold.TrackValue(110,"Laptop", "Nvidia", "DDR4", "39Wh", "1TB");

カスタム メトリックの構成

メトリックの構成を変更する場合は、メトリックが初期化されている場所で変更を行う必要があります。

特殊なディメンション名

メトリックでは、アクセスに使われる TelemetryClient のテレメトリ コンテキストは使用されません。 MetricDimensionNames クラスの定数として有効である特殊なディメンション名の使用は、このような制限がある場合に最適な回避策です。

次の Special Operation Request Size メトリックによって送信されたメトリック集計では、Context.Operation.NameSpecial Operation に設定されませんTrackMetric() メソッドやその他の TrackXXX() メソッドでは、OperationNameSpecial Operation に正しく設定されます。

        //...
        TelemetryClient specialClient;
        private static int GetCurrentRequestSize()
        {
            // Do stuff
            return 1100;
        }
        int requestSize = GetCurrentRequestSize()

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                //...
                specialClient.Context.Operation.Name = "Special Operation";
                specialClient.GetMetric("Special Operation Request Size").TrackValue(requestSize);
                //...
            }
                   
        }

この場合、TelemetryContext 値を指定するために、MetricDimensionNames クラスに一覧表示されている特殊なディメンション名を使用します。

たとえば、次のステートメントの結果として得られるメトリック集計が Application Insights クラウド エンドポイントに送信されると、その Context.Operation.Name データ フィールドは Special Operation に設定されます。

_telemetryClient.GetMetric("Request Size", MetricDimensionNames.TelemetryContext.Operation.Name).TrackValue(requestSize, "Special Operation");

この特殊なディメンションの値は TelemetryContext にコピーされ、"通常の" ディメンションとしては使用されません。 通常のメトリック探索のために操作ディメンションも保持する場合は、その目的のために別のディメンションを作成する必要があります。

_telemetryClient.GetMetric("Request Size", "Operation Name", MetricDimensionNames.TelemetryContext.Operation.Name).TrackValue(requestSize, "Special Operation", "Special Operation");

ディメンションと時系列の上限

テレメトリ サブシステムで誤ってリソースを使い切らないようにするために、メトリックごとのデータ系列の最大数を制御できます。 既定の制限は、メトリックごとのデータ系列の合計が 1,000 を超えず、ディメンションごとの異なる値が 100 を超えないことです。

重要

調整を回避するには、ディメンションに使用する基数の値を小さくします。

ディメンションと時系列の上限のコンテキストでは、Metric.TrackValue(..) を使用して、制限が観測されていることを確認します。 制限に既に達している場合、Metric.TrackValue(..) により False が返され、値は追跡されません。 それ以外の場合は、 Trueを返します。 この動作は、メトリックのデータがユーザー入力によって生じる場合に便利です。

MetricConfiguration コンストラクターでは、各メトリック内のさまざまな系列を管理する方法に関するいくつかのオプションを使用します。また、メトリックの個々の系列に対して集計動作を指定する IMetricSeriesConfiguration を実装するクラスのオブジェクトを使用します。

var metConfig = new MetricConfiguration(seriesCountLimit: 100, valuesPerDimensionLimit:2,
                new MetricSeriesConfigurationForMeasurement(restrictToUInt32Values: false));

Metric computersSold = _telemetryClient.GetMetric("ComputersSold", "Dimension1", "Dimension2", metConfig);

// Start tracking.
computersSold.TrackValue(100, "Dim1Value1", "Dim2Value1");
computersSold.TrackValue(100, "Dim1Value1", "Dim2Value2");

// The following call gives 3rd unique value for dimension2, which is above the limit of 2.
computersSold.TrackValue(100, "Dim1Value1", "Dim2Value3");
// The above call does not track the metric, and returns false.
  • seriesCountLimit は、メトリックに含めることができるデータ時系列の最大数です。 この制限に達すると、TrackValue() が呼び出されます。この結果、通常、新しい系列によって false が返されます。
  • valuesPerDimensionLimit では、同じようにディメンションごとに個別の値の数を制限します。
  • restrictToUInt32Values では、負でない整数値のみを追跡するかどうかを決定します。

上限を超えているかどうかを確認するためのメッセージを送信する方法の例を以下に示します。

if (! computersSold.TrackValue(100, "Dim1Value1", "Dim2Value3"))
{
// Add "using Microsoft.ApplicationInsights.DataContract;" to use SeverityLevel.Error
_telemetryClient.TrackTrace("Metric value not tracked as value of one of the dimension exceeded the cap. Revisit the dimensions to ensure they are within the limits",
SeverityLevel.Error);
}

次のステップ