Écrire des données de télémétrie dans votre ressource Application Insights à l’aide d’ILogger

Important

Pour utiliser cette fonctionnalité, vous devez d’abord activer la fonctionnalité d’intégration d’Application Insights à l’aide d’un compte d’administrateur. Assurez-vous que l’utilisateur qui active la fonctionnalité dispose des privilèges nécessaires pour modifier l’organisation Dataverse (par exemple, le rôle Administrateur système ou être un administrateur Power Platform/Dynamics 365) et dispose d’un accès de contributeur à la ressource Application Insights. Si un utilisateur sans les autorisations nécessaires permet l’intégration, les données de télémétrie ne sont pas écrites dans Application Insights. Pour plus d’informations, consultez Analyser la télémétrie des applications basées sur des modèles et de Microsoft Dataverse avec Application Insights.

Il n’existe actuellement aucune prise en charge de ILogger dans le cadre d’une session de profilage ou de débogage de plug-in de l’outil d’inscription de plug-in ou de l’extension Power Platform Tools pour Visual Studio.

Lorsque vous activez Application Insights pour votre organisation, tous les plug-ins écrits à l’aide de l’interface ILogger fournie dans le Kit de développement logiciel (SDK) pour .NET assemblys écrivent des données de télémétrie dans votre ressource Application Insights.

La plateforme Dataverse capture les données de télémétrie de l’application pilotée par modèle et Dataverse et les exporte vers votre ressource Application Insights. Vous constatez une certaine latence entre le moment de la capture et la disponibilité des données dans Application Insights. Cette télémétrie étant collectée par Microsoft, vous n′avez pas besoin d′écrire de code pour l′activer.

Les données de télémétrie provenant de plug-ins à l’aide de l’interface ILogger sont différentes de deux façons :

  • Cette télémétrie est écrite directement dans votre ressource Application Insights et n′est jamais envoyée à Microsoft.
    • Cela signifie que la visualisation de ces données prend moins de temps.
  • Vous devez mettre à jour le code du plug-in pour utiliser l′interface ILogger.

L’utilisation d’ILogger fournit des données de télémétrie vraies et est destinée à fonctionner avec les journaux de trace de plug-in existants écrits à l’aide de l’interface ITracingService. Le tableau suivant offre un comparatif des fonctionnalités :

Critères ILogger pour Application Insights Suivi ITracingService pour les journaux de suivi des plug-ins
Utilisation prévue Capturez la télémétrie au fil du temps pour l’analyse et le débogage. Lors du développement et du débogage des plug-ins
Durée de stockage des données En fonction de la période de conservation des données Application Insights choisie, elle est de 90 jours par défaut 24 heures
Disponible Uniquement pour les organisations qui souscrivent un abonnement à l′intégration d′Application Insights. Disponible pour toute organisation si le traçage des plug-ins est activé.
Quantité de données Chaque message de journal peut transmettre une valeur de chaîne. Seuls 10 Ko de texte peuvent être écrits à chaque exécution du module externe. Le texte est tronqué au-delà des 10 premiers kb.
Disponible dans les erreurs de runtime Non Disponible dans les erreurs client des application pilotées par modèle et comme annotations dans l′API web. Pour plus d’informations, consultez Ajouter plus de détails aux erreurs.

Vous devez continuer à utiliser ITracingService.Trace pour écrire dans la table Journal des traces du plug-in si nécessaire. Toutes les organisations n′activent pas Application Insights. Si le code de plug-in utilise l′interface ILogger et que l′organisation n′a pas activé l′intégration Application Insights, rien n′est écrit. Il est donc important de continuer à utiliser la méthode de suivi ITracingService dans les plug-ins. Les journaux de suivi des plug-ins restent un moyen intéressant pour capturer les données lors du développement et du débogage des plug-ins même s′ils n′ont jamais été conçus pour fournir des données de télémétrie. Pour plus d’informations, consultez Plug-ins : Suivi et journalisation.

Vous devez utiliser ILogger, car elle fournit une télémétrie sur ce qui se passe dans un plug-in. Cette télémétrie est intégrée à la plus grande étendue de données capturées avec l’intégration Application Insights. L′intégration Application Insights indique à quel moment un plug-in s′exécute, la durée de son exécution et s′il effectue des requêtes http externes, mais Microsoft ne peut ajouter aucun code de télémétrie dans les plug-ins écrits pour étendre le comportement de la plateforme.

