Section courante

A propos

Section administrative du site

Filtrage des erreurs

Lorsqu'une exception non gérée est signalée à ELMAH par ASP.NET, une application peut décider de la rejeter ou non. Deux méthodes s'offrent à elle : par programmation ou de manière déclarative via le fichier de configuration. La plus simple est la programmation, car elle ne nécessite aucune apprentissage particulier, si ce n'est l'écriture d'un gestionnaire d'événements dans votre langage de programmation. L'inconvénient de l'approche programmatique est qu'elle nécessite d'écrire du code et de modifier votre application web (ce qui peut nécessiter une recompilation statique). Avec l'approche basée sur la configuration, vous pouvez simplement appliquer le filtrage des exceptions à une application en cours d'exécution.

Filtrage par programmation

Supposons que :

Pour ajouter un filtre d'exception par programmation, placez les éléments suivants dans votre fichier Global.asax (ou dans le fichier code-behind) :

  1. // N'oubliez pas de référencer l'assembly Elmah et d'importer son espace de noms.
  2.  
  3. void ErrorLog_Filtering(object sender, ExceptionFilterEventArgs e)
  4. {
  5.     if (e.Exception.GetBaseException() is HttpRequestValidationException)
  6.         e.Dismiss();
  7. }

C'est aussi simple que cela. Lorsque le module de journalisation des erreurs reçoit une exception, il déclenche l'événement Filtering pour voir si des écouteurs souhaitent la supprimer. Si un gestionnaire d'événements appelle la méthode Dismiss de l'objet ExceptionFilterEventArgs, l'exception n'est pas journalisée. Notez que la suppression d'une exception ne supprime pas l'erreur ; elle continue donc d'apparaître comme une exception non gérée. Dans ce cas, la suppression signifie que le module ne la journalisera pas.

Si le module ErrorMailModule est également activé, la procédure de filtrage est similaire. Vous configurez un autre gestionnaire d'événements basé sur le nom du module et de l'événement :

  1. void ErrorMail_Filtering(object sender, ExceptionFilterEventArgs e)
  2. {
  3.     if (e.Exception.GetBaseException() is HttpRequestValidationException)
  4.         e.Dismiss();
  5. }

Naturellement, vous pouvez combiner les deux en déléguant à une méthode commune afin d'avoir l'ensemble des conditions de filtrage en un seul endroit :

  1. void ErrorLog_Filtering(object sender, ExceptionFilterEventArgs args)
  2. {
  3.     Filter(args);
  4. }
  5.  
  6. void ErrorMail_Filtering(object sender, ExceptionFilterEventArgs args)
  7. {
  8.     Filter(args);
  9. }
  10.  
  11. void Filter(ExceptionFilterEventArgs args)
  12. {
  13.     if (args.Exception.GetBaseException() is HttpRequestValidationException)
  14.         args.Dismiss();
  15. }

Filtrage déclaratif via la configuration

Le filtrage via la configuration est un peu plus complexe que l'approche programmatique, mais son avantage est qu'il peut être appliqué à n'importe quelle application web en cours d'exécution sans avoir à la modifier ou à la recompiler.

La première étape consiste à configurer un module supplémentaire appelé Elmah.ErrorFilterModule. Assurez-vous de l'ajouter après n'importe quel module de journalisation d'ELMAH, comme illustré ici avec ErrorLogModule :

<httpModules>
    ...
    <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah"/>
    <add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah"/>
    ...
</httpModules>

Le module ErrorFilterModule s'occupe de l'abonnement à l'événement de filtrage des modules le précédant. Lorsque les modules de journalisation et de messagerie déclenchent leur événement de filtrage, le module ErrorFilterModule exécute une ou plusieurs assertions pour décider de rejeter l'exception. Si les assertions sont vraies, l'exception est rejetée. Les assertions sont indiquées dans une section de configuration distincte. Cette section est gérée par Elmah.ErrorFilterSectionHandler et doit être enregistrée comme indiqué ici :

<configSections>
    ...
    <sectionGroup name="elmah">
        ...
        <section name="errorFilter" type="Elmah.ErrorFilterSectionHandler, Elmah"/>
        ...
    </sectionGroup>
    ...
</configSections>

Les applications ASP.NET exécutées sous une confiance moyenne doivent utiliser la version suivante pour l'entrée de section spécifiant l'attribut requirePermission supplémentaire et définit sa valeur sur false :

<section name="errorFilter" type="Elmah.ErrorFilterSectionHandler, Elmah" requirePermission="false" />

Vous pouvez maintenant ajouter la section réelle sous le groupe <elmah> :

<elmah>
    ...
    <errorFilter>
        <test>
            <equal binding="HttpStatusCode" value="404" type="Int32" />
        </test>
    </errorFilter>
</elmah>

