12

I'm using asp.net core on windows and have a file with classes generated by the dotnet-svcutil. I'm using nlog for the logging purpose. Is there a way I can log all the raw requests and responses to and from the external service?

Already tried logman https://github.com/dotnet/wcf/blob/master/Documentation/HowToUseETW.md, but first - it doesn't show the raw soap, only events, and second - I need logs to be logged by the configured nlog.

Lanayx
  • 2,731
  • 1
  • 23
  • 35

2 Answers2

42

Behaviour:

public class LoggingEndpointBehaviour : IEndpointBehavior
{
    public LoggingMessageInspector MessageInspector { get; }

    public LoggingEndpointBehaviour(LoggingMessageInspector messageInspector)
    {
        MessageInspector = messageInspector ?? throw new ArgumentNullException(nameof(messageInspector));
    }

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        clientRuntime.ClientMessageInspectors.Add(MessageInspector);
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    }
}

Inspector:

 public class LoggingMessageInspector : IClientMessageInspector
{
    public LoggingMessageInspector(ILogger<LoggingMessageInspector> logger)
    {
        Logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
    }

    public ILogger<LoggingMessageInspector> Logger { get; }

    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
        using (var buffer = reply.CreateBufferedCopy(int.MaxValue))
        {
            var document = GetDocument(buffer.CreateMessage());
            Logger.LogTrace(document.OuterXml);

            reply = buffer.CreateMessage();
        }
    }

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        using (var buffer = request.CreateBufferedCopy(int.MaxValue))
        {
            var document = GetDocument(buffer.CreateMessage());
            Logger.LogTrace(document.OuterXml);

            request = buffer.CreateMessage();
            return null;
        }
    }

    private XmlDocument GetDocument(Message request)
    {
        XmlDocument document = new XmlDocument();
        using (MemoryStream memoryStream = new MemoryStream())
        {
            // write request to memory stream
            XmlWriter writer = XmlWriter.Create(memoryStream);
            request.WriteMessage(writer);
            writer.Flush();
            memoryStream.Position = 0;

            // load memory stream into a document
            document.Load(memoryStream);
        }

        return document;
    }
}

Usage:

 if (configuration.GetValue<bool>("Logging:MessageContent"))
     client.Endpoint.EndpointBehaviors.Add(serviceProvider.GetRequiredService<LoggingEndpointBehaviour>());
Kaido
  • 3,383
  • 24
  • 34
  • 1
    The usage part of this answer was very helpful to me. I would note that you could also add the class to the EndpointBehaviors like this: client.Endpoint.EndpointBehaviors.Add(new LoggingEndpointBehaviour()) assuming the class you are implementing has a parameterless constructor. – Ralph Jan 31 '19 at 16:32
  • I spent my last three hours trying to capture my request and response. Did everything that is adviced. Especially the message inspector. But none of them worked due to poor explanation except yours. You really saved my day. – Doruk Oct 18 '19 at 16:19
-1

Found the answer here: https://learn.microsoft.com/en-us/dotnet/framework/wcf/extending/how-to-inspect-or-modify-messages-on-the-client

The order of actions:

  1. Implement the System.ServiceModel.Dispatcher.IClientMessageInspector interface. Here you can inspect/modify/log messages
  2. Implement a System.ServiceModel.Description.IEndpointBehavior or System.ServiceModel.Description.IContractBehavior depending upon the scope at which you want to insert the client message inspector. System.ServiceModel.Description.IEndpointBehavior allows you to change behavior at the endpoint level. System.ServiceModel.Description.IContractBehavior allows you to change behavior at the contract level.
  3. Insert the behavior prior to calling the ClientBase.Open or the ICommunicationObject.Open method on the System.ServiceModel.ChannelFactory.
Lanayx
  • 2,731
  • 1
  • 23
  • 35