Tratamento de exceções e falhas

As 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 através dos limites do 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 de 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 de volta uma falha). Este documento consiste em três seções que fornecem orientação aos autores de canais personalizados. A primeira seção fornece orientação sobre quando e como definir e lançar exceções. A segunda seção fornece orientação sobre como gerar e consumir falhas. A terceira seção explica como fornecer informações de rastreamento para ajudar o usuário do seu 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 tem que ser de um tipo que permita aos usuários escrever código correto que possa reagir adequadamente à exceção. Em segundo lugar, ele tem que fornecer informações suficientes para o usuário entender o que deu errado, o impacto da falha e como corrigi-lo. As seções a seguir fornecem orientação sobre tipos de exceção e mensagens para canais do Windows Communication Foundation (WCF). Há também orientações gerais sobre exceções no .NET no documento Design Guidelines for Exceptions.

Tipos de exceção

Todas as exceções lançadas por canais devem ser um System.TimeoutException, System.ServiceModel.CommunicationExceptionou um tipo derivado de CommunicationException. (Exceções como as que ObjectDisposedException também podem ser lançadas, mas apenas para indicar que o código de chamada utilizou indevidamente o canal. Se um canal for usado corretamente, ele só deve lançar as exceções dadas.) WCF fornece sete tipos de exceção que derivam de CommunicationException e são projetados para serem usados por canais. Existem outras CommunicationExceptionexceções derivadas que são 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 presente, fornece mais detalhes sobre o erro de transporte que causou essa exceção. Por exemplo. PipeException, HttpListenerException, ou SocketException. Tente um endereço diferente.
AddressAccessDeniedException O processo não tem permissão de acesso ao endereço do ponto de extremidade especificado para escuta. Se presente, fornece mais detalhes sobre o erro de transporte que causou essa exceção. Por exemplo, PipeExceptionou HttpListenerException. Tente com credenciais diferentes.
CommunicationObjectFaultedException O ICommunicationObject que está sendo usado está no estado com falha (para obter mais informações, consulte Noções básicas sobre alterações de estado). Observe que quando um objeto com várias chamadas pendentes transita para o estado Faulted, apenas uma chamada lança uma exceção relacionada à falha e o restante das chamadas lança um CommunicationObjectFaultedExceptionarquivo . Essa exceção normalmente é lançada porque um aplicativo ignora alguma exceção e tenta usar um objeto já com defeito, possivelmente em um thread diferente daquele que detetou a exceção original. Se presente fornece detalhes sobre a exceção interna. Criar um novo objeto. Note que, dependendo do que causou a ICommunicationObject falha em primeiro lugar, pode haver outro trabalho necessário para recuperar.
CommunicationObjectAbortedException O ICommunicationObject que está sendo usado foi Abortado (para obter mais informações, consulte Noções básicas sobre alterações de estado). Semelhante a CommunicationObjectFaultedException, essa exceção indica que o aplicativo chamou Abort o objeto, possivelmente de outro thread, e o objeto não é mais utilizável por esse motivo. Se presente fornece detalhes sobre a exceção interna. Criar um novo objeto. Note que, dependendo do que causou o ICommunicationObject abortamento em primeiro lugar, pode haver outro trabalho necessário para recuperar.
EndpointNotFoundException O ponto de extremidade remoto de destino não está escutando. Isso pode resultar de qualquer parte do endereço do ponto de extremidade estar incorreta, irresolúvel ou o ponto de extremidade estar inativo. Os exemplos incluem erro de DNS, Gerenciador de Filas não disponível e serviço não em execução. A exceção interna fornece detalhes, normalmente do transporte subjacente. Tente um endereço diferente. Como alternativa, o remetente pode esperar 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 tipo de conteúdo de enquadramento ou tamanho máximo de mensagem excedido. Se presente fornece 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 protocolo recebido correspondem. Uma maneira de fazer isso é reimportar 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 presente, a exceção interna fornece a falha SOAP ou detalhes de erro de nível de transporte. Recuperação: Aguarde e tente novamente 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 tente novamente 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, 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 se perguntando qual seção de configuração está incorreta e por que ela está incorreta. Uma mensagem melhorada seria "Invalid configuration section <customBinding>". Uma mensagem ainda melhor seria "Não é possível adicionar o transporte chamado myTransport à associação chamada myBinding porque a ligação já tem um transporte chamado myTransport". Esta é 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 faltam alguns componentes-chave.

