Journalisation des exceptions pour les services WCF utilisant ELMAH

Nous utilisons l’excellent ELMAH pour gérer les exceptions non gérées dans une application Web ASP.NET 3.5. Cela fonctionne extrêmement bien pour tout le site, à l’exception des services WCF qui sont consommés en utilisant les fonctionnalités REST. Lorsqu’une exception se produit dans les méthodes d’opération non gérées par le code de l’application, WCF le gère de différentes manières en fonction des contrats de service et des parameters de configuration. Cela signifie que l’exception ne déclenche pas l’événement ASP.NET HttpApplication.Error utilisé par ELMAH . Les deux solutions dont je suis au courant sont:

  • Enveloppez tous les appels de méthode dans un try {} catch (Exception ex) {Elmah.ErrorSignal.FromCurrentContext (). Relevez (ex); jeter; } appeler explicitement Elmah dans le bloc catch.
  • Utilisez IErrorHandler comme décrit dans l’ article du blog de Will Hughes. Facilitez la liaison entre WCF et ELMAH pour prendre en compte l’appel à ELMAH vers un ErrorHandler distinct.

La première option est extrêmement simple mais n’est pas exactement sèche . La deuxième option nécessite uniquement que vous décoriez chaque service avec l’atsortingbut personnalisé après avoir implémenté l’atsortingbut et le ErrorHandler. Je l’ai fait en fonction du travail de Will, mais je veux vérifier que c’est la bonne approche avant de poster le code.

Y a-t-il un meilleur moyen que j’ai manqué?

La documentation MSDN pour IErrorHandler indique que la méthode HandleError est l’endroit où effectuer la journalisation, mais ELMAH accède à HttpContext.Current. ApplicationInstance , qui est null dans cette méthode même si HttpContext.Current est disponible. L’appel à Elmah dans la méthode ProvideFault est une solution de contournement car ApplicationInstance est défini, mais cela ne correspond pas à l’intention décrite dans la documentation de l’API. Est-ce que j’ai râté quelque chose? La documentation indique que vous ne devez pas vous fier à l’appel de la méthode HandleError sur le thread d’opération, ce qui peut être la raison pour laquelle ApplicationInstance est nulle dans cette étendue.

La solution de mon article de blog (référencé dans l’OP) était basée sur une solution existante que nous utilisions / modifions pour modifier les codes de réponse HTTP lors d’un état d’erreur.

Donc, pour nous, il s’agissait d’un changement d’une ligne pour passer l’exception à ELMAH. S’il y a une meilleure solution, j’aimerais en savoir plus.

Pour la postérité / référence et l’amélioration potentielle, voici le code de la solution actuelle.

Classes HttpErrorHandler et ServiceErrorBehaviourAtsortingbute

using System; using System.ServiceModel; using System.ServiceModel.Dispatcher; using System.ServiceModel.Channels; using System.ServiceModel.Description; using System.Collections.ObjectModel; using System.Net; using System.Web; using Elmah; namespace YourApplication { ///  /// Your handler to actually tell ELMAH about the problem. ///  public class HttpErrorHandler : IErrorHandler { public bool HandleError(Exception error) { return false; } public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { if (error != null ) // Notify ELMAH of the exception. { if (System.Web.HttpContext.Current == null) return; Elmah.ErrorSignal.FromCurrentContext().Raise(error); } } } ///  /// So we can decorate Services with the [ServiceErrorBehaviour(typeof(HttpErrorHandler))] /// ...and errors reported to ELMAH ///  public class ServiceErrorBehaviourAtsortingbute : Atsortingbute, IServiceBehavior { Type errorHandlerType; public ServiceErrorBehaviourAtsortingbute(Type errorHandlerType) { this.errorHandlerType = errorHandlerType; } public void Validate(ServiceDescription description, ServiceHostBase serviceHostBase) { } public void AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, Collection endpoints, BindingParameterCollection parameters) { } public void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase) { IErrorHandler errorHandler; errorHandler = (IErrorHandler)Activator.CreateInstance(errorHandlerType); foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers) { ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher; channelDispatcher.ErrorHandlers.Add(errorHandler); } } } } 

Exemple d’utilisation

Décorez vos services WCF avec l’atsortingbut ServiceErrorBehaviour:

 [ServiceContract(Namespace = "http://example.com/api/v1.0/")] [ServiceErrorBehaviour(typeof(HttpErrorHandler))] public class MyServiceService { // ... } 

Lors de la création d’un BehaviorExtensionElement, il est même possible d’activer le comportement en utilisant config:

 public class ErrorBehaviorExtensionElement : BehaviorExtensionElement { public override Type BehaviorType { get { return typeof(ServiceErrorBehaviourAtsortingbute); } } protected override object CreateBehavior() { return new ServiceErrorBehaviourAtsortingbute(typeof(HttpErrorHandler)); } } 

Config:

               

De cette façon, il est également possible d’utiliser ELMAH en combinaison avec les services RIA!

Je l’ai fait en fonction du travail de Will, mais je veux vérifier que c’est la bonne approche avant de poster le code.

Je pense que c’est une excellente approche (bravo à Will pour cet article!). Je ne pense pas que Will ou vous avez manqué quelque chose ici. L’implémentation d’IErrorHandler est la méthode privilégiée pour capturer toutes les exceptions côté serveur susceptibles d’entraîner la défaillance du canal de communication, ce qui en fait un lieu naturel pour la journalisation, comme ELMAH.

Marc

Cela peut sembler évident pour certaines personnes mais j’ai juste passé pas mal de temps à essayer de comprendre pourquoi mon HttpContext.Current était nul malgré toutes les réponses de Will Hughes. De manière embarrassante, j’ai réalisé que c’était parce que mon service WCF est activé par un message MSMQ.

J’ai fini par réécrire la méthode ProvideFault() :

 if (HttpContext.Current == null) { ErrorLog.GetDefault(null).Log(new Error(error)); } else { ErrorSignal.FromCurrentContext().Raise(error); } 

Je n’ai pas pu obtenir la réponse proposée avec un service de données WCF. J’ai câblé l’atsortingbut comportement, etc., mais je n’ai toujours pas enregistré d’erreur. Au lieu de cela, j’ai fini par append les éléments suivants à l’implémentation du service:

 protected override void HandleException(HandleExceptionArgs args) { Elmah.ErrorSignal.FromCurrentContext().Raise(args.Exception); base.HandleException(args); } 

Je n’ai pas essayé de le faire explicitement avec les éléments REST, et je n’ai pas utilisé ELMAH moi-même, mais une autre option intéressante pourrait être de connecter WCF à l’aide d’un IDispatchMessageInspector au lieu d’un IErrorHandler.