Skip to content

Commit

Permalink
Merge branch 'event-counters' of https://github.com/IEvangelist/dotne…
Browse files Browse the repository at this point in the history
…tdocs into event-counters
  • Loading branch information
IEvangelist committed Jul 31, 2020
2 parents 3d9556a + 5bbc470 commit 46727c0
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 18 deletions.
12 changes: 6 additions & 6 deletions docs/core/diagnostics/event-counter-perf.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ ms.date: 07/22/2020

While an <xref:System.Diagnostics.Tracing.EventSource> is fast, logging too many events for frequent events is still a performance consideration. In this tutorial, you'll learn how an <xref:System.Diagnostics.Tracing.EventCounter> can be used to measure performance of a high frequency of events.

For events that happen every few milliseconds, you'll want the performance per event to be low (less than a millisecond). Otherwise, it is going to cost a significant performance overhead. Logging an event means you're going to write something to disk. If the disk is not fast enough, you will lose events. You need a solution other than logging the event itself.
For events that happen every few milliseconds, you'll want the overhead per event to be low (less than a millisecond). Otherwise, the impact on performance will be significant. Logging an event means you're going to write something to disk. If the disk is not fast enough, you will lose events. You need a solution other than logging the event itself.

When dealing with a large number of events, knowing the measure per event is not useful either. Most of the time all you need is just some statistics out of it. So you could crank the statistics within the process itself and then write an event once in a while to report the statistics, that's what <xref:System.Diagnostics.Tracing.EventCounter> will do. Let's take a look at an example how to do this in <xref:System.Diagnostics.Tracing.EventSource?displayProperty=fullName>.

:::code language="csharp" source="snippets/EventCounters/MinimalEventCounterSource.cs":::

The <xref:System.Diagnostics.Tracing.EventSource.WriteEvent%2A?displayProperty=nameWithType> line is the <xref:System.Diagnostics.Tracing.EventSource> part and is not part of <xref:System.Diagnostics.Tracing.EventCounter>, it was written to show that you can log a message together with the event counter.

You logged the metric to the <xref:System.Diagnostics.Tracing.EventCounter>, but unless you can actually get the statistics out of it, it is not useful. To get the statistics, you need to enable the <xref:System.Diagnostics.Tracing.EventCounter> by setting off a timer how frequently you want the events, as well as a listener to capture the events, to do that, you can use PerfView.
You logged the metric to the <xref:System.Diagnostics.Tracing.EventCounter>, but unless you can actually get the statistics out of it, it is not useful. To get the statistics, you need to enable the <xref:System.Diagnostics.Tracing.EventCounter> by creating a timer that fires as frequently as you want the events, as well as a listener to capture the events. To do that, you can use PerfView.

There is an extra keyword that you will need to specify the turn on the EventCounters.
There is an extra keyword that you will need to turn on the EventCounters.

```
PerfView /onlyProviders=*Samples-EventCounterDemos-Minimal:EventCounterIntervalSec=1 collect
Expand All @@ -29,17 +29,17 @@ PerfView /onlyProviders=*Samples-EventCounterDemos-Minimal:EventCounterIntervalS
> [!NOTE]
> The `EventCounterIntervalSec` segment is used to indicate the frequency of the sampling.
As usual, turn on PerfView, and then run the sample code - we get have something like this.
Turn on PerfView, and then run the sample code - we get have something like this.

:::image type="content" source="media/perfview-counters.png" lightbox="media/perfview-counters.png" alt-text="PerfView of EventCounter traces":::

Now let's drill into what the data captured means - when I copied from PerfView, it looks like this
Examine the captured data. When you copy from PerfView, it looks like this:

```
ThreadID="17,800" ProcessorNumber="5" Payload="{ Name:"request", Mean:142.0735, StandardDeviation:42.07355, Count:2, Min:100, Max:184.1471, IntervalSec:1.000588 }"
```

Now it is obvious that within a sampling period, we have nine events, and all the other statistics.
Within a sampling period, there are nine events and all the other statistics.

Notice that this command also logs the events, so we will get both the events and the counter statistics.

Expand Down
29 changes: 17 additions & 12 deletions docs/core/diagnostics/event-counters.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
---
title: EventCounters in .NET Core
<<<<<<< HEAD
description: In this article you'll learn what EventCounters are, how to implement them, and how to consume them.
ms.date: 07/31/2020
=======
description: In this article, you'll learn what EventCounters are, how to implement them, and how to consume them.
ms.date: 07/22/2020
>>>>>>> 5bbc470727df863cdf8c1a0631df27870a96e68b
---

# EventCounters in .NET Core
Expand All @@ -10,15 +15,15 @@ ms.date: 07/31/2020

EventCounters are .NET Core APIs used for lightweight, cross-platform, and near real-time performance metric collection. EventCounters were added as a cross-platform alternative to the "performance counters" of the .NET Framework on Windows. In this article you'll learn what EventCounters are, how to implement them, and how to consume them.

The .NET Core runtime, and a few .NET libraries publish basic diagnostics information using EventCounters starting in .NET Core 3.0. Apart from the EventCounters that are provided by the .NET runtime, you may choose to implement your own EventCounters. EventCounters can be used to track various metrics.
The .NET Core runtime and a few .NET libraries publish basic diagnostics information using EventCounters starting in .NET Core 3.0. Apart from the EventCounters that are provided by the .NET runtime, you may choose to implement your own EventCounters. EventCounters can be used to track various metrics.

EventCounters live as a part of an <xref:System.Diagnostics.Tracing.EventSource>, and are automatically pushed to listener tools on a regular basis. Like all other events on an <xref:System.Diagnostics.Tracing.EventSource>, they can be consumed both in-proc and out-of-proc via <xref:System.Diagnostics.Tracing.EventListener> and EventPipe/ETW (Event Tracing for Windows).

[![EventCounters in-proc and out-of-proc diagram image](media/eventcounters.jpg)](media/eventcounters.jpg#lightbox)

## Runtime counters

The .NET runtime publishes the many counters.
The .NET runtime publishes many counters.

### `System.Runtime` providers

Expand Down Expand Up @@ -62,21 +67,21 @@ The following counters are published as part of [ASP.NET Core SignalR](/aspnet/c

## EventCounters API overview

There are two primary purposes for counters. Some counters are for ever-increasing values such as, total # of exceptions, total # of GCs, and total # of requests. Other counters are "snapshot" values, such as heap usage, CPU usage, and working set size. Within each of these categories of counters, there are two types of counters that vary by how they get their value. Polling counters retrieve their value via a callback, and non-polling counters have their values directly set on the counter instance.
There are two primary categories of counters. Some counters are for ever-increasing values, such as total # of exceptions, total # of GCs, and total # of requests. Other counters are "snapshot" values, such as heap usage, CPU usage, and working set size. Within each of these categories of counters, there are two types of counters that vary by how they get their value. Polling counters retrieve their value via a callback, and non-polling counters have their values directly set on the counter instance.

The counters are represented by the following implementations <xref:System.Diagnostics.Tracing.EventCounter>, <xref:System.Diagnostics.Tracing.IncrementingEventCounter>, <xref:System.Diagnostics.Tracing.IncrementingPollingCounter>, and <xref:System.Diagnostics.Tracing.PollingCounter>.

1. The <xref:System.Diagnostics.Tracing.EventCounter> records a set of values. The <xref:System.Diagnostics.Tracing.EventCounter.WriteMetric%2A?displayProperty=nameWithType> method adds a new value to the set. With each interval, a statistical summary for the set is computed, such as the min, max, and mean. The [dotnet-counters](dotnet-counters.md) tool will always display the mean value. The <xref:System.Diagnostics.Tracing.EventCounter> is useful to describe a discrete set of operations. Common usage may include monitoring the average size in bytes of recent IO operations, or the average monetary value of a set of financial transactions.

1. The <xref:System.Diagnostics.Tracing.IncrementingEventCounter> records a running total. The <xref:System.Diagnostics.Tracing.IncrementingEventCounter.Increment%2A?displayProperty=nameWithType> method increases the running total. With each period of time, the difference between the total value for that period, and the total of the previous period is reported as an increment. The [dotnet-counters](dotnet-counters.md) tool will display the rate as the recorded total / time. The <xref:System.Diagnostics.Tracing.IncrementingEventCounter> is useful to measure how frequently an action is occurring, such as the number of requests processed per second.

1. The <xref:System.Diagnostics.Tracing.IncrementingPollingCounter> is a customizable counter that has no state, and uses a callback to determine the reported increment value. With each time interval, the callback is invoked, and then the difference between the current invocation, and the last invocation is the reported value. The [dotnet-counters](dotnet-counters.md) tool will always display the difference as a rate, the reported value / time. Sometimes it isn't feasible to call an API on each occurrence, but it's possible to query the total number of occurrences. For example, you could report the number of bytes written to a file / sec, even if without a notification each time a byte is written.
1. The <xref:System.Diagnostics.Tracing.IncrementingPollingCounter> is a customizable counter that has no state and uses a callback to determine the reported increment value. With each time interval, the callback is invoked, and then the difference between the current invocation, and the last invocation is the reported value. The [dotnet-counters](dotnet-counters.md) tool will always display the difference as a rate, the reported value / time. Sometimes it isn't feasible to call an API on each occurrence, but it's possible to query the total number of occurrences. For example, you could report the number of bytes written to a file / sec, even without a notification each time a byte is written.

1. The <xref:System.Diagnostics.Tracing.PollingCounter> is customizable, doesn't have any state, and uses a callback to determine the value that is reported. With each time interval, the user provided callback function is invoked. Each invocation to the callback writes the current value of the counter. A <xref:System.Diagnostics.Tracing.PollingCounter> can be used to query a metric from an external source, for example getting the current free bytes on a disk. It can also be used to report custom statistics that can be computed on demand by an application. Examples include, reporting the 95th percentile of recent request latencies, or the current hit or miss ratio of a cache.
1. The <xref:System.Diagnostics.Tracing.PollingCounter> is customizable, doesn't have any state, and uses a callback to determine the value that is reported. With each time interval, the user provided callback function is invoked. Each invocation to the callback writes the current value of the counter. A <xref:System.Diagnostics.Tracing.PollingCounter> can be used to query a metric from an external source, for example getting the current free bytes on a disk. It can also be used to report custom statistics that can be computed on demand by an application. Examples include reporting the 95th percentile of recent request latencies, or the current hit or miss ratio of a cache.

## Writing EventCounters

The following code implements a sample <xref:System.Diagnostics.Tracing.EventSource> exposed as the named `"Samples-EventCounterDemos-Minimal"` provider. This source contains an <xref:System.Diagnostics.Tracing.EventCounter> representing request processing time. Such a counter has a name (that is, its unique ID in the source) and a display name both used by listener tools such as [dotnet-counter](dotnet-counters.md).
The following code implements a sample <xref:System.Diagnostics.Tracing.EventSource> exposed as the named `"Samples-EventCounterDemos-Minimal"` provider. This source contains an <xref:System.Diagnostics.Tracing.EventCounter> representing request processing time. Such a counter has a name (that is, its unique ID in the source) and a display name, both used by listener tools such as [dotnet-counter](dotnet-counters.md).

:::code language="csharp" source="snippets/EventCounters/MinimalEventCounterSource.cs":::

Expand Down Expand Up @@ -122,11 +127,11 @@ var workingSetCounter = new PollingCounter(
};
```

The <xref:System.Diagnostics.Tracing.PollingCounter> reports the current working set of the app, since it captures a metric at a moment in time. The callback for polling a value is the provided lambda expression, which is just a call to the <xref:System.Environment.WorkingSet?displayProperty=fullName> API. The <xref:System.Diagnostics.Tracing.DiagnosticCounter.DisplayName> and <xref:System.Diagnostics.Tracing.DiagnosticCounter.DisplayUnits> is an optional property that can be set to help the consumer side of the counter to display the value more easily/accurately. For example [dotnet-counters](dotnet-counters.md) uses these properties to display the more "pretty" version of the counter names.
The <xref:System.Diagnostics.Tracing.PollingCounter> reports the current working set of the app, since it captures a metric at a moment in time. The callback for polling a value is the provided lambda expression, which is just a call to the <xref:System.Environment.WorkingSet?displayProperty=fullName> API. <xref:System.Diagnostics.Tracing.DiagnosticCounter.DisplayName> and <xref:System.Diagnostics.Tracing.DiagnosticCounter.DisplayUnits> are optional properties that can be set to help the consumer side of the counter to display the value more clearly. For example, [dotnet-counters](dotnet-counters.md) uses these properties to display the more "pretty" version of the counter names.

For the <xref:System.Diagnostics.Tracing.PollingCounter>, and the <xref:System.Diagnostics.Tracing.IncrementingPollingCounter>), nothing else that needs to be done. They both poll the values themselves at an interval requested by the consumer.
For the <xref:System.Diagnostics.Tracing.PollingCounter>, and the <xref:System.Diagnostics.Tracing.IncrementingPollingCounter>), nothing else needs to be done. They both poll the values themselves at an interval requested by the consumer.

Here is another example of runtime counter implemented using <xref:System.Diagnostics.Tracing.IncrementingPollingCounter>.
Here is an example of a runtime counter implemented using <xref:System.Diagnostics.Tracing.IncrementingPollingCounter>.

```csharp
var monitorContentionCounter = new IncrementingPollingCounter(
Expand All @@ -147,7 +152,7 @@ There are more counter implementations to use as a reference in the [.NET runtim
## Concurrency

> [!NOTE]
> The EventCounters API does not guarantee thread safety, when the delegates passed to the <xref:System.Diagnostics.Tracing.PollingCounter>, and the <xref:System.Diagnostics.Tracing.IncrementingPollingCounter> instances are called by multiple threads. It is the author's responsibility to guarantee the thread-safety of the delegates being passed to the counter APIs.
> The EventCounters API does not guarantee thread safety. When the delegates passed to <xref:System.Diagnostics.Tracing.PollingCounter> or <xref:System.Diagnostics.Tracing.IncrementingPollingCounter> instances are called by multiple threads, it's your responsibility to guarantee the delegates' thread-safety.
For example, consider the following <xref:System.Diagnostics.Tracing.EventSource> to keep track of requests.

Expand All @@ -165,15 +170,15 @@ There are two primary ways of consuming EventCounters, either in-proc, or out-of

### Consuming in-proc

You can consume the counter values via the <xref:System.Diagnostics.Tracing.EventListener> API. An <xref:System.Diagnostics.Tracing.EventListener> is an in-proc way of consuming any events written by all instances of an <xref:System.Diagnostics.Tracing.EventSource> in your application. For more information on how to use the `EventListener` API, see to <xref:System.Diagnostics.Tracing.EventListener>.
You can consume the counter values via the <xref:System.Diagnostics.Tracing.EventListener> API. An <xref:System.Diagnostics.Tracing.EventListener> is an in-proc way of consuming any events written by all instances of an <xref:System.Diagnostics.Tracing.EventSource> in your application. For more information on how to use the `EventListener` API, see <xref:System.Diagnostics.Tracing.EventListener>.

First, the <xref:System.Diagnostics.Tracing.EventSource> that produces the counter value needs to be enabled. Override the <xref:System.Diagnostics.Tracing.EventListener.OnEventSourceCreated%2A> method to get a notification when an <xref:System.Diagnostics.Tracing.EventSource> is created, and if this is the correct <xref:System.Diagnostics.Tracing.EventSource> with your EventCounters, then you can call <xref:System.Diagnostics.Tracing.EventListener.EnableEvents%2A?displayProperty=nameWithType> on it. Here is an example override:

:::code language="csharp" source="snippets/EventCounters/SimpleEventListener.cs" id="OnEventSourceCreated":::

#### Sample code

Here is a sample <xref:System.Diagnostics.Tracing.EventListener> class that prints out all the counter names, and values from the .NET runtime's <xref:System.Diagnostics.Tracing.EventSource> for publishing its internal counters (`System.Runtime`) at some interval.
Here is a sample <xref:System.Diagnostics.Tracing.EventListener> class that prints out all the counter names and values from the .NET runtime's <xref:System.Diagnostics.Tracing.EventSource>, for publishing its internal counters (`System.Runtime`) at some interval.

:::code language="csharp" source="snippets/EventCounters/SimpleEventListener.cs":::

Expand Down

0 comments on commit 46727c0

Please sign in to comment.