Raccolta di metriche personalizzata in .NET e .NET Core

Gli SDK di Application Insights di Monitoraggio di Azure per .NET e .NET Core hanno due diversi metodi di raccolta di metriche personalizzate: TrackMetric() e GetMetric(). La differenza principale tra questi due metodi è l'aggregazione locale. Il metodo TrackMetric() non dispone della preaggregazione. Il metodo GetMetric() ha preaggregazione. È consigliabile usare l'aggregazione, quindi TrackMetric() non è più il metodo preferito per raccogliere metriche personalizzate. Questo articolo illustra come usare il metodo GetMetric() e alcune delle motivazioni alla base del funzionamento.

Nota

La documentazione seguente si basa sull'API classica di Application Insights. Il piano a lungo termine per Application Insights prevede la raccolta di dati con OpenTelemetry. Per altre informazioni, vedere Abilitare OpenTelemetry di Monitoraggio di Azure per le applicazioni .NET, Node.js, Python e Java oltre che la Roadmap OpenTelemetry. Le indicazioni sulla migrazione sono disponibili per .NET, Node.js e Python.

Preaggregazione e non preaggregazione dell'API

Il metodo TrackMetric() invia dati di telemetria non elaborati che denota una metrica. Non è efficiente inviare un singolo elemento di telemetria per ogni valore. Il metodo TrackMetric() è anche inefficiente in termini di prestazioni perché ogni TrackMetric(item) attraversa la pipeline SDK completa di inizializzatori e processori di telemetria.

Al contrario di TrackMetric(), GetMetric() gestisce la preaggregazione locale e quindi invia solo una metrica riepilogativa aggregata a un intervallo fisso di un minuto. Se è necessario monitorare attentamente alcune metriche personalizzate al secondo o anche al livello di millisecondo, è possibile farlo solo in caso di costi di archiviazione e traffico di rete solo ogni minuto. Questo comportamento riduce notevolmente anche il rischio di limitazione perché il numero totale di elementi di telemetria che devono essere inviati per una metrica aggregata viene notevolmente ridotto.

In Application Insights le metriche personalizzate raccolte tramite TrackMetric() e GetMetric() non sono soggette al campionamento. Il campionamento di metriche importanti può causare scenari in cui gli avvisi che potrebbero essere stati compilati in base a tali metriche potrebbero diventare inaffidabili. Non eseguendo mai il campionamento delle metriche personalizzate, in genere è possibile assicurarsi che quando le soglie di avviso vengono superate, viene generato un avviso. Poiché le metriche personalizzate non vengono campionate, esistono alcune potenziali preoccupazioni.

Il rilevamento delle tendenze in una metrica ogni secondo o a un intervallo ancora più granulare può comportare:

  • Aumento dei costi di archiviazione dei dati. È previsto un costo associato alla quantità di dati inviati a Monitoraggio di Azure. Maggiore è il costo complessivo del monitoraggio.
  • Aumento del traffico di rete o sovraccarico delle prestazioni. In alcuni scenari, questo sovraccarico potrebbe avere un costo delle prestazioni monetarie e dell'applicazione.
  • Rischio di limitazione dell'inserimento. I punti dati ("limitazioni") di Monitoraggio di Azure quando l'app invia una frequenza elevata di dati di telemetria in un breve intervallo di tempo.

La limitazione è un problema perché può causare la mancata visualizzazione di avvisi. La condizione per attivare un avviso può verificarsi in locale e quindi essere eliminata nell'endpoint di inserimento a causa di troppi dati inviati. Non è consigliabile usare TrackMetric() per .NET e .NET Core, a meno che non sia stata implementata la propria logica di aggregazione locale. Se si sta tentando di tenere traccia di ogni istanza che si verifica un evento in un determinato periodo di tempo, è possibile che TrackEvent() sia una scelta migliore. Tenere presente che, a differenza delle metriche personalizzate, gli eventi personalizzati sono soggetti al campionamento. È comunque possibile usare TrackMetric() anche senza scrivere la propria preaggregazione locale. Ma se lo fai, tieni presente le insidie.

In sintesi, è consigliabile GetMetric() perché esegue la preaggregazione, accumula valori da tutte le chiamate Track() e invia un riepilogo/aggregazione una volta ogni minuto. Il metodo GetMetric() può ridurre significativamente il costo e il sovraccarico delle prestazioni inviando un minor numero di punti dati pur raccogliendo tutte le informazioni pertinenti.

