Verifica delle operazioni personalizzate con Application Insights .NET SDK

Gli SDK di Application Insights tengono automaticamente traccia delle richieste HTTP in ingresso e delle chiamate ai servizi dipendenti, ad esempio le richieste HTTP e le query SQL. La verifica e la correlazione di richieste e dipendenze offre visibilità sui tempi di risposta e sull'affidabilità dell'intera applicazione in tutti i microservizi che combinano questa applicazione.

Esiste una classe di modelli di applicazione che non possono essere supportati in modo generico. Per il corretto monitoraggio di tali modelli, è necessaria la strumentazione manuale del codice. Questo articolo descrive alcuni criteri che potrebbero richiedere una strumentazione manuale, ad esempio per l’elaborazione di code personalizzate e l'esecuzione prolungata di attività in background.

Questo articolo fornisce indicazioni su come tenere traccia delle operazioni personalizzate con Application Insights SDK. Questa documentazione è relativa a:

  • Application Insights per .NET (noto anche come Base SDK) versione 2.4+.
  • Application Insights per applicazioni Web (che esegue ASP.NET) versione 2.4+.
  • Application Insights per ASP.NET Core versione 2.1+.

Nota

La documentazione seguente si basa sull'API classica di Application Insights. Il piano a lungo termine per Application Insights consiste nel raccogliere dati usando OpenTelemetry. Per altre informazioni, vedere Abilitare OpenTelemetry di Monitoraggio di Azure per le applicazioni .NET, Node.js, Python e Java.

Panoramica

Un'operazione è un lavoro logico eseguito da un'applicazione. Sono indicati nome, ora di avvio, durata, risultato e contesto dell'esecuzione, ad esempio nome utente, proprietà e risultato. Se l'operazione A è stata avviata dall'operazione B, l'operazione B è impostata come elemento padre per A. Un'operazione può avere un solo padre, ma può avere molte operazioni figlio. Per altre informazioni sulle operazioni e sulla correlazione dei dati di telemetria, vedere Correlazione dei dati di telemetria di Application Insights.

Nell’SDK di Application Insights .NET l'operazione viene descritta dalla classe astratta OperationTelemetry e dai discendenti RequestTelemetry e DependencyTelemetry.

Verifica delle operazioni in ingresso

Application Insights Web SDK raccoglie automaticamente le richieste HTTP per le applicazioni ASP.NET eseguite nella pipeline di IIS e per tutte le applicazioni ASP.NET Core. Sono disponibili soluzioni supportate dalla community per altre piattaforme e altri framework. Se l'applicazione non è supportata da nessuna delle soluzioni standard o supportate dalla community, è possibile instrumentarla manualmente.

Un altro esempio che richiede la verifica personalizzata è offerto dal ruolo di lavoro che riceve gli elementi dalla coda. Per alcune code, la chiamata per aggiungere un messaggio a questa coda viene registrata come dipendenza. L'operazione di alto livello che descrive l'elaborazione dei messaggi non viene raccolta automaticamente.

Di seguito è illustrato come si può tenere traccia di queste operazioni.

A un livello elevato, l'attività consiste nel creare RequestTelemetry e impostare le proprietà note. Al termine dell'operazione, tenere traccia dei dati di telemetria. L'esempio seguente illustra questa attività.

Richiesta HTTP nell'app con self-hosting Owin

In questo esempio il contesto della traccia viene propagato in base al protocollo HTTP per la correlazione. Si deve prevedere di ricevere le intestazioni descritte qui.

public class ApplicationInsightsMiddleware : OwinMiddleware
{
    // You may create a new TelemetryConfiguration instance, reuse one you already have,
    // or fetch the instance created by Application Insights SDK.
    private readonly TelemetryConfiguration telemetryConfiguration = TelemetryConfiguration.CreateDefault();
    private readonly TelemetryClient telemetryClient = new TelemetryClient(telemetryConfiguration);
    
    public ApplicationInsightsMiddleware(OwinMiddleware next) : base(next) {}

