问题
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