Recopilación de métricas personalizadas en .NET y .NET Core

Los SDK de Application Insights de Azure Monitor para .NET y .NET Core tienen dos métodos diferentes para recopilar métricas personalizadas: TrackMetric() y GetMetric(). La diferencia principal entre estos dos métodos es la agregación local. El método TrackMetric() carece de agregación previa. El método GetMetric() tiene agregación previa. Se recomienda usar la agregación, por lo que TrackMetric() ya no es el método preferido para recopilar métricas personalizadas. En este artículo se le guiará por el uso del método GetMetric() y parte de la lógica en la que se basa su funcionamiento.

Nota:

La siguiente documentación se basa en la API clásica de Application Insights. El plan a largo plazo de Application Insights consiste en recopilar datos mediante OpenTelemetry. Para más información, consulte Habilitación de Azure Monitor OpenTelemetry para aplicaciones de .NET, Node.js, Python y Java.

API de agregación previa frente a la de sin agregación previa

El método TrackMetric() envía datos de telemetría sin procesar que denotan una métrica. No es eficaz enviar un elemento de telemetría único para cada valor. El método TrackMetric() también es ineficaz en términos de rendimiento porque cada TrackMetric(item) pasa a través de la canalización del SDK de inicializadores y procesadores de telemetría.

A diferencia de TrackMetric(), GetMetric() controla la agregación previa local automáticamente y, a continuación, solo envía una métrica de resumen agregada cada intervalo fijo de un minuto. Si necesita supervisar de cerca algunas métricas personalizadas en el nivel de segundos o incluso en el de milisegundos, puede hacerlo y además solo se incurre en el costo de almacenamiento y tráfico de red de la supervisión en cada minuto. Este comportamiento también reduce en gran medida el riesgo de que se produzca una limitación, ya que se reduce considerablemente el número total de elementos de telemetría que se deben enviar para una métrica agregada.

En Application Insights, las métricas personalizadas recopiladas mediante TrackMetric() y GetMetric() no están sujetas a muestreo. El muestreo de métricas importantes puede dar lugar a escenarios en los que las alertas que se podrían haber creado en torno a esas métricas resulten poco confiables. Si nunca se realiza el muestreo de las métricas personalizadas, normalmente puede estar seguro de que se activará una alerta cuando se infrinjan los umbrales de alerta. Dado que no se muestrean las métricas personalizadas, existen algunas posibles preocupaciones.

El seguimiento de las tendencias de una métrica cada segundo o en un intervalo aún más granular puede dar lugar a:

  • Aumento de los costos de almacenamiento de datos. Hay un costo asociado con la cantidad de datos que se envían a Azure Monitor. Cuantos más datos envíe, mayor será el costo total de la supervisión.
  • Aumento del tráfico de red o sobrecarga del rendimiento. En algunos escenarios esta sobrecarga podría tener un costo monetario y de rendimiento de la aplicación.
  • Riesgo de limitación de la ingesta de datos. Azure Monitor descarta ["limita"] puntos de datos cuando la aplicación envía un número elevado de datos de telemetría en un intervalo de tiempo corto.

La limitación es un problema, ya que puede provocar la pérdida de alertas. La condición para desencadenar una alerta puede producirse localmente y, con posterioridad, anularse en el punto de conexión de ingesta debido a que se envían demasiados datos. No se recomienda usar TrackMetric() para .NET y .NET Core a menos que haya implementado su propia lógica de agregación local. Si intenta realizar un seguimiento de cada instancia en la que se produce un evento durante un período de tiempo determinado, es posible que sea mejor opción TrackEvent(). Tenga en cuenta que, a diferencia de las métricas personalizadas, los eventos personalizados están sujetos a muestreo. Puede utilizar TrackMetric() incluso sin escribir su propia agregación previa local. Pero si lo hace, tenga en cuenta los problemas.

En resumen, recomendamos el enfoque GetMetric(), ya que realiza la agregación previa, acumula los valores de todas las llamadas a Track() y envía un resumen o agregado cada minuto. El método GetMetric() puede reducir significativamente los costos y la sobrecarga de rendimiento, ya que envía menos puntos de datos, pero recopila toda la información pertinente.

Nota

