web-dev-qa-db-fra.com

Exception de la journalisation des services de WCF utilisant Elmah

Nous utilisons l'excellent [~ # ~ # ~] elmah [~ # ~] Pour faire face à des exceptions non gérées dans une application Web ASP.NET 3.5. Cela fonctionne extrêmement bien pour tout le site à part des services de WCF qui sont consommés à l'aide des fonctionnalités REST. Lorsqu'une exception survient dans les méthodes d'opération non traitées par le code de l'application, WCF les gère de différentes manières en fonction des contrats de service et des paramètres de configuration. Cela signifie que l'exception ne finit pas le déclenchement de l'événement ASP.NET httpApplication.Error qui ELMAH Utilisations. Les deux solutions que je suis au courant de traiter cela sont:

  • Enveloppez tous les appels de méthode dans un essai {} catch (exception ex) {elmah.errorsignal.fromCurrentContext (). Relève (ex); lancer; } Appeler explicitement Elmah dans le bloc de capture.
  • UTILISATION IERRORHANDLER comme décrit dans Will Hughes ' Blog post fabrication de wcf et elmah joue bien ensemble Pour prendre en compte l'appel à Elmah à une erreur distincte.

La première option est extrêmement simple mais n'est pas exactement SEC . La deuxième option ne nécessite que de décorer chaque service avec l'attribut personnalisé après la mise en oeuvre de l'attribut et de la tâche de la tâche. Je l'ai fait sur la base - Will's Travail mais je veux vérifier que c'est la approche correcte Avant de poster le code.

Y a-t-il une meilleure façon d'avoir manqué ?

La documentation MSDN pour - iERRORHANDLER dit que le guérisseur La méthode est l'endroit pour faire la journalisation mais [~ # ~ # ~ # ~] Accède à httpcontext.current. ApplicationInstance, qui est null dans cette méthode, même si httpcontext.current est disponible. Faire appel à Elmah dans la méthode FedFault est une solution de contournement en tant qu'applicationInstance est définie, mais cela ne correspond pas à l'intention décrite dans la documentation de l'API. Est-ce que je manque quelque chose ici ? La documentation indique que vous ne devez pas compter sur la méthode du guidon à appeler sur le thread de fonctionnement qui peut être la raison pour laquelle ApplicationInstance est nulle dans cette portée.

62

La solution de mon article de blog (référencé dans OP) était basée sur une solution existante que nous étions/utilisez pour modifier les codes de réponse HTTP pendant un état d'erreur.

Ainsi, pour nous, c'était un changement d'une ligne pour passer l'exception à Elmah. S'il y a une meilleure solution, j'aimerais aussi savoir à ce sujet.

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

Httperrorhandler et serviceerrorbehaviouratRibute classes

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
{
    /// <summary>
    /// Your handler to actually tell ELMAH about the problem.
    /// </summary>
    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);
            }
        }
    }
    /// <summary>
    /// So we can decorate Services with the [ServiceErrorBehaviour(typeof(HttpErrorHandler))]
    /// ...and errors reported to ELMAH
    /// </summary>
    public class ServiceErrorBehaviourAttribute : Attribute, IServiceBehavior
    {
        Type errorHandlerType;

        public ServiceErrorBehaviourAttribute(Type errorHandlerType)
        {
            this.errorHandlerType = errorHandlerType;
        }

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

        public void AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> 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'attribut ServiceErrorbeHaviour:

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

Lors de la création d'un comportement de comportement, il est même possible d'activer le comportement à l'aide de configuration:

public class ErrorBehaviorExtensionElement : BehaviorExtensionElement
{
    public override Type BehaviorType
    {
        get { return typeof(ServiceErrorBehaviourAttribute); }
    }

    protected override object CreateBehavior()
    {
        return new ServiceErrorBehaviourAttribute(typeof(HttpErrorHandler));
    }
}

Config:

<system.serviceModel>
    <extensions>
      <behaviorExtensions>
        <add name="elmah" type="Namespace.ErrorBehaviorExtensionElement, YourAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </behaviorExtensions>
    </extensions>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <elmah />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

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

9
riezebosch

Cela pourrait bien être évident pour certaines personnes, mais je viens de passer tout en œuvre en essayant de comprendre pourquoi mon httpcontext.current était null malgré la suite de toutes les meilleures réponses de Hughes. Embarquez-vous, j'ai compris 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);
}
2
Steve Rukuts

Je l'ai fait en fonction de la tâche de la volonté, mais je veux vérifier que c'est la bonne approche avant de poster le code.

Je pense que c'est une excellente approche (félicitations à volonté pour cette publication!). Je ne pense pas que vous eurez ou vous avez manqué quoi que ce soit ici. La mise en œuvre de IERRORHANDLER est la manière préférée de capturer toutes les exceptions possibles sur le côté serveur qui pourraient sinon que le canal de communication soit défaillant (déchiré) et donc c'est un endroit naturel à accrocher dans une journalisation comme Elmah.

Marc

2
marc_s

Je n'ai pas pu obtenir la réponse proposée travailler avec un service de données WCF. J'ai câblé l'attribut comportemental, etc., mais je n'ai toujours pas été enregistré. Au lieu de cela, j'ai fini par ajouter ce qui suit à la mise en œuvre du service:

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

Je n'ai pas essayé de faire cela explicitement avec le REST Stuff et je n'ai pas utilisé Elmah moi-même, mais une autre option qui vaut la peine d'être examinée pourrait être de se connecter à WCF à l'aide d'un IDISPatchMessageInSpector au lieu d'un iErrorhandler.

0
tomasr