Home » C# » How to use log every call of your WCF service’s methods

How to use log every call of your WCF service’s methods

If you want to log information like which method has been called with which parameters on every methods of your WCF service, you shouldn’t set the log every time on each method. Two approaches is possible to create something clean.

The first option is to create an attribute and when this one is present will log. The second is to automatically log every call. I’ll show you the second approach since it’s seem more logic to log every methods. If you are curious about how to do it with an attribute, I’ll post in few times the code to apply security with WCF with attribute which will show you how to do it.

This article shows you how to log every calls of WCF’s methods. It will use Unity as IOC. So, this article show you how to use an IOC (Unity) with WCF also.

The first step is to modify your service definition. This can be done by editing the .scv (not the .svc.cs).

<%@ ServiceHost Language="C#" 
Debug="true" 
Service="MyNameSpace.MyServices.MyService" 
CodeBehind="MyService.svc.cs" 
Factory="MyNameSpace.MyServices.Common.MyServiceHostFactory " %>

The important modification is the Factory attribute. This one is required to be able to use Unity. This act as the global.asax.cs. By this, I mean, it’s called when the service is initialized and not at every calls. Behind this factory, you own class is instanced and you are able to return your own ServiceHost class. This is a door to initialize Unity (IoC), AutoMapper and many others utilities class that need to be instanced once.

public class MyServiceHostFactory : ServiceHostFactory
{
	protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
	{
		var serviceHost = new MyServiceHost(serviceType, baseAddresses);
		//ConfigureUnity(serviceHost.Container);
		//ConfigureAutoMapper();
		return serviceHost;
	}
}

The MyServiceHostFactory creates a MyServiceHost and configure the inversion of control container. It also initialize the Automapper configuration. The MyServiceHost inherit from ServiceHost, which let you add the implementation of the IoC container and handle behaviors. To be able to log on every service’s method, a new behavior need to be defined.

public class MyServiceHost : ServiceHost
{
	public UnityContainer Container { get; set; }
	
	public MyServiceHost()
	{
		Container = new UnityContainer();
	}

	public MyServiceHost(Type serviceType, params Uri[] baseAddresses): base(serviceType, baseAddresses)
	{
		Container = new UnityContainer();
	}

	protected override void OnOpening()
	{
		base.OnOpening();

		if (this.Description.Behaviors.Find<MyServiceBehavior>() == null)
		{
			this.Description.Behaviors.Add(new MyServiceBehavior(Container));
		}
	}
}

The MyServiceHost initializes the container and add a single behavior which handle the logging feature.

public class MyServiceBehavior : IServiceBehavior
{
	public UnityInstanceProvider InstanceProvider { get; set; }

	public MyServiceBehavior()
	{
		InstanceProvider = new UnityInstanceProvider();
	}

	public MyServiceBehavior(UnityContainer unity)
	{
		InstanceProvider = new UnityInstanceProvider {Container = unity};
	}

	public void AddBindingParameters(ServiceDescription serviceDescription, 
									ServiceHostBase serviceHostBase, 
									Collection<ServiceEndpoint> endpoints,
									BindingParameterCollection bindingParameters)
	{	
	}

	public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
	{
                InstanceProvider.ServiceType = serviceDescription.ServiceType;
		foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
		{
			var cd = cdb as ChannelDispatcher;
			if (cd != null)
			{
				foreach (EndpointDispatcher ed in cd.Endpoints)
				{
					ed.DispatchRuntime.InstanceProvider = InstanceProvider;
				}
			}
		}

		var log = InstanceProvider.Container.Resolve<ILog>();
		IOperationBehavior behavior = new LoggingOperationBehavior(log);
		foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
		{
			foreach (OperationDescription operation in endpoint.Contract.Operations)
			{
				if (!operation.Behaviors.Any(d => d is LoggingOperationBehavior))
				{
					operation.Behaviors.Add(behavior);
				}
			}
		}
	}