A importância do erro. A menos que a mensagem indique claramente o que o erro significa, é provável que o usuário se pergunte se é um erro fatal ou se pode ser ignorado. Em geral, as mensagens devem levar com o significado ou significado do erro. Para melhorar o exemplo anterior, a mensagem poderia ser "ServiceHost failed to Open due to a configuration error: Cannot add the transport named myTransport to the binding named myBinding because the binding already have a transport named myTransport".

Como o usuário deve corrigir o problema. A parte mais importante da mensagem é ajudar o usuário a corrigir o problema. A mensagem deve incluir algumas orientações ou dicas sobre o que verificar ou corrigir para resolver o problema. Por exemplo, "ServiceHost falhou ao abrir 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. Por favor, certifique-se de que há apenas um transporte na encadernação".

Comunicando falhas

SOAP 1.1 e SOAP 1.2 definem uma estrutura específica para falhas. Existem 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 para 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 filho de <env:Body>. O conteúdo do elemento de falha difere ligeiramente entre SOAP 1.1 e SOAP 1.2, como mostrado na figura 1. No entanto, a System.ServiceModel.Channels.MessageFault classe 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 Code propriedade corresponde ao env:Code (ou faultCode em SOAP 1.1) e identifica o tipo de falha. SOAP 1.2 define cinco valores permitidos para faultCode (por exemplo, Remetente e Recetor) e define um elemento que pode conter qualquer valor de Subcode subcódigo. (Veja o Especificação SOAP 1.2 para a lista de códigos de falha permitidos e seu significado.) SOAP 1.1 tem um mecanismo ligeiramente diferente: ele define quatro faultCode valores (por exemplo, Cliente e Servidor) que podem ser estendidos definindo valores inteiramente novos ou usando a notação de ponto para criar valores mais específicos faultCodes, por exemplo, Client.Authentication.

Quando você usa MessageFault para programar falhas, o FaultCode.Name e FaultCode.Namespace mapeia para o nome e namespace do SOAP 1.2 env:Code ou o SOAP 1.1 faultCode. O FaultCode.SubCode mapeia para env:Subcode SOAP 1.2 e é null 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 O Perfil Básico do WS-I também desencoraja o uso da notação de pontos do 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 Reason propriedade corresponde à env:Reason (ou faultString em SOAP 1.1) uma descrição legível por humanos da condição de erro análoga à mensagem de uma exceção. A FaultReason classe (e SOAP env:Reason/faultString) tem suporte embutido para ter várias traduções no interesse 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 o GetDetail<T> e GetReaderAtDetailContents(). O detalhe da falha é um elemento opaco para carregar detalhes adicionais sobre a falha. Isso é útil se houver algum detalhe estruturado arbitrário que você deseja carregar com a falha.

Gerando falhas

Esta seção explica o processo de geração de uma falha em resposta a uma condição de erro detetada em um canal ou em uma propriedade de mensagem criada pelo canal. Um exemplo típico é enviar de volta 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 a camada acima decidir se converte essa exceção em uma falha e como enviá-la. Para ajudar nessa conversão, o canal deve fornecer uma FaultConverter implementação que possa converter a exceção lançada pelo canal personalizado para a falha apropriada. FaultConverter define-se 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 implementá-lo FaultConverter e retorná-lo de uma chamada para .GetProperty<FaultConverter> A implementação personalizada OnTryCreateFaultMessage deve converter a exceção em uma falha ou delegar ao canal interno.FaultConverter Se o canal for um transporte, ele deve converter a exceção ou delegar para o codificador FaultConverter ou o padrão FaultConverter fornecido no WCF . O padrão FaultConverter converte erros correspondentes a mensagens de falha especificadas por WS-Addressing e SOAP. Aqui está um exemplo OnTryCreateFaultMessage de implementação.

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 lançadas 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 um autor de canal personalizado, você pode definir tipos de exceção que correspondem a diferentes condições de falha se tais exceções ainda não existirem. Observe que as exceções que atravessam as camadas do canal devem comunicar a condição de erro em vez de dados de falha opacos.

Categorias de falhas

Existem geralmente 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 dizem respeito a uma transação fluída 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 WS-RM.