Nota

Solo gli SDK .NET e .NET Core hanno un metodo GetMetric(). Se si usa Java, vedere Invio di metriche personalizzate con micrometri. Per JavaScript e Node.js, è comunque possibile usare TrackMetric(), ma tenere presente le avvertenze descritte nella sezione precedente. Per Python, è possibile usare OpenCensus.stats per inviare metriche personalizzate, ma l'implementazione delle metriche è diversa.

Introduzione a GetMetric

Per gli esempi si userà un'applicazione di servizio di lavoro .NET Core 3.1 di base. Se si vuole replicare l'ambiente di test usato con questi esempi, seguire i passaggi da 1 a 6 nell'articolo Monitoraggio del servizio di lavoro. Questi passaggi aggiungono Application Insights a un modello di progetto del servizio di lavoro di base. I concetti si applicano a qualsiasi applicazione generale in cui è possibile usare l'SDK, incluse le app Web e le app console.

Inviare metriche

Sostituire il contenuto del file worker.cs con il codice seguente:

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

Quando si esegue il codice di esempio, viene visualizzato il ciclo while in esecuzione ripetutamente senza inviare dati di telemetria nella finestra di output di Visual Studio. Un singolo elemento di telemetria viene inviato intorno al contrassegno di 60 secondi, che nel test ha un aspetto simile al seguente:

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

Questo singolo elemento di telemetria rappresenta un'aggregazione di 41 misurazioni di metriche distinte. Poiché stavamo inviando lo stesso valore più e più volte, abbiamo una deviazione standard (stDev) di 0 con valori massimi identici (max) e minimo (min). La proprietà value rappresenta una somma di tutti i singoli valori aggregati.

Nota

Il metodo GetMetric non supporta il rilevamento dell'ultimo valore (ad esempio, gauge) o il rilevamento di istogrammi o distribuzioni.

Se si esamina la risorsa di Application Insights nell'esperienza Log (Analytics), il singolo elemento di telemetria sarà simile allo screenshot seguente.

Screenshot che mostra la visualizzazione query di Log Analytics.

Nota

Anche se l'elemento di telemetria non elaborato non contiene una proprietà/campo di somma esplicito una volta inserita, ne creiamo uno automaticamente. In questo caso, sia la proprietà value chee valueSum rappresentano la stessa cosa.

È anche possibile accedere ai dati di telemetria delle metriche personalizzati nella sezione Metriche del portale sia come metrica basata su log che personalizzata. Lo screenshot seguente è un esempio di metrica basata su log.

Screenshot che mostra la visualizzazione Esplora metriche.

Informazioni di riferimento sulle metriche della cache per l'utilizzo a velocità effettiva elevata

I valori delle metriche possono essere osservati frequentemente in alcuni casi. Ad esempio, un servizio a velocità effettiva elevata che elabora 500 richieste al secondo potrebbe voler generare 20 metriche di telemetria per ogni richiesta. Il risultato indica il rilevamento di 10.000 valori al secondo. In questi scenari con velocità effettiva elevata, gli utenti potrebbero dover aiutare l'SDK evitando alcune ricerche.

Ad esempio, l'esempio precedente ha eseguito una ricerca per un handle per la metrica ComputersSold e quindi ha rilevato un valore osservato di 42. Al contrario, l'handle potrebbe essere memorizzato nella cache per più chiamate di traccia:

//...

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

Oltre a memorizzare nella cache l'handle delle metriche, l'esempio precedente si riduce Task.Delay anche a 50 millisecondi in modo che il ciclo venga eseguito più frequentemente. Il risultato è 772 chiamate TrackValue().

Metriche multidimensionali

Gli esempi nella sezione precedente mostrano le metriche zero-dimensionali. Le metriche possono anche essere multidimensionali. Attualmente sono supportate fino a 10 dimensioni.

Ecco un esempio di come creare una metrica unidimensionale:

//...

        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'esecuzione del codice di esempio per almeno 60 secondi comporta l'invio di tre elementi di telemetria distinti ad Azure. Ogni elemento rappresenta l'aggregazione di uno dei tre fattori di forma. Come in precedenza, è possibile esaminare ulteriormente nella visualizzazione Log (Analytics).

Screenshot che mostra la visualizzazione Log Analytics della metrica multidimensionale.