	public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
	{ 
	}
}

This is where we attach for all endpoints, all operations (methods) the behavior to be executed. This is where we add the logging behavior. The first foreach is required for Unity to be the provider while the second foreach is there to add the logging behavior.

public class UnityInstanceProvider : IInstanceProvider
    {
        public IUnityContainer Container { set; get; }
        public Type ServiceType { set; get; }


        public UnityInstanceProvider()
            : this(null)
        {
        }

        public UnityInstanceProvider(Type type)
        {
            ServiceType = type;
            Container = new UnityContainer();
        }

        public object GetInstance(InstanceContext instanceContext, Message message)
        {
            return Container.Resolve(ServiceType);
        }

        public object GetInstance(System.ServiceModel.InstanceContext instanceContext)
        {
            return GetInstance(instanceContext, null);
        }

        public void ReleaseInstance(InstanceContext instanceContext, object instance)
        {

            var myInstance = instance as IDisposable;

            if (myInstance != null)
            {
                myInstance.Dispose();
            }
        }
    }
public class LoggingOperationBehavior : IOperationBehavior
{
	private readonly ILog _myLog;

	public LoggingOperationBehavior(ILog myLog)
	{
		_myLog = myLog;
	}

	public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
	{
		dispatchOperation.Invoker = new LoggingOperationInvoker(_myLog, dispatchOperation.Invoker, dispatchOperation);
	}
	
	public void Validate(OperationDescription operationDescription)
	{
	}
	
	public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
	{
	}

	public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
	{
	}
}

At this point, we need to invoke the log. We need to specify an invoker that will be executed. This is why, we need to create a logging invoker which take the log that come from Unity.

public class LoggingOperationInvoker : IOperationInvoker
{
	private readonly IOperationInvoker _baseInvoker;
	private readonly string _operationName;
        private readonly string _controllerName;
	private readonly ILog _myLog;

	public LoggingOperationInvoker(ILog myLog, IOperationInvoker baseInvoker, DispatchOperation operation)
	{
		_myLog = myLog;
		_baseInvoker = baseInvoker;
		_operationName = operation.Name;
                _controllerName = operation.Parent.Type==null?"[None]":operation.Parent.Type.FullName;
	}
	
	public object Invoke(object instance, object[] inputs, out object[] outputs)
	{
		_myLog.Log("Method " + _operationName + " of class " + _controllerName + " called");
		try
		{
			return _baseInvoker.Invoke(instance, inputs, out outputs);
		}
		catch (Exception ex)
		{
			_myLog.Log(ex);
			throw;
		}
	}

	public object[] AllocateInputs() {return _baseInvoker.AllocateInputs();	}

	public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
	{
		_myLog.Log("Method " + _operationName + " of class " + _controllerName + " called");
		return _baseInvoker.InvokeBegin(instance, inputs, callback, state);
	}

	public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)	{return _baseInvoker.InvokeEnd(instance, out outputs, result);	}

	public bool IsSynchronous { get {return _baseInvoker.IsSynchronous;	}	}
}

This is where the log is really written. It uses the interface to log which came from Unity from the Factory. The log is written when the method is invoked.

If you like my article, think to buy my annual book, professionally edited by a proofreader. directly from me or on Amazon. I also wrote a TypeScript book called Holistic TypeScript

5 Responses so far.

  1. […] to do it is in the startup. OData service doesn’t have global.asax, neither does it have a ServiceHostFactory where you could setup your […]

  2. […] have already talk about how to log every call of your WCF service with a custom factory for all your WCF service. This is a good way to log or to verify access to all your service methods. The main point was to […]

  3. saygili says:

    Dude, you are the man!

  4. thequest says:

    Its great, solved my purpose…
    Thanks buddy.

  5. Preguntón Cojonero Cabrón says:

    Great!!

    And for RESTful Services – Web API (with MVC and without MVC) for Client (consuming API) and Service (Server hosting API) ?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.