WCF, IErrorHandler and log4Net

痴心易碎 提交于 2019-12-08 06:09:34

问题


I am trying to implement an error handling behavior on a wcf service that will use log4net to log an exception

[AttributeUsage(AttributeTargets.Class)]
public class AErrorHandlerBehaviorAttribute : Attribute, IServiceBehavior, IErrorHandler{

  private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

  protected Type ServiceType { get; set; }
  public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
  {
   //Dont do anything
  }

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

  public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
  {
   ServiceType = serviceDescription.ServiceType;
   foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
   {
    dispatcher.ErrorHandlers.Add(this);
   }
  }

  public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
  {
   fault = null; //Suppress any faults in contract
  }

  public bool HandleError(Exception error)
  {   
   log.Error("Page Load failed : " + error.Message); 
   return false;
  }
}

I then implement a service that uses the behavior. This works fine if I declare the ILog variable within the service

[AErrorHandlerBehavior]
public class AService : IAService
{

     private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        ....//various methods
}

However, when the ILog variable is not declared the logging stops working.

[AErrorHandlerBehavior]
public class AService : IAService
{

     //private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        ....//various methods
}

Ideally I don't want to have to declare the ILog variable in every service I produce, especially when it is already declared in the behavior.

Could someone please explain a) why this has to be declared in both the behavior and in the service. and b) any way of avoiding the double declaration or c) a better way of logging in wcf.


回答1:


I'm not convinced of the code you're using to resolve that Log object:

private static readonly ILog log = 
  LogManager.GetLogger(
    System.Reflection.MethodBase.GetCurrentMethod().DeclaringType
  );

This will always resolve to the type within which the line of code is written as it resolves first the auto-defined static constructor (with MethodInfo.GetCurrentMethod() of the surrounding class) and then gets its declaring type.

Well, I say always...

I don't like this kind of pattern as it implicitly relies on code that you haven't physically written yourself (and therefore an internal feature of the compiler that could change at any time); it would be marginally better if you explicitly placed it inside a static constructor you write yourself.

If you want to do this, then use

typeof(_whatever_type_you_declare_it_in_);

... don't rely on the compiler and the runtime.

Equally - I have a feeling you might be using this pattern under the mistaken belief that it'll resolve to the service on which the attribute is defined, it won't.

Is your intention to be able to control the log to be used at the service-level, with a behaviour-level default?

If so, then I suggest you:

1) Add an instance-level ILog property to your behaviour, called _serviceLog

2) In your implementation of ApplyDispatchBehaviour do this:

_serviceLog = LogManager.GetLogger(serviceDescription.ServiceType);

3) And then your implementation of HandleError could be as follows:

public bool HandleError(Exception error)
{
  //use the service-level log, or a default
  ILog targetLog = _serviceLog ?? log;
  if(targetLog != null)
    targetLog.Error("Page Load failed : " + error.Message);
  return false;
}



回答2:


the actual problem, ignoring what is the correct way to get the logger, was that in the behaviour I had not configured the logger.

The fix was simply to add the Configure statement to ApplyDispatchBehavior

public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            XmlConfigurator.Configure();            
            foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
            {
                dispatcher.ErrorHandlers.Add(this);
            }
        }

I presume the reason why I could log from the behaiour without this was because the declaration for ILog in the service caused the configuration of the logger before the error was thrown



来源:https://stackoverflow.com/questions/4786954/wcf-ierrorhandler-and-log4net

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!