    public override async Task Invoke(IOwinContext context)
    {
        // Let's create and start RequestTelemetry.
        var requestTelemetry = new RequestTelemetry
        {
            Name = $"{context.Request.Method} {context.Request.Uri.GetLeftPart(UriPartial.Path)}"
        };

        // If there is a Request-Id received from the upstream service, set the telemetry context accordingly.
        if (context.Request.Headers.ContainsKey("Request-Id"))
        {
            var requestId = context.Request.Headers.Get("Request-Id");
            // Get the operation ID from the Request-Id (if you follow the HTTP Protocol for Correlation).
            requestTelemetry.Context.Operation.Id = GetOperationId(requestId);
            requestTelemetry.Context.Operation.ParentId = requestId;
        }

        // StartOperation is a helper method that allows correlation of 
        // current operations with nested operations/telemetry
        // and initializes start time and duration on telemetry items.
        var operation = telemetryClient.StartOperation(requestTelemetry);

        // Process the request.
        try
        {
            await Next.Invoke(context);
        }
        catch (Exception e)
        {
            requestTelemetry.Success = false;
            requestTelemetry.ResponseCode;
            telemetryClient.TrackException(e);
            throw;
        }
        finally
        {
            // Update status code and success as appropriate.
            if (context.Response != null)
            {
                requestTelemetry.ResponseCode = context.Response.StatusCode.ToString();
                requestTelemetry.Success = context.Response.StatusCode >= 200 && context.Response.StatusCode <= 299;
            }
            else
            {
                requestTelemetry.Success = false;
            }

            // Now it's time to stop the operation (and track telemetry).
            telemetryClient.StopOperation(operation);
        }
    }
    
    public static string GetOperationId(string id)
    {
        // Returns the root ID from the '|' to the first '.' if any.
        int rootEnd = id.IndexOf('.');
        if (rootEnd < 0)
            rootEnd = id.Length;

        int rootStart = id[0] == '|' ? 1 : 0;
        return id.Substring(rootStart, rootEnd - rootStart);
    }
}

Il protocollo HTTP per la correlazione dichiara inoltre l’intestazione Correlation-Context. Viene omesso qui per semplicità.

Strumentazione della coda

Il contesto di traccia W3C e il protocollo HTTP per la correlazione passano i dettagli di correlazione con le richieste HTTP, ma ogni protocollo della coda deve definire il modo in cui gli stessi dettagli vengono passati lungo il messaggio della coda. Alcuni protocolli di coda, ad esempio AMQP, consentono di passare più metadati. Altri protocolli, ad esempio Archiviazione di Azure Queue, richiedono che il contesto venga codificato nel payload del messaggio.

Nota

La traccia tra componenti non è ancora supportata per le code.

Con HTTP, se il producer e il consumer inviano dati di telemetria a risorse di Application Insights diverse, l'esperienza di diagnostica delle transazioni e La mappa delle applicazioni mostrano le transazioni e mappano end-to-end. Nel caso delle code, questa funzionalità non è ancora supportata.

coda del bus di servizio

Per informazioni di traccia, vedere Traccia distribuita e correlazione tramite bus di servizio di Azure messaggistica.

Coda di archiviazione di Azure

L'esempio seguente illustra come tenere traccia delle operazioni della coda di archiviazione di Azure e correlare i dati di telemetria tra producer, consumer e Archiviazione di Azure.

La coda di archiviazione ha un'API HTTP. Tutte le chiamate alla coda vengono tracciate dall'agente di raccolta di dipendenze Application Insights per le richieste HTTP. Viene configurato per impostazione predefinita nelle applicazioni ASP.NET e ASP.NET Core. Con altri tipi di applicazioni, vedere la documentazione delle applicazioni console.

Inoltre è possibile correlare l'ID operazione di Application Insights con l'ID di richiesta di Archiviazione. Per informazioni su come impostare e ottenere un client di richiesta di Archivazione e un ID di richiesta del server, vedere Monitoraggio, diagnosi e risoluzione dei problemi dell'archiviazione di Azure.

Accodare

Poiché le code di archiviazione di Azure supportano l'API HTTP, tutte le operazioni con la coda vengono automaticamente registrate da Application Insights. In molti casi, questa strumentazione dovrebbe essere sufficiente. Per correlare le tracce sul lato consumer con le tracce producer, è necessario passare un contesto di correlazione in modo analogo a come viene eseguito nel protocollo HTTP per la correlazione.

Questo esempio illustra come tenere traccia dell'operazione Enqueue. È possibile:

  • Correlare gli eventuali tentativi, che hanno tutti un'operazione padre comune, ovvero Enqueue. In caso contrario, vengono registrati come elementi figlio della richiesta in ingresso. Se sono presenti più richieste logiche per la coda, potrebbe risultare difficile trovare la chiamata che ha restituito i tentativi.
  • Correlare i log di archiviazione (se e quando necessario) con i dati di telemetria di Application Insights.

