Diagnostica in Durable Functions in Azure

Per la diagnostica dei problemi con Funzioni permanenti sono disponibili diverse opzioni. Alcune opzioni sono le stesse disponibili per le funzioni normali e alcune sono specifiche di Funzioni permanenti.

Application Insights

Per eseguire la diagnostica e il monitoraggio in Funzioni di Azure, è consigliabile usare Application Insights. Lo stesso consiglio vale per Funzioni permanenti. Per una panoramica su come usare Application Insights nell'app per le funzioni, vedere Monitorare Funzioni di Azure.

L'estensione Funzioni permanenti di Funzioni di Azure genera anche eventi di rilevamento che consentono di tenere traccia dell'esecuzione end-to-end di un'orchestrazione. È possibile individuare e sottoporre a query questi eventi di rilevamento usando lo strumento Application Insights Analytics nel portale di Azure.

Dati di rilevamento

Ogni evento del ciclo di vita di un'istanza di orchestrazione genera la scrittura di un evento di rilevamento nella raccolta traces in Application Insights. Questo evento contiene un payload customDimensions con diversi campi. Ai nomi dei campi viene anteposta la stringa prop__.

  • hubName: il nome dell'hub attività in cui vengono eseguite le orchestrazioni.
  • appName: il nome dell'app per le funzioni. Questo campo è utile quando più app per le funzioni condividono la stessa istanza di Application Insights.
  • slotName: slot di distribuzione in cui è in esecuzione l'app per le funzioni corrente. Questo campo è utile quando si usano slot di distribuzione per controllare le versioni delle orchestrazioni.
  • functionName: il nome della funzione dell'agente di orchestrazione o di attività.
  • functionType: il tipo di funzione, ad esempio agente di orchestrazione o attività.
  • instanceId: l'ID univoco dell'istanza di orchestrazione.
  • state: lo stato di esecuzione del ciclo di vita dell'istanza. I valori validi includono:
    • Scheduled: la funzione è stata pianificata per l'esecuzione, ma non è stata ancora avviata.
    • Started: la funzione è stata avviata, ma non è ancora in stato di attesa o non è stata completata.
    • Awaited: l'agente di orchestrazione ha pianificato un lavoro ed è in attesa del completamento.
    • Listening: l'agente di orchestrazione è in ascolto di una notifica degli eventi esterni.
    • Completed: la funzione è stata completata.
    • Failed: la funzione ha avuto esito negativo generando un errore.
  • reason: dati aggiuntivi associati all'evento di rilevamento. Ad esempio, se un'istanza è in attesa di una notifica degli eventi esterni, questo campo indica il nome dell'evento di cui è in attesa. Se una funzione ha avuto esito negativo, il campo contiene i dettagli dell'errore.
  • isReplay: valore booleano che indica se per l'evento di rilevamento è prevista la riesecuzione.
  • extensionVersion: la versione dell'estensione Durable Task. Questi dati sono particolarmente importanti quando si segnalano possibili bug nell'estensione. Le istanze con esecuzione prolungata possono segnalare più versioni, se si verifica un aggiornamento durante l'esecuzione.
  • sequenceNumber: numero di sequenza di esecuzione per un evento. In combinazione con il timestamp consente di ordinare gli eventi per ora di esecuzione. Si noti che questo numero verrà reimpostato su zero se l'host viene riavviato durante l'esecuzione dell'istanza. È quindi importante eseguire sempre l'ordinamento prima per timestamp e quindi per sequenceNumber.

Il livello di dettaglio dei dati di rilevamento generati ad Application Insights può essere configurato nella sezione logger (Funzioni 1.x) o logging (Funzioni 2.0) del file host.json.

Funzioni 1.0

{
    "logger": {
        "categoryFilter": {
            "categoryLevels": {
                "Host.Triggers.DurableTask": "Information"
            }
        }
    }
}

Funzioni 2.0

{
    "logging": {
        "logLevel": {
            "Host.Triggers.DurableTask": "Information",
        },
    }
}

Per impostazione predefinita vengono generati tutti gli eventi di rilevamento senza riesecuzione. Il volume dei dati può essere ridotto impostando Host.Triggers.DurableTask su "Warning" o "Error"; in questo caso gli eventi di rilevamento verranno generati solo per le situazioni eccezionali. Per abilitare l'emissione degli eventi di riproduzione dell'orchestrazione dettagliata, impostare logReplayEvents su true nel file di configurazione host.json.

Nota

