Compartilhar via


Lidando com exceções e falhas

Exceções são usadas para comunicar erros localmente dentro do serviço ou da implementação do cliente. As falhas, por outro lado, são usadas para comunicar erros entre os limites de serviço, como do servidor para o cliente ou vice-versa. Além das falhas, os canais de transporte geralmente usam mecanismos específicos de transporte para comunicar erros no nível do transporte. Por exemplo, o transporte HTTP usa códigos de status como 404 para comunicar uma URL de ponto de extremidade não existente (não há ponto de extremidade para enviar uma falha de volta). Este documento consiste em três seções que fornecem diretrizes para autores de canais personalizados. A primeira seção fornece diretrizes sobre quando e como definir e gerar exceções. A segunda seção fornece diretrizes sobre como gerar e consumir falhas. A terceira seção explica como fornecer informações de rastreamento para ajudar o usuário do canal personalizado na solução de problemas de aplicativos em execução.

Exceções

Há duas coisas a ter em mente ao lançar uma exceção: primeiro, ela precisa ser de um tipo que permita que os usuários escrevam o código correto que possa reagir adequadamente à exceção. Em segundo lugar, ele precisa fornecer informações suficientes para que o usuário entenda o que deu errado, o impacto da falha e como corrigi-lo. As seções a seguir fornecem diretrizes sobre tipos de exceção e mensagens para canais do WCF (Windows Communication Foundation). Também há diretrizes gerais sobre exceções no .NET no documento Diretrizes de design para exceções.

Tipos de exceção

Todas as exceções geradas por canais devem ser System.TimeoutException, System.ServiceModel.CommunicationExceptionou um tipo derivado de CommunicationException. (Exceções como ObjectDisposedException também podem ser geradas, mas apenas para indicar que o código de chamada usou indevidamente o canal. Se um canal for usado corretamente, ele só deverá gerar as exceções fornecidas.) O WCF fornece sete tipos de exceção que derivam de CommunicationException e foram projetados para serem usados por canais. Há outras exceções derivadas de CommunicationException que foram projetadas para serem usadas por outras partes do sistema. Esses tipos de exceção são:

Tipo de exceção Significado Conteúdo de exceção interna Estratégia de recuperação
AddressAlreadyInUseException O endereço do ponto de extremidade especificado para escuta já está em uso. Se estiver presente, fornecerá mais detalhes sobre o erro de transporte que causou essa exceção. Por exemplo. PipeException, HttpListenerException ou SocketException. Experimente um endereço diferente.
AddressAccessDeniedException O processo não tem permissão para acessar o endereço do ponto de extremidade especificado para escuta. Se estiver presente, fornecerá mais detalhes sobre o erro de transporte que causou essa exceção. Por exemplo, PipeException, ou HttpListenerException. Experimente credenciais diferentes.
CommunicationObjectFaultedException O ICommunicationObject usado está no estado Com falha (para saber mais, confira Noções básicas sobre alterações de estado). Observe que quando um objeto com várias chamadas pendentes faz a transição para o estado Com falha, apenas uma chamada gera uma exceção relacionada à falha e o restante das chamadas gera uma CommunicationObjectFaultedException. Normalmente, essa exceção é gerada porque um aplicativo ignora alguma exceção e tenta usar um objeto já com falha, possivelmente em um thread diferente daquele que pegou a exceção original. Se ela estiver presente, fornecerá detalhes sobre a exceção interna. Crie um novo objeto. Observe que, dependendo do que causou a falha de ICommunicationObject em primeiro lugar, poderá haver a necessidade de recuperar outro trabalho.
CommunicationObjectAbortedException O ICommunicationObject usado foi Anulado (para saber mais, confira Noções básicas sobre alterações de estado). Semelhante a CommunicationObjectFaultedException, essa exceção indica que o aplicativo chamou Abort no objeto, possivelmente de outro thread, e o objeto não é mais utilizável por esse motivo. Se ela estiver presente, fornecerá detalhes sobre a exceção interna. Crie um novo objeto. Observe que, dependendo do que causou a anulação de ICommunicationObject em primeiro lugar, poderá haver a necessidade de recuperar outro trabalho.
EndpointNotFoundException O ponto de extremidade remoto de destino não está escutando. Isso pode ser resultado de alguma parte do endereço do ponto de extremidade incorreta, irresolvível ou da inoperação do ponto de extremidade. Os exemplos incluem erro DNS, Gerenciador de Filas não disponível e serviço sem execução. A exceção interna fornece detalhes, normalmente do transporte subjacente. Experimente um endereço diferente. Como alternativa, o remetente pode aguardar um pouco e tentar novamente caso o serviço esteja inativo
ProtocolException Os protocolos de comunicação, conforme descrito pela política do ponto de extremidade, são incompatíveis entre os pontos de extremidade. Por exemplo, incompatibilidade de enquadramento de tipo de conteúdo ou tamanho máximo de mensagem excedido. Se estiver presente, fornecerá mais informações sobre o erro de protocolo específico. Por exemplo, QuotaExceededException é a exceção interna quando a causa do erro está excedendo MaxReceivedMessageSize. Recuperação: verifique se as configurações de protocolo do remetente e do destinatário correspondem. Uma maneira de fazer isso é importar novamente os metadados (política) do ponto de extremidade de serviço e usar a associação gerada para recriar o canal.
ServerTooBusyException O ponto de extremidade remoto está escutando, mas não está preparado para processar mensagens. Se estiver presente, a Exceção interna fornecerá os detalhes da falha SOAP ou do erro no nível do transporte. Recuperação: aguarde e repita a operação mais tarde.
TimeoutException A operação não foi concluída dentro do período de tempo limite. Pode fornecer detalhes sobre o tempo limite. Aguarde e repita a operação mais tarde.