Si vous êtes éditeur de logiciels indépendant et disposez d′un produit qui inclut des plug-ins, vos clients qui activent Application Insights apprécient de pouvoir visualiser ce qui se passe dans vos plug-ins et ces données peuvent contribuer à les aider en cas de problème. Mais les données capturées à l’aide d’ILogger ne sont envoyées qu’à la ressource du client abonné. Vous ne pouvez voir que les données capturées pour vos environnements personnels si vous avez activé Application Insights.

Utiliser ILogger

ILogger est une interface commune pour la capture d′informations de journal. L’implémentation fournie avec les assemblages SDK pour .NET fournit des méthodes communes pour prendre en charge l’établissement d’une portée ainsi que différents niveaux de journalisation. Il n'existe actuellement aucun paramètre permettant de contrôler le niveau de journalisation. Utilisez les niveaux dans Application Insights pour filtrer les logs à consulter.

L′exemple de plug-in suivant illustre l′utilisation de ILogger et de ITracingService.Trace.

Remarque

Vérifiez que vous incluez using Microsoft.Xrm.Sdk.PluginTelemetry;. N′utilisez pas using Microsoft.Extensions.Logging;, sinon l’instance ILogger est nulle.

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.PluginTelemetry;
using System;
using System.Net.Http;

namespace ILoggerExample
{
    public class AccountPostOperation : IPlugin
    {
        private string webAddress;
        public AccountPostOperation(string config)
        {

            if (string.IsNullOrEmpty(config))
            {
                webAddress = "https://www.bing.com";
            }
            else
            {
                webAddress = config;
            }
        }