Per impostazione predefinita, la telemetria di Application Insights viene campionata dal runtime di Funzioni di Azure per evitare di generare dati con eccessiva frequenza. In questo modo è possibile che le informazioni di rilevamento vengano perse quando si verificano molti eventi del ciclo di vita in un breve periodo di tempo. L'articolo sul monitoraggio in Funzioni di Azure illustra come configurare questo comportamento.

Gli input e gli output dell'agente di orchestrazione, dell'attività e delle funzioni di entità non vengono registrati per impostazione predefinita. Questo comportamento predefinito è consigliato perché la registrazione di input e output potrebbe aumentare i costi di Application Insights. I payload di input e output delle funzioni possono contenere anche informazioni riservate. Al contrario, viene registrato il numero di byte per gli input e gli output della funzione anziché i payload effettivi per impostazione predefinita. Perché l'estensione Durable Functions registri i payload di input e output completi, impostare la proprietà traceInputsAndOutputs su true nel file di configurazione host.json.

Query per singola istanza

La query seguente mostra i dati di rilevamento cronologici per una singola istanza di orchestrazione della funzione Hello Sequence. Viene scritta usando il linguaggio di query Kusto. Esclude la riesecuzione in modo da mostrare solo il percorso di esecuzione logico. Gli eventi possono essere ordinati per timestamp e sequenceNumber, come illustrato nella query seguente:

let targetInstanceId = "ddd1aaa685034059b545eb004b15d4eb";
let start = datetime(2018-03-25T09:20:00);
traces
| where timestamp > start and timestamp < start + 30m
| where customDimensions.Category == "Host.Triggers.DurableTask"
| extend functionName = customDimensions["prop__functionName"]
| extend instanceId = customDimensions["prop__instanceId"]
| extend state = customDimensions["prop__state"]
| extend isReplay = tobool(tolower(customDimensions["prop__isReplay"]))
| extend sequenceNumber = tolong(customDimensions["prop__sequenceNumber"])
| where isReplay != true
| where instanceId == targetInstanceId
| sort by timestamp asc, sequenceNumber asc
| project timestamp, functionName, state, instanceId, sequenceNumber, appName = cloud_RoleName

Il risultato è un elenco di eventi di traccia che mostrano il percorso di esecuzione dell'orchestrazione, incluse eventuali funzioni di attività ordinate per ora di esecuzione in ordine ascendente.

Query ordinata a istanza singola di Application Insights

Query di riepilogo dell'istanza

La query seguente mostra lo stato di tutte le istanze di orchestrazione eseguite in un intervallo di tempo specificato.

let start = datetime(2017-09-30T04:30:00);
traces
| where timestamp > start and timestamp < start + 1h
| where customDimensions.Category == "Host.Triggers.DurableTask"
| extend functionName = tostring(customDimensions["prop__functionName"])
| extend instanceId = tostring(customDimensions["prop__instanceId"])
| extend state = tostring(customDimensions["prop__state"])
| extend isReplay = tobool(tolower(customDimensions["prop__isReplay"]))
| extend output = tostring(customDimensions["prop__output"])
| where isReplay != true
| summarize arg_max(timestamp, *) by instanceId
| project timestamp, instanceId, functionName, state, output, appName = cloud_RoleName
| order by timestamp asc

Il risultato è un elenco di ID istanza e dello stato di runtime corrente.

Query a istanza singola di Application Insights

Registrazione di Durable Task Framework

I log delle estensioni permanenti sono utili per comprendere il comportamento della logica di orchestrazione. Tuttavia, questi log non contengono sempre informazioni sufficienti per eseguire il debug di problemi di affidabilità e prestazioni a livello di framework. A partire dalla versione 2.3.0 dell'estensione Durable, per la raccolta sono disponibili anche i log generati da Durable Task Framework (DTFx) sottostante.

Quando si esaminano i log generati da DTFx, è importante comprendere che il motore DTFx è composto da due componenti: il motore di distribuzione principale (DurableTask.Core) e uno dei molti provider di archiviazione supportati (Durable Functions usa DurableTask.AzureStorage per impostazione predefinita, ma sono disponibili altre opzioni).

  • DurableTask.Core: esecuzione dell'orchestrazione principale e log di pianificazione di basso livello e dati di telemetria.
  • DurableTask.AzureStorage: log back-end specifici del provider di stato di Archiviazione di Azure. Questi log includono interazioni dettagliate con le code interne, i BLOB e le tabelle di archiviazione usate per archiviare e recuperare lo stato di orchestrazione interna.
  • DurableTask.Netherite: log back-end specifici del provider di archiviazione Netherite, se abilitato.
  • DurableTask.SqlServer: log back-end specifici del provider di archiviazione Microsoft SQL (MSSQL), se abilitato.