Defina um novo tipo de exceção somente se esse tipo corresponder a uma estratégia de recuperação específica diferente de todos os tipos de exceção existentes. Se você definir um novo tipo de exceção, ele deverá derivar de CommunicationException ou de uma de suas classes derivadas.

Mensagens de Exceção

As mensagens de exceção são direcionadas ao usuário e não ao programa, portanto, elas devem fornecer informações suficientes para ajudar o usuário a entender e resolver o problema. As três partes essenciais de uma boa mensagem de exceção são:

o que aconteceu. Forneça uma descrição clara do problema usando termos relacionados à experiência do usuário. Por exemplo, uma mensagem de exceção incorreta seria "Seção de configuração inválida". Isso deixa o usuário sem saber qual é a seção de configuração incorreta e por que ela está incorreta. Uma mensagem aprimorada seria "<customBinding> de seção de configuração inválido". Uma mensagem ainda melhor seria "Não é possível adicionar o transporte chamado myTransport à associação chamada myBinding porque a associação já tem um transporte chamado myTransport". Essa é uma mensagem muito específica usando termos e nomes que o usuário pode identificar facilmente no arquivo de configuração do aplicativo. No entanto, ainda há alguns componentes principais ausentes.

A significância do erro. A menos que a mensagem declare claramente o que significa o erro, é provável que o usuário se pergunte se é um erro fatal ou se pode ser ignorado. Em geral, as mensagens devem começar com o significado do erro. Para melhorar o exemplo anterior, a mensagem pode ser "Falha de abertura do ServiceHost devido a um erro de configuração: não é possível adicionar o transporte chamado myTransport à associação chamada myBinding porque a associação já tem um transporte chamado myTransport".

Como o usuário deve corrigir o problema. A parte mais importante da mensagem serve para ajudar o usuário a corrigir o problema. A mensagem deve incluir algumas diretrizes ou dicas sobre o que verificar ou corrigir para resolver o problema. Por exemplo, "Falha de abertura do ServiceHost devido a um erro de configuração: não é possível adicionar o transporte chamado myTransport à associação chamada myBinding porque a associação já tem um transporte chamado myTransport. Verifique se há apenas um transporte na associação".

Comunicação de falhas

SOAP 1.1 e SOAP 1.2 definem uma estrutura específica para falhas. Há algumas diferenças entre as duas especificações, mas, em geral, os tipos Message e MessageFault são usados para criar e consumir falhas.

SOAP 1.2 Fault and SOAP 1.1 Fault
Falha SOAP 1.2 (esquerda) e Falha SOAP 1.1 (direita). No SOAP 1.1, somente o elemento Fault é qualificado como namespace.