Solo los SDK de .NET y .NET Core tienen el método GetMetric(). Si usa Java, consulte Cómo enviar métricas personalizadas mediante Micrometer. Para JavaScript y Node.js puede utilizar TrackMetric(), pero tenga en cuenta las advertencias que se han descrito en la sección anterior. En el caso de Python, puede usar OpenCensus.stats para enviar métricas personalizadas, pero la implementación de las métricas es diferente.

Introducción a GetMetric

En los ejemplos, vamos a usar una aplicación básica de servicio de trabajo de .NET Core 3.1. Si quiere replicar el entorno de prueba utilizado con estos ejemplos, siga los pasos 1 a 6 del Artículo sobre supervisión del servicio de trabajo. En estos pasos se agrega Application Insights a una plantilla de proyecto de servicio de trabajo básica. Estos conceptos se aplican a cualquier aplicación general en la que se pueda usar el SDK, incluidas las aplicaciones web y las aplicaciones de consola.

Envío de métricas

Reemplace el contenido del archivo worker.cs por el siguiente código:

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.ApplicationInsights;

namespace WorkerService3
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        private TelemetryClient _telemetryClient;

        public Worker(ILogger<Worker> logger, TelemetryClient tc)
        {
            _logger = logger;
            _telemetryClient = tc;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {   // The following line demonstrates usages of GetMetric API.
            // Here "computersSold", a custom metric name, is being tracked with a value of 42 every second.
            while (!stoppingToken.IsCancellationRequested)
            {
                _telemetryClient.GetMetric("ComputersSold").TrackValue(42);

                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(1000, stoppingToken);
            }
        }
    }
}

Al ejecutar el código de ejemplo, verá que el bucle while se ejecuta repetidamente sin que se envíen datos de telemetría en la ventana de salida de Visual Studio. Un único elemento de telemetría se envía alrededor de la marca de 60 segundos, que en nuestra prueba tiene el siguiente aspecto:

Application Insights Telemetry: {"name":"Microsoft.ApplicationInsights.Dev.00000000-0000-0000-0000-000000000000.Metric", "time":"2019-12-28T00:54:19.0000000Z",
"ikey":"00000000-0000-0000-0000-000000000000",
"tags":{"ai.application.ver":"1.0.0.0",
"ai.cloud.roleInstance":"Test-Computer-Name",
"ai.internal.sdkVersion":"m-agg2c:2.12.0-21496",
"ai.internal.nodeName":"Test-Computer-Name"},
"data":{"baseType":"MetricData",
"baseData":{"ver":2,"metrics":[{"name":"ComputersSold",
"kind":"Aggregation",
"value":1722,
"count":41,
"min":42,
"max":42,
"stdDev":0}],
"properties":{"_MS.AggregationIntervalMs":"42000",
"DeveloperMode":"true"}}}}

Este único elemento de telemetría representa un agregado de 41 medidas de métricas distintas. Dado que se envía el mismo valor una y otra vez, tenemos una desviación estándar (stDev) de 0 con valores idénticos de máximo (max) y mínimo (min). La propiedad value representa la suma de todos los valores individuales que se agregaron.

Nota

El método GetMetric no admite el seguimiento del último valor (por ejemplo, gauge) o el seguimiento de histogramas o distribuciones.

Si examinamos el recurso de Application Insights en la experiencia de Registros (Analytics), el elemento de telemetría individual tendrá un aspecto al de la siguiente captura de pantalla.

Screenshot that shows the Log Analytics query view.

Nota:

Aunque el elemento de telemetría sin procesar no contenía una propiedad ni campo de suma explícitos, creamos uno automáticamente para usted. En este caso, las propiedades value y valueSum representan lo mismo.

También puede acceder a la telemetría de métricas personalizada en la sección Métricas del portal como métricas basadas en registros y personalizadas. La captura de pantalla siguiente es un ejemplo de una métrica basada en registros.

Screenshot that shows the Metrics explorer view.

Almacenamiento en caché de referencias de métricas para uso de alto rendimiento

Los valores de métricas se podrían observar con frecuencia en algunos casos. Por ejemplo, un servicio de alto rendimiento que procesa 500 solicitudes por segundo podría querer emitir 20 métricas de datos de telemetría por cada solicitud. El resultado significa un seguimiento de 10 000 valores por segundo. En dichos escenarios de alto rendimiento, es posible que los usuarios necesiten ayudar al SDK evitando algunas búsquedas.

