Partage via


Diagnostics de Durable Functions dans Azure

Il existe plusieurs options permettant de diagnostiquer les problèmes avec Fonctions durables. Certaines de ces options sont les mêmes pour les fonctions standard, et d’autres sont uniques à Fonctions durables.

Application Insights

Application Insights est la méthode recommandée pour les diagnostics et la surveillance dans Azure Functions. Il en va de même pour Fonctions durables. Pour obtenir une vue d’ensemble montrant comment tirer parti d’Application Insights dans votre application de fonction, consultez Surveiller l’exécution des fonctions Azure.

L’extension Fonctions durables d’Azure émet également des événements de suivi vous permettant de tracer l’exécution de bout en bout d’une orchestration. Ces événements de suivi sont accessibles et interrogés à l’aide de l’outil Application Insights Analytics dans le portail Azure.

Suivi des données

Chaque événement du cycle de vie d’une instance d’orchestration entraîne l’inscription d’un événement de suivi dans la collection des suivis d’Application Insights. Cet événement contient une charge utile customDimensions avec plusieurs champs. Les noms de champs contiennent tous le préfixe prop__.

  • hubName : Nom du hub de tâches dans lequel vos orchestrations sont en cours d’exécution.
  • appName : Le nom de l’application de fonction. Ce champ est utile si plusieurs de vos applications de fonction partagent la même instance Application Insights.
  • slotName : emplacement de déploiement dans lequel s’exécute l’application de fonction actuelle. Ce champ est utile pour tirer parti des emplacements de déploiement pour la version de vos orchestrations.
  • functionName : Nom de la fonction d’orchestrateur ou d’activité.
  • functionType : Type de la fonction, par exemple Orchestrateur ou Activité.
  • instanceId : ID unique de l’instance d’orchestration.
  • state : État d’exécution du cycle de vie de l’instance. Les valeurs valides sont les suivantes :
    • Scheduled : La fonction a été planifiée pour être exécutée, mais elle n’a pas encore démarré.
    • Started : La fonction a démarré, mais elle n’a pas encore été attendue ou s’est terminée.
    • Awaited : L’orchestrateur a planifié des tâches et attend qu’elles se terminent.
    • Listening : L’orchestrateur écoute une notification d’événement externe.
    • Completed : La fonction s’est terminée avec succès.
    • Échec : La fonction a échoué avec une erreur.
  • reason : données supplémentaires associées à l’événement de suivi. Par exemple, si une instance attend une notification d’événement externe, ce champ indique le nom de l’événement attendu. Si une fonction a échoué, ce champ contient les détails de l’erreur.
  • isReplay : Valeur booléenne qui indique si l’événement de suivi est destiné à une réexécution.
  • extensionVersion : Version de l’extension Tâche durable. Ces informations de version sont particulièrement importantes pour signaler d’éventuels bogues dans l’extension. Des instances à long terme peuvent signaler plusieurs versions si une mise à jour se produit pendant leur exécution.
  • sequenceNumber : Numéro séquentiel d’extension pour un événement. Combiné avec le timestamp, il permet de classer les événements par durée d’exécution. Notez que ce numéro sera réinitialisé à zéro si l’hôte redémarre alors que l’instance est en cours d’exécution, il est donc important de toujours d’abord trier par timestamp, puis par sequenceNumber.

Le niveau de détail des données de suivi transmises à Application Insights peut être configuré dans la section logger (Functions 1.x) ou logging (Functions 2.0) du fichier host.json.

Functions 1.0

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

Functions 2.0

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

Par défaut, tous les événements de suivi non rejoués sont transmis. Le volume de données peut être réduit en définissant Host.Triggers.DurableTask sur "Warning" ou "Error". Dans ce cas, les événements de suivi seront uniquement transmis en cas de situation exceptionnelle. Pour activer l’émission d’événements de relecture d’orchestration détaillés, définissez logReplayEvents sur true dans le fichier de configuration host.json.

Notes

Par défaut, les données de télémétrie Application Insights sont échantillonnées par le runtime Azure Functions pour éviter un transfert trop fréquent de données. Cela peut entraîner une perte des informations de suivi si de nombreux événements de cycle de vie se produisent sur une courte période. L’article sur la surveillance d’Azure Functions explique comment configurer ce comportement.