SOAP define uma mensagem de falha como uma mensagem que contém apenas um elemento de falha (um elemento cujo nome é <env:Fault>) como um filho de <env:Body>. O conteúdo do elemento de falha difere ligeiramente entre SOAP 1.1 e SOAP 1.2, conforme mostrado na figura 1. No entanto, a classe System.ServiceModel.Channels.MessageFault normaliza essas diferenças em um modelo de objeto:

public abstract class MessageFault  
{  
    protected MessageFault();  
  
    public virtual string Actor { get; }  
    public virtual string Node { get; }  
    public static string DefaultAction { get; }  
    public abstract FaultCode Code { get; }  
    public abstract bool HasDetail { get; }  
    public abstract FaultReason Reason { get; }  
  
    public T GetDetail<T>();  
    public T GetDetail<T>( XmlObjectSerializer serializer);  
    public System.Xml.XmlDictionaryReader GetReaderAtDetailContents();  
  
    // other methods omitted  
}  

A propriedade Code corresponde ao env:Code (ou faultCode em SOAP 1.1) e identifica o tipo da falha. SOAP 1.2 define cinco valores permitidos para faultCode (por exemplo, Remetente e Receptor) e define um elemento Subcode que pode conter qualquer valor de subcódigo. (Confira a especificação SOAP 1.2 para obter a lista de códigos de falha permitidos e seus significados.) SOAP 1.1 tem um mecanismo ligeiramente diferente: ele define quatro valores faultCode (por exemplo, Cliente e Servidor) que podem ser estendidos definindo outros totalmente novos ou usando a notação de ponto para criar faultCodes mais específicos, por exemplo, Client.Authentication.

Quando você usa MessageFault para programar falhas, o FaultCode.Name e FaultCode.Namespace mapeia para o nome e o namespace do SOAP 1.2 env:Code ou SOAP 1.1 faultCode. O FaultCode.SubCode mapeia para env:Subcode SOAP 1.2 e é nulo para SOAP 1.1.

Você deve criar novos subcódigos de falha (ou novos códigos de falha se estiver usando SOAP 1.1) se for interessante distinguir programaticamente uma falha. Isso é análogo à criação de um novo tipo de exceção. Você deve evitar usar a notação de ponto com códigos de falha SOAP 1.1. (O perfil básico do WS-I também desencoraja o uso da notação de ponto de código de falha.)

public class FaultCode  
{  
    public FaultCode(string name);  
    public FaultCode(string name, FaultCode subCode);  
    public FaultCode(string name, string ns);  
    public FaultCode(string name, string ns, FaultCode subCode);  
  
    public bool IsPredefinedFault { get; }  
    public bool IsReceiverFault { get; }  
    public bool IsSenderFault { get; }  
    public string Name { get; }  
    public string Namespace { get; }  
    public FaultCode SubCode { get; }  
  
//  methods omitted  
  
}  

A propriedade Reason corresponde ao env:Reason (ou ao faultString no SOAP 1.1), uma descrição legível da condição de erro análoga à mensagem de uma exceção. A classe FaultReason (e SOAP env:Reason/faultString) tem suporte interno para várias traduções em nome da globalização.

public class FaultReason  
{  
    public FaultReason(FaultReasonText translation);  
    public FaultReason(IEnumerable<FaultReasonText> translations);  
    public FaultReason(string text);  
  
    public SynchronizedReadOnlyCollection<FaultReasonText> Translations
    {
       get;
    }  
  
 }  

O conteúdo de detalhes da falha é exposto em MessageFault usando vários métodos, incluindo GetDetail<T> e GetReaderAtDetailContents(). O detalhe da falha é um elemento opaco para carregar detalhes adicionais sobre a falha. Isso será útil se houver alguns detalhes arbitrários estruturados que você deseja carregar com a falha.

Geração de falhas

Esta seção explica o processo de geração de uma falha em resposta a uma condição de erro detectada em um canal ou em uma propriedade de mensagem criada pelo canal. Um exemplo típico é o envio de volta de uma falha em resposta a uma mensagem de solicitação que contém dados inválidos.

