はじめに
System.Diagnostics.Metricsの記事で軽く触れた、MetricsEventSourceなるものについて、こちらで解説を入れる。
System.Diagnostics.Metricsについての基本的なことについては上記記事を参照のこと。
MetricsEventSourceとは
簡単に言うと、 Metricsで各種Instrumentが出している値を、EventSourceのイベントとして出力しているクラス である。
EventSourceについてはdocs.microsoft.comの記事を参照。
普通InstrumentはMetricsListenerで拾うのが筋だが、外部ツール等で情報を一時的に収集したいときなどに使う。
dotnet-traceでEventSourceのイベントをEventPipe経由で収集できるので、MetricsEventSourceを指定して収集するというわけである。
使い方
サンプルプログラム
今回は例として以下のようなプログラムを走らせるとする。
このプログラムでは、
- Meterの名前"appmeter"を作成
-
Counter<int>
のカウンター"counter1"を作成 - "counter1"の値2で、tag1=1とtag1=2となるイベントを交互に500msecの感覚で発生させる
ということを行っている。
using System.Diagnostics.Metrics;
{
Console.WriteLine(System.Diagnostics.Process.GetCurrentProcess().Id);
using var meter = new Meter("appmeter");
var counter = meter.CreateCounter<int>("counter1", "unit", "desc");
async Task CounterTask(CancellationToken ct)
{
int count = 2;
int tagval = 1;
while(!ct.IsCancellationRequested)
{
await Task.Delay(500).ConfigureAwait(false);
if(counter.Enabled)
{
counter.Add(count,
new KeyValuePair<string, object?>("tag1", tagval)
);
}
tagval = tagval ^ 3;
}
}
using var cts = new CancellationTokenSource();
await Task.WhenAll(CounterTask(cts.Token), Task.Run(() =>
{
// エンターキーを押したら終了
Console.ReadLine();
cts.Cancel();
}));
}
収集の開始
収集の開始はdotnet-trace collect
を使用する。ここで指定するプロバイダはSystem.Diagnostics.Metrics
で、簡単な例として以下のように指定する。
dotnet-trace collect -p [対象プロセスのPID] --providers "System.Diagnostics.Metrics:::Metrics=appmeter"
このコマンドの--providers
の引数でMetricsEventSourceの監視対象や監視間隔を指定できる。この--providers
の引数の構造だが、
[EventSource名]:[監視フラグ(デフォルト0xffffffff=全て]:[レベル(デフォルト5=Verbose)]:[EventSource固有の引数(';'区切りのA=B形式)]
のようになっている。
つまり、上記コマンドの意味は、
-
System.Diagnostics.Metrics
という名前のEventSource名の - イベントの監視フラグを全て立て
- 監視レベルを5にして
-
System.Diagnostics.Metrics
への引数としてMetrics=appmeter
というキーと値を渡す
という意味になる。
System.Diagnostics.Metricsへ渡せるパラメーターとして、以下のようなものがある
- SessionId
- トレースセッションを判別するためのID
- 一つのトレース開始から終了まで保持され、各MetricsEventSourceで発生するイベントに値が付与される
- デフォルトはランダムなGUID
- Metrics
- 何のInstrumentを監視するか指定する
- 形式は
[Meter名]
あるいは[Meter名]\[カウンタ名]
の二つがある - 複数監視する場合は";"で区切る
- dotnet-traceの引数リストは
;
で分ける仕様なので、値部分を二重引用符("
)で囲むこと
- dotnet-traceの引数リストは
- RefreshInterval
- カウンタの値をとる間隔(sec)
- doubleなので、0.5秒のような指定も可
- MetricsEventSourceは記録された値を保持する処理をするものがあるので、メモリ制約にも気を付けること
- MaxTimeSeries
- 一つのセッションで記録されるInstrumentの数
- 早い者勝ち
- デフォルトは1000
- 超えた場合、
- ここで保持のためのメモリを消費するのはMeterを保持している側なので注意
- MaxHistograms
- 一つのセッションで記録されるHistograms/ObservableGaugeの数
- デフォルトは20
- 超えた場合、HistogramLimitReachedイベントが起こり、それ以降Histogram系メトリックが作成されても新規追加されなくなる
イベントキーワード
EventSourceには、キーワードというフラグのようなものがあり、これを指定することによってカテゴリごとに収集するかしないかを指定できる。実際はenumなので、指定するときは数値型に直して指定することになる。
MetricsEventSourceで指定できるキーワードは以下
- Messages(0x1) : 主にデバッグ用途で発生されるイベントのキーワード
- TimeSeriesValues(0x2) : 実際の値が収集されたイベントのキーワード
- InstrumentPublishing(0x4) : Instrumentが作成されたときに発生するイベントのキーワード
発生しうるイベント
Message(1)
Keyword=Messages
主にデバッグ用途で使われるその他のメッセージ。
パラメーターはstring message
のみ
CollectionStart(2)
Keyword=TimeSeriesValue
RefreshIntervalで設定された秒数ごとに発生する、メトリックの収集開始イベント
渡されてくるパラメーターは、
-
string sessionId
: セッションID(イベントパラメーターSessionIdの値) -
DateTime intervalStartTime
: 収集開始時刻 -
DateTime intervalEndTime
: 次の収集開始時刻
の三つ
CollectionStop(3)
Keyword=TimeSeriesValue
メトリック収集が終わったら発生するイベント。渡される引数はCollectionStartと同一
CounterRateValuePublished(4)
Keyword=TimeSeriesValue
Counter<T>
と、 ObservableCounter<T>
の値を収集したときに発生するイベント。
パラメーターは以下
-
string sessionId
: セッションID -
string meterName
:Meter
作成時に指定した名前 -
string meterVersion
:Meter
作成時に指定した名前 -
string instrumentName
: Instrumentを作成時に指定した名前 -
string unit
: Instrument作成時に指定した単位 -
string tags
: 収集時に追加されたタグの、,
区切りのキーと値のペア(なければ空文字列) -
string rate
: 増分(少数と整数両方の場合が存在)
rateには Counter<T>
は、前回収集された時点からAddされた値の総計値が渡される。
ObservableCounter<T>
は、 [その時収集した値] - [前回収集した値]
の値が渡される。
GaugeValuePublished(5)
Keyword=TimeSeriesValue
ObservableGauge<T>
の値を収集したときに発生するイベント。
パラメーターは以下
-
string sessionId
: セッションID -
string meterName
:Meter
作成時に指定した名前 -
string meterVersion
:Meter
作成時に指定した名前 -
string instrumentName
: Instrumentを作成時に指定した名前 -
string unit
: Instrument作成時に指定した単位 -
string tags
: 収集時に追加されたタグの、,
区切りのキーと値のペア(なければ空文字列) -
string lastValue
: その時収集された値
HistogramValuePublished(6)
Keyword=TimeSeriesValue
Histogram<T>
の値を収集したときに発生するイベント
パラメーターは以下
-
string sessionId
: セッションID -
string meterName
:Meter
作成時に指定した名前 -
string meterVersion
:Meter
作成時に指定した名前 -
string instrumentName
: Instrumentを作成時に指定した名前 -
string unit
: Instrument作成時に指定した単位 -
string tags
: 収集時に追加されたタグの、,
区切りのキーと値のペア(なければ空文字列) -
string quantiles
: 前回収集された後に発行されたRecord(value)
の値の50%ile、95%ile、99%ileの値を、以下の形式でフォーマットしたもの(高い値の方が1に近くなる)0.5=[50%ileの値];0.95=[95%ileの値];0.99=[99%ileの値]
BeginInstrumentReporting(7)
/EndInstrumentReporting(8)
Keyword=TimeSeriesValue
Instrumentの監視を開始/終了したときに発生する。
パラメーターは以下
-
string sessionId
: セッションID -
string meterName
:Meter
作成時に指定した名前 -
string meterVersion
:Meter
作成時に指定した名前 -
string instrumentName
: Instrumentを作成時に指定した名前 -
string instrumentType
: Instrumentの型 -
string unit
: Instrument作成時に指定した単位 -
string description
: Instrument作成時に指定した概要
Error(9)
Keyword=TimeSeriesValues | Messages | InstrumentPublishing
なにがしかの例外が、Instrument監視中に内部で発生したときに発生するイベント
パラメーターは以下
-
string sessionId
: セッションID -
string errorMessage
: エラーメッセージ
InitialInstrumentEnumerationComplete(10)
Keyword=TimeSeriesValues | InstrumentPublishing
セッションの初期化が終わって、監視を開始する時に発生するイベント
パラメーターは以下
-
string sessionId
: セッションID
InstrumentPublished(11)
Keyword=InstrumentPublishing
Instrumentが作成されて、セッションに捕捉されたときに発生するイベント。
セッションの開始以前に作られたInstrumentは初期化後にこのイベントが発生し、監視対象に入れられる
TimeSeriesLimitReached(12)
Keyword=TimeSeriesValues
監視されるInstrumentの数がMaxTimeSeriesに到達したときに発生するイベント。
パラメーターは string sessionId
のみ。
これ以後新規に発生するメトリックに関しては、イベントパラメーターMetrics
に合致しても無視される。
なお、タグの値が別でも別物として扱われることに注意。
(tag1=a
とtag1=b
のメトリックがあると、それぞれTimeSeriesとしては別物として扱われる)
HistogramLimitReached(13)
Keyword=TimeSeriesValues
監視されるHistogram<T>
の数が上限に達したときに発生するイベント。
パラメーターはstring sessionId
のみ。
これ以後新規に発生するHistogram<T>
のメトリックに関しては無視される。
Histogramのカウントに現在バグがあるので、MaxHistograms
の値は必ず超えないようにすること。
なお、修正PRはマージされているので遅くともnet7.0にはこの問題は解消する。
ObservableInstrumentCallbackError(14)
Keyword=TimeSeriesValues
ObservableInstrument<T>
のコールバックの中で例外が発生したときに呼ばれる。
パラメーターは以下。
-
string sessionId
: セッションID -
string errorMessage
: エラーメッセージ
MultipleSessionsNotSupportedError(15)
Keyword=TimeSeriesValues | Messages | InstrumentPublishing
MetricsEventSourceは 現在二つ以上のセッションを同時展開することはできない仕様となっている。
このイベントは、セッションを追加しようとしたときに発生するエラーイベントとなる。
引数はstring sessionId
のみ
注意点
執筆時点の最新版リリースでは以下の問題がある
-
dotnet-traceがEventSourceに関するパラメーターをうまくパースできない- 二つ以上のEventSourceパラメーターがあると、最後のもの以外正常に解釈できない
- v6.0.257301で解消
-
MaxHistogramsを超えた状態でさらにHistogramsのRecordを追加しようとすると、MaxCountersの制限に引っかかる場合がある
- MaxHistogramsを超えない分には問題ないが、超えてから更に超えた分の
Histograms<T>.Record
が発生すると、なぜかMaxCountersの制限に引っかかって、それ以後Counterが作成されても監視対象にならなくなる - 遅くともdotnet7では解消される予定
- MaxHistogramsを超えない分には問題ないが、超えてから更に超えた分の
終わりに
Metricsの監視をアプリに組み込む場合は普通にMetricsListenerで処理すればいい話なので、MetricsEventSourceを使う機会は少ないかもしれない。
しかし、こういうものが存在するということがわかっていれば、例えば開発中や、あるいは緊急対応の時などにアドホックに値を監視することによって、トラブルシューティングの役に立つかもしれない。
注意点を最後に挙げたが、これは修正され次第また記事を更新することにする。