È possibile abilitare questi log aggiornando la sezione logging/logLevel del file dell'app per le funzioni host.json. Nell'esempio seguente viene illustrato come abilitare i log degli avvisi e degli errori da DurableTask.Core e DurableTask.AzureStorage:

{
  "version": "2.0",
  "logging": {
    "logLevel": {
      "DurableTask.AzureStorage": "Warning",
      "DurableTask.Core": "Warning"
    }
  }
}

Se Application Insights è abilitato, questi log verranno aggiunti automaticamente alla raccolta trace. È possibile cercarli nello stesso modo in cui si cercano altri log trace usando query Kusto.

Nota

Per le applicazioni di produzione, è consigliabile abilitare DurableTask.Core e i log del provider di archiviazione appropriato (ad esempio, DurableTask.AzureStorage) usando il filtro "Warning". I filtri di dettaglio più elevati, ad esempio "Information", sono molto utili per il debug dei problemi di prestazioni. Tuttavia, questi eventi di log possono essere elevati e possono aumentare significativamente i costi di archiviazione dei dati di Application Insights.

La query Kusto seguente illustra come eseguire query per i log DTFx. La parte più importante della query è where customerDimensions.Category startswith "DurableTask" che filtra i risultati per i log nelle categorie DurableTask.Core e DurableTask.AzureStorage.

traces
| where customDimensions.Category startswith "DurableTask"
| project
    timestamp,
    severityLevel,
    Category = customDimensions.Category,
    EventId = customDimensions.EventId,
    message,
    customDimensions
| order by timestamp asc 

Il risultato è un set di log scritti dai provider di log Durable Task Framework.

Risultati della query DTFx di Application Insights

Per altre informazioni sugli eventi di log disponibili, vedere la documentazione sulla registrazione strutturata di Durable Task Framework in GitHub.

Registrazione dell'app

È importante tenere presente il comportamento di riesecuzione dell'agente di orchestrazione quando si scrivono i log direttamente da una funzione dell'agente di orchestrazione. Ad esempio, si consideri la seguente funzione dell'agente di orchestrazione:

[FunctionName("FunctionChain")]
public static async Task Run(
    [OrchestrationTrigger] IDurableOrchestrationContext context,
    ILogger log)
{
    log.LogInformation("Calling F1.");
    await context.CallActivityAsync("F1");
    log.LogInformation("Calling F2.");
    await context.CallActivityAsync("F2");
    log.LogInformation("Calling F3");
    await context.CallActivityAsync("F3");
    log.LogInformation("Done!");
}

I dati di log risultanti appariranno simili all'output di esempio seguente:

Calling F1.
Calling F1.
Calling F2.
Calling F1.
Calling F2.
Calling F3.
Calling F1.
Calling F2.
Calling F3.
Done!

Nota

Tenere presente che mentre i log contengono la dichiarazione di chiamata F1, F2 e F3, queste funzioni vengono chiamate effettivamente solo al primo rilevamento. Le chiamate successive eseguite durante la riesecuzione vengono ignorate e gli output vengono rieseguiti nella logica dell'agente di orchestrazione.

Per scrivere solo i log nelle esecuzioni non di riproduzione, è possibile scrivere un'espressione condizionale per registrare solo se il flag "is replaying" è false. Si consideri l'esempio precedente, ma questa volta con i controlli di riesecuzione.

[FunctionName("FunctionChain")]
public static async Task Run(
    [OrchestrationTrigger] IDurableOrchestrationContext context,
    ILogger log)
{
    if (!context.IsReplaying) log.LogInformation("Calling F1.");
    await context.CallActivityAsync("F1");
    if (!context.IsReplaying) log.LogInformation("Calling F2.");
    await context.CallActivityAsync("F2");
    if (!context.IsReplaying) log.LogInformation("Calling F3");
    await context.CallActivityAsync("F3");
    log.LogInformation("Done!");
}

A partire da Durable Functions 2.0, le funzioni dell'agente di orchestrazione .NET hanno anche la possibilità di creare un oggetto ILogger che filtra automaticamente le istruzioni di log durante la riproduzione. Questo filtro automatico viene eseguito usando l'API IDurableOrchestrationContext.CreateReplaySafeLogger(ILogger).

