色んな事を書く

シンプルさを極めたエンジニアになりたい

.NET の MetricsQueryClient を使って Azure Resource のメトリクスを取得する

下記を使います。

www.nuget.org

Repository はこちら。

github.com

この SDK を使えば Azure Monitor に対して ReadOnly なクエリをよしなに実行して結果を得られるみたいですね。これまでは Azure Portal 上からメトリクス見て~と判断していたものを、プログラム上から簡単に出来そうな期待が持てます。README を参考に Service Bus のメトリクスを取得してみます。

Service Bus のメトリクスを取ってみる

コードはシンプルで、取得したいリソースの resourceId があればとりあえず OK です。あとは取得したいメトリクスを QueryResourceAsync に渡します。サンプルでは Service BusActive Message Count を取得するようにしてみます。

var client = new MetricsQueryClient(new DefaultAzureCredential());
var result = await client.QueryResourceAsync(
    "resourceId",
    new[] { "ActiveMessages" });

第二引数にメトリクス名を入れていますが、正確には MetricsId が求められます。Azure Portal 上の表記と MetricsId の対比が書かれているドキュメントがなかったのでこちらにまとめました。

まじでこれだけでメトリクスが取得出来てしまいます。ただ一点注意してもらいたいのが、上記のコードだと Service Bus の名前空間全体に対するメトリクスを取得しているという事です。つまり各 Entity ごとのメトリクスではないという事です。

また、ActiveMessage の場合は Average の値しか取れません。おそらくこちらの表に書いてある Aggregation type しか取れないのではないかと思います。

実際のユースケースでは特定 queue のメトリクスが欲しいとか Max の値が欲しいとか様々だと思います。MetricsQueryClientOptions を使うとうまくできるので、ユースケースごとの実装パターンを残しときます。

特定 Queue/Topic のメトリクスを取得したい

Filter を使って取得対象の Entity を絞り込めます。

var filteredNyQueueNameOptions = new MetricsQueryOptions
{
    Filter = @"EntityName eq 'hogehoge'
or EntityName eq 'fugafuga'"
};
var filteredNyQueueNameMetrics = await client.QueryResourceAsync(
    "resourceId",
    new[] { "ActiveMessages" },
    filteredNyQueueNameOptions );

厄介だなぁと思うのが、in など sql で使い慣れた句が使えないという事です。複数の Queue 名で絞り込みたい時は、サンプルのように冗長な記述になってしまいます。この辺りは今後に期待ですね。

ここで指定した Entity が戻り値の Metadata として戻ってきます。なので、KeyName ごとの Active Message 数Dictionary に変換したい時は以下のようなことをやってます。

var m = resources.Value.Metrics
    .SelectMany(m => m.TimeSeries)
    .Select(d =>
    {
        return new
        {
            EntityName = d.Metadata.Values.First(),
            MaxCount = d.Values.Max(v => v.Average)
        };
    }).ToDictionary(v => v.EntityName, v => v.MaxCount);

ちなみにですが、名前空間に存在しない Entity を指定してもエラーにはなりませんでした。その Entity のメトリクスが取得できない、というだけです。ディメンション分割のために Filter* も指定出来るみたいです。

過去 3 時間分のメトリクスを 30 分間隔で集計したい

TimeRangeGranularity を使います。

var rangeOptions = new MetricsQueryOptions
{
    Filter = @"EntityName eq 'hogehoge'
or EntityName eq 'fugefuge'",
    TimeRange = new QueryTimeRange(TimeSpan.FromHours(3)),
    Granularity = TimeSpan.FromMinutes(30)
};

var rangeOptionsMetrics = await client.QueryResourceAsync(
    "resourceId",
    new[] { "ActiveMessages" }
    , rangeOptions);

TimeRange のほうはコンストラクタのオーバーロードも複数あって柔軟性が高そうです。サンプルの TimeRange のみを指定するパターンだと、取得した日時を起点に duration 分過去 (例だと 3時間分) のメトリクスを取得します。何も指定しない場合は過去 1 時間分のメトリクスを取得します。

Granularity のほうは TimSpan を渡すだけなのでシンプルですね。一点注意なのが、サポートされている粒度が限られているという事です。以下の粒度しか指定出来ず。これら以外を指定すると実行時エラーが起きます。

  • 1 分
  • 5 分
  • 15 分
  • 30 分
  • 1 時間
  • 6 時間
  • 12 時間
  • 1 日

何も指定しない場合は、1 分の粒度になります。

過去 1 時間分のメトリクスの最大値を取得して最大値の降順に並べたい

AggregationsOrderBy を使います。

var orderByMaxOptions = new MetricsQueryOptions
{
    Filter = @"EntityName eq 'hogehoge'
or EntityName eq 'fugefuge'",
    OrderBy = "Maximum desc"
};
orderByMaxOptions.Aggregations.Add(MetricAggregationType.Maximum);

var resources = await client.QueryResourceAsync(
    "resourceId",
    new[] { "ActiveMessages" },
    orderByMaxOptions);

Aggregations には setter が用意されていないのでこのようにします。

複数の AggregationType は指定できないようです。指定した場合、一つ目の AggregationType のみが取得されます。例えば以下の場合、Maximus のメトリクスのみ取得出来、Average は全て Null となってしまいます。

orderByMaxOptions.Aggregations.Add(MetricAggregationType.Maximum);
orderByMaxOptions.Aggregations.Add(MetricAggregationType.Average);

なんでだろうと思ってコードを読んでみたのですが、よくわからず。。。Aggregation Type が複数あればそれらを join しているのでいけると思ったのですがね。。。内部的には REST API を使ってるみたいだし、Aggregation Type を複数指定出来てるし。謎です。

カスタムメトリクスを取得する

MetricNamespace を使います。何も指定しなければ、既定 (?) の名前空間のメトリクスを取得します。Service Bus の場合は microsoft.servicebus/namespaces です。

そもそも各リソースにどのようなメトリクス名前空間が知るには GetMetricNamespacesAsync を使います。戻り値に FullyQualifiedName というプロパティがあるので、この値を MetricNamespace に指定します。

var customMetricsOption = new MetricsQueryOptions
{
    MetricNamespace = "custom metrics"
};

var customMetrics= await client.QueryResourceAsync(
    "resourceId",
    new[] { "ActiveMessages" },
    customMetricsOption);

としてあげるだけです。

Queue の状態によってどうなるのか

Service Bus 固有の話になってしまいますが、Queue の状態毎にメトリクスがどうなるのかまとめです。Service Bus の Queue には以下の 4 種類の Status が存在します。

  • Active
  • Diabled
  • SendDisabled
  • ReceiveDisabled

これらの Status と取得したメトリクスの関係のまとめです。

Status Metrics
Active 取れる
Diabled 取れる
SendDisabled 取れる
ReceiveDisabled 取れる

結論、関係ないです。

MetricResult の Error

Error というプロパティがあります。コードを読んだ感じ、200 以外が返ってきたときは全て Exception になるので、200 でかつ何かしらのエラーがある時。

API Client の方には InvalidSamplingType というのが返ってきているが、試してもうまくいかず。。。

Service Bus のメトリクスと MetricsId

Azure Portal 上のメトリクス名 MetricsId
CompletedMessages CompleteMessage
Count of Active messages in Queue/Topic ActiveMessages