Ao gerar uma falha, o canal personalizado não deve enviar a falha diretamente, em vez disso, ele deve lançar uma exceção e deixar que a camada acima decida se deve converter essa exceção em uma falha e como enviá-la. Para auxiliar nessa conversão, o canal deve fornecer uma implementação FaultConverter que possa converter a exceção gerada pelo canal personalizado na falha apropriada. FaultConverter é definido como:

public class FaultConverter  
{  
    public static FaultConverter GetDefaultFaultConverter(  
                                   MessageVersion version);  
    protected abstract bool OnTryCreateFaultMessage(  
                                   Exception exception,
                                   out Message message);  
    public bool TryCreateFaultMessage(  
                                   Exception exception,
                                   out Message message);  
}  

Cada canal que gera falhas personalizadas deve implementar FaultConverter e retorná-lo de uma chamada a GetProperty<FaultConverter>. A implementação de OnTryCreateFaultMessage personalizada deve converter a exceção em uma falha ou delegar para o FaultConverter do canal interno. Se o canal for um transporte, ele deverá converter a exceção ou delegar para o FaultConverter do codificador ou o FaultConverter padrão fornecido no WCF. O FaultConverter padrão converte erros correspondentes a mensagens de falha especificadas por WS-Addressing e SOAP. Confira um exemplo de implementação de OnTryCreateFaultMessage.

public override bool OnTryCreateFaultMessage(Exception exception,
                                             out Message message)  
{  
    if (exception is ...)  
    {  
        message = ...;  
        return true;  
    }  
  
#if IMPLEMENTING_TRANSPORT_CHANNEL  
    FaultConverter encoderConverter =
                    this.encoder.GetProperty<FaultConverter>();  
    if ((encoderConverter != null) &&
        (encoderConverter.TryCreateFaultMessage(  
         exception, out message)))  
    {  
        return true;  
    }  
  
    FaultConverter defaultConverter =
                   FaultConverter.GetDefaultFaultConverter(  
                   this.channel.messageVersion);  
    return defaultConverter.TryCreateFaultMessage(  
                   exception,
                   out message);  
#else  
    FaultConverter inner =
                   this.innerChannel.GetProperty<FaultConverter>();  
    if (inner != null)  
    {  
        return inner.TryCreateFaultMessage(exception, out message);  
    }  
    else  
    {  
        message = null;  
        return false;  
    }  
#endif  
}  

Uma implicação desse padrão é que as exceções geradas entre camadas para condições de erro que exigem falhas devem conter informações suficientes para que o gerador de falhas correspondente crie a falha correta. Como autor de canal personalizado, você pode definir tipos de exceção que correspondem a diferentes condições de falha se essas exceções ainda não existirem. Observe que as exceções que atravessam camadas de canal devem comunicar a condição de erro em vez de dados de falha opacos.

Categorias de falha

Geralmente, há três categorias de falhas:

  1. Falhas que são generalizadas em toda a pilha. Essas falhas podem ser encontradas em qualquer camada na pilha de canais, por exemplo InvalidCardinalityAddressingException.

  2. Falhas que podem ser encontradas em qualquer lugar acima de uma determinada camada na pilha, por exemplo, alguns erros que pertencem a uma transação fluida ou a funções de segurança.

  3. Falhas direcionadas a uma única camada na pilha, por exemplo, erros como falhas de número de sequência do WS-RM.

Categoria 1. Geralmente, as falhas são WS-Addressing e SOAP. A classe base FaultConverter fornecida pelo WCF converte erros correspondentes a mensagens de falha especificadas por WS-Addressing e SOAP para que você não precise lidar com a conversão dessas exceções por conta própria.

Categoria 2. As falhas ocorrem quando uma camada adiciona uma propriedade à mensagem que não consome completamente as informações de mensagem que pertencem a essa camada. Erros podem ser detectados posteriormente quando uma camada superior solicita que a propriedade da mensagem processe ainda mais as informações da mensagem. Esses canais devem implementar o GetProperty especificado anteriormente para permitir que a camada superior envie de volta a falha correta. Um exemplo disso é TransactionMessageProperty. Essa propriedade é adicionada à mensagem sem validar totalmente todos os dados no cabeçalho (isso pode envolver o contato com o DTC [coordenador de transação distribuída]).

