如何统计C# .NET Core项目各方法调用次数(支持生产/开发模式)
针对.NET Core方法调用次数统计的解决方案(支持开发/生产模式)
针对你的需求,我整理了几种适合.NET Core + VS2017 Enterprise环境的方法调用次数统计方案,分别适配开发和生产模式,你可以按需选择:
一、开发模式:用VS2017自带工具快速搞定
作为VS2017 Enterprise用户,其实不用写代码就能快速完成开发阶段的统计需求——自带的性能探查器完全能满足:
- 打开你的项目,点击顶部菜单栏的
分析->性能探查器(快捷键Alt+F2) - 在弹出的性能会话窗口里,选择
CPU使用率(方法调用次数属于CPU分析的一部分),然后点击开始 - 运行项目,执行你要测试的业务操作后,停止探查器。在生成的报告里,展开
调用树或者函数详细信息面板,就能看到每个方法的调用次数(Call Count),还能顺带看到执行时间、CPU占用等额外数据 - 优势:零代码侵入,可视化界面直观,适合开发阶段快速验证业务逻辑的调用情况
二、代码实现:支持开关控制的通用统计方案
如果需要在生产环境也能灵活开启/关闭统计,或者要自定义统计逻辑,推荐用代码实现,通过配置开关控制,不影响正常业务性能。
1. 基于AOP拦截器的方案(适合DI管理的服务)
这里用Castle动态代理实现AOP拦截,好处是不用修改业务代码,通过DI配置就能给指定服务添加统计逻辑:
首先定义统计配置和拦截器:
using System; using System.Collections.Concurrent; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Castle.DynamicProxy; // 统计开关配置,可通过appsettings控制 public class MethodCountOptions { public bool EnableCounting { get; set; } = false; // 默认关闭,生产环境按需开启 } // 方法调用统计拦截器 public class MethodCountInterceptor : IInterceptor { // 线程安全的统计字典,存储方法名和调用次数 private static readonly ConcurrentDictionary<string, int> _methodCallCounts = new ConcurrentDictionary<string, int>(); private readonly MethodCountOptions _options; private readonly ILogger<MethodCountInterceptor> _logger; public MethodCountInterceptor(IOptions<MethodCountOptions> options, ILogger<MethodCountInterceptor> logger) { _options = options.Value; _logger = logger; } public void Intercept(IInvocation invocation) { // 如果统计未开启,直接执行原方法 if (!_options.EnableCounting) { invocation.Proceed(); return; } // 生成唯一方法标识(命名空间+类名+方法名) var methodKey = $"{invocation.TargetType.FullName}.{invocation.Method.Name}"; // 更新调用次数 _methodCallCounts.AddOrUpdate(methodKey, 1, (key, count) => count + 1); // 区分异步/同步方法处理 if (invocation.Method.ReturnType.IsGenericType && invocation.Method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) { invocation.Proceed(); var task = (Task)invocation.ReturnValue; task.ContinueWith(t => _logger.LogInformation("方法 [{Method}] 累计调用次数: {Count}", methodKey, _methodCallCounts[methodKey]) ); } else if (invocation.Method.ReturnType == typeof(Task)) { invocation.Proceed(); var task = (Task)invocation.ReturnValue; task.ContinueWith(t => _logger.LogInformation("方法 [{Method}] 累计调用次数: {Count}", methodKey, _methodCallCounts[methodKey]) ); } else { invocation.Proceed(); _logger.LogInformation("方法 [{Method}] 累计调用次数: {Count}", methodKey, _methodCallCounts[methodKey]); } } // 暴露统计数据的方法,可用于接口查询或导出 public static ConcurrentDictionary<string, int> GetMethodCallCounts() { return _methodCallCounts; } }
然后在Startup.cs里配置DI:
using Castle.DynamicProxy; using Microsoft.Extensions.DependencyInjection; public void ConfigureServices(IServiceCollection services) { // 绑定配置到appsettings.json的MethodCount节点 services.Configure<MethodCountOptions>(Configuration.GetSection("MethodCount")); // 注册动态代理生成器和拦截器 services.AddSingleton<IProxyGenerator, ProxyGenerator>(); services.AddScoped<MethodCountInterceptor>(); // 给需要统计的服务注册代理(以IMyService为例) services.AddScoped<IMyService>(sp => { var generator = sp.GetRequiredService<IProxyGenerator>(); var interceptor = sp.GetRequiredService<MethodCountInterceptor>(); var actualService = new MyService(); // 你的真实服务实现 return generator.CreateInterfaceProxyWithTarget<IMyService>(actualService, interceptor); }); // 其他服务配置... }
最后在appsettings.json里配置开关:
{ "MethodCount": { "EnableCounting": true // 开发模式设为true,生产模式可根据环境变量或配置中心动态切换 } }
2. 基于DiagnosticSource的低侵入方案(适合所有方法)
如果需要统计非DI管理的类方法,推荐用.NET Core原生的DiagnosticSource,性能开销极低,非常适合生产环境:
using System; using System.Collections.Concurrent; using System.Diagnostics; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; public class MethodCountDiagnosticListener { private static readonly ConcurrentDictionary<string, int> _methodCallCounts = new ConcurrentDictionary<string, int>(); private readonly MethodCountOptions _options; private readonly ILogger<MethodCountDiagnosticListener> _logger; private readonly DiagnosticListener _diagnosticListener; public MethodCountDiagnosticListener(IOptions<MethodCountOptions> options, ILogger<MethodCountDiagnosticListener> logger, DiagnosticListener diagnosticListener) { _options = options.Value; _logger = logger; _diagnosticListener = diagnosticListener; // 只有开启统计时才订阅事件 if (_options.EnableCounting) { _diagnosticListener.SubscribeWithAdapter(this); } } // 监听方法进入事件 [DiagnosticName("System.Runtime.MethodEnter")] public void OnMethodEnter(string methodName, Type type) { var methodKey = $"{type.FullName}.{methodName}"; _methodCallCounts.AddOrUpdate(methodKey, 1, (key, count) => count + 1); } // 可选:监听方法退出事件,输出统计日志 [DiagnosticName("System.Runtime.MethodExit")] public void OnMethodExit(string methodName, Type type) { var methodKey = $"{type.FullName}.{methodName}"; if (_methodCallCounts.TryGetValue(methodKey, out var count)) { _logger.LogDebug("方法 [{Method}] 累计调用次数: {Count}", methodKey, count); } } // 暴露统计数据 public static ConcurrentDictionary<string, int> GetMethodCallCounts() { return _methodCallCounts; } }
在Startup.cs注册并激活:
public void ConfigureServices(IServiceCollection services) { services.Configure<MethodCountOptions>(Configuration.GetSection("MethodCount")); services.AddSingleton<MethodCountDiagnosticListener>(); // 其他配置... } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // 激活诊断监听器 app.ApplicationServices.GetRequiredService<MethodCountDiagnosticListener>(); // 其他中间件配置... }
三、生产模式的注意事项
- 代码方案建议仅在需要排查问题或统计时开启,平时保持关闭,避免不必要的性能开销
- 高并发场景下,务必使用
ConcurrentDictionary保证线程安全 - 可以将统计数据输出到专业日志系统(如Serilog+ELK),方便后续分析和可视化
- 如果需要实时监控,也可以用.NET Core自带的
dotnet counters命令行工具:dotnet counters monitor --process-id <你的项目进程ID> --counters System.Runtime
内容的提问来源于stack exchange,提问作者TestIt
相关产品推荐
相关产品推荐