Les entrées et sorties des fonctions d’orchestrateur, d’activité et d’entité ne sont pas journalisées par défaut. Ce comportement par défaut est recommandé, car l’enregistrement des entrées et sorties peut augmenter les coûts d’Application Insights. Les charges utiles d’entrée et de sortie de fonction peuvent également contenir des informations sensibles. Au lieu de cela, les nombres d’octets pour les entrées et sorties de fonction sont journalisés, et non pas les charges utiles réelles par défaut. Si vous souhaitez que l’extension Durable Functions consigne l’intégralité des charges utiles d’entrée et de sortie, définissez la propriété traceInputsAndOutputs sur true dans le fichier de configuration host.json.

Requête d’instance unique

La requête suivante affiche les données de suivi historiques d’une seule instance de la fonction d’orchestration Séquence Hello. Ces informations sont écrites à l’aide du langage de requête Kusto. Elle exclut la réexécution afin que seul le chemin d’exécution logique s’affiche. Les événements peuvent être classés en les triant par timestamp et sequenceNumber comme indiqué dans la requête ci-dessous :

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

Le résultat est une liste d’événements de suivi indiquant le chemin d’exécution de l’orchestration, y compris toutes les fonctions d’activité triées par durée d’exécution par ordre ascendant.

Requête triée sur une seule instance Application Insights

Requête de résumé de l’instance

La requête suivante affiche l’état de toutes les instances d’orchestration qui ont été exécutées dans un intervalle de temps spécifié.

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

Le résultat est une liste d’ID d’instances et leur actuel état d’exécution.

Requête sur une seule instance Application Insights

Journalisation durable de l’infrastructure des tâches

Les journaux d’extension Durable sont utiles pour comprendre le comportement de votre logique d’orchestration. Toutefois, ces journaux ne contiennent pas toujours assez d’informations pour déboguer les problèmes de performances et de fiabilité au niveau de l’infrastructure. À partir de la version 2.3.0 de l’extension Durable, les journaux émis par l’infrastructure Durable Task Framework (DTFx) sous-jacentes sont également disponibles pour la collecte.

Lorsque vous examinez les journaux émis par l’infrastructure DTFx, il est important de comprendre que le moteur DTFx est doté de deux composants : le moteur de distribution principal (DurableTask.Core) et l’un des nombreux fournisseurs de stockage pris en charge (Durable Functions utilise DurableTask.AzureStorage par défaut, mais d’autres options sont disponibles).

  • DurableTask.Core : exécution de l’orchestration principale et des journaux de planification et de la télémétrie de bas niveau.
  • DurableTask.AzureStorage : journaux principaux spécifiques au fournisseur d’état de stockage Azure. Ces journaux incluent des interactions détaillées avec les files d’attente internes, les objets blob et les tables de stockage utilisés pour stocker et récupérer l’état d’orchestration interne.
  • DurableTask.Netherite : journaux principaux spécifiques au fournisseur de stockage Netherite, s’ils sont activés.
  • DurableTask.SqlServer : journaux principaux spécifiques au fournisseur de stockage Microsoft SQL (MSSQL), s’ils sont activés.

Vous pouvez activer ces journaux en mettant à jour la section logging/logLevel du fichierhost.json de votre application de fonction. L’exemple suivant montre comment activer les journaux d’avertissement et d’erreur depuis DurableTask.Core et DurableTask.AzureStorage :

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

Si Application Insights est activé, ces journaux sont automatiquement ajoutés à la collecte trace. Vous pouvez les rechercher de la même façon que vous recherchez d’autres journaux trace à l’aide de requêtes Kusto.

Notes

Pour les applications de production, il est recommandé d’activer DurableTask.Core et le fournisseur de stockage approprié (par exemple, les journaux DurableTask.AzureStorage) à l’aide du filtre "Warning". Des filtres plus détaillés, tels que "Information" , sont très utiles pour le débogage des problèmes de performances. Toutefois, ces événements de journaux peuvent être volumineux et considérablement augmenter les coûts de stockage de données Application Insights.

La requête Kusto suivante montre comment interroger les journaux DTFx. La partie la plus importante de la requête est where customerDimensions.Category startswith "DurableTask" dans la mesure où celle-ci filtre les résultats dans les journaux des catégories DurableTask.Core et DurableTask.AzureStorage.

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

Le résultat est un ensemble de journaux écrits par les fournisseurs de journaux Durable Task Framework.

Résultats de requête DTFx dans Application Insights

Pour plus d’informations sur les événements de journalisation disponibles, consultez la documentation sur Durable Task Framework dans le cadre de la journalisation structurée sur GitHub.

Journalisation des applications