Categoria 3. As falhas são geradas e enviadas apenas por uma única camada no processador. Portanto, todas as exceções estão contidas na camada. Para melhorar a consistência entre canais e facilitar a manutenção, seu canal personalizado deve usar o padrão especificado anteriormente para gerar mensagens de falha mesmo para falhas internas.

Interpretação das falhas recebidas

Esta seção fornece diretrizes para gerar a exceção apropriada ao receber uma mensagem de falha. A árvore de decisões para processar uma mensagem em cada camada da pilha é a seguinte:

  1. Se a camada considerar a mensagem inválida, a camada deverá fazer seu processamento de "mensagem inválida". Esse processamento é específico para a camada, mas pode incluir a remoção da mensagem, o rastreamento ou a geração de uma exceção que é convertida em falha. Entre os exemplos estão a segurança recebendo uma mensagem que não está protegida corretamente ou RM recebendo uma mensagem com um número de sequência inválido.

  2. Caso contrário, se a mensagem for uma mensagem de falha que se aplica especificamente à camada e não for significativa fora da interação da camada, a camada deverá lidar com a condição de erro. Um exemplo disso é uma falha Sequência RM Recusada sem sentido para camadas acima do canal RM e que implica falha no canal RM e geração de operações pendentes.

  3. Caso contrário, a mensagem deverá ser retornada de Request() ou Receive(). Isso inclui casos em que a camada reconhece a falha, mas a falha apenas indica que uma solicitação falhou e não implica falha no canal e geração de operações pendentes. Para melhorar a usabilidade nesse caso, a camada deve implementar GetProperty<FaultConverter> e retornar uma classe FaultConverter derivada que pode converter a falha em uma exceção substituindo OnTryCreateException.

O modelo de objeto a seguir dá suporte à conversão de mensagens em exceções:

public class FaultConverter  
{  
    public static FaultConverter GetDefaultFaultConverter(  
                                  MessageVersion version);  
    protected abstract bool OnTryCreateException(  
                                 Message message,
                                 MessageFault fault,
                                 out Exception exception);  
    public bool TryCreateException(  
                                 Message message,
                                 MessageFault fault,
                                 out Exception exception);  
}  

Uma camada de canal pode implementar GetProperty<FaultConverter> para dar suporte à conversão de mensagens de falha em exceções. Para fazer isso, substitua OnTryCreateException e inspecione a mensagem de falha. Se for reconhecida, faça a conversão; caso contrário, peça ao canal interno para convertê-la. Os canais de transporte devem delegar a FaultConverter.GetDefaultFaultConverter para obter o Padrão SOAP/WS-Addressing FaultConverter.

Uma implementação típica tem esta aparência:

public override bool OnTryCreateException(  
                            Message message,
                            MessageFault fault,
                            out Exception exception)  
{  
    if (message.Action == "...")  
    {  
        exception = ...;  
        return true;  
    }  
    // OR  
    if ((fault.Code.Name == "...") && (fault.Code.Namespace == "..."))  
    {  
        exception = ...;  
        return true;  
    }  
  
    if (fault.IsMustUnderstand)  
    {  
        if (fault.WasHeaderNotUnderstood(  
                   message.Headers, "...", "..."))  
        {  
            exception = new ProtocolException(...);  
            return true;  
        }  
    }  
  
#if IMPLEMENTING_TRANSPORT_CHANNEL  
    FaultConverter encoderConverter =
              this.encoder.GetProperty<FaultConverter>();  
    if ((encoderConverter != null) &&
        (encoderConverter.TryCreateException(  
                              message, fault, out exception)))  
    {  
        return true;  
    }  
  
    FaultConverter defaultConverter =  
             FaultConverter.GetDefaultFaultConverter(  
                             this.channel.messageVersion);  
    return defaultConverter.TryCreateException(  
                             message, fault, out exception);  
#else  
    FaultConverter inner =
                    this.innerChannel.GetProperty<FaultConverter>();  
    if (inner != null)  
    {  
        return inner.TryCreateException(message, fault, out exception);  
    }  
    else  
    {  
        exception = null;  
        return false;  
    }  
#endif  
}  