In Esplora metriche:

Screenshot che mostra le metriche personalizzate.

Si noti che non è possibile suddividere la metrica in base alla nuova dimensione personalizzata o visualizzare la dimensione personalizzata con la visualizzazione delle metriche.

Screenshot che mostra il supporto della suddivisione.

Per impostazione predefinita, le metriche multidimensionali all'interno di Esplora metriche non sono attivate nelle risorse di Application Insights.

Abilitare le metriche multidimensionali

Per abilitare le metriche multidimensionali per una risorsa di Application Insights, selezionare Utilizzo e costi stimati>Metriche personalizzate> Abilita avvisi sulle dimensioni delle metriche personalizzate> OK. Per altre informazioni, vedere Dimensioni e preaggregazione delle metriche personalizzate.

Dopo aver apportato questa modifica e inviato nuovi dati di telemetria multidimensionali, è possibile selezionare Applica suddivisione.

Nota

Solo le metriche appena inviate dopo l'attivazione della funzionalità nel portale avranno dimensioni archiviate.

Screenshot che mostra l'applicazione della suddivisione.

Visualizzare le aggregazioni delle metriche per ogni dimensione FormFactor.

Screenshot che mostra i fattori di forma.

Usare MetricIdentifier quando sono presenti più di tre dimensioni

Attualmente sono supportate 10 dimensioni. Più di tre dimensioni richiede l'uso di 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");

Configurazione della metrica personalizzata

Se si vuole modificare la configurazione della metrica, è necessario apportare modifiche nella posizione in cui viene inizializzata la metrica.

Nomi di dimensioni speciali

Le metriche non usano il contesto di telemetria dell'oggetto TelemetryClient usato per accedervi. L'uso di nomi di dimensioni speciali disponibili come costanti nella classe MetricDimensionNames è la soluzione migliore per questa limitazione.

Le aggregazioni delle metriche inviate dalla metrica Special Operation Request Size seguente non saranno Context.Operation.Name impostate su Special Operation. Il metodo TrackMetric() o qualsiasi altro metodo TrackXXX() avrà OperationName impostato correttamente su 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);
                //...
            }
                   
        }

In questa circostanza, utilizzare i nomi delle dimensioni speciali elencati nella classe MetricDimensionNames per specificare i valori TelemetryContext.

Ad esempio, quando l'aggregazione delle metriche risultante dall'istruzione successiva viene inviata all'endpoint cloud di Application Insights, il relativo campo dati Context.Operation.Name verrà impostato su Special Operation:

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

I valori di questa dimensione speciale verranno copiati in TelemetryContext e non verranno usati come dimensione normale. Se si desidera mantenere anche una dimensione operativa per l'esplorazione normale delle metriche, è necessario creare una dimensione separata a tale scopo:

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

Limite di dimensioni e serie temporali

Per impedire che il sottosistema di telemetria usi accidentalmente le risorse, è possibile controllare il numero massimo di serie di dati per metrica. I limiti predefiniti non sono più di 1.000 serie di dati totali per metrica e non più di 100 valori diversi per dimensione.

Importante

Usare valori cardinali bassi per le dimensioni per evitare la limitazione.

Nel contesto del limite di dimensioni e serie temporali, viene usato Metric.TrackValue(..) per assicurarsi che vengano osservati i limiti. Se i limiti sono già raggiunti, Metric.TrackValue(..) restituisce False e il valore non verrà monitorato. In caso contrario, viene restituito True. Questo comportamento è utile se i dati per una metrica provengono dall'input dell'utente.

Il costruttore MetricConfiguration accetta alcune opzioni su come gestire serie diverse all'interno della rispettiva metrica e un oggetto di una classe che implementa IMetricSeriesConfiguration che specifica il comportamento di aggregazione per ogni singola serie della metrica:

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 è il numero massimo di serie temporali di dati che una metrica può contenere. Quando viene raggiunto questo limite, le chiamate a TrackValue() che normalmente comportano la restituzione di una nuova serie, ora restituiscono false.
  • valuesPerDimensionLimit limita il numero di valori distinti per dimensione in modo analogo.
  • restrictToUInt32Values determina se devono essere rilevati solo valori integer non negativi.

Ecco un esempio di come inviare un messaggio per sapere se vengono superati i limiti limite:

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

Passaggi successivi