[FunctionName("FunctionChain")]
public static async Task Run(
    [OrchestrationTrigger] IDurableOrchestrationContext context,
    ILogger log)
{
    log = context.CreateReplaySafeLogger(log);
    log.LogInformation("Calling F1.");
    await context.CallActivityAsync("F1");
    log.LogInformation("Calling F2.");
    await context.CallActivityAsync("F2");
    log.LogInformation("Calling F3");
    await context.CallActivityAsync("F3");
    log.LogInformation("Done!");
}

Nota

Gli esempi C# precedenti sono relativi a Durable Functions 2.x. Per Durable Functions 1.x, è necessario usare DurableOrchestrationContext anziché IDurableOrchestrationContext. Per altre informazioni sulle differenze tra le versioni, vedere l'articolo Versioni di Durable Functions.

Con le modifiche indicate in precedenza, l'output del log è il seguente:

Calling F1.
Calling F2.
Calling F3.
Done!

Stato personalizzato

Lo stato dell'orchestrazione personalizzato consente di impostare un valore di stato per la funzione dell'agente di orchestrazione. Questo stato personalizzato è quindi visibile ai client esterni tramite l'API di query sullo stato HTTP o tramite chiamate API specifiche del linguaggio. Lo stato di un'orchestrazione personalizzato consente di eseguire il monitoraggio in modo più completo per le funzioni dell'agente di orchestrazione. Ad esempio, il codice della funzione dell'agente di orchestrazione può richiamare l'API "imposta stato personalizzato" per aggiornare lo stato di avanzamento per un'operazione a esecuzione prolungata. Un client, ad esempio una pagina Web o un sistema esterno, può eseguire una query periodicamente sulle API di query dello stato HTTP per ottenere informazioni più dettagliate. Di seguito è riportato il codice di esempio per l'impostazione di un valore di stato personalizzato in una funzione dell'agente di orchestrazione:

