EventPipe + Prometheus
.Net Core 3.0以後,總算有官方的套件可以做 performance counter,
不管在 windows上還是 linux上,都可以用一致的方法來建立效能監控指標。
若要建立效能指標,我們需要先建立一個EventSource的實體,
這個類別在 System.Diagnostics.Tracing 中,
可以利用以下的程式碼定義
public class SampleEventSource : EventSource
{
public SampleEventSource() : base("Sample")
{
}
}
定義好類別後,可以建立其實體,然後以這個實體來建立接下來的EventCounter,
EventCounter內建的有四種Counter,可以看MSDN的介紹
建立Counter需要傳入EventSource,建立的程式如下
public SampleEventHostedService(SampleEventSource eventSource)
{
m_Counter = new IncrementingEventCounter("test-incrementing", eventSource);
}
有了Counter後,就可以使用來記錄效能指標,
m_Counter.Increment();
程式中把效能指標埋好後,接下來就是怎麼取得這些效能指標,
如果安裝了 dotnet-counters,在命令列下執行
dotnet counters monitor -p <要監控的process id>
就可以看到預設的performance counters,
像這樣自定義的 counters,可以執行
dotnet counters monitor -p <要監控的process id> System.Runtime Sample
將要監控的指標以空白串起來放在命令的最後,
執行起來會看到類似這樣的畫面
不過這樣子雖然可以用來快速了解系統的狀態,感覺不容易讓目前常見的監控系統讀取,
我所參考的文章中,作者讀取的程式還沒有套件可以使用,後來微軟把這個套件釋出了,
所以只要加入nuget 套件 Microsoft.Diagnostics.NETCore.Client 就可以寫出像dotnet-counters這樣的程式來讀取效能指標,
我想建立一個monitor的站台,讀取出指標,並且產生 Prometheus 格式的內容來整合到監控軟體上,
目標的環境預計要跑在docker中,而linux docker的dotnet process剛好 pid 都是 1,
這樣一來 pid 的問題就能排除,
不過接下來是如果讓 monitor的 app 可以讀取到目標的 EventPipe 的資料,
這個資料在Linux上是使用 Domain Socket file來達成,而在 Windows上則是 NamePipe,
在 Linux 上這個檔案會放置在 /tmp 中,
所以只要讓兩個container之間可以共用 /tmp,就可以讀取到目標的效能指標,
另外,因為 dotnet 的 container預設執行的pid 是 1,監控的 app也將會 pid 是 1,
這樣資料會錯亂,在docker run 的時候,可以使用 --pid 來讓監控的 container 引用別的 container的 pid,如此,自己的pid 也會錯開,
monitor的程式碼會如下
var pid = 1;
var client = new DiagnosticsClient(pid);
var providerArguments = new Dictionary<string, string>
{
["EventCounterIntervalSec"] = "10"
};
var provider = new EventPipeProvider(
"System.Runtime",
System.Diagnostics.Tracing.EventLevel.Verbose,
0xffffffff,
providerArguments);
var customEventProvider = new EventPipeProvider(
"Sample",
System.Diagnostics.Tracing.EventLevel.Verbose,
0xffffffff,
providerArguments);
var session = client.StartEventPipeSession(new[] { provider, customEventProvider });
m_EventSource = new EventPipeEventSource(session.EventStream);
m_EventSource.Dynamic.All += ProcessEvents;
事件處理程式
private void ProcessEvents(TraceEvent obj)
{
if (obj.EventName.Equals("EventCounters"))
{
try
{
IDictionary<string, object> payloadVal = (IDictionary<string, object>)(obj.PayloadValue(0));
IDictionary<string, object> payloadFields = (IDictionary<string, object>)(payloadVal["Payload"]);
Console.WriteLine(string.Join(", ", payloadFields.Select(p => $"{p.Key}=>{p.Value}")));
var counterName = payloadFields["Name"].ToString();
var displayName = payloadFields["DisplayName"].ToString();
var displayUnits = payloadFields["DisplayUnits"].ToString();
var value = 0D;
if (payloadFields["CounterType"].Equals("Mean"))
{
value = (double)payloadFields["Mean"];
}
else if (payloadFields["CounterType"].Equals("Sum"))
{
var intervalSec = (float)payloadFields["IntervalSec"];
var increment = (double)payloadFields["Increment"];
value = increment / intervalSec;
if (string.IsNullOrEmpty(displayUnits))
{
displayUnits = "count";
}
displayUnits += "/sec";
}
var gaugeName = $"{obj.ProviderName.Replace('.', ':')}:{counterName.Replace('-', '_')}";
var gauge = GetGauge(gaugeName, displayName, displayUnits);
gauge.Set(value);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
完成後,可以看到成功取得內容
這個實作參考了兩篇文章才得以完成
https://im5tu.io/article/2020/01/diagnostics-in-.net-core-3-using-dotnet-counters-with-docker/
實作的程式碼放置在GitHub中