Por ejemplo, en el ejemplo anterior se realizó una búsqueda de un manipulador de la métrica ComputersSold y, a continuación, se supervisó un valor observado de 42. En su lugar, el manipulador se podría almacenar en caché para las invocaciones de varios seguimientos:

//...

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            // This is where the cache is stored to handle faster lookup
            Metric computersSold = _telemetryClient.GetMetric("ComputersSold");
            while (!stoppingToken.IsCancellationRequested)
            {

                computersSold.TrackValue(42);

                computersSold.TrackValue(142);

                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(50, stoppingToken);
            }
        }

Además de almacenar en caché el manipulador de la métrica, en el ejemplo anterior también se redujo Task.Delay a 50 milisegundos para que el bucle se ejecute con más frecuencia. El resultado es 772 TrackValue() invocaciones.

Métricas multidimensionales

Los ejemplos de la sección anterior muestran métricas de dimensión cero. Las métricas también pueden ser multidimensionales. Actualmente se admiten hasta 10 dimensiones.

Este es un ejemplo de cómo crear una métrica de una dimensión:

//...

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            // This is an example of a metric with a single dimension.
            // FormFactor is the name of the dimension.
            Metric computersSold= _telemetryClient.GetMetric("ComputersSold", "FormFactor");

            while (!stoppingToken.IsCancellationRequested)
            {
                // The number of arguments (dimension values)
                // must match the number of dimensions specified while GetMetric.
                // Laptop, Tablet, etc are values for the dimension "FormFactor"
                computersSold.TrackValue(42, "Laptop");
                computersSold.TrackValue(20, "Tablet");
                computersSold.TrackValue(126, "Desktop");


                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(50, stoppingToken);
            }
        }

La ejecución del código de ejemplo durante al menos 60 segundos da como resultado el envío de tres elementos de telemetría distintos a Azure. Cada elemento representa la agregación de uno de los tres factores de forma. Como antes, puede examinarlos en mayor profundidad en la vista de Registros (Analytics).

Screenshot that shows the Log Analytics view of multidimensional metric.

En el explorador de métricas:

Screenshot that shows Custom metrics.

Tenga en cuenta que no puede dividir la métrica por su nueva dimensión personalizada ni ver su dimensión personalizada con la vista de métricas.

Screenshot that shows splitting support.

De manera predeterminada, las métricas multidimensionales dentro del explorador de métricas no están activadas en recursos de Application Insights.

Habilitar métricas multidimensionales

Para habilitar las métricas multidimensionales para un recurso de Application Insights, seleccione Uso y costos estimados>Métricas personalizadas>Habilitar la creación de alertas sobre las dimensiones de las métricas personalizadas>Aceptar. Para más información, consulte Dimensiones de métricas personalizadas y agregación previa.

Una vez realizado ese cambio y enviado la nueva telemetría multidimensional, puede seleccionar Aplicar división.

Nota

Solo se almacenarán las dimensiones de las métricas enviadas después de activar la característica en el portal.

Screenshot that shows applying splitting.

Vea las agregaciones de métricas para cada dimensión FormFactor.

Screenshot that shows form factors.

Uso de MetricIdentifier cuando hay más de tres dimensiones

Actualmente, se admiten 10 dimensiones. Más de tres dimensiones requieren el uso de MetricIdentifier:

// Add "using Microsoft.ApplicationInsights.Metrics;" to use MetricIdentifier
// MetricIdentifier id = new MetricIdentifier("[metricNamespace]","[metricId],"[dim1]","[dim2]","[dim3]","[dim4]","[dim5]");
MetricIdentifier id = new MetricIdentifier("CustomMetricNamespace","ComputerSold", "FormFactor", "GraphicsCard", "MemorySpeed", "BatteryCapacity", "StorageCapacity");
Metric computersSold  = _telemetryClient.GetMetric(id);
computersSold.TrackValue(110,"Laptop", "Nvidia", "DDR4", "39Wh", "1TB");

Configuración de métricas personalizadas

Si quiere modificar la configuración de la métrica, debe realizar las modificaciones donde se inicializa la métrica.

Nombres de dimensión especiales

