如何在.NET Core应用内访问Npgsql计数器?
如何在代码中捕获Npgsql的EventCounters数据
我之前也碰到过类似的问题,你的代码里有几个关键细节需要调整,才能正确获取到idle-connections这类计数器数据。下面是具体的解决方案和修正后的代码:
问题分析
你遇到的核心问题有两个:
- 计数器刷新间隔的配置方式错误:你试图给单个计数器设置刷新间隔,但EventCounter的刷新是全局配置的,需要用统一的参数名。
- 主线程阻塞导致回调无法执行:你的同步
while(true)循环会完全占用主线程,EventListener的事件回调没有机会处理计数器推送的数据。 - 计数器Payload解析不够细致:EventCounters的 payload 结构需要针对性解析才能提取出具体的计数器名称和数值。
修正后的代码
using System; using System.Collections.Generic; using System.Diagnostics.Tracing; using System.Linq; using System.Threading.Tasks; using Npgsql; class Program { static async Task Main(string[] args) { var listener = new TestEventListener(); // 改用异步循环,避免阻塞主线程,让EventListener回调能正常执行 while (true) { using var cn = new NpgsqlConnection("Server=localhost;Port=5432;Database=test;User Id=postgres;Password=postgres;"); await cn.OpenAsync(); var cmd = new NpgsqlCommand("select 1", cn); await cmd.ExecuteNonQueryAsync(); await Task.Delay(500); // 加短暂延迟,给计数器留足统计时间 } } } class TestEventListener : EventListener { private readonly EventLevel _level = EventLevel.Verbose; protected override void OnEventSourceCreated(EventSource eventSource) { if (eventSource.Name.Equals("Npgsql", StringComparison.OrdinalIgnoreCase)) { // 全局设置计数器刷新间隔为1秒,这是EventCounter的标准配置参数 var refreshOptions = new Dictionary<string, string> { { "EventCounterIntervalSec", "1" } }; EnableEvents(eventSource, _level, EventKeywords.None, refreshOptions); } base.OnEventSourceCreated(eventSource); } protected override void OnEventWritten(EventWrittenEventArgs eventData) { // 单独处理EventCounters事件 if (eventData.EventName.Equals("EventCounters", StringComparison.OrdinalIgnoreCase)) { ParseEventCounters(eventData.Payload); return; } // 保留原有事件输出(比如CommandStart/CommandEnd) Console.WriteLine($"触发事件: {eventData.EventName}"); } private void ParseEventCounters(IReadOnlyList<object> payload) { foreach (var item in payload) { if (item is IDictionary<string, object> counterData) { if (counterData.TryGetValue("Name", out var nameObj) && counterData.TryGetValue("Value", out var valueObj)) { var counterName = nameObj.ToString(); // 只输出你关心的计数器 if (new[] { "idle-connections", "busy-connections", "connection-pools" }.Contains(counterName)) { Console.WriteLine($"计数器: {counterName} = {valueObj}"); } } } } } }
关键修改点说明
- 异步主线程:把同步循环改成异步
async/await模式,并加入Task.Delay,避免主线程被完全阻塞,确保EventListener的回调能及时处理事件。 - 全局刷新间隔配置:用
EventCounterIntervalSec这个标准参数设置计数器的推送间隔,和dotnet counters monitor的底层逻辑保持一致。 - 精细化Payload解析:新增
ParseEventCounters方法,专门提取计数器的名称和数值,只筛选你需要的计数器输出,避免冗余信息。
这样调整后,你就能在代码中捕获到和dotnet counters monitor一样的Npgsql计数器数据了。
内容的提问来源于stack exchange,提问作者AlfeG
相关产品推荐
相关产品推荐