Para condições de falha específicas que têm cenários de recuperação distintos, considere definir uma classe derivada de ProtocolException.

Processamento MustUnderstand

SOAP define uma falha geral para sinalizar que um cabeçalho necessário não foi compreendido pelo receptor. Essa falha é conhecida como falha mustUnderstand. No WCF, os canais personalizados nunca geram falhas mustUnderstand. Em vez disso, o Dispatcher do WCF, que está localizado na parte superior da pilha de comunicação do WCF, verifica se todos os cabeçalhos marcados como MustUnderstand=true foram compreendidos pela pilha subjacente. Se alguma não tiver sido compreendida, uma mustUnderstand falha será gerada nesse ponto. (O usuário pode optar por desativar esse processamento mustUnderstand e fazer com que o aplicativo receba todos os cabeçalhos de mensagem. Nesse caso, o aplicativo é responsável por executar o processamento de mustUnderstand.) A falha gerada inclui um cabeçalho NotUnderstood que contém os nomes de todos os cabeçalhos com MustUnderstand=true que não foram compreendidos.

Se o canal de protocolo enviar um cabeçalho personalizado com MustUnderstand=true e receber uma falha mustUnderstand, ele deverá descobrir se essa falha é devido ao cabeçalho enviado. Há dois membros na classe MessageFault que são úteis para isso:

public class MessageFault  
{  
    ...  
    public bool IsMustUnderstandFault { get; }  
    public static bool WasHeaderNotUnderstood(MessageHeaders headers,
        string name, string ns) { }  
    ...  
  
}  

IsMustUnderstandFault retornará true se a falha for uma falha mustUnderstand. WasHeaderNotUnderstood retornará true se o cabeçalho com o nome e o namespace especificados estiver incluído na falha como um cabeçalho NotUnderstood. Caso contrário, ele retornará false.

Se um canal emitir um cabeçalho marcado como MustUnderstand = true, essa camada também deverá implementar o padrão de API de Geração de Exceção e deve converter as falhas mustUnderstand causadas por esse cabeçalho em uma exceção mais útil, conforme descrito anteriormente.

Rastreamento

O .NET Framework fornece um mecanismo para rastrear a execução do programa como uma maneira de ajudar a diagnosticar problemas com aplicativos de produção ou intermitentes em que não é possível apenas anexar um depurador e percorrer o código. Os principais componentes desse mecanismo estão no namespace System.Diagnostics e consistem em:

Tracing components

Rastreamento de um canal personalizado

Os canais personalizados devem gravar mensagens de rastreamento para ajudar a diagnosticar problemas quando não for possível anexar um depurador ao aplicativo em execução. Isso envolve duas tarefas de alto nível: criar uma instância de um TraceSource e chamar seus métodos para gravar rastreamentos.

Ao instanciar um TraceSource, a cadeia de caracteres especificada se torna o nome dessa origem. Esse nome é usado para configurar (habilitar/desabilitar/definir o nível de rastreamento) a origem do rastreamento. Ele também aparece na própria saída do rastreamento. Os canais personalizados devem usar um nome de origem exclusivo para ajudar os leitores da saída de rastreamento a entender de onde vêm as informações de rastreamento. Usar o nome do assembly que está gravando as informações como o nome da origem do rastreamento é a prática comum. Por exemplo, o WCF usa System.ServiceModel como a fonte de rastreamento para obter informações gravadas do assembly System.ServiceModel.

Assim que você tiver uma fonte de rastreamento, chame seus métodos TraceData, TraceEvent ou TraceInformation para gravar entradas de rastreamento nos ouvintes de rastreamento. Para cada entrada de rastreamento que você escreve, precisa classificar o tipo de evento como um dos tipos de evento definidos em TraceEventType. Essa classificação e a configuração de nível de rastreamento na configuração determinam se a entrada de rastreamento é a saída para o ouvinte. Por exemplo, definir o nível de rastreamento na configuração para Warning permite que as entradas de rastreamento Warning, Error e Critical sejam gravadas, mas bloqueia informações e entradas detalhadas. Confira um exemplo de instanciação de uma fonte de rastreamento e gravação de uma entrada no nível de Informações:

using System.Diagnostics;  
//...  
TraceSource udpSource = new TraceSource("Microsoft.Samples.Udp");  
//...  
udpsource.TraceInformation("UdpInputChannel received a message");  

Importante

É altamente recomendável que você especifique um nome de origem de rastreamento exclusivo para seu canal personalizado para ajudar os leitores da saída de rastreamento a entender de onde veio a saída.

Integração com o Visualizador de Rastreamento

Os rastreamentos gerados pelo canal podem ser mostrados em um formato legível pela Ferramenta visualizador de rastreamento de serviço (SvcTraceViewer.exe) usando System.Diagnostics.XmlWriterTraceListener como ouvinte de rastreamento. Isso não é algo que você, como desenvolvedor do canal, precisa fazer. Em vez disso, é o usuário do aplicativo (ou a pessoa que está solucionando problemas do aplicativo) que precisa configurar esse ouvinte de rastreamento no arquivo de configuração do aplicativo. Por exemplo, a configuração a seguir gera informações de rastreamento de System.ServiceModel e Microsoft.Samples.Udp para o arquivo chamado TraceEventsFile.e2e:

<configuration>  
  <system.diagnostics>  
    <sources>  
      <!-- configure System.ServiceModel trace source -->  
      <source name="System.ServiceModel" switchValue="Verbose"
              propagateActivity="true">  
        <listeners>  
          <add name="e2e" />  
        </listeners>  
      </source>  
      <!-- configure Microsoft.Samples.Udp trace source -->  
      <source name="Microsoft.Samples.Udp" switchValue="Verbose" >  
        <listeners>  
          <add name="e2e" />  
        </listeners>  
      </source>  
    </sources>  
    <!--   
    Define a shared trace listener that outputs to TraceFile.e2e  
    The listener name is e2e   
    -->  
    <sharedListeners>  
      <add name="e2e" type="System.Diagnostics.XmlWriterTraceListener"  
        initializeData=".\TraceFile.e2e"/>  
    </sharedListeners>  
    <trace autoflush="true" />  
  </system.diagnostics>  
</configuration>  

Rastreamento de dados estruturados

System.Diagnostics.TraceSource tem um método TraceData que usa um ou mais objetos que devem ser incluídos na entrada de rastreamento. Em geral, o método Object.ToString é chamado em cada objeto e a cadeia de caracteres resultante é gravada como parte da entrada de rastreamento. Ao usar System.Diagnostics.XmlWriterTraceListener para gerar rastreamentos, você pode passar um System.Xml.XPath.IXPathNavigable como o objeto de dados para TraceData. A entrada de rastreamento resultante inclui o XML fornecido pelo System.Xml.XPath.XPathNavigator. Confira um exemplo de entrada com dados de aplicativo XML:

<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">  
  <System xmlns="...">  
    <EventID>12</EventID>  
    <Type>3</Type>  
    <SubType Name="Information">0</SubType>  
    <Level>8</Level>  
    <TimeCreated SystemTime="2006-01-13T22:58:03.0654832Z" />  
    <Source Name="Microsoft.ServiceModel.Samples.Udp" />  
    <Correlation ActivityID="{00000000-0000-0000-0000-000000000000}" />  
    <Execution  ProcessName="UdpTestConsole"
                ProcessID="3348" ThreadID="4" />  
    <Channel />  
    <Computer>COMPUTER-LT01</Computer>  
  </System>  
<!-- XML application data -->  
  <ApplicationData>  
  <TraceData>  
   <DataItem>  
   <TraceRecord
     Severity="Information"  
     xmlns="…">  
        <TraceIdentifier>some trace id</TraceIdentifier>  
        <Description>EndReceive called</Description>  
        <AppDomain>UdpTestConsole.exe</AppDomain>  
        <Source>UdpInputChannel</Source>  
      </TraceRecord>  
    </DataItem>  
  </TraceData>  
  </ApplicationData>  
</E2ETraceEvent>  

O visualizador de rastreamento do WCF entende o esquema do elemento TraceRecord mostrado anteriormente e extrai os dados de seus elementos filho e os exibe em um formato tabular. Seu canal deve usar esse esquema ao rastrear dados estruturados do aplicativo a fim de ajudar os usuários do Svctraceviewer.exe a lerem os dados.