[FunctionName("SetStatusTest")]
public static async Task SetStatusTest([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    // ...do work...

    // update the status of the orchestration with some arbitrary data
    var customStatus = new { completionPercentage = 90.0, status = "Updating database records" };
    context.SetCustomStatus(customStatus);

    // ...do more work...
}

Nota

L'esempio C# precedente è relativo a Durable Functions 2.x. Per Durable Functions 1.x, è necessario usare DurableOrchestrationContext anziché IDurableOrchestrationContext. Per altre informazioni sulle differenze tra le versioni, vedere l'articolo Versioni di Durable Functions.

Mentre l'orchestrazione è in esecuzione, i client esterni possono recuperare questo stato personalizzato:

GET /runtime/webhooks/durabletask/instances/instance123?code=XYZ

I client visualizzano la risposta seguente:

{
  "runtimeStatus": "Running",
  "input": null,
  "customStatus": { "completionPercentage": 90.0, "status": "Updating database records" },
  "output": null,
  "createdTime": "2017-10-06T18:30:24Z",
  "lastUpdatedTime": "2017-10-06T19:40:30Z"
}

Avviso

Il payload dello stato personalizzato è limitato a 16 kB di testo JSON UTF-16, perché deve rientrare in una colonna dell'archivio tabelle di Azure. È possibile usare l'archiviazione esterna se è necessario un payload di dimensioni maggiori.

Traccia distribuita

La traccia distribuita monitora le richieste e mostra in che modo i diversi servizi interagiscono tra loro. In Durable Functions mette in correlazione anche le orchestrazioni e le attività insieme. Ciò è utile per comprendere il tempo necessario per l'orchestrazione rispetto all'intera orchestrazione. È anche utile comprendere dove un'applicazione presenta un problema o dove è stata generata un'eccezione. Questa funzionalità è supportata per tutti i linguaggi e i provider di archiviazione.

Nota

La traccia distribuita V2 richiede Durable Functions v2.12.0 o versione successiva. Inoltre, Distributed Tracing V2 è in uno stato di anteprima e pertanto alcuni modelli di Funzioni permanenti non sono instrumentati. Ad esempio, le operazioni Durable Entities non vengono instrumentate e le tracce non verranno visualizzate in Application Insights.

Configurazione della traccia distribuita

Per configurare la traccia distribuita, aggiornare il file host.json e configurare una risorsa di Application Insights.

host.json

"durableTask": {
  "tracing": {
    "distributedTracingEnabled": true,
    "Version": "V2"
  }
}

Application Insights

Se l'app per le funzioni non è configurata con una risorsa di Application Insights, configurarla seguendo le istruzioni riportate qui.

Controllo delle tracce

Nella risorsa di Application Insights passare a Ricerca transazioni. Nei risultati, verificare la presenza di eventi Request e Dependency che iniziano con prefissi specifici di Durable Functions (ad esempio orchestration:, activity: e così via). Se si seleziona uno di questi eventi, verrà aperto un diagramma di Gantt che mostrerà la traccia distribuita end-to-end.

Diagramma di Gantt che mostra la traccia distribuita di Application Insights.

Risoluzione dei problemi

Se non vengono visualizzate le tracce in Application Insights, assicurarsi di attendere circa cinque minuti dopo l'esecuzione dell'applicazione per garantire che tutti i dati vengano propagati alla risorsa di Application Insights.

Debug

Funzioni di Azure supporta direttamente il debug del codice della funzione e lo stesso supporto si estende a Funzioni permanenti, in esecuzione in Azure o in locale. Quando si esegue il debug, è tuttavia opportuno conoscere alcuni comportamenti:

  • Replay: le funzioni dell'agente di orchestrazione vengono rieseguite regolarmente alla ricezione di nuovi input. Questo comportamento implica che una singola esecuzione logica di una funzione dell'agente di orchestrazione può comportare l'accesso più volte allo stesso punto di interruzione, soprattutto se è impostata all'inizio del codice della funzione.
  • Await: ogni volta che viene rilevato un oggetto await in una funzione dell'agente di orchestrazione, restituisce il controllo al dispatcher Durable Task Framework. Se è la prima volta che uno specifico await è stato rilevato, l'attività associata non viene ripresa mai. Poiché l'attività non viene ripresa mai, non è possibile eseguire lo step over per await (F10 in Visual Studio). Questa operazione funziona solo se un'attività è in corso di riesecuzione.
  • Timeout di messaggistica: Durable Functions usa internamente i messaggi in coda per gestire l'esecuzione delle funzioni, di orchestrazione, attività ed entità. In un ambiente con più macchine virtuali, a causa dell'interruzione del debug per lunghi periodi di tempo è possibile che un'altra macchina virtuale prelevi il messaggio, generando un'esecuzione duplicata. Questo comportamento esiste anche per le normali funzioni attivate da coda, ma è importante sottolinearlo in questo contesto poiché le code sono un dettaglio dell'implementazione.
  • Arresto e avvio: i messaggi nelle funzioni permanenti vengono mantenuti tra le sessioni di debug. Se si arresta il debug e si termina il processo host locale durante l'esecuzione di una funzione durevole, tale funzione può essere eseguita di nuovo automaticamente in una sessione di debug futura. Questo comportamento può generare confusione quando non previsto. L'uso di un nuovo hub attività o la cancellazione del contenuto dell'hub attività tra le sessioni di debug è una tecnica per evitare questo comportamento.

Suggerimento

Quando si impostano punti di interruzione nelle funzioni dell'agente di orchestrazione, se si vuole interrompere solo l'esecuzione non di riproduzione, è possibile impostare un punto di interruzione condizionale che si interrompe solo se il valore di riproduzione è false.

Storage

Per impostazione predefinita, Funzioni permanenti archivia lo stato in Archiviazione di Azure. Questo comportamento significa che è possibile esaminare lo stato delle orchestrazioni usando strumenti come Microsoft Azure Storage Explorer.

Screenshot di Azure Storage Explorer

Risulta utile per il debug perché viene visualizzato esattamente lo stato in cui potrebbe trovarsi un'orchestrazione. È possibile esaminare i messaggi nelle code anche per individuare le attività in sospeso o, in alcuni casi, bloccate.

Avviso

Sebbene sia utile esaminare la cronologia di esecuzioni in archiviazione tabelle, evitare di creare qualsiasi dipendenza in questa tabella, che può cambiare con l'evoluzione dell'estensione Funzioni permanenti.

Nota

È possibile configurare altri provider di archiviazione anziché il provider predefinito di Archiviazione di Azure. A seconda del provider di archiviazione configurato per l'app, potrebbe essere necessario usare diversi strumenti per controllare lo stato sottostante. Per altre informazioni, vedere la documentazione relativa ai Provider di archiviazione di Durable Functions.

Durable Functions Monitor

Durable Functions Monitor è uno strumento grafico per il monitoraggio, la gestione e il debug di istanze di orchestrazione ed entità. È disponibile come estensione di Visual Studio Code o come app autonoma. Le informazioni sulla configurazione e un elenco di funzionalità sono disponibili in questo wiki.

Guida alla risoluzione dei problemi di Durable Functions

Per risolvere i sintomi comuni del problema, ad esempio orchestrazioni bloccate, mancata avvio, esecuzione lenta e così via, vedere questa guida alla risoluzione dei problemi.

Passaggi successivi