Skip to content

Migration guidance for Windows Performance Counters #45943

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions docs/core/diagnostics/migrate-from-windows-performance-counters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
---
title: Migrate from .NET Windows Performance Counters to .NET Core metrics
description: Learn how to migrate from .NET Framework Windows Performance Counters to .NET Core/NET 5+ metrics using EventCounters and Meters.
ms.date: 04/27/2025
---

# Migrate from .NET Framework Windows Performance Counters to .NET metrics

.NET Framework applications, which only run on Windows, can use Windows Performance Counters to monitor application health and performance. However, in .NET Core and later versions, the platform provides [cross-platform alternatives](compare-metric-apis.md) through the [EventCounters](./event-counters.md) and [System.Diagnostics.Metrics](./metrics.md) APIs.

This article provides guidance on migrating from Windows Performance Counters to the newer metrics systems available in modern .NET versions.

## A short history of .NET metrics

.NET applications can be monitored using different mechanisms depending on the .NET version and platform:

- **.NET Framework**: Primarily uses Windows Performance Counters (Windows only)
- **.NET Core 3.0-3.1, .NET 5+**: Introduced EventCounters (cross-platform) and built-in [runtime metrics](./available-counters.md#systemruntime-counters), [networking metrics](./available-counters.md#systemnethttp-counters), and [ASP.NET metrics](./available-counters.md#microsoft-aspnetcore-server-kestrel-counters) using these APIs
- **.NET 6+**: Added System.Diagnostics.Metrics API (cross-platform, OpenTelemetry compatible)
- **.NET 8+**: Added built-in [networking metrics](./built-in-metrics-system-net.md) and [ASP.NET metrics](/aspnet/core/log-mon/metrics/built-in) using the new System.Diagnostics.Metrics API
- **.NET 9+**: Added built-in [runtime metrics](./built-in-metrics-runtime.md) using the new System.Diagnostics.Metrics API

The newer metrics systems offer several advantages:

- **Cross-platform operation**: Works on Windows, Linux, macOS
- **Container-friendly**: Works in containerized environments
- **Modern tooling**: Integrates with OpenTelemetry and observability platforms
- **Supports xcopy install**: No additional installation steps or privileges are required

For more information, see the [Metrics API comparison](compare-metric-apis.md).

## Collect metrics in modern .NET applications

To collect and analyze metrics, see the [System.Diagnostics.Metrics](./metrics-collection.md) and [EventCounters](./event-counters.md) guides.

## Map common Windows Performance Counters to modern metrics

If the monitoring system for your .NET Framework application uses runtime-provided Windows Performance Counters, you'll need to select alternative [EventCounters](./event-counters.md)
or [System.Diagnostics.Metrics](./metrics.md)-based metrics instead. The following tables show alternatives for many common counters. Not all .NET Framework counters have been ported to new alternatives.
In some cases, infrequently used counters were discontinued, and in other cases, implementation changes in the platform have made certain counters irrelevant.

### Memory counters

| Windows Performance Counter | EventCounter equivalent | Metrics API equivalent |
|----------------------------|--------------------------|------------------------|
| `.NET CLR Memory\# Bytes in all Heaps` | `System.Runtime\GC Heap Size` (`gc-heap-size`) | `System.Runtime\dotnet.gc.last_collection.heap.size` |
| `.NET CLR Memory\# Gen 0 Collections` | `System.Runtime\Gen 0 GC Count` (`gen-0-gc-count`) | `System.Runtime\dotnet.gc.collections` with attribute `gc.heap.generation=gen0` |
| `.NET CLR Memory\# Gen 1 Collections` | `System.Runtime\Gen 1 GC Count` (`gen-1-gc-count`) | `System.Runtime\dotnet.gc.collections` with attribute `gc.heap.generation=gen1` |
| `.NET CLR Memory\# Gen 2 Collections` | `System.Runtime\Gen 2 GC Count` (`gen-2-gc-count`) | `System.Runtime\dotnet.gc.collections` with attribute `gc.heap.generation=gen2` |
| `.NET CLR Memory\% Time in GC` | `System.Runtime\% Time in GC since last GC` (`time-in-gc`) | `System.Runtime\dotnet.gc.pause.time` (calculate as percentage of total time) |
| `.NET CLR Memory\# Total committed Bytes` | None | `System.Runtime\dotnet.gc.last_collection.memory.committed_size` |
| `.NET CLR Memory\Large Object Heap Size` | `System.Runtime\LOH Size` (`loh-size`) | `System.Runtime\dotnet.gc.last_collection.heap.size` with attribute `gc.heap.generation=loh` |
| `.NET CLR Memory\Allocated Bytes/sec` | `System.Runtime\Allocation Rate` (`alloc-rate`) | Calculate rate from `System.Runtime\dotnet.gc.heap.total_allocated` |

> [!NOTE]
> `dotnet.gc.pause.time` allows an improved calculation that avoids some undesirable behavior in the older `% Time in GC` metric. `% Time in GC` computed
> 100 * `pause_time_in_most_recent_GC` / `time_between_most_recent_two_GCs`. In some cases two GCs would occur very close together producing a high value
> based on a tiny non-representative portion of the overall time interval. `gc.heap.pause.time` accumulates the total time the GC has paused application threads
> so far in a process, which allows computing the GC pause time during any measured time interval. This is a truer measurement of GC overhead, but the change in calculation
> means the metrics might not match even when the underlying GC behavior is unchanged.

### JIT and Loading counters

| Windows Performance Counter | EventCounter equivalent | Metrics API equivalent |
|----------------------------|--------------------------|------------------------|
| `.NET CLR Jit\# of Methods Jitted` | `System.Runtime\Methods Jitted Count` (`methods-jitted-count`) | `System.Runtime\dotnet.jit.compiled_methods` |
| `.NET CLR Jit\IL Bytes Jitted` | `System.Runtime\IL Bytes Jitted` (`il-bytes-jitted`) | `System.Runtime\dotnet.jit.compiled_il.size` |
| `.NET CLR Loading\Current Assemblies` | `System.Runtime\Number of Assemblies Loaded` (`assembly-count`) | `System.Runtime\dotnet.assembly.count` |
| `.NET CLR Jit\Total # of IL Bytes Jitted` | `System.Runtime\IL Bytes Jitted` (`il-bytes-jitted`) | `System.Runtime\dotnet.jit.compiled_il.size` |

### Threading counters

| Windows Performance Counter | EventCounter equivalent | Metrics API equivalent |
|----------------------------|--------------------------|------------------------|
| `.NET CLR LocksAndThreads\Current Queue Length` | `System.Runtime\ThreadPool Queue Length` (`threadpool-queue-length`) | `System.Runtime\dotnet.thread_pool.queue.length` |
| `.NET CLR LocksAndThreads\Contention Rate / sec` | `System.Runtime\Monitor Lock Contention Count` (`monitor-lock-contention-count`) | Calculate rate from `System.Runtime\dotnet.monitor.lock_contentions` |

### Exceptions counters

| Windows Performance Counter | EventCounter equivalent | Metrics API equivalent |
|----------------------------|--------------------------|------------------------|
| `.NET CLR Exceptions\# of Exceps Thrown / sec` | `System.Runtime\Exception Count` (`exception-count`) | Calculate rate from `System.Runtime\dotnet.exceptions` |
| `.NET CLR Exceptions\# of Exceps Thrown` | None | `System.Runtime\dotnet.exceptions` |

### Socket networking counters

| Windows Performance Counter | EventCounter equivalent | Metrics API equivalent |
|----------------------------|--------------------------|------------------------|
| `.NET CLR Networking\Bytes Received` | `System.Net.Sockets\Bytes Received` (`bytes-received`) | None |
| `.NET CLR Networking\Bytes Sent` | `System.Net.Sockets\Bytes Sent` (`bytes-sent`) | None |
| `.NET CLR Networking\Connections Established` | `System.Net.Sockets\Outgoing Connections Established` (`outgoing-connections-established`) | None |
| `.NET CLR Networking\Datagrams Received` | `System.Net.Sockets\Datagrams Received` (`datagrams-received`) | None |
| `.NET CLR Networking\Datagrams Sent` | `System.Net.Sockets\Datagrams Sent` (`datagrams-sent`) | None |

### DNS networking counters

| Windows Performance Counter | EventCounter equivalent | Metrics API equivalent |
|----------------------------|--------------------------|------------------------|
| `.NET CLR Networking\DNS Lookups` | `System.Net.NameResolution\DNS Lookups Requested` (`dns-lookups-requested`) | Sum the histogram buckets in `System.Net.NameResolution\dns.lookup.duration` |
| `.NET CLR Networking\DNS Resolution Time` | `System.Net.NameResolution\Average DNS Lookup Duration` (`dns-lookups-duration`) | `System.Net.NameResolution\dns.lookup.duration` |

### HttpWebRequest counters

`HttpWebRequest` has been superseded by <xref:System.Net.Http.HttpClient>. To learn what metrics are built-in, see the HttpClient [EventCounters](./available-counters.md#systemnethttp-counters) and [System.Diagnostics.Metrics](./built-in-metrics-system-net.md#systemnethttp).

### ASP.NET counters

ASP.NET has changed dramatically between .NET Framework and .NET Core. Many counters are obsolete or are measured differently than in the past. To learn what metrics are built-in, see the ASP.NET [EventCounters](./available-counters.md#microsoftaspnetcorehosting-counters) and [System.Diagnostics.Metrics](/aspnet/core/log-mon/metrics/built-in).

## Next steps

- Learn more about [EventCounters in .NET](./event-counters.md)
- Explore the [System.Diagnostics.Metrics API](./metrics.md)
- Understand how to [collect metrics](./metrics-collection.md)
- Review [well-known EventCounters](./available-counters.md) and [built-in metrics](./built-in-metrics.md)
2 changes: 2 additions & 0 deletions docs/navigate/tools-diagnostics/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,8 @@ items:
href: ../../core/diagnostics/event-counter-perf.md
- name: Compare metric APIs
href: ../../core/diagnostics/compare-metric-apis.md
- name: Migrate from Windows Performance Counters
href: ../../core/diagnostics/migrate-from-windows-performance-counters.md
- name: Distributed tracing
items:
- name: Overview
Expand Down