Il est important de garder à l’esprit le comportement de réexécution de l’orchestrateur lors de l’écriture des journaux d’activité directement à partir d’une fonction d’orchestrateur. Par exemple, considérez la fonction d’orchestrateur suivante :

[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!");
}

Les données de journal obtenues se présentent comme dans l’exemple suivant :

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

Notes

N’oubliez pas que même si les journaux d’activité annoncent appeler les fonctions F1, F2 et F3, ces fonctions sont uniquement réellement appelées la première fois qu’elles sont rencontrées. Les prochains appels qui se produisent lors de la réexécution sont ignorés et les sorties sont réexécutées à la logique d’orchestrateur.

Si vous souhaitez uniquement consigner une non réexécution, vous pouvez écrire une expression conditionnelle qui écrit dans le journal uniquement si l’indicateur « is replaying » est false. Considérez l’exemple ci-dessus, mais cette fois avec des vérifications de réexécution.

[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!");
}

À partir de Durable Functions 2.0, les fonctions de l’orchestrateur .NET ont également la possibilité de créer un ILogger qui filtre automatiquement les instructions de journal lors de la relecture. Ce filtrage automatique s’effectue à l’aide de 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!");
}

Notes

Les exemples C# précédents portent sur Durable Functions 2.x. Pour Durable Functions 1.x, vous devez utiliser DurableOrchestrationContext au lieu de IDurableOrchestrationContext. Pour en savoir plus sur les différences entre les versions, consultez l’article Versions de Durable Functions.

Avec les modifications mentionnées précédemment, la sortie du journal est la suivante :

Calling F1.
Calling F2.
Calling F3.
Done!

État personnalisé

L’état d’orchestration personnalisé vous permet de définir une valeur d’état personnalisée pour votre fonction d’orchestrateur. Cet état personnalisé est ensuite visible par les clients externes via l’API de requête d’état HTTP ou via des appels d’API spécifiques à une langue. L’état d’une orchestration personnalisée permet de surveiller plus précisément les fonctions d’orchestrateur. Par exemple, le code de fonction d’orchestrateur peut appeler l’API « définir le statut personnalisé » afin de mettre à jour la progression d’une opération de longue durée. Un client, comme une page web ou un autre système externe, peut ensuite interroger régulièrement les API de requête d’état HTTP pour en savoir plus sur l’état d’avancement. Vous trouverez ci-dessous un exemple de code pour la définition d’une valeur d’état personnalisée dans une fonction d’orchestrateur :

[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...
}

Notes

L’exemple C# précédent porte sur Durable Functions 2.x. Pour Durable Functions 1.x, vous devez utiliser DurableOrchestrationContext au lieu de IDurableOrchestrationContext. Pour en savoir plus sur les différences entre les versions, consultez l’article Versions de Durable Functions.

Pendant l’exécution de l’orchestration, les clients externes peuvent récupérer cet état personnalisé :

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

Les clients obtiennent la réponse suivante :

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

Avertissement

La charge utile de l’état personnalisé est limitée à 16 Ko de texte JSON UTF-16, car elle doit pouvoir tenir dans une colonne de Stockage Table Azure. Vous pouvez utiliser un stockage externe si vous avez besoin d’une charge utile plus importante.

Suivi distribué

Distributed Tracing effectue le suivi des requêtes et montre comment les différents services interagissent entre eux. Dans Durable Functions, il met également en corrélation les orchestrations et les activités. Cela est utile pour comprendre le temps que prend chaque étape de l’orchestration par rapport à l’intégralité de l’orchestration. C’est également utile pour comprendre où une application rencontre un problème ou où une exception a été levée. Cette fonctionnalité est prise en charge pour tous les langages et fournisseurs de stockage.

Remarque

Distributed Tracing V2 nécessite Durable Functions v2.12.0 ou une version ultérieure. En outre, Distributed Tracing V2 est en préversion et, par conséquent, certains modèles Durable Functions ne sont pas instrumentés. Par exemple, les opérations Durable Entities ne sont pas instrumentées et les traces ne s’affichent pas dans Application Insights.

Configuration de Distributed Tracing

Pour configurer le suivi distribué, mettez à jour le host.json et configurez une ressource Application Insights.

host.json

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

Application Insights

Si l’application de fonction n’est pas configurée avec une ressource Application Insights, configurez-la en suivant les instructions fournies ici.

Inspection des traces

Dans la ressource Application Insights, accédez à Recherche de transactions. Dans les résultats, recherchez les événements Request et Dependency qui commencent par des préfixes spécifiques à Durable Functions (par exemple orchestration:, activity:, etc.). La sélection de l’un de ces événements ouvre un diagramme de Gantt qui affiche la trace distribuée de bout en bout.

