- Filtering Programmatically
- Filtering Declaratively via Configuration
When an unhandled exception is reported to ELMAH by ASP.NET, an application can decide whether to dismiss the exception or not. There are two ways for an application to do this, either programmatically or declaratively via the configuration file. The simpler of the two is programmatically because you do not need to learn anything new except write an event handler in your favorite language. The downside of the programmatic approach is that you need to write code and modify your web application (requiring possibly a static re-compile). With the configuration-based approach, you can simply apply filtering of exceptions to a running application.
Let’s assume the following:
- You have the
ErrorLogModuleinstalled and configured to log errors.
nameattribute of the entry under the
- You wish to filter exceptions of type
To add an exception filter programmatically, put the following in your
Global.asax file (or in the code-behind file):
It’s really simple as that. When the error logging module receives an exception, it raises the
Filtering event to see if any listeners wish to dismiss the exception. If an event handler calls the
Dismiss method of the
ExceptionFilterEventArgs object then the exception is not logged. Note that dismissing an exception does not clear the error so it still continues to surface as an unhandled exception. Dismissing here means that the module will not log it.
If you also have the
ErrorMailModule enabled, the filtering procedure is similar. You setup another event handler based on the name of the module and the event:
Naturally, you can combine the two by delegating to a common method so that you have the set of conditions for filtering in one place:
Filtering Declaratively via Configuration
Filtering via configuration is a bit more involved than the programmatic approach but the benefit is that you can apply it to any running web application without the need to modify or re-compile the application.
The first step is to configure an additional module called
Elmah.ErrorFilterModule. Make sure you add it after any of the logging modules from ELMAH, as shown here with
ErrorFilterModule takes care of subscribing to the
Filtering event of the modules that precede it. When the logging and mailing modules fire their
ErrorFilterModule runs through one or more assertions to decide whether to dismiss the exception or not. If the assertions test true, the exception is dismissed. The assertions are indicated in a separate configuration section. The configuration section is handled by
Elmah.ErrorFilterSectionHandler and must be registered as shown here:
ASP.NET applications running under medium trust should use the following version for the
section entry that specifies the additional
requirePermission attribute and sets its value to
Now you can add the actual section under the
In the above example, there is a single assertion that will be true when the
HttpStatusCode equals the integer value
404. There are a lot of components at work here so let’s dissect this a bit more. ELMAH comes with a number of built-in assertions and you can even add your own. The most interesting of the assertions test their environment or context for a condition. If the condition is met, the assertion outcome is true. The context is usually the exception that is being raised and the HTTP transaction running its course. Both of these are available to the handler of the
Filtering event as properties of the
ErrorFilterModule takes these two and adds a few more helper properties that are subsequently available as the total context for all assertions. The
<equal> assertion above has a
binding attribute that is actually a data-binding expression that you already know. It is the very same expression and syntax you specify to the
Eval method when data-binding within the server-side markup of ASP.NET pages and user controls. This expression is evaluated against the context so naturally
HttpStatusCode is a property of the context. The assertion evaluates the expression and uses the resulting value to compare against the literal given in the
value attribute. To compare apples to apples and oranges to oranges, you have to specify the type of the value expected from the binding expression as well as that of the text in the
value attribute. That is the purpose of the
type attribute, which can be one of the TypeCode enumeration members. Consequently, the current set of comparison assertions are limited to primitive types represented by the
TypeCode members (except
Object). This may seem limiting at first but it will suffice the majority of filtering you will need. For exotic cases, you can always write your own assertions. If you omit the
type attribute then the value type of
String is assumed. Finally, if the comparison succeeds, the assertion flags true and the exception is eventually dismissed. With this explanation in mind, here is a quick recap of what happens in the above example. First
HttpStatusCode is evaluated against the filter context. Its result is converted to a 32-bit integer and compared against the value 404. If they equal, the assertion tells the the
ErrorFilterModule that the exception matches the condition for dismissal.
As mentioned earlier, ELMAH ships with several built-in assertions and you can use them together to create complex conditions. For example, here the
<lesser> assertions are used together to filter all exceptions where the HTTP status code for the response falls in the integer range 400 to 499 (inclusive).
<and> is a composite assertion that results to true when all its sub-assertions also result to true. Likewise, you also have the logical
<not> composite assertions.
NOTE! For versions earlier than ELMAH 1.2 SP2, replace
$ in the example above with the undocumented
$context to work around issue #278.
The above causes errors to be filtered when any of the following is true:
- The status code is set to 404
- The root/base cause is
- The root/base cause is
- The user agent making the request identifies itself as “crawler”
- The request is from the local machine
Filtering By Source
With version 1.1, ELMAH allows filtering based on the source module, enabling scenarios like logging all errors but only having a subset being e-mailed. The three properties available for binding that enable this are called
FilterSource represents the object requesting filtering whereas
FilterSourceAssemblyName are just helpers that yield the source object’s type and assembly name. Using any of these, you can set up assertions that filter errors based on aspects of the source object. The following example shows how to prevent having 404 HTTP errors being mailed.
regex assertion binds to the type name of the source object and checks if it contains the word “mail”. When the mailing module from ELMAH is filtering,
FilterSource will be an instance of
Elmah.ErrorMailModule. The assertion will therefore get the type name (irrespective of namespace) and filter the error if it contains “mail”. When the logging module from ELMAH is filtering,
FilterSource will be an instance of
Elmah.ErrorLogModule and the assertion will let the error pass through and get logged.
Writing Your Own Assertion
Writing your own assertions is a two-step process. First, you create a class that implements the
IAssertion interface from the
Elmah.Assertions namespace. This interface contains only a single method called
Test that receives a context as parameter and which should return a Boolean result depending on whether the condition represented by the assertion is met or not. Next, you create a factory method that will be called to initialize the assertion as the configuration is applied at run-time.
Here is how the
IAssertion interface is defined:
And here is a very simple implementation of the interface that always returns
true no matter what:
If you were to add this assertion as an error filter test then you effectively end up suppressing exceptions from being logged or mailed. Granted it is not very useful in practice, but it should be good enough to see its effect in action.
As a second step, you need to setup a factory class for your assertions. The factory class is used to convert the configuration elements into
IAssertion objects at runtime. Here is the assertion factory for creating
To keep things simple, the assertion factory class is required to follow a few conventions, which are:
- The factory class must be public
- The factory class must live in a namespace
- The factory class must be called
The factory class hosts factory methods, each of which is responsible for creating, configuring and returning an
IAssertion object. Each factory method must observe the following rules:
- The factory method must be public.
- The factory method must be static (not bound to any class instance).
- The return value of the factory method must be
- The factory method must accept a single parameter of type
- The factory method must be named after the XML configuration element. An underscore may be used in the method name where a dash is expected in the XML element name such that
Now on to using the actual assertion in the configuration file:
Since our method was called
always_true, it is a simple matter of naming the XML element correspondingly. In addition, you need to scope your element to an XML-based namespace whose value provides the necessary hints for the namespace and assembly where the factory class is located. In the above example, the
my prefix corresponds to the XML namespace
http://schemas.microsoft.com/clr/nsassem/MyAssertions/MyAssertionsLib and is used to scope
always-true. Here is a deconstruction of how ELMAH converts the
always-true element into an
- The element prefix is used to lookup the corresponding XML namespace. If there is no prefix then the assertion is assumed to be one of the built-in ones provided by ELMAH.
- If the XML namesapce starts with
http://schemas.microsoft.com/clr/nsassem/then the remaining two path components are extracted as the type namespace followed by the assembly name where the assertion class factory can be found.
MyAssertionsLibassembly is loaded.
- The assembly is probed for the type
AssertionFactoryis simply dot-appended to the type namespace extracted from the XML namespace.
- The XML element name (
always-true) is unmangled by replacing dashes with underscores and then used to find a public and static method on
- Finally, the
always_truemethod is invoked (after passing signature compatibility checks) and given the entire XML configuration element as its sole parameter so that it can extract further information needed for the configuration of the actual assertion.
In the sample case of
TrueAssertion, there is no configuration to be done so the factory method simply creates a new instance and returns it. In fact, it wouldn’t even have to create a new instance each time since the assertion result is constant.
To be completed.