Może część z Was zastanawiała się kiedyś jak wprowadzić własny mechanizm logowania błędów w WCF? Ja miałem potrzebę implementacji takiego mechanizmu logowania i podzielę się jedną z prostszych opcji.
O co właściwie chodzi?
Mianowicie walczymy o to, aby po stronie serwera hostującego nasz serwis WCF była możliwość zapisu do logów błędów występujących w tymże serwisie. Wiemy, że domyślnie wszystkie wyjątki jakie wystąpią w serwisach WCF nie są wysyłane do klienta. Wiemy także, że to domyślne zachowanie możemy łatwo zmienić ustawiając includeExceptionDetailInFaults="true".
Może na etapie developingu (tfu co za słowo) jest to wygodne, ale na pewno w systemie produkcyjnym … hmm no cóż powiedzmy, że klienta nie do końca interesują szczegóły wyjątku i cały stack trace:). W systemie produkcyjnym chcemy mieć szczegóły błędów w naszym logu … czymkolwiek on będzie.
Z pomocą przychodzi nam prosty interfejs IErrorHandler
z przestrzeni System.ServiceModel.Dispatcher. Zaimplementujmy go:
public class CustomErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
Logger.LogError(error); // Tutaj użyj swojego loggera!
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message msg)
{
}
}
To wszystko? Tak, a właściwie prawie:). Metoda HandleError to miejsce logowania błędu. Ale to jednak za mało gdyż jak się pewnie domyślacie teraz trzeba jakoś poinformować WCF-a, żeby użył naszej klasy CustomErrorHandler. Z pomocą przychodzi sam WCF, a mianowicie mechanizm rozszerzeń WCF-a, konkretniej mechanizm rozszerzeń zachowania serwisu BehaviorExtensionElement.
A zatem:
public class CustomErrorHandlerExtension : BehaviorExtensionElement, IServiceBehavior
{
public override Type BehaviorType
{
get { return GetType(); }
}
protected override object CreateBehavior()
{
return this;
}
private IErrorHandler GetInstance()
{
return new CustomErrorHandler();
}
void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
IErrorHandler errorHandlerInstance = GetInstance();
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
dispatcher.ErrorHandlers.Add(errorHandlerInstance);
}
}
void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
Dwa najistotniejsze elementy powyższego kodu został wyróżnione, tj. instancjonowanie naszego CustomErrorHandler oraz zarejestrowanie go.
Czy teraz to już wszystko? Prawie :). Teraz przenosimy się do pliku konfiguracyjnego naszego serwera (w wypadu self-hosting to będzie pewnie usługa Windows, a w wypadku hostowania na IIS to jakaś aplikacja Web).
W ramach <system.serviceModel>
dodajemy:
<extensions> <behaviorExtensions> <add name="customErrorHandler" type="TwojaAplikacja.CustomErrorHandlerExtension, TwojeAssembly" /> </behaviorExtensions> </extensions>
Rozszerzyliśmy WCF o nasz error handler, teraz pozostaje tylko go użyć. W ramach sekcji behavior naszego serwisu dodajemy <customErrorHandler/>.
Teraz to już naprawdę wszystko i działa!