You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在.NET Core应用内访问Npgsql计数器?

如何在代码中捕获Npgsql的EventCounters数据

我之前也碰到过类似的问题,你的代码里有几个关键细节需要调整,才能正确获取到idle-connections这类计数器数据。下面是具体的解决方案和修正后的代码:

问题分析

你遇到的核心问题有两个:

  1. 计数器刷新间隔的配置方式错误:你试图给单个计数器设置刷新间隔,但EventCounter的刷新是全局配置的,需要用统一的参数名。
  2. 主线程阻塞导致回调无法执行:你的同步while(true)循环会完全占用主线程,EventListener的事件回调没有机会处理计数器推送的数据。
  3. 计数器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}");
                    }
                }
            }
        }
    }
}

关键修改点说明

  1. 异步主线程:把同步循环改成异步async/await模式,并加入Task.Delay,避免主线程被完全阻塞,确保EventListener的回调能及时处理事件。
  2. 全局刷新间隔配置:用EventCounterIntervalSec这个标准参数设置计数器的推送间隔,和dotnet counters monitor的底层逻辑保持一致。
  3. 精细化Payload解析:新增ParseEventCounters方法,专门提取计数器的名称和数值,只筛选你需要的计数器输出,避免冗余信息。

这样调整后,你就能在代码中捕获到和dotnet counters monitor一样的Npgsql计数器数据了。

内容的提问来源于stack exchange,提问作者AlfeG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 16:42:47