Diagramme de Gantt illustrant une trace distribuée Application Insights.

Dépannage

Si vous ne voyez pas les traces dans Application Insights, veillez à attendre environ cinq minutes après l’exécution de l’application pour vous assurer que toutes les données sont propagées à la ressource Application Insights.

Débogage

Azure Functions prend directement en charge un code de fonction de débogage, et cette prise en charge s’applique également à Fonctions durables, que ce soit avec une exécution dans Azure ou localement. Toutefois, vous devez tenir compte de certains comportements lors du débogage :

  • Réexécution : Les fonctions d’orchestrateur sont régulièrement réexécutées lors de la réception de nouvelles entrées. Ce comportement signifie qu’une seule exécution logique d’une fonction d’orchestrateur peut atteindre le même point d’arrêt plusieurs fois, en particulier si elle est définie très tôt dans le code de la fonction.
  • Await : Chaque fois qu’un élément await est rencontré dans une fonction d’orchestrateur, le contrôle repasse au répartiteur de l’infrastructure Durable Task Framework. Si c’est la première fois qu’un événement await particulier est détecté, la tâche associée n’est jamais reprise. Puisque la tâche ne reprend jamais, vous ne pouvez pas parcourir l’événement await (F10 dans Visual Studio). Parcourez uniquement des travaux lorsqu’une tâche est réexécutée.
  • Délais d’expiration des messages : Durable Functions utilise en interne des files d’attente de messages pour gérer l’exécution des fonctions d’orchestrateur, d’activité et d’entité. Dans un environnement à plusieurs machines virtuelles, si vous arrêtez le débogage pendant de longues périodes, une autre machine virtuelle risque de récupérer le message, ce qui entraîne une double exécution. Ce comportement s’applique également aux fonctions déclenchées par une file d’attente standard, mais il est important de le souligner car les files d’attente constituent un détail d’implémentation.
  • Arrêt et démarrage : Les messages dans Durable Functions persistent entre les sessions de débogage. Si vous arrêtez le débogage et mettez fin au processus hôte local pendant l’exécution d’une fonction durable, cette fonction peut être réexécutée automatiquement dans une session de débogage ultérieure. Ce comportement peut prêter à confusion lorsque ce n’est pas attendu. L’utilisation d’un nouveau hub de tâches ou l’effacement du contenu du hub de tâches entre les sessions de débogage est une technique pour éviter ce comportement.

Conseil

Lors de la définition de points d’arrêt dans les fonctions d’orchestrateur, si vous voulez uniquement arrêter en cas de non-réexécution, vous pouvez définir un point d’arrêt conditionnel qui s’arrête uniquement si la valeur « is replaying » est false.

Stockage

Par défaut, Durable Functions stocke l’état dans Stockage Azure. Ce comportement signifie que vous pouvez examiner l’état de vos orchestrations à l’aide d’outils tels que l’Explorateur Stockage Azure Microsoft.

Capture d'écran de l'Explorateur Stockage Azure

Cet outil est utile pour le débogage car il vous permet de visualiser exactement l’état d’une orchestration. Vous pouvez également examiner les messages de files d’attente pour identifier les tâches en attente (ou bloquées dans certains cas).

Avertissement

Même s’il est pratique d’afficher l’historique d’exécution dans le stockage de table, évitez de créer une dépendance sur cette table. Elle peut changer à mesure que l’extension Fonctions durables évolue.

Notes

D’autres fournisseurs de stockage peuvent être configurés à la place du fournisseur Stockage Azure par défaut. Selon le fournisseur de stockage configuré pour votre application, vous devrez peut-être utiliser différents outils pour inspecter l’état sous-jacent. Pour plus d’informations, consultez la documentation sur les fournisseurs de stockage Durable Functions.

Moniteur Durable Functions

Le moniteur Durable Functions est un outil graphique permettant de surveiller, gérer et déboguer vos instances d’entité et d’orchestration. Il est disponible en tant qu’extension Visual Studio Code ou d’application autonome. Vous trouverez des informations sur la configuration et une liste de fonctionnalités dans ce Wiki.

Guide de résolution des problèmes de Durable Functions

Pour résoudre les problèmes courants tels que les orchestrations bloquées, l’échec du démarrage, l’exécution lente, etc., reportez-vous à ce guide de résolution des problèmes.

Étapes suivantes