        public void Execute(IServiceProvider serviceProvider)
        {
            ITracingService tracingService =
               (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            ILogger logger = (ILogger)serviceProvider.GetService(typeof(ILogger));

            IPluginExecutionContext context = (IPluginExecutionContext)
               serviceProvider.GetService(typeof(IPluginExecutionContext));

            try
            {
                string startExecMsg = "Start execution of AccountPostOperation";
                logger.LogInformation(startExecMsg);
                tracingService.Trace(startExecMsg);

                Entity entity = (Entity)context.InputParameters["Target"];
                if (entity.LogicalName != "account")
                {

                    string wrongEntityMsg = "Plug-in registered for wrong entity {0}";
                    logger.LogWarning(wrongEntityMsg, entity.LogicalName);
                    tracingService.Trace(wrongEntityMsg, entity.LogicalName);
                    return;
                }

                string activityMsg = "Callback";

                using (logger.BeginScope(activityMsg))
                {
                    tracingService.Trace(activityMsg);

                    string startTaskMsg = "Start Task Creation";
                    logger.LogInformation(startTaskMsg);
                    tracingService.Trace(startTaskMsg);

                    Entity followup = new Entity("task");
                    followup["subject"] = "Send e-mail to the new customer.";
                    followup["description"] =
                        "Follow up with the customer. Check if there are any new issues that need resolution.";
                    followup["scheduledstart"] = DateTime.Now.AddDays(7);
                    followup["scheduledend"] = DateTime.Now.AddDays(7);
                    followup["category"] = context.PrimaryEntityName;

                    // Refer to the account in the task activity.
                    if (context.OutputParameters.Contains("id"))
                    {
                        Guid regardingobjectid = new Guid(context.OutputParameters["id"].ToString());
                        string regardingobjectidType = "account";

                        followup["regardingobjectid"] =
                        new EntityReference(regardingobjectidType, regardingobjectid);

                    }

                    // Obtain the IOrganizationService reference.
                    IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider
                    .GetService(typeof(IOrganizationServiceFactory));

                    IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
                    //Create the task
                    service.Create(followup);

                    string endTaskMsg = "Task creation completed";
                    logger.LogInformation(endTaskMsg);
                    tracingService.Trace(endTaskMsg);
                }

                string outBoundScope = "OutboundCall";

                using (logger.BeginScope(outBoundScope))
                {

                    string outboundStartMsg = "Outbound call started";
                    logger.LogInformation(outboundStartMsg);
                    tracingService.Trace(outboundStartMsg);

                    using (HttpClient client = new HttpClient())
                    {
                        client.Timeout = TimeSpan.FromMilliseconds(15000); //15 seconds
                        client.DefaultRequestHeaders.ConnectionClose = true; //Set KeepAlive to false

                        HttpResponseMessage response = client
                            .GetAsync(webAddress)
                            .GetAwaiter()
                            .GetResult(); //Make sure it is synchronous

                        response.EnsureSuccessStatusCode();

                        string responseText = response.Content
                            .ReadAsStringAsync()
                            .GetAwaiter()
                            .GetResult(); //Make sure it is synchronous

                        string shortResponseText = responseText.Substring(0, 20);

                        logger.LogInformation(shortResponseText);
                        tracingService.Trace(shortResponseText);

                        string outboundEndMsg = "Outbound call ended successfully";

                        logger.LogInformation(outboundEndMsg);
                        tracingService.Trace(outboundEndMsg);

                    }

                }

            }
            catch (Exception e)
            {
                string errMsg = "Plugin failed";
                logger.LogError(e, errMsg);
                tracingService.Trace($"{errMsg}:{e.Message}");
                throw new InvalidPluginExecutionException(e.Message, e);
            }
        }
    }
}

Lorsque vous inscrivez ce plug-in sur une étape synchrone PostOperation pour l’entité Createaccount , vous pouvez utiliser les journaux Application Insights pour afficher la sortie en quelques minutes. Utilisez kusto Query Language (KQL) pour interroger les résultats.

Filtrez les éléments pour une opération unique à l’aide de operation_ParentId, qui représente l’identifiant de requête de l’en-tête de réponse.

Filtrez les éléments pour une seule opération à l′aide d′operation_ParentId.

L′entrée du journal de suivi des plug-ins correspondante ressemble à ce qui suit :

Start execution of AccountPostOperation
Callback
Start Task Creation
Task creation completed
Outbound call started
<!doctype html><html
Outbound call ended successfully 

Les lignes retournées dans Application Insights n’affichent pas les informations que vous avez définies à l’aide de la méthode BeginScope. Ces données sont définies dans le customDimensions des journaux ajoutés dans cette portée. Utilisez cette requête pour afficher les journaux d’activité dans l’étendue.

Cette requête limite les résultats aux journaux ajoutés dans la portée Callback.

La requête limite les résultats aux entrées de journal ajoutées pendant la portée de rappel.

Cette requête limite les résultats aux logs ajoutés dans la portée OutboundCall :

requête limite les résultats aux logs ajoutés dans le contexte OutboundCall.

Journalisation des exceptions

Sous l′exemple de code de plug-in précédent, le code suivant utilise LogError pour enregistrer une exception détectée et renvoie une exception InvalidPluginExecutionException :

catch (Exception e)
{
    string errMsg = "Plugin failed";
    logger.LogError(e, errMsg);
    tracingService.Trace($"{errMsg}:{e.Message}");
    throw new InvalidPluginExecutionException(e.Message, e);
}

À l’aide du code de plug-in précédent, vous pouvez provoquer une exception en passant une valeur non valide aux données de configuration d’inscription pas à pas. Dans cet exemple, la valeur est NOT_A_URL.

Provoque une erreur en entrant une valeur de configuration non valide dans l′enregistrement de l′étape du plug-in.

Cette valeur remplace la valeur par défaut (https://www.bing.com) et provoque l’échec de l’appel sortant.

Il n′y a rien d′anormal dans la requête qu′un client peut envoyer :

POST [Organization URI]/api/data/v9.1/accounts HTTP/1.1
Prefer: odata.include-annotations="*"
Authorization: Bearer [REDACTED]
Content-Type: application/json

{
  "name":"Test account"
}

Toutefois, en raison de l’inscription incorrecte de l’étape de plug-in, la réponse retourne l’erreur suivante avec tous les détails lorsque l’en-tête Prefer: odata.include-annotations="*" est utilisé :

HTTP/1.1 400 Bad Request
Content-Type: application/json; odata.metadata=minimal
x-ms-service-request-id: 8fd35fd6-5329-4bd5-a1b7-757f91822322
REQ_ID: 8fd35fd6-5329-4bd5-a1b7-757f91822322
OData-Version: 4.0
Date: Sat, 24 Apr 2021 18:24:46 GMT

{
    "error": {
        "code": "0x80040265",
        "message": "An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set.",
        "@Microsoft.PowerApps.CDS.ErrorDetails.OperationStatus": "0",
        "@Microsoft.PowerApps.CDS.ErrorDetails.SubErrorCode": "-2146233088",
        "@Microsoft.PowerApps.CDS.HelpLink": "http://go.microsoft.com/fwlink/?LinkID=398563&error=Microsoft.Crm.CrmException%3a80040265&client=platform",
        "@Microsoft.PowerApps.CDS.TraceText": "\r\n[ILoggerExample: ILoggerExample.AccountPostOperation]\r\n[2ee952aa-90a4-eb11-b1ac-000d3a8f6891: ILoggerExample.AccountPostOperation: Create of account]\r\n\r\n\t\r\n\tStart execution of AccountPostOperation\r\n\tCallback\r\n\tStart Task Creation\r\n\tTask creation completed\r\n\tOutbound call started\r\n\tPlugin failed:An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set.\r\n\t\r\n",
        "@Microsoft.PowerApps.CDS.InnerError.Message": "An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set."
    }
}

Le journal de suivi des plug-ins contient ces données d′exception, qui incluent les données ExceptionDetails.

Exception type: System.ServiceModel.FaultException`1[Microsoft.Xrm.Sdk.OrganizationServiceFault]
Message: An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set.Detail: 
<OrganizationServiceFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
  <ActivityId>09bf305c-8272-4fc4-801b-479280cb3069</ActivityId>
  <ErrorCode>-2147220891</ErrorCode>
  <ErrorDetails xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
    <KeyValuePairOfstringanyType>
      <d2p1:key>OperationStatus</d2p1:key>
      <d2p1:value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:int">0</d2p1:value>
    </KeyValuePairOfstringanyType>
    <KeyValuePairOfstringanyType>
      <d2p1:key>SubErrorCode</d2p1:key>
      <d2p1:value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:int">-2146233088</d2p1:value>
    </KeyValuePairOfstringanyType>
  </ErrorDetails>
  <HelpLink i:nil="true" />
  <Message>An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set.</Message>
  <Timestamp>2021-04-24T18:24:46.4900727Z</Timestamp>
  <ExceptionRetriable>false</ExceptionRetriable>
  <ExceptionSource>PluginExecution</ExceptionSource>
  <InnerFault i:nil="true" />
  <OriginalException>PluginExecution</OriginalException>
  <TraceText>
Start execution of AccountPostOperation
Callback
Start Task Creation
Task creation completed
Outbound call started
Plugin failed:An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set.
</TraceText>
</OrganizationServiceFault>

Dans Application Insights, si vous affichez les suivis limités à cette requête et avec l′étendue définie sur OutboundCall comme indiqué précédemment, vous observez que la seule entrée indique que l′appel sortant a commencé.

Afficher les traces associées à cette requête et dont la portée est définie sur OutboundCall.

Dans Application Insights, si vous modifiez votre requête pour utiliser exceptions plutôt que traces, trois exceptions sont consignées :

Modifiez votre requête pour utiliser des exceptions plutôt que des traces.

Celle où cloud_RoleInstance est égal à SandboxRoleInstance est celle qui a été écrite en raison de la méthode LogError d’ILogger. Les deux autres exceptions correspondent aux différents emplacements où l′erreur a été journalisée sur le serveur.

Remarque

SandboxRoleInstance client_Type est PC. Cette valeur est due au fait que le plug-in s’exécute dans un bac à sable isolé en tant que client plutôt que sur le serveur.

Vous pouvez vous concentrer sur le journal des erreurs écrit par votre code en filtrant sur cloud_RoleInstance :

Concentrez-vous sur le journal des erreurs généré par votre code en filtrant sur cloud_RoleInstance.

Le texte du message formaté est capturé dans customDimensions.

Voir aussi

Analyser les applications pilotées par modèle et la télémétrie Microsoft Dataverse avec Application Insights
Plug-ins
Déboguer un plug-in
Afficher les journaux de suivi
Service de traçage
Table PluginTraceLog