Jason King Team : Web Development

Easy WCF Audit Logging

Jason King Team : Web Development

I've recently been working on a project which consumes a lot of soap web services. Towards the end of the project we had a new requirement to log every web service request and response.

This is actually very easy to do with WCF and turned out to be a very useful debugging tool. The reason for not using the built in WCF diagnostics was because I needed to remove certain parts of the request for security reasons and also so that the data could be logged to the database for easy access.

To set this up, all you need are three classes which hook into WCF events.

    public class WcfAuditMessageInspector : IClientMessageInspector
    {
        public void AfterReceiveReply(ref Message reply, object correlationState)
        {
            var buffer = reply.CreateBufferedCopy(Int32.MaxValue);
            reply = buffer.CreateMessage();

            Task.Run(() =>
            {
                this.LogMessage(buffer, false);
            });
        }

        private void LogMessage(MessageBuffer buffer, bool isRequest)
        {
            var originalMessage = buffer.CreateMessage();
            string messageContent;

            using (StringWriter stringWriter = new StringWriter())
            {
                using (XmlTextWriter xmlTextWriter = new XmlTextWriter(stringWriter))
                {
                    originalMessage.WriteMessage(xmlTextWriter);
                    xmlTextWriter.Flush();
                    xmlTextWriter.Close();
                }
                messageContent = stringWriter.ToString();
            }

            // log messageContent to the database
        }


        public object BeforeSendRequest(ref Message request, IClientChannel channel) { }
    }
    public class WcfAuditBehaviorExtensionElement : BehaviorExtensionElement
    {
        protected override object CreateBehavior()
        {
            return new WcfAuditBehavior();
        }

        public override Type BehaviorType
        {
            get { return typeof(WcfAuditBehavior); }
        }
    }
    public class WcfAuditBehavior : IEndpointBehavior
    {
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            WcfAuditMessageInspector inspector = new WcfAuditMessageInspector();
            clientRuntime.ClientMessageInspectors.Add(inspector);
        }

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

        public void Validate(ServiceEndpoint endpoint)
        {
        }
    }

The two most important methods of the above code are AfterReceiveReply and BeforeSendRequest. It's here that you can create a copy of the request or response for logging. I've chosen to perform the logging asynchronously so there isn't any unnecessary delay for the user. The LogMessage method is quite simple and simply creates a string from the message. This can then be logged to wherever you want.

The logging can be easily be enabled/disabled in the web.config. Once the project has been live for a while, we should be able to disable the logging.

To enable the logging for a particular service you just need to add the behaviorConfiguration attribute to your endpoint e.g.

  <system.serviceModel>
    <client>
      <endpoint address="https://...." behaviorConfiguration="wcfAuditBehavior" binding="basicHttpBinding" bindingConfiguration=" " contract=" " name="WebServiceHttpSoapEndpoint" />
    </client>
    <extensions>
      <behaviorExtensions>
        <add name="wcfAuditBehaviorExtension" type="MCA101.BusinessLogic.Auditing.WCF.WcfAuditBehaviorExtensionElement, MCA101.BusinessLogic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </behaviorExtensions>
    </extensions>
    <behaviors>
      <endpointBehaviors>
        <behavior name="wcfAuditBehavior">
          <wcfAuditBehaviorExtension />
        </behavior>
      </endpointBehaviors>
    </behaviors>
  </system.serviceModel>