Las métricas no usan el contexto de telemetría del TelemetryClient usado para acceder a ellas. La mejor solución a esta limitación es utilizar nombres de dimensiones especiales disponibles como constantes en la clase MetricDimensionNames.

Los agregados de métrica enviados por la siguiente Special Operation Request Size métrica no tendrán Context.Operation.Name establecido en Special Operation. El método TrackMetric() o cualquier otro TrackXXX() método se habrá OperationName establecido correctamente en Special Operation.

        //...
        TelemetryClient specialClient;
        private static int GetCurrentRequestSize()
        {
            // Do stuff
            return 1100;
        }
        int requestSize = GetCurrentRequestSize()

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                //...
                specialClient.Context.Operation.Name = "Special Operation";
                specialClient.GetMetric("Special Operation Request Size").TrackValue(requestSize);
                //...
            }
                   
        }

En este caso, use los nombres de dimensión especiales que aparecen en la clase MetricDimensionNames para especificar los valores de TelemetryContext.

Por ejemplo, cuando el agregado de métricas resultante de la siguiente instrucción se envía al punto de conexión en la nube de Application Insights, el campo de datos Context.Operation.Name estará establecido en Special Operation:

_telemetryClient.GetMetric("Request Size", MetricDimensionNames.TelemetryContext.Operation.Name).TrackValue(requestSize, "Special Operation");

Los valores de esta dimensión especial se copiarán en TelemetryContext y no se utilizarán como una dimensión normal. Si también desea mantener una dimensión de operación para la exploración normal de métricas, debe crear una dimensión independiente para ese propósito:

_telemetryClient.GetMetric("Request Size", "Operation Name", MetricDimensionNames.TelemetryContext.Operation.Name).TrackValue(requestSize, "Special Operation", "Special Operation");

Límites de las dimensiones y las series temporales

Para evitar que el subsistema de telemetría agote accidentalmente los recursos, puede controlar el número máximo de series de datos por métrica. Los límites predeterminados son no más de 1,000 series de datos totales por métrica y no más de 100 valores diferentes por dimensión.

Importante

Use valores cardinales bajos para las dimensiones a fin de evitar la limitación.

En el contexto de los límites de dimensiones y series temporales, utilice Metric.TrackValue(..) para asegurarse de que se observan los límites. Si se han alcanzado los límites, Metric.TrackValue(..) devolverá False y no se supervisará el valor. En caso contrario, devuelve True. Este comportamiento resulta útil si los datos de una métrica se originan a partir de la entrada de usuario.

El constructor MetricConfiguration toma algunas opciones para administrar series diferentes dentro de la métrica respectiva y un objeto de una clase que implementa IMetricSeriesConfiguration, que especifica el comportamiento de agregación para cada serie individual de la métrica:

var metConfig = new MetricConfiguration(seriesCountLimit: 100, valuesPerDimensionLimit:2,
                new MetricSeriesConfigurationForMeasurement(restrictToUInt32Values: false));

Metric computersSold = _telemetryClient.GetMetric("ComputersSold", "Dimension1", "Dimension2", metConfig);

// Start tracking.
computersSold.TrackValue(100, "Dim1Value1", "Dim2Value1");
computersSold.TrackValue(100, "Dim1Value1", "Dim2Value2");

// The following call gives 3rd unique value for dimension2, which is above the limit of 2.
computersSold.TrackValue(100, "Dim1Value1", "Dim2Value3");
// The above call does not track the metric, and returns false.
  • seriesCountLimit es el número máximo de series temporales de datos que puede contener una métrica. Una vez alcanzado este límite, las llamadas a TrackValue() que normalmente generarían una nueva serie devolverán false.
  • valuesPerDimensionLimit limita el número de valores distintos por dimensión de forma similar.
  • restrictToUInt32Values determina si solo se debe realizar el seguimiento de los valores enteros no negativos.

Este es un ejemplo de cómo enviar un mensaje para saber si se han superado los límites:

if (! computersSold.TrackValue(100, "Dim1Value1", "Dim2Value3"))
{
// Add "using Microsoft.ApplicationInsights.DataContract;" to use SeverityLevel.Error
_telemetryClient.TrackTrace("Metric value not tracked as value of one of the dimension exceeded the cap. Revisit the dimensions to ensure they are within the limits",
SeverityLevel.Error);
}

Pasos siguientes