Dans l'exemple ci-dessus, une seule assertion est vraie lorsque HttpStatusCode est égal à l'entier 404. De nombreuses composantes sont impliqués, alors analysons cela plus en détail. ELMAH intègre plusieurs assertions, et vous pouvez même en ajouter. Les plus intéressantes testent leur environnement ou contexte pour une condition. Si la condition est remplie, le résultat de l'assertion est vrai. Le contexte est généralement l'exception levée et la transaction HTTP en cours. Ces deux éléments sont disponibles pour le gestionnaire de l'événement Filtering en tant que propriétés de ExceptionFilterEventArgs. Le module ErrorFilterModule les utilise et y ajoute quelques propriétés d'assistance supplémentaires, disponibles ensuite comme contexte global pour toutes les assertions. L'assertion <equal> ci-dessus possède un attribut de liaison qui est en fait une expression de liaison de données que vous connaissez déjà. Il s'agit de la même expression et de la même syntaxe que celles spécifiées pour la méthode Eval lors de la liaison de données dans le balisage côté serveur des pages ASP.NET et des contrôles utilisateur. Cette expression est évaluée par rapport au contexte; HttpStatusCode est donc naturellement une propriété de ce contexte. L'assertion évalue l'expression et utilise la valeur résultante pour la comparer au littéral fourni dans l'attribut value. Pour comparer des pommes avec des pommes et des oranges avec des oranges, vous devez spécifier le type de la valeur attendue de l'expression de liaison ainsi que celui du texte dans l'attribut value. C'est l'objectif de l'attribut type, qui peut être l'un des membres de l'énumération TypeCode. Par conséquent, l'ensemble actuel d'assertions de comparaison est limité aux types primitifs représentés par les membres TypeCode (à l'exception de Empty, DBNull et Object). Cela peut sembler restrictif au premier abord, mais cela suffira pour la plupart des filtrages nécessaires. Pour les cas particuliers, vous pouvez toujours écrire vos propres assertions. Si vous omettez l'attribut type, le type de valeur String est supposé. Enfin, si la comparaison réussit, l'assertion est validée et l'exception est finalement levée. Ceci étant dit, voici un bref résumé de l'exemple ci-dessus. Le code HttpStatusCode est d'abord évalué par rapport au contexte du filtre. Son résultat est converti en entier 32 bits et comparé à la valeur 404. En cas d'égalité, l'assertion indique au module ErrorFilterModule que l'exception remplit la condition de levée.

Comme mentionné précédemment, ELMAH intègre plusieurs assertions, que vous pouvez combiner pour créer des conditions complexes. Par exemple, les assertions <and>, <greater> et <lesser> sont utilisées conjointement pour filtrer toutes les exceptions dont le code d'état HTTP de la réponse est compris entre 400 et 499 (inclus).

<elmah>
    ...
    <errorFilter>
        <test>
            <and>
                <greater binding="HttpStatusCode" value="399" type="Int32" />
                <lesser  binding="HttpStatusCode" value="500" type="Int32" />
            </and>
        </test>
    </errorFilter>
</elmah>

L'assertion <and> est une assertion composée étant vraie lorsque toutes ses sous-assertions sont également vraies. De même, il existe les assertions composées logiques <or> et <not>.

Utilisation de JScript

Si vous maîtrisez le langage de programmation JavaScript, vous pouvez également utiliser l'assertion <jscript> pour spécifier une condition de filtrage sous forme d'une simple expression JavaScript plutôt que d'utiliser un arbre complet d'assertions. Voici un exemple de condition complexe exprimée en JavaScript :

<elmah>
    ...
    <errorFilter>
        <test>
            <jscript>
                <expression>
                <![CDATA[
                // @assembly mscorlib
                // @assembly System.WebVersion=2.0.0.0Culture=neutralPublicKeyToken=b03f5f7f11d50a3a
                // @import System.IO
                // @import System.Web

                $.HttpStatusCode == 404
                || $.BaseException instanceof FileNotFoundException 
                || $.BaseException instanceof HttpRequestValidationException
                /En utilisant RegExp ci-dessous (voir http://msdn.microsoft.com/en-us/library/h6e2eb7w.aspx) */
                || $.Context.Request.UserAgent.match(/crawler/i)                      
                || $.Context.Request.ServerVariables['REMOTE_ADDR'] == '127.0.0.1' // IPv4 uniquement
                ]]>
                </expression>
            </jscript>
        </test>
    </errorFilter>
</elmah>

Remarque ! Pour les versions antérieures à ELMAH 1.2 SP2, remplacez $ dans l'exemple ci-dessus par le $context non documenté pour contourner le problème n° 278.

Ceci provoque le filtrage des erreurs lorsque l'une des conditions suivantes est vraie :

Filtrage par source

Avec la version 1.1, ELMAH permet le filtrage basé sur le module source, permettant ainsi de consigner toutes les erreurs, mais d'en envoyer une partie par courriel. Les trois propriétés disponibles pour la liaison permettant cette opération sont FilterSource, FilterSourceType et FilterSourceAssemblyName. FilterSource représente l'objet demandant le filtrage, tandis que FilterSourceType et FilterSourceAssemblyName sont de simples aides qui fournissent le type et le nom de l'assembly de l'objet source. L'utilisation de ces propriétés permet de configurer des assertions qui filtrent les erreurs en fonction de certains aspects de l'objet source. L'exemple suivant montre comment empêcher l'envoi d'erreurs HTTP 404 par courriel :

<elmah>
    ...
    <errorFilter>
        <test>
            <and>
                <equal binding="HttpStatusCode" value="404" type="Int32" />
                <regex binding="FilterSourceType.Name" pattern="mail" />
            </and>
        </test>
    </errorFilter>
</elmah>

L'assertion regex se lie au nom de type de l'objet source et vérifie s'il contient le mot «mail». Lorsque le module de messagerie d'ELMAH effectue le filtrage, FilterSource est une instance de Elmah.ErrorMailModule. L'assertion récupère donc le nom de type (quel que soit l'espace de noms) et filtre l'erreur si elle contient « mail ». Lorsque le module de journalisation d'ELMAH effectue le filtrage, FilterSource est une instance de Elmah.ErrorLogModule et l'assertion laisse passer l'erreur et la journalise.

Écrire votre propre assertion

L'écriture de vos propres assertions se fait en deux étapes. Tout d'abord, vous créez une classe implémentant l'interface IAssertion depuis l'espace de noms Elmah.Assertions. Cette interface contient une seule méthode, appelée Test, recevant un contexte en paramètre et doit renvoyer un résultat booléen selon que la condition représentée par l'assertion est remplie ou non. Ensuite, vous créez une méthode de fabrique qui sera appelée pour initialiser l'assertion lors de l'application de la configuration à l'exécution.

Voici comment l'interface IAssertion est définie :

  1. public interface IAssertion
  2. {
  3.     bool Test(object context);
  4. }

Et voici une implémentation très simple de l'interface qui renvoie toujours vrai quoi qu'il arrive :

  1. namespace MyAssertions
  2. {
  3.     public sealed class TrueAssertion : IAssertion
  4.     {
  5.         public bool Test(object context) 
  6.         {
  7.             return true;
  8.         }
  9.     }
  10. }

Si vous ajoutez cette assertion comme test de filtrage des erreurs, vous empêcherez ainsi les exceptions d'être journalisées ou envoyées par courrier. Certes, ce n'est pas très utile en pratique, mais cela devrait suffire pour en observer les effets.

Dans un deuxième temps, vous devez configurer une classe de fabrique pour vos assertions. Cette classe permet de convertir les éléments de configuration en objets IAssertion lors de l'exécution. Voici la fabrique d'assertions permettant de créer des objets TrueAssertion :

  1. namespace MyAssertions
  2. {
  3.     public sealed class AssertionFactory
  4.     {
  5.         public static bool always_true(XmlElement config) 
  6.         {
  7.             return new TrueAssertion();
  8.         }
  9.     }
  10. }

Pour simplifier les choses, la classe d'usine d'assertions doit respecter quelques conventions :

La classe factory héberge des méthodes factory, chacune chargée de créer, configurer et renvoyer un objet IAssertion. Chaque méthode factory doit respecter les règles suivantes :

Passons maintenant à l'utilisation de l'assertion réelle dans le fichier de configuration :

<errorFilter>
    <test 
        xmlns:my="http://schemas.microsoft.com/clr/nsassem/MyAssertions/MyAssertionsLib">
        <my:always-true />
    </test>
</errorFilter>

Puisque notre méthode s'appelle always_true, il suffit de nommer l'élément XML en conséquence. De plus, vous devez définir la portée de votre élément sur un espace de noms XML dont la valeur fournit les indications nécessaires pour l'espace de noms et l'assembly où se trouve la classe factory. Dans l'exemple ci-dessus, le préfixe my correspond à l'espace de noms XML http://schemas.microsoft.com/clr/nsassem/MyAssertions/MyAssertionsLib et est utilisé pour définir la portée de always-true. Voici une décomposition de la façon dont ELMAH convertit l'élément always-true en objet IAssertion :

Dans l'exemple de TrueAssertion, aucune configuration n'est nécessaire ; la méthode factory crée donc simplement une nouvelle instance et la renvoie. En fait, elle n'aurait même pas besoin de créer une nouvelle instance à chaque fois, puisque le résultat de l'assertion est constant.

Référence des assertions



Dernière mise à jour : Mercredi, le 23 novembre 2022