Monitoring C# Applications: Advanced Metrics and Alerts
\n\nIn modern C# (.NET) development, a simply "working" application is no longer a sufficient quality criterion. Production systems require constant observation: you need to know how much memory the service consumes, how quickly requests are processed, and whether the queue in the message broker is growing. Without a monitoring and alerting system, you risk learning about a problem only from users. In this article, we will cover advanced techniques for collecting metrics, visualizing them, and configuring alerts for C# applications using modern tools like Prometheus, Grafana, and OpenTelemetry.
\n\nWhy Do You Need Advanced Monitoring?
\nBasic monitoring (CPU, RAM, disk) does not provide the full picture. For C# applications, specific metrics are critical: the number of garbage collections (GC), thread blocking time, and database connection pool size. Alerts on these metrics allow you to react before a failure occurs. An advanced approach implies three levels:
\n- \n
- Infrastructure level — standard server metrics. \n
- Application level — business logic metrics (latency, errors, call frequency). \n
- Business level — domain-related metrics (number of orders, conversion rate). \n
We will focus on the application level, which provides the greatest benefit for C# developers.
\n\nCollecting Metrics with OpenTelemetry and Prometheus
\nOpenTelemetry (OTel) is the de facto standard for instrumenting .NET applications. It allows you to collect metrics, traces, and logs via a unified API. Let's look at an example of integration with Prometheus.
\n\nInstall the NuGet packages:
\ndotnet add package OpenTelemetry.Extensions.Hosting\ndotnet add package OpenTelemetry.Exporter.Prometheus.AspNetCore\ndotnet add package OpenTelemetry.Instrumentation.AspNetCore\n\nConfiguration in Program.cs:
using OpenTelemetry.Metrics;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddOpenTelemetry()\n .WithMetrics(metrics => metrics\n .AddAspNetCoreInstrumentation()\n .AddRuntimeInstrumentation()\n .AddPrometheusExporter());\n\nvar app = builder.Build();\n\napp.MapPrometheusScrapingEndpoint(); // endpoint for Prometheus\n\napp.Run();\n\nNow, at the /metrics endpoint, your service will expose metrics in Prometheus format. These include the number of HTTP requests, their processing time, and GC and memory metrics.
Custom Metrics
\nFor business metrics, use Meter and Counter:
using System.Diagnostics.Metrics;\n\npublic class OrderMetrics\n{\n private readonly Counter<int> _ordersCreated;\n\n public OrderMetrics(IMeterFactory meterFactory)\n {\n var meter = meterFactory.Create("MyApp.Orders");\n _ordersCreated = meter.CreateCounter<int>("orders.created.count");\n }\n\n public void OrderCreated() => _ordersCreated.Add(1);\n}\n\n// Registration in DI:\nbuilder.Services.AddSingleton<OrderMetrics>();\n\nThese metrics will automatically be picked up by Prometheus and will be available for building dashboards in Grafana.
\n\nConfiguring Alerts: From Simple Rules to a Multi-Level System
\nMetrics themselves are useless without alerts. In Prometheus, alerts are configured via Alertmanager. Let's look at an advanced scenario: an alert for anomalous API response time.
Rule in Prometheus (alert.rules.yml)
\ngroups:\n - name: csharp_app_alerts\n rules:\n - alert: HighRequestLatency\n expr: |\n histogram_quantile(0.95, \n rate(http_server_duration_seconds_bucket[5m])\n ) > 2\n for: 2m\n labels:\n severity: critical\n annotations:\n summary: "High request latency ({{ $value }}s)"\n descr