Collecte de métriques personnalisées dans .NET et .NET Core

Les kits SDK .NET et .NET Core Azure Monitor Application Insights offrent deux méthodes de collecte des métriques personnalisées, TrackMetric() et GetMetric(). La principale différence entre ces deux méthodes est l’agrégation locale. La TrackMetric() méthode ne dispose pas de pré-agrégation. La GetMetric() méthode dispose d’une pré-agrégation. Nous vous recommandons d’utiliser l’agrégation, donc TrackMetric() n’est plus la méthode préférée de collecte des métriques personnalisées. Cet article vous guide dans l'utilisation de la méthode GetMetric() et vous expliquera son fonctionnement.

Remarque

La documentation suivante s’appuie sur l’API classique d’Application Insights. Le plan à long terme pour Application Insights est de collecter des données en utilisant OpenTelemetry. Pour plus d’informations, consultez Activer Azure Monitor OpenTelemetry pour les applications .NET, Node.js, Python et Java.

API pré-agrégée vs. non pré-agrégée

La méthode TrackMetric() envoie des données de télémétrie brutes indiquant une métrique. L’envoi d’un seul élément de télémétrie pour chaque valeur n’est pas efficace. La méthode TrackMetric() est également inefficace en termes de performances, car chaque TrackMetric(item) transite par le pipeline de SDK complet des initialiseurs et des processeurs de télémétrie.

Contrairement à TrackMetric(), GetMetric() gère la pré-agrégation locale pour vous et envoie ensuite une métrique récapitulative agrégée à un intervalle fixe d’une minute. Si vous avez besoin de superviser étroitement une métrique personnalisée au niveau de la seconde ou même de la milliseconde, vous pouvez le faire tout en n’encourant que le coût de stockage et de trafic réseau de la supervision à intervalle d’une minute. Ce comportement réduit aussi considérablement le risque de limitation, car le nombre total d’éléments de télémétrie à envoyer pour une métrique agrégée est fortement réduit.

Dans Application Insights, les métriques personnalisées collectées par le biais de TrackMetric() et GetMetric() ne sont pas soumises à l’échantillonnage. L’échantillonnage de métriques importantes peut entraîner des scénarios dans lesquels les alertes que vous avez créées autour de ces métriques peuvent devenir peu fiables. En n’échantillonnant jamais vos métriques personnalisées, vous pouvez généralement être sûr que quand vos seuils d’alerte sont enfreints, une alerte se déclenche. Parce que les métriques personnalisées ne sont pas échantillonnées, il existe des problèmes potentiels.

Le suivi des tendances dans une métrique chaque seconde, ou à un intervalle encore plus détaillé, peut secondes, ou à un intervalle encore plus granulaire, peut entraîner :

  • Une augmentation des coûts de stockage de données. Un coût est associé à la quantité de données que vous envoyez à Azure Monitor. Plus vous envoyez de données, plus le coût global de supervision est élevé.
  • Une augmentation du trafic réseau ou une surcharge de performance. Dans certains scénarios, cette surcharge peut avoir des conséquences en termes de performances d’application et un coût financier.
  • Un risque de limitation de l’ingestion. Azure Monitor supprime (« limite ») des points de données quand votre application envoie un taux de télémétrie élevé dans un court laps de temps.

La limitation constitue une préoccupation car elle peut entraîner un non-signalement des alertes. La condition nécessaire au déclenchement d’une alerte peut se produire localement, puis être supprimée au point de terminaison d’ingestion en raison d’un trop grand nombre de données envoyées. Nous ne recommandons pas l’utilisation de TrackMetric() pour .NET et .NET Core, sauf si vous avez implémenté votre propre logique d’agrégation locale. Si vous essayez de suivre chaque occurrence d’un événement sur une période donnée, vous constaterez peut-être que TrackEvent() est plus adaptée. Toutefois, n’oubliez pas que contrairement aux métriques personnalisées, les événements personnalisés sont soumis à l’échantillonnage. Vous pouvez toujours utiliser TrackMetric() même sans écrire votre propre pré-agrégation locale. Mais si vous le faites, soyez conscient des pièges.

En résumé, nous recommandons GetMetric(), car il effectue une pré-agrégation, il accumule les valeurs de tous les appels Track() et envoie un résumé/agrégat une fois par minute. La méthode GetMetric() peut réduire considérablement le coût et l'impact sur les performances en envoyant moins de points de données, tout en recueillant néanmoins toutes les informations pertinentes.

Notes

Seuls les kits SDK .NET et .NET Core ont une méthode GetMetric(). Si vous utilisez Java, consultez Envoi de métriques personnalisées à l’aide de micromètre. Pour JavaScript et Node.js, vous utiliserez quand même TrackMetric(), mais gardez à l’esprit les avertissements décrits dans la section précédente. Pour Python, vous pouvez utiliser OpenCensus.stats pour envoyer des métriques personnalisées, mais l’implémentation de métriques est différente.

Bien démarrer avec GetMetric

Pour nos exemples, nous allons utiliser une application de service Worker .NET Core 3.1 de base. Si vous souhaitez reproduire l’environnement de test utilisé avec ces exemples, suivez les étapes 1 à 6 de l’article Monitoring worker service. Ces étapes permettent d'ajouter Application Insights à un modèle de projet de service worker de base. Ces concepts s’appliquent à toute application générale dans laquelle le SDK peut être utilisé, notamment les applications web et les applications de console.

Envoyer des mesures

Remplacez le contenu de votre fichier worker.cs par le code suivant :

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);
            }
        }
    }
}

Lors de l’exécution de l’exemple de code, vous voyez la boucle while s'exécuter de manière répétée sans qu'aucune télémétrie ne soit envoyée dans la fenêtre de sortie Visual Studio. Un seul élément de télémétrie est envoyé vers la marque de 60 secondes, qui, dans notre test, se présente comme suit :

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"}}}}

Cet élément de télémétrie unique représente une agrégation de 41 mesures de métriques distinctes. Puisque nous envoyions sans cesse la même valeur, nous avons un écart type (stDev) de 0 avec des valeurs maximale (max) et minimale (min) identiques. La propriété value représente la somme de toutes les valeurs individuelles qui ont été agrégées.

Notes

La méthode GetMetric ne prend pas en charge le suivi de la dernière valeur (par exemple, gauge) ou le suivi des histogrammes ou des distributions.

Si nous examinons notre ressource Application Insights dans l’expérience Logs (Analytics), l’élément de télémétrie individuel ressemblerait à la capture d'écran suivante.

Screenshot that shows the Log Analytics query view.

Remarque

Si l’élément de télémétrie brut ne contenait pas de champ ou de propriété de somme explicite une fois ingéré, nous en créons un pour vous. En l’occurrence, la propriété value et valueSum représentent la même chose.

Vous pouvez également accéder à vos données de télémétrie de métriques personnalisées dans la section Métriques du portail, à la fois en tant que métrique personnalisée et basée sur un journal. La capture d’écran suivante est un exemple de métrique basée sur un journal.

Screenshot that shows the Metrics explorer view.

Référence de métrique de mise en cache pour une utilisation à haut débit

Des valeurs métriques peuvent être observées fréquemment dans certains cas. Par exemple, un service à haut débit qui traite 500 requêtes par seconde peut vouloir émettre 20 métriques de télémétrie pour chaque requête. Le résultat signifie qu’il faut effectuer le suivi de 10 000 valeurs par seconde. Dans de tels scénarios à haut débit, les utilisateurs peuvent avoir besoin d'aider le kit de développement logiciel (SDK) en évitant certaines recherches.

Par exemple, l’exemple ci-dessus a effectué une recherche pour un descripteur pour la métrique ComputersSold, puis a suivi une valeur observée 42. Au lieu de cela, le descripteur peut être mis en cache pour plusieurs appels de suivi :

//...

        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);
            }
        }

Outre la mise en cache du descripteur de métrique, l’exemple ci-dessus a également réduit Task.Delay à 50 millisecondes, de sorte que la boucle s’exécute plus fréquemment. Le résultat est 772 TrackValue() appels.

Métriques multidimensionnelles

Les exemples de la section précédente présentent des métriques à zéro dimension. Les métriques peuvent également être multidimensionnelles. Nous prenons actuellement en charge jusqu’à 10 dimensions.

Voici un exemple qui illustre comment créer une métrique unidimensionnelle :

//...

        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);
            }
        }

L'exécution de l'exemple de code pendant au moins 60 secondes entraîne l'envoi de trois éléments télémétriques distincts à Azure. Chaque élément représente l’agrégation de l’un des trois facteurs de forme. Comme auparavant, vous pouvez les examiner plus en détail dans la vue Logs (Analytics) :

Screenshot that shows the Log Analytics view of multidimensional metric.

Dans l'explorateur de métriques :

Screenshot that shows Custom metrics.

Remarquez que vous ne pouvez pas diviser la métrique en fonction de votre nouvelle dimension personnalisée, ni afficher votre dimension personnalisée avec la vue des métriques.

Screenshot that shows splitting support.

Par défaut, les métriques multidimensionnelles dans l’explorateur de métriques ne sont pas activées dans les ressources Application Insights.

Activer les métriques multidimensionnelles

Pour activer les métriques multidimensionnelles pour une ressource Application Insights, sélectionnez Utilisation et estimation des coûts>Métriques personnalisées>Activer les alertes sur les dimensions des métriques personnalisées>OK. Pour plus d’informations, consultez Dimensions des métriques personnalisées et pré-agrégation.

Après avoir effectué cette modification et envoyé de nouvelles données de télémétrie multidimensionnelles, vous pouvez sélectionner Appliquer le fractionnement.

Notes

Seules les métriques envoyées après l’activation de la fonctionnalité dans le portail auront des dimensions stockées.

Screenshot that shows applying splitting.

Affichez vos agrégations de métriques pour chaque FormFactor dimension.

Screenshot that shows form factors.

Utilisez MetricIdentifier lorsqu'il y a plus de trois dimensions

Actuellement, 10 dimensions sont supportées. Plus de trois dimensions nécessitent l'utilisation deMetricIdentifier :

// 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");

Configuration de métrique personnalisée

Si vous souhaitez modifier la configuration des métriques, vous devez apporter des modifications à l'endroit où la métrique est initialisée.

Noms de dimensions spéciales

Les métriques n’utilisent pas le contexte de télémétrie du TelemetryClient utilisé pour y accéder. Utiliser des noms de dimension spéciaux disponibles comme constantes dans la MetricDimensionNames classe est la meilleure solution de contournement pour cette limitation.

Les agrégats de métriques envoyés par la métrique suivante Special Operation Request Sizen’auront pas la valeur Context.Operation.Name définie sur Special Operation. La TrackMetric() méthode ou toute autre méthode TrackXXX() aura OperationName correctement défini sur 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);
                //...
            }
                   
        }

Dans cette situation, utilisez les noms de dimensions spéciales listés dans la classe MetricDimensionNames pour spécifier des valeurs TelemetryContext.

Par exemple, quand l’agrégation de métriques résultant de l’instruction suivante est envoyé au point de terminaison cloud Application Insights, son champ de données Context.Operation.Name est défini sur Special Operation :

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

Les valeurs de cette dimension spéciale sont copiées dans TelemetryContext et ne seront pas utilisées comme dimension normale. Si vous souhaitez également conserver une dimension d’opération pour l’exploration normale des métriques, vous devez créer une dimension distincte à cet effet :

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

Limitation des dimensions et des séries chronologiques

Pour empêcher que le sous-système de télémétrie n’utilise accidentellement vos ressources, vous pouvez contrôler la quantité maximale de séries de données par métrique. Les limites par défaut sont un maximum de 1 000 séries de données au total par métrique, et un maximum de 100 valeurs différentes par dimension.

Important

Utilisez des valeurs cardinales faibles pour les dimensions afin d’éviter la limitation.

Dans le contexte de la limitation des séries chronologiques et des dimensions, nous utilisons Metric.TrackValue(..) pour nous assurer que les limites sont respectées. Si les limites sont déjà atteintes, Metric.TrackValue(..) affiche False et la valeur n’est pas suivie. Sinon, Trueest retourné. Ce comportement est utile si les données d’une métrique proviennent d’une entrée d’utilisateur.

Le constructeur MetricConfiguration accepte certaines options relatives à la façon de gérer différentes séries au sein de la métrique et un objet d’une classe implémentant IMetricSeriesConfiguration qui spécifie le comportement d’agrégation pour chaque série de la métrique :

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 est la quantité maximale de séries chronologiques de données qu’une métrique peut contenir. Lorsque cette limite atteinte, les appels à TrackValue() qui aboutissent normalement à une nouvelle série renvoient false.
  • valuesPerDimensionLimit limite le nombre de valeurs distinctes par dimension d’une manière similaire.
  • restrictToUInt32Values détermine si seules les valeurs entières non négatives doivent être suivies.

Voici un exemple qui illustre comment envoyer un message pour savoir si les limites sont dépassées :

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);
}

Étapes suivantes