Gravar telemetria em seu recurso do Application Insights usando ILogger

Important

Para usar essa funcionalidade, primeiro você deve habilitar o recurso de integração do Application Insights usando uma conta de administrador. Certifique-se de que o usuário que habilita o recurso tenha os privilégios necessários para modificar a organização do Dataverse (como a função administrador do sistema ou ser um administrador do Power Platform/Dynamics 365) e tenha acesso de colaborador ao recurso do Application Insights. Se um usuário sem as permissões necessárias habilitar a integração, os dados de telemetria não serão gravados no Application Insights. Para obter mais informações, consulte Analisar aplicativos controlados por modelos e Microsoft Dataverse telemetria com o Application Insights.

No momento, não há suporte para ILogger durante uma sessão de criação de perfil ou depuração de plug-in na ferramenta Registro de Plug-in ou na extensão Power Platform Tools para Visual Studio.

Quando você habilita o Application Insights para sua organização, todos os plug-ins gravados usando a Interface ILogger fornecida no SDK para .NET assemblies gravam telemetria em seu recurso do Application Insights.

A plataforma Dataverse captura os dados de telemetria do Dataverse e de aplicativos baseados em modelo e os exporta para o recurso do Application Insights. Há alguma latência entre o momento em que ele foi capturado e quando ele fica disponível para você no Application Insights. Como Microsoft coleta essa telemetria, você não precisa escrever nenhum código para habilitá-la.

Os dados de telemetria provenientes de plug-ins usando a interface ILogger são diferentes de duas maneiras:

  • Essa telemetria é gravada diretamente no recurso do Application Insights e nunca é enviada para Microsoft.
    • Há menos latência ao exibir esses dados.
  • Você deve atualizar o código do plug-in para usar a interface ILogger.

O uso do ILogger fornece dados de telemetria verdadeiros e destina-se a trabalhar em conjunto com os logs de rastreamento de plug-in existentes escritos usando a interface ITracingService. A tabela a seguir compara os recursos:

Criteria ILogger para Application Insights Rastreamento do ITracingService nos logs de rastreamento de plug-in
Utilização pretendida Capture a telemetria ao longo do tempo para análise e depuração. Ao desenvolver e depurar plug-ins
Quanto tempo os dados são armazenados De acordo com o período de retenção de dados do Application Insights, que é de 90 dias por padrão 24 horas
Available Somente para organizações que assinam a integração do Application Insights. Disponível para qualquer organização quando o rastreamento de plug-in está habilitado.
Quantidade de dados Cada mensagem de log pode passar um valor de cadeia de caracteres. Somente 10 kb de texto podem ser gravados para cada execução de plug-in. O texto é truncado após os primeiros 10 kb.
Disponível em erros de runtime No Disponível nos erros do cliente de aplicativos orientados por modelo e como anotações na API Web. Para obter mais informações, consulte Incluir mais detalhes sobre os erros.

Você deve continuar a usar o ITracingService.Trace para gravar na tabela Log de Rastreamento do Plug-in quando necessário. Nem todas as organizações habilitam o Application Insights. Se o código do plug-in usar a interface ILogger e a organização não tiver a integração do Application Insights habilitada, nada será gravado. Portanto, é importante continuar usando o método Trace do ITracingService nos seus plug-ins. Os logs de rastreamento de plug-ins continuam sendo uma forma importante de capturar dados durante o desenvolvimento e a depuração de plug-ins, mas nunca tiveram a finalidade de fornecer dados de telemetria. Para obter mais informações, consulte Plug-ins: Rastreamento e registro em log.

Você deve usar o ILogger porque ele fornece telemetria sobre o que acontece dentro de um plug-in. Essa telemetria é integrada ao escopo maior dos dados capturados com a integração do Application Insights. A integração do Application Insights informa quando um plug-in é executado, quanto tempo leva para ser executado e se ele faz solicitações http externas, mas Microsoft não pode adicionar nenhum código de telemetria nos plug-ins que você grava para estender o comportamento da plataforma.

Se você é um ISV com um produto que inclui plug-ins, os clientes que permitem o Application Insights apreciam poder ver o que está acontecendo em seus plug-ins e esses dados podem ajudá-lo a dar suporte a eles se houver problemas. Mas os dados capturados usando o ILogger só são enviados para o recurso do cliente inscrito. Você só poderá ver os dados capturados para seus próprios ambientes quando tiver o Application Insights habilitado.