Categoria 1. As falhas são geralmente WS-Addressing e falhas SOAP. A classe base FaultConverter fornecida pelo WCF converte erros correspondentes a mensagens de falha especificadas pelo 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 da mensagem que pertencem a essa camada. Os erros podem ser detetados mais tarde quando uma camada superior solicita que a propriedade message processe as informações da mensagem ainda mais. Esses canais devem implementar o GetProperty especificado anteriormente para permitir que a camada superior envie de volta a falha correta. Um exemplo disso é o TransactionMessageProperty. Essa propriedade é adicionada à mensagem sem validar totalmente todos os dados no cabeçalho (fazer isso pode envolver entrar em contato com o coordenador de transações distribuídas (DTC).

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 os 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.

Interpretando falhas recebidas

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

  1. Se a camada considerar a mensagem inválida, a camada deve fazer seu processamento de "mensagem inválida". Esse processamento é específico para a camada, mas pode incluir descartar a mensagem, rastrear ou lançar uma exceção que seja convertida em falha. Os exemplos incluem segurança recebendo uma mensagem que não está protegida corretamente ou RM recebendo uma mensagem com um número de sequência incorreto.

  2. Caso contrário, se a mensagem for uma mensagem de falha que se aplica especificamente à camada, e a mensagem 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 RM Sequence Refused que não faz sentido para as camadas acima do canal RM e que implica falhar o canal RM e lançar de operações pendentes.

  3. Caso contrário, a mensagem deve 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 lançamento de operações pendentes. Para melhorar a usabilidade nesse caso, a camada deve implementar GetProperty<FaultConverter> e retornar uma FaultConverter classe derivada que possa converter a falha em uma exceção substituindo OnTryCreateException.

O modelo de objeto a seguir oferece 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 ser implementada GetProperty<FaultConverter> para suportar a conversão de mensagens de falha em exceções. Para fazer isso, substitua OnTryCreateException e inspecione a mensagem de falha. Se for reconhecido, faça a conversão, caso contrário, peça ao canal interno para convertê-lo. Os canais de transporte devem delegar para FaultConverter.GetDefaultFaultConverter obter o SOAP/WS-Addressing FaultConverter padrão.

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 tenham 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 recetor. Esta falha é conhecida como a mustUnderstand falha. No WCF, os canais personalizados nunca geram mustUnderstand falhas. Em vez disso, o WCF Dispatcher, que está localizado na parte superior da pilha de comunicação do WCF, verifica se todos os cabeçalhos que foram marcados como MustUnderstand=true foram compreendidos pela pilha subjacente. Se algum não foi compreendido, uma mustUnderstand falha é gerada nesse ponto. (O usuário pode optar por desativar esse mustUnderstand processamento e fazer com que o aplicativo receba todos os cabeçalhos de mensagens. Nesse caso, o aplicativo é responsável por realizar mustUnderstand o processamento.) 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 mustUnderstand falha, ele deverá descobrir se essa falha é devida ao cabeçalho enviado. Há dois membros na MessageFault classe que são úteis para isso:

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

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

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

Rastreio

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

Tracing components

Rastreamento a partir de um canal personalizado

Os canais personalizados devem escrever mensagens de rastreamento para ajudar no diagnóstico de problemas quando não for possível anexar um depurador ao aplicativo em execução. Isso envolve duas tarefas de alto nível: instanciar um TraceSource e chamar seus métodos para escrever rastreamentos.

Ao instanciar um TraceSource, a cadeia de caracteres especificada torna-se o nome dessa fonte. Esse nome é usado para configurar (habilitar/desabilitar/definir o nível de rastreamento) a fonte de rastreamento. Ele também aparece na própria saída de 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 fonte de rastreamento é a prática comum. Por exemplo, o WCF usa System.ServiceModel como a fonte de rastreamento para informações gravadas a partir do assembly System.ServiceModel.

Depois de ter uma fonte de rastreamento, você chama seus TraceDatamétodos , TraceEventou TraceInformation para gravar entradas de rastreamento para os ouvintes de rastreamento. Para cada entrada de rastreamento gravada, você 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 é saída para o ouvinte. Por exemplo, definir o nível de rastreamento na configuração para Warning permitir que Warningas entradas de rastreamento sejam gravadas, ErrorCritical mas bloqueia as entradas de informações e detalhadas. Aqui está 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 especificar um nome de origem de rastreamento que seja exclusivo para seu canal personalizado para ajudar os leitores de saída de rastreamento a entender de onde a saída veio.

Integração com o Visualizador de Rastreamento

Os rastreamentos gerados pelo seu canal podem ser enviados em um formato legível pela Service Trace Viewer Tool (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 soluciona 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 ambos 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>  

Rastreando dados estruturados

System.Diagnostics.TraceSource tem um TraceData método que usa um ou mais objetos que devem ser incluídos na entrada de rastreamento. Em geral, o Object.ToString método é chamado em cada objeto e a cadeia de caracteres resultante é escrita 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. Aqui está um exemplo de entrada com dados de aplicativos 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 TraceRecord elemento 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 de aplicativos estruturados para ajudar Svctraceviewer.exe usuários a ler os dados.