L'operazione Enqueue è l'elemento figlio di un'operazione padre. Un esempio è una richiesta HTTP in ingresso. La chiamata di dipendenza HTTP è l'elemento figlio dell'operazione Enqueue e il nipote della richiesta in ingresso.

public async Task Enqueue(CloudQueue queue, string message)
{
    var operation = telemetryClient.StartOperation<DependencyTelemetry>("enqueue " + queue.Name);
    operation.Telemetry.Type = "Azure queue";
    operation.Telemetry.Data = "Enqueue " + queue.Name;

    // MessagePayload represents your custom message and also serializes correlation identifiers into payload.
    // For example, if you choose to pass payload serialized to JSON, it might look like
    // {'RootId' : 'some-id', 'ParentId' : '|some-id.1.2.3.', 'message' : 'your message to process'}
    var jsonPayload = JsonConvert.SerializeObject(new MessagePayload
    {
        RootId = operation.Telemetry.Context.Operation.Id,
        ParentId = operation.Telemetry.Id,
        Payload = message
    });
    
    CloudQueueMessage queueMessage = new CloudQueueMessage(jsonPayload);

    // Add operation.Telemetry.Id to the OperationContext to correlate Storage logs and Application Insights telemetry.
    OperationContext context = new OperationContext { ClientRequestID = operation.Telemetry.Id};

    try
    {
        await queue.AddMessageAsync(queueMessage, null, null, new QueueRequestOptions(), context);
    }
    catch (StorageException e)
    {
        operation.Telemetry.Properties.Add("AzureServiceRequestID", e.RequestInformation.ServiceRequestID);
        operation.Telemetry.Success = false;
        operation.Telemetry.ResultCode = e.RequestInformation.HttpStatusCode.ToString();
        telemetryClient.TrackException(e);
    }
    finally
    {
        // Update status code and success as appropriate.
        telemetryClient.StopOperation(operation);
    }
}  

Per ridurre la quantità di dati di telemetria segnalati dall'applicazione o per non tenere traccia dell'operazione Enqueue per altri motivi, è possibile usare direttamente l'API Activity:

  • Creare (e avviare) un nuovo Activity anziché avviare l'operazione Application Insights. Non è necessario assegnare proprietà ad essa, tranne il nome dell'operazione.
  • Serializzare yourActivity.Id nel payload dei messaggi invece di operation.Telemetry.Id. È anche possibile usare Activity.Current.Id.

Rimuovere dalla coda

In modo simile a Enqueue, la richiesta HTTP effettiva per la coda di archiviazione viene automaticamente registrata da Application Insights. L'operazione Enqueue si verifica presumibilmente nel contesto padre, ad esempio un contesto di richiesta in ingresso. Gli SDK di Application Insights correlano automaticamente tale operazione e la relativa parte HTTP, con la richiesta padre e altri dati di telemetria segnalati nello stesso ambito.

L'operazione Dequeue è un'operazione complessa. L’SDK Application Insights tiene automaticamente traccia delle richieste HTTP. Ma non conosce il contesto di correlazione finché il messaggio non viene analizzato. Non è possibile correlare la richiesta HTTP per ottenere il messaggio con il resto dei dati di telemetria, soprattutto quando vengono ricevuti più messaggi.

public async Task<MessagePayload> Dequeue(CloudQueue queue)
{
    var operation = telemetryClient.StartOperation<DependencyTelemetry>("dequeue " + queue.Name);
    operation.Telemetry.Type = "Azure queue";
    operation.Telemetry.Data = "Dequeue " + queue.Name;
    
    try
    {
        var message = await queue.GetMessageAsync();
    }
    catch (StorageException e)
    {
        operation.telemetry.Properties.Add("AzureServiceRequestID", e.RequestInformation.ServiceRequestID);
        operation.telemetry.Success = false;
        operation.telemetry.ResultCode = e.RequestInformation.HttpStatusCode.ToString();
        telemetryClient.TrackException(e);
    }
    finally
    {
        // Update status code and success as appropriate.
        telemetryClient.StopOperation(operation);
    }

    return null;
}

Processo

Nell'esempio seguente viene rilevato un messaggio in arrivo in modo simile a una richiesta HTTP in ingresso:

public async Task Process(MessagePayload message)
{
    // After the message is dequeued from the queue, create RequestTelemetry to track its processing.
    RequestTelemetry requestTelemetry = new RequestTelemetry { Name = "process " + queueName };
    
    // It might also make sense to get the name from the message.
    requestTelemetry.Context.Operation.Id = message.RootId;
    requestTelemetry.Context.Operation.ParentId = message.ParentId;

    var operation = telemetryClient.StartOperation(requestTelemetry);

    try
    {
        await ProcessMessage();
    }
    catch (Exception e)
    {
        telemetryClient.TrackException(e);
        throw;
    }
    finally
    {
        // Update status code and success as appropriate.
        telemetryClient.StopOperation(operation);
    }
}

Analogamente, è possibile instrumentare le altre operazioni della coda. L'operazione di visualizzazione deve essere instrumentata in modo simile a quella di rimozione dalla coda. Non è necessario instrumentare operazioni di gestione della coda. Application Insights tiene traccia di operazioni come HTTP e nella maggior parte dei casi è sufficiente.

Quando si instrumenta l'eliminazione di un messaggio, assicurarsi di impostare gli identificatori delle operazioni (correlazione). In alternativa, è possibile usare l'API Activity. Non è quindi necessario impostare gli identificatori delle operazioni negli elementi di telemetria perché Application Insights SDK esegue questa operazione automaticamente:

  • Creare un nuovo oggetto Activity dopo avere ottenuto un elemento dalla coda.
  • Usare Activity.SetParentId(message.ParentId) per correlare i log del consumer e del producer.
  • Avviare il Activity.
  • Tenere traccia delle operazioni di rimozione dalla coda, elaborazione ed eliminazione usando gli helper Start/StopOperation. dallo stesso flusso di controllo asincrono (contesto di esecuzione). In questo modo la correlazione sarà corretta.
  • Arrestare il Activity.
  • Usare Start/StopOperation o chiamare Track manualmente i dati di telemetria.

Tipi di dipendenza

Application Insights usa il tipo di dipendenza per personalizzare le esperienze dell'interfaccia utente. Per le code, riconosce i tipi seguenti di che migliorano l'esperienza di DependencyTelemetry diagnostica delle transazioni:

  • Azure queueper le code di Archiviazione di Azure
  • Azure Event Hubsper Hub eventi di Azure
  • Azure Service Busper bus di servizio di Azure

Elaborazione batch

Per alcune code, è possibile una rimozione dalla coda di più messaggi con una singola richiesta. L'elaborazione di tali messaggi è presumibilmente indipendente e appartiene a diverse operazioni logiche. Non è possibile correlare l'operazione Dequeue a un determinato messaggio elaborato.

Ogni elaborazione dei messaggi deve essere eseguita nel proprio flusso di controllo asincrono. Per ulteriori informazioni, vedere la sezione Verifica delle dipendenze in uscita.

Attività in background a esecuzione prolungata

Alcune applicazioni avviano operazioni a esecuzione prolungata che possono essere causate dalle richieste degli utenti. Dal punto di vista della verifica/strumentazione, non c'è differenza dalla strumentazione delle richieste o delle dipendenze:

async Task BackgroundTask()
{
    var operation = telemetryClient.StartOperation<DependencyTelemetry>(taskName);
    operation.Telemetry.Type = "Background";
    try
    {
        int progress = 0;
        while (progress < 100)
        {
            // Process the task.
            telemetryClient.TrackTrace($"done {progress++}%");
        }
        // Update status code and success as appropriate.
    }
    catch (Exception e)
    {
        telemetryClient.TrackException(e);
        // Update status code and success as appropriate.
        throw;
    }
    finally
    {
        telemetryClient.StopOperation(operation);
    }
}

In questo esempio telemetryClient.StartOperation crea DependencyTelemetry e specifica il contesto di correlazione. Si supponga di avere un'operazione padre creata dalle richieste in ingresso che hanno pianificato l'operazione. BackgroundTask, purché venga avviato nello stesso flusso di controllo asincrono di una richiesta in ingresso, viene correlato con tale operazione padre. BackgroundTask e tutti gli elementi di telemetria annidati vengono automaticamente correlati alla richiesta che l'ha generato anche dopo la fine della richiesta.