Use ILogger

ILogger é uma interface comum para capturar informações de log. A implementação fornecida com o SDK para assemblies do .NET oferece métodos comuns para oferecer suporte à definição de um escopo e a diferentes níveis de log. No momento, não há nenhuma configuração para controlar qual nível de logs são gravados. Use os níveis no Application Insights para filtrar os logs a serem exibidos.

O plug-in de exemplo a seguir mostra o uso de ILogger e ITracingService.Trace.

Note

Certifique-se de incluir using Microsoft.Xrm.Sdk.PluginTelemetry;. Não use using Microsoft.Extensions.Logging;, caso contrário, a ILogger instância é nula.

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

Ao registrar esse plug-in em uma etapa síncrona PostOperation para o Create de uma entidade account, você pode usar os Logs do Application Insights para visualizar a saída dentro de alguns minutos. Use a Linguagem de Consulta Kusto (KQL) para consultar os resultados.

Filtre itens para uma única operação usando o operation_ParentId, que representa o ID da solicitação no cabeçalho da resposta.

Filtre itens para uma única operação usando o operation_ParentId.

A entrada correspondente no log de rastreamento do plug-in se parece com isto:

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

As linhas retornadas no Application Insights não mostram as informações definidas usando o Método BeginScope. Esses dados são definidos no customDimensions dos registros adicionados nesse escopo. Use essa consulta para mostrar os logs dentro do escopo.

Essa consulta limita os resultados aos logs adicionados durante o escopo Callback.

A consulta limita os resultados aos logs adicionados durante o escopo de Callback.

Essa consulta limita os resultados aos logs adicionados durante o OutboundCall escopo:

A consulta limita os resultados aos registros adicionados no escopo OutboundCall.

Registro em log de exceções

Na parte inferior do exemplo de código de plug-in anterior, o código a seguir usa LogError para registrar uma exceção capturada e lança um InvalidPluginExecutionException:

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

Usando o código de plug-in anterior, você pode causar uma exceção passando um valor inválido para os dados de configuração de registro de etapa. Neste exemplo, o valor é NOT_A_URL.

Causando um erro inserindo o valor de configuração inválido no registro da etapa de plug-in.

Esse valor substitui o valor padrão (https://www.bing.com) e faz com que a chamada de saída falhe.

Não há nada de errado com a solicitação que um cliente pode enviar:

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

Mas devido ao registro incorreto da etapa de plug-in, a resposta retorna o seguinte erro com todos os detalhes quando o Prefer: odata.include-annotations="*" cabeçalho é usado:

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

O Log de Rastreamento do Plug-in contém esses dados de exceção, que incluem os ExceptionDetails dados.

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>

No Application Insights, se você exibir os rastreamentos no escopo desta solicitação e com o escopo definido para OutboundCall, como mostrado anteriormente, poderá ver que a única entrada indica que a chamada de saída foi iniciada.

Exibir rastreamentos limitados a esta solicitação e com o escopo definido como OutboundCall.

No Application Insights, ao alternar sua consulta para usar exceptions em vez de traces, você verá três exceções registradas:

Alterne sua consulta para usar exceções em vez de rastreamentos.

Aquele em que cloud_RoleInstance é igual a SandboxRoleInstance é aquele que foi escrito devido ao método ILogger LogError. Os outros dois representam locais diferentes em que o erro foi registrado no servidor.

Note

O SandboxRoleInstance client_Type é PC. Esse valor ocorre porque o plug-in é executado em uma área restrita isolada como um cliente e não no servidor.

Você pode se concentrar no log de erros gerado pelo seu código filtrando por cloud_RoleInstance:

Concentre-se no log de erros gerado pelo seu código filtrando por cloud_RoleInstance.

O texto da mensagem formatada é capturado como parte do customDimensions.

Consulte também

Analisar aplicativos controlados por modelos e Microsoft Dataverse telemetria com o Application Insights
Plug-ins
Depurar um plug-in
Exibir logs de rastreamento
Serviço de rastreamento
Tabela PluginTraceLog