Skip to content

Commit d0435a5

Browse files
authored
Merge pull request #196746 from AaronMaxwell/aaronmax-custom-metrics-update
[AppInsights][AaronMax] Added a note about cardinality
2 parents dad4d2d + f9a2be4 commit d0435a5

File tree

1 file changed

+29
-26
lines changed

1 file changed

+29
-26
lines changed

articles/azure-monitor/app/get-metric.md

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,30 @@ The Azure Monitor Application Insights .NET and .NET Core SDKs have two differen
1313

1414
## Pre-aggregating vs non pre-aggregating API
1515

16-
`TrackMetric()` sends raw telemetry denoting a metric. It is inefficient to send a single telemetry item for each value. `TrackMetric()` is also inefficient in terms of performance since every `TrackMetric(item)` goes through the full SDK pipeline of telemetry initializers and processors. Unlike `TrackMetric()`, `GetMetric()` handles local pre-aggregation for you and then only submits an aggregated summary metric at a fixed interval of one minute. So if you need to closely monitor some custom metric at the second or even millisecond level you can do so while only incurring the storage and network traffic cost of only monitoring every minute. This also greatly reduces the risk of throttling occurring since the total number of telemetry items that need to be sent for an aggregated metric are greatly reduced.
16+
`TrackMetric()` sends raw telemetry denoting a metric. It's inefficient to send a single telemetry item for each value. `TrackMetric()` is also inefficient in terms of performance since every `TrackMetric(item)` goes through the full SDK pipeline of telemetry initializers and processors. Unlike `TrackMetric()`, `GetMetric()` handles local pre-aggregation for you and then only submits an aggregated summary metric at a fixed interval of one minute. So if you need to closely monitor some custom metric at the second or even millisecond level you can do so while only incurring the storage and network traffic cost of only monitoring every minute. This behavior also greatly reduces the risk of throttling occurring since the total number of telemetry items that need to be sent for an aggregated metric are greatly reduced.
1717

18-
In Application Insights, custom metrics collected via `TrackMetric()` and `GetMetric()` are not subject to [sampling](./sampling.md). Sampling important metrics can lead to scenarios where alerting you may have built around those metrics could become unreliable. By never sampling your custom metrics, you can generally be confident that when your alert thresholds are breached, an alert will fire. But since custom metrics aren't sampled, there are some potential concerns.
18+
In Application Insights, custom metrics collected via `TrackMetric()` and `GetMetric()` aren't subject to [sampling](./sampling.md). Sampling important metrics can lead to scenarios where alerting you may have built around those metrics could become unreliable. By never sampling your custom metrics, you can generally be confident that when your alert thresholds are breached, an alert will fire. But since custom metrics aren't sampled, there are some potential concerns.
1919

20-
If you need to track trends in a metric every second, or at an even more granular interval this can result in:
20+
Trend tracking in a metric every second, or at an even more granular interval can result in:
2121

22-
- Increased data storage costs. There is a cost associated with how much data you send to Azure Monitor. (The more data you send the greater the overall cost of monitoring.)
23-
- Increased network traffic/performance overhead. (In some scenarios this could have both a monetary and application performance cost.)
24-
- Risk of ingestion throttling. (The Azure Monitor service drops ("throttles") data points when your app sends a very high rate of telemetry in a short time interval.)
22+
- Increased data storage costs. There's a cost associated with how much data you send to Azure Monitor. (The more data you send the greater the overall cost of monitoring.)
23+
- Increased network traffic/performance overhead. (In some scenarios this overhead could have both a monetary and application performance cost.)
24+
- Risk of ingestion throttling. (The Azure Monitor service drops ("throttles") data points when your app sends a high rate of telemetry in a short time interval.)
2525

26-
Throttling is of particular concern in that like sampling, throttling can lead to missed alerts since the condition to trigger an alert could occur locally and then be dropped at the ingestion endpoint due to too much data being sent. This is why for .NET and .NET Core we don't recommend using `TrackMetric()` unless you have implemented your own local aggregation logic. If you are trying to track every instance an event occurs over a given time period, you may find that [`TrackEvent()`](./api-custom-events-metrics.md#trackevent) is a better fit. Though keep in mind that unlike custom metrics, custom events are subject to sampling. You can of course still use `TrackMetric()` even without writing your own local pre-aggregation, but if you do so be aware of the pitfalls.
26+
Throttling is a concern as it can lead to missed alerts. The condition to trigger an alert could occur locally and then be dropped at the ingestion endpoint due to too much data being sent. We don't recommend using `TrackMetric()` for .NET and .NET Core unless you've implemented your own local aggregation logic. If you're trying to track every instance an event occurs over a given time period, you may find that [`TrackEvent()`](./api-custom-events-metrics.md#trackevent) is a better fit. Though keep in mind that unlike custom metrics, custom events are subject to sampling. You can still use `TrackMetric()` even without writing your own local pre-aggregation, but if you do so be aware of the pitfalls.
2727

28-
In summary `GetMetric()` is the recommended approach since it does pre-aggregation, it accumulates values from all the Track() calls and sends a summary/aggregate once every minute. This can significantly reduce the cost and performance overhead by sending fewer data points, while still collecting all relevant information.
28+
In summary `GetMetric()` is the recommended approach since it does pre-aggregation, it accumulates values from all the Track() calls and sends a summary/aggregate once every minute. `GetMetric()` can significantly reduce the cost and performance overhead by sending fewer data points, while still collecting all relevant information.
2929

3030
> [!NOTE]
3131
> Only the .NET and .NET Core SDKs have a GetMetric() method. If you are using Java, see [sending custom metrics using micrometer](./java-in-process-agent.md#send-custom-metrics-by-using-micrometer). For JavaScript and Node.js you would still use `TrackMetric()`, but keep in mind the caveats that were outlined in the previous section. For Python you can use [OpenCensus.stats](./opencensus-python.md#metrics) to send custom metrics but the metrics implementation is different.
3232
3333
## Getting started with GetMetric
3434

35-
For our examples, we are going to use a basic .NET Core 3.1 worker service application. If you would like to exactly replicate the test environment that was used with these examples, follow steps 1-6 of the [monitoring worker service article](./worker-service.md#net-core-30-worker-service-application) to add Application Insights to a basic worker service project template. These concepts apply to any general application where the SDK can be used including web apps and console apps.
35+
For our examples, we're going to use a basic .NET Core 3.1 worker service application. If you would like to replicate the test environment used with these examples, follow steps 1-6 of the [monitoring worker service article](./worker-service.md#net-core-30-worker-service-application). These steps will add Application Insights to a basic worker service project template and the concepts apply to any general application where the SDK can be used including web apps and console apps.
3636

3737
### Sending metrics
3838

39-
Replace the contents of your `worker.cs` file with the following:
39+
Replace the contents of your `worker.cs` file with the following code:
4040

4141
```csharp
4242
using System;
@@ -74,7 +74,7 @@ namespace WorkerService3
7474
}
7575
```
7676

77-
If you run the code above and watch the telemetry being sent via the Visual Studio output window or a tool like Telerik's Fiddler, you will see the while loop repeatedly executing with no telemetry being sent and then a single telemetry item will be sent by around the 60-second mark, which in the case of our test looks as follows:
77+
When running the sample code, you'll see the while loop repeatedly executing with no telemetry being sent in the Visual Studio output window. A single telemetry item will be sent by around the 60-second mark, which in our test looks as follows:
7878

7979
```json
8080
Application Insights Telemetry: {"name":"Microsoft.ApplicationInsights.Dev.00000000-0000-0000-0000-000000000000.Metric", "time":"2019-12-28T00:54:19.0000000Z",
@@ -100,7 +100,7 @@ This single telemetry item represents an aggregate of 41 distinct metric measure
100100
> [!NOTE]
101101
> GetMetric does not support tracking the last value (i.e. "gauge") or tracking histograms/distributions.
102102
103-
If we examine our Application Insights resource in the Logs (Analytics) experience, this individual telemetry item would look as follows:
103+
If we examine our Application Insights resource in the Logs (Analytics) experience, the individual telemetry item would look as follows:
104104

105105
![Log Analytics query view](./media/get-metric/log-analytics.png)
106106

@@ -112,9 +112,9 @@ You can also access your custom metric telemetry in the [_Metrics_](../essential
112112

113113
### Caching metric reference for high-throughput usage
114114

115-
In some cases metric values are observed very frequently. For example, a high-throughput service that processes 500 requests/second may want to emit 20 telemetry metrics for each request. This means tracking 10,000 values per second. In such high-throughput scenarios, users may need to help the SDK by avoiding some lookups.
115+
Metric values may be observed frequently in some cases. For example, a high-throughput service that processes 500 requests/second may want to emit 20 telemetry metrics for each request. The result means tracking 10,000 values per second. In such high-throughput scenarios, users may need to help the SDK by avoiding some lookups.
116116

117-
For example, in this case, the example above performed a lookup for a handle for the metric "ComputersSold" and then tracked an observed value 42. Instead, the handle may be cached for multiple track invocations:
117+
For example, the example above performed a lookup for a handle for the metric "ComputersSold" and then tracked an observed value 42. Instead, the handle may be cached for multiple track invocations:
118118

119119
```csharp
120120
//...
@@ -143,7 +143,7 @@ In addition to caching the metric handle, the example above also reduced the `Ta
143143

144144
The examples in the previous section show zero-dimensional metrics. Metrics can also be multi-dimensional. We currently support up to 10 dimensions.
145145

146-
Here is an example of how to create a one-dimensional metric:
146+
Here's an example of how to create a one-dimensional metric:
147147

148148
```csharp
149149
//...
@@ -171,25 +171,25 @@ The examples in the previous section show zero-dimensional metrics. Metrics can
171171

172172
```
173173

174-
Running this code for at least 60 seconds will result in three distinct telemetry items being sent to Azure, each representing the aggregation of one of the three form factors. As before you can examine these in Logs (Analytics) view:
174+
Running the sample code for at least 60 seconds will result in three distinct telemetry items being sent to Azure, each representing the aggregation of one of the three form factors. As before you can further examine in the Logs (Analytics) view:
175175

176176
![Log analytics view of multidimensional metric](./media/get-metric/log-analytics-multi-dimensional.png)
177177

178-
As well as in the Metrics explorer experience:
178+
In the Metrics explorer experience:
179179

180180
![Custom metrics](./media/get-metric/custom-metrics.png)
181181

182-
However, you will notice that you aren't able to split the metric by your new custom dimension, or view your custom dimension with the metrics view:
182+
However, you'll notice that you aren't able to split the metric by your new custom dimension, or view your custom dimension with the metrics view:
183183

184184
![Splitting support](./media/get-metric/splitting-support.png)
185185

186-
By default multi-dimensional metrics within the Metric explorer experience are not turned on in Application Insights resources.
186+
By default multi-dimensional metrics within the Metric explorer experience aren't turned on in Application Insights resources.
187187

188188
### Enable multi-dimensional metrics
189189

190-
To enable multi-dimensional metrics for an Application Insights resource, Select **Usage and estimated costs** > **Custom Metrics** > **Enable alerting on custom metric dimensions** > **OK**. More details about this can be found [here](pre-aggregated-metrics-log-metrics.md#custom-metrics-dimensions-and-pre-aggregation).
190+
To enable multi-dimensional metrics for an Application Insights resource, Select **Usage and estimated costs** > **Custom Metrics** > **Enable alerting on custom metric dimensions** > **OK**. More details about can be found [here](pre-aggregated-metrics-log-metrics.md#custom-metrics-dimensions-and-pre-aggregation).
191191

192-
Once you have made that change and send new multi-dimensional telemetry, you will be able to **Apply splitting**.
192+
Once you have made that change and send new multi-dimensional telemetry, you'll be able to **Apply splitting**.
193193

194194
> [!NOTE]
195195
> Only newly sent metrics after the feature was turned on in the portal will have dimensions stored.
@@ -214,11 +214,11 @@ computersSold.TrackValue(110,"Laptop", "Nvidia", "DDR4", "39Wh", "1TB");
214214

215215
## Custom metric configuration
216216

217-
If you want to alter the metric configuration, you need to do this in the place where the metric is initialized.
217+
If you want to alter the metric configuration, you need make alterations in the place where the metric is initialized.
218218

219219
### Special dimension names
220220

221-
Metrics do not use the telemetry context of the `TelemetryClient` used to access them, special dimension names available as constants in `MetricDimensionNames` class is the best workaround for this limitation.
221+
Metrics don't use the telemetry context of the `TelemetryClient` used to access them, special dimension names available as constants in `MetricDimensionNames` class is the best workaround for this limitation.
222222

223223
Metric aggregates sent by the below "Special Operation Request Size"-metric will **not** have their `Context.Operation.Name` set to "Special Operation". Whereas `TrackMetric()` or any other TrackXXX() will have `OperationName` set correctly to "Special Operation".
224224

@@ -253,7 +253,7 @@ For example, when the metric aggregate resulting from the next statement is sent
253253
_telemetryClient.GetMetric("Request Size", MetricDimensionNames.TelemetryContext.Operation.Name).TrackValue(requestSize, "Special Operation");
254254
```
255255

256-
The values of this special dimension will be copied into the `TelemetryContext` and will not be used as a 'normal' dimension. If you want to also keep an operation dimension for normal metric exploration, you need to create a separate dimension for that purpose:
256+
The values of this special dimension will be copied into the `TelemetryContext` and won't be used as a 'normal' dimension. If you want to also keep an operation dimension for normal metric exploration, you need to create a separate dimension for that purpose:
257257

258258
```csharp
259259
_telemetryClient.GetMetric("Request Size", "Operation Name", MetricDimensionNames.TelemetryContext.Operation.Name).TrackValue(requestSize, "Special Operation", "Special Operation");
@@ -263,7 +263,10 @@ _telemetryClient.GetMetric("Request Size", "Operation Name", MetricDimensionName
263263

264264
To prevent the telemetry subsystem from accidentally using up your resources, you can control the maximum number of data series per metric. The default limits are no more than 1000 total data series per metric, and no more than 100 different values per dimension.
265265

266-
In the context of dimension and time series capping we use `Metric.TrackValue(..)` to make sure that the limits are observed. If the limits are already reached, `Metric.TrackValue(..)` will return "False" and the value will not be tracked. Otherwise it will return "True". This is useful if the data for a metric originates from user input.
266+
> [!IMPORTANT]
267+
> Use low cardinal values for dimensions to avoid throttling.
268+
269+
In the context of dimension and time series capping, we use `Metric.TrackValue(..)` to make sure that the limits are observed. If the limits are already reached, `Metric.TrackValue(..)` will return "False" and the value won't be tracked. Otherwise it will return "True". This behavior is useful if the data for a metric originates from user input.
267270

268271
The `MetricConfiguration` constructor takes some options on how to manage different series within the respective metric and an object of a class implementing `IMetricSeriesConfiguration` that specifies aggregation behavior for each individual series of the metric:
269272

@@ -286,7 +289,7 @@ computersSold.TrackValue(100, "Dim1Value1", "Dim2Value3");
286289
* `valuesPerDimensionLimit` limits the number of distinct values per dimension in a similar manner.
287290
* `restrictToUInt32Values` determines whether or not only non-negative integer values should be tracked.
288291

289-
Here is an example of how to send a message to know if cap limits are exceeded:
292+
Here's an example of how to send a message to know if cap limits are exceeded:
290293

291294
```csharp
292295
if (! computersSold.TrackValue(100, "Dim1Value1", "Dim2Value3"))

0 commit comments

Comments
 (0)