Quando l'attività viene avviata dal thread in background a cui non sono associate operazioni (Activity), BackgroundTask non ha elementi padre. Tuttavia, può avere operazioni annidate. Tutti gli elementi di telemetria segnalati dall'attività sono correlati a DependencyTelemetry creato in BackgroundTask.

Verifica delle dipendenze in uscita

È possibile tenere traccia della propria tipologia di dipendenza o di operazioni non supportate da Application Insights.

Il metodo Enqueue nella coda del bus di servizio o nella coda di archiviazione è un esempio di tale verifica personalizzata.

L'approccio generale per la verifica personalizzata delle dipendenze è:

  • Chiamare il TelemetryClient.StartOperation metodo (estensione) che riempie le proprietà necessarie per la DependencyTelemetry correlazione e altre proprietà, ad esempio start, timestamp e durata.
  • Impostare le altre proprietà personalizzate in DependencyTelemetry, ad esempio nome e altri contesti necessari.
  • Effettuare una chiamata di dipendenza e attendere.
  • Al termine, arrestare l'operazione con StopOperation.
  • Gestire le eccezioni.
public async Task RunMyTaskAsync()
{
    using (var operation = telemetryClient.StartOperation<DependencyTelemetry>("task 1"))
    {
        try 
        {
            var myTask = await StartMyTaskAsync();
            // Update status code and success as appropriate.
        }
        catch(...) 
        {
            // Update status code and success as appropriate.
        }
    }
}

L'eliminazione di un'operazione causa l'arresto dell'operazione, pertanto è possibile eseguirla invece di chiamare StopOperation.

Avviso

In alcuni casi, un'eccezione non gestita potrebbe impedirefinally la chiamata, pertanto le operazioni potrebbero non essere rilevate.

Elaborazione e verifica di operazioni parallele

La chiamata arresta StopOperation solo l'operazione avviata. Se l'operazione corrente in esecuzione non corrisponde all'operazione che si desidera arrestare, StopOperation non esegue alcuna operazione. Questa situazione può verificarsi se si avviano più operazioni in parallelo nello stesso contesto di esecuzione.

var firstOperation = telemetryClient.StartOperation<DependencyTelemetry>("task 1");
var firstTask = RunMyTaskAsync();

var secondOperation = telemetryClient.StartOperation<DependencyTelemetry>("task 2");
var secondTask = RunMyTaskAsync();

await firstTask;

// FAILURE!!! This will do nothing and will not report telemetry for the first operation
// as currently secondOperation is active.
telemetryClient.StopOperation(firstOperation); 

await secondTask;

Assicurarsi di chiamare StartOperation ed elaborare sempre l'operazione nello stesso metodo asincrono per isolare le operazioni in esecuzione in parallelo. Se l'operazione è sincrona (o non asincrona), eseguire il wrapping del processo e tenere traccia con Task.Run.

public void RunMyTask(string name)
{
    using (var operation = telemetryClient.StartOperation<DependencyTelemetry>(name))
    {
        Process();
        // Update status code and success as appropriate.
    }
}

public async Task RunAllTasks()
{
    var task1 = Task.Run(() => RunMyTask("task 1"));
    var task2 = Task.Run(() => RunMyTask("task 2"));
    
    await Task.WhenAll(task1, task2);
}

Operazioni di ApplicationInsights e System.Diagnostics.Activity

System.Diagnostics.Activity rappresenta il contesto di traccia distribuita e viene usato da framework e librerie per creare e propagare il contesto all'interno e all'esterno del processo e correlare gli elementi di telemetria. Activity interagisce con System.Diagnostics.DiagnosticSource come meccanismo di notifica tra il framework o la libreria per notificare eventi interessanti, ad esempio richieste in ingresso o in uscita ed eccezioni.

Le attività sono cittadini di prima classe in Application Insights. Le dipendenze automatiche e la raccolta di richieste si basano principalmente su di essi insieme agli DiagnosticSource eventi. Se è stata creata Activity nell'applicazione, non verrà creata la telemetria di Application Insights. Application Insights deve ricevere eventi e conoscere DiagnosticSource i nomi degli eventi e i payload per tradursi Activity in dati di telemetria.

Ogni operazione di Application Insights (richiesta o dipendenza) comporta Activity. Quando StartOperation viene chiamato, crea Activity sotto. StartOperation è il modo consigliato per tenere traccia manualmente delle telemetrie di richiesta o dipendenza e assicurarsi che tutto sia correlato.

Passaggi successivi