Tim

Footprints in the snow of a warped mind

ASPNetErrorReporting

Where to find me

Flickr Icon  Twitter Icon  Linked In Icon  FaceBook Icon  Windows Live Alerts Butterfly  RSS 2.0 

Tag Cloud

AJAX (4) ASP (6) ASP.Net (50) Error Reporting (4) Web Service (1) WSDL (1) Atlas (2) Business (76) Business Start-up Advice (25) Client (14) Expanding Your Business (17) C# (16) Canoeing (4) Canoe Racing (5) Cheshire Ring Race (5) Racing (2) Training (4) CIMA (1) Cisco (1) 7970G (1) CSS (3) dasBlog (4) DDD (1) Design (9) Icons (1) Development (12) General (39) Christmas (6) Fun and Games (11) Internet (22) Random (46) RX-8 (8) Home Cinema (2) Hosting (2) IIS (10) iPhone (1) JavaScript (4) jQuery (1) Marketing (5) Email (1) Multipack (1) Networking (2) Nintendo (1) OS Commerce (1) Photography (1) PHP (1) PowerShell (2) Press Release (1) Productivity (2) Security (2) SEO (5) Server Maintenance (4) Server Management (9) Social Media (1) Social Networking (2) Experiment (1) Software (9) Office (5) Visual Studio (12) Windows (4) Vista (1) SQL (1) SQL Server (13) Stored Procedure (1) Testing (1) The Site Doctor (104) Turnover Challenge (1) Twitter (2) Umbraco (17) 2009 (1) Web Development (54) WebDD (33) Wii (1)

Blog Archive

Search

<July 2010>
SunMonTueWedThuFriSat
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

Recent Comments

Blog Archive

Various Links

Blogs I Read

[Feed] Google Blog
Official Google Webmaster Central Blog
[Feed] Matt Cutts
Gadgets, Google, and SEO
[Feed] Ol' Deano's Blog
My mate Dean's blog on my space, equally as random as mine but not off on as much of a tangent!
[Feed] Sam's Blog
Sam is one of my younger brothers studying Product Design and Manufacture at Loughborough, this is his blog :) Enjoy!

Recent Tracks

last.fm - The Social Music Revolution

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

newtelligence dasBlog 2.2.8279.16125

Send mail to the author(s) Email Me (Tim Gaunt)

© 2010 Tim Gaunt.

Sign In

    # Thursday, July 09, 2009

    CodeGarden 09 Open Space Minutes -Space 2: Exception Handling in Umbraco

    Thursday, July 09, 2009 1:23:38 AM (GMT Daylight Time, UTC+01:00)

    Those of you lucky enough to go to CodeGarden '09 you'll know the format of the Open Space already but for those of you who didn't, Open Space is the time that the attendees are invited to talk about something they're interested in so I proposed two:

    1. Space 1: Selling Umbraco
    2. Space 2: Exception handing and error reporting in Umbraco (and other .net websites/applications)

    I'll write up the Selling Umbraco talk shortly but I wanted to put a few resources together for it first so decided to write this one up first.

    First of all we had a brief chat about how everyone handles errors in their applications and the various error handling options available. We discussed three options:

    1. Error Handler v2.0
    2. ELMAH
    3. Exceptioneer

    I've only had a brief look at ELMAH and found at the time it was a little too much in the way of RSS feeds etc and I just want an email alert, that said, Lee Kelleher has written a good article about integrating ELMAH with Umbraco here and I've written another article about integrating Error Handler v2.0 into Umbraco here so I'll overview how to integrate Exceptioneer into Umbraco here instead.

    Wiring up Exceptioneer with your site couldn't be easier, the best bit is that they do all the hard work for you with their "Integrate" section of the site but to give you a quick snapshot of how easy it is, first of all, download the dll and pop it into your bin folder. Then edit your web.config:

    <?xml version="1.0"?>
    <configuration> 
        <configSections> 
            <section name="Exceptioneer" type="Exceptioneer.WebClient.ClientModuleConfiguration, Exceptioneer.WebClient" requirePermission="true" /> 
        </configSections>
        
        <!-- This is where you get to specify your API Key and Application Name --> 
        <Exceptioneer ApiKey="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" ApplicationName="YOUR APPLICATION NAME" /> 
        
        <!-- If you're using IIS 6.0 or Visual Studio's built in web server you'll need to add this bit --> 
        <system.web> 
            <httpModules> 
                <add name="Exceptioneer" type="Exceptioneer.WebClient.ClientModule, Exceptioneer.WebClient" /> 
            </httpModules> 
            <!-- If you want to use the JavaScript handling then add the Http Handler as so --> 
            <httpHandlers> 
                <add path="ExceptioneerJavaScript.axd" verb="GET,POST" type="Exceptioneer.WebClient.JavaScriptHandler, Exceptioneer.WebClient" /> 
            </httpHandlers> 
        </system.web> 
        
        <!-- If you're using IIS 7.0 you'll need to add this bit too --> 
        <system.webServer> 
            <validation validateIntegratedModeConfiguration="false"/> 
            <modules> 
                <add name="Exceptioneer" preCondition="managedHandler" type="Exceptioneer.WebClient.ClientModule, Exceptioneer.WebClient" /> 
            </modules> 
            <handlers> 
                <add name="ExceptioneerJavaScript" path="ExceptioneerJavaScript.axd" verb="GET,POST" type="Exceptioneer.WebClient.JavaScriptHandler, Exceptioneer.WebClient" /> 
            </handlers> 
        </system.webServer>
    </configuration>

    Now, one of the coolest things about Exceptioneer is that you can now also debug JavaScript errors! To debug the javascript errors, just include this script in your templates:

    <script src="/ExceptioneerJavaScript.axd?Reporter=true" type="text/javascript"></script>

    That's it, you're done. Easy eh? If you want to know more about what it can do, Phil's put together this "lovely" video overview. Exceptioneer have done a great comparison of the main features of comparison Exceptioneer and ELMAH here, the downside though is Exceptioneer is still in beta.

    Remember, regardless of how good you think your code is, you should always integrate some form of error handling in your website even if it is just an email to alert you to the fact.

    # Friday, February 27, 2009

    Advanced Error Reporting in Umbraco, dasBlog and other ASP.Net sites

    Friday, February 27, 2009 3:51:13 PM (GMT Standard Time, UTC+00:00)

    If you've been following my blog you'll know that I've been raving about error reporting within ASP.Net (you can see my ASP.Net Error Reporting category for a couple of them if you like) but until now it's been limited to those sites that you have access to the global.asax file.

    One of the irritations I've found with Umbraco and dasBlog is that I don't get notified of errors as they're just logged to a text file/database somewhere. This is fine if you run 2 or 3 sites but we manage too many to check them all everyday. Instead we rely on email error notifications which until today have been a PITA to integrate into Umbraco.

    Today I'd like to introduce to you Error Handling v2.0 which instead of relying on the global.asax file for the error hooks, uses a HttpModule which means you can install it into any existing/pre-built application such as Umbraco and dasBlog.

    Adding it into the site is simple, you'll need to install the module into the web.config file and add the configuration section a sample (cut down) web.config is below:

    <?xml version="1.0"?> 
    <configuration> 
        <configSections> 
            <section name="tsdErrorsConfigSection" allowExeDefinition="MachineToApplication" restartOnExternalChanges="true" type="System.Configuration.NameValueFileSectionHandler, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> 
        </configSections> 
     
        <tsdErrorsConfigSection file="ErrorHandling.config"/> 
     
        <system.net> 
            <mailSettings> 
                <smtp from="you@yourdomain.com"> 
                    <network host="127.0.0.1" port="25" /> 
                </smtp> 
            </mailSettings> 
        </system.net> 
     
        <system.web> 
            <httpModules> 
                <add name="ErrorModule" type="TheSiteDoctor.ErrorHandling.ErrorModule, TheSiteDoctor.ErrorHandling" /> 
            </httpModules> 
        </system.web> 

    <!--  
    IIS 7 Settings 
        <system.webServer> 
            <validation validateIntegratedModeConfiguration="false" /> 
            <modules> 
                <add name="ErrorModule" type="TheSiteDoctor.ErrorHandling.ErrorModule, TheSiteDoctor.ErrorHandling" /> 
            </modules> 
        </system.webServer> 
    -->
    </configuration>

    Then you'll need to check all the settings -I recommend storing these in another .config file for clarities sake. Make sure you've configured your SMTP settings and you should be good to go.

    If you want to test your settings, I've included a test page for you that will check your settings and show you the defaults if you've not set them. I've got this running now on a couple of Umbraco and dasBlog installs without an issue.

    There's also a useful logging system in it which I'll look to overview in a later post but if you want to see it, check out the included aspx file.

    Download ErrorHandling_v2.0.zip (25Kb)

    If you do use this code I'd be interested to hear how you get on, I think it requires a little more refinement un some areas but it's pretty robust.

    Enjoy.

    # Monday, August 06, 2007

    Server cannot modify cookies after HTTP headers have been sent (RoleManagerModule.OnLeave)

    Monday, August 06, 2007 1:52:08 PM (GMT Daylight Time, UTC+01:00)

    Over the weekend we had to upgrade the server's version of ASP.Net AJAX which went fine until this morning when we started receiving the error "Server cannot modify cookies after HTTP headers have been sent."

    Luckily this wasn't bubbled up to the UI but incase other people are getting the error I thought I would share the fix. The error is apparently a known error with ASP.Net AJAX and the RolesManager. To fix the error all you need to do is add the following to your RolesManager web.config node:

    cacheRolesInCookie="false"


    The full error message was

    Message:
    Server cannot modify cookies after HTTP headers have been sent.

    StackTrace:
    at System.Web.HttpCookieCollection.Add(HttpCookie cookie)
    at System.Web.Security.RoleManagerModule.OnLeave(Object source, EventArgs eventArgs)
    at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
    at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

    # Monday, March 19, 2007

    How to use Phil's error reporting code

    Monday, March 19, 2007 7:34:20 PM (GMT Standard Time, UTC+00:00)

    I’ve done a number of posts now on Phil Whinstanley’s error reporting class and this blog appears to be getting a lot of hits because of that which is pretty neat, as a result I’ve had a couple of people write to me asking similar questions about the code so I thought it would be an idea to write a little summary.

    Where can I download the code?

    It would appear that most of the old copies of Phil’s code have disappeared from the web, I’m not sure why so I’ve uploaded the versions I’ve got below. For convenience I have compiled the code into DLLs for those that don’t know/want to do this and I’ve also included the Visual Studio solutions. I’m not sure if these are based on the original codebase but I don’t think I’ve made any major alterations to these versions:

    1 This is a version I was sent as his original including changes and example email was lost...

    DLLs only:

    If you have Visual Studio:

    If you don’t have Visual Studio you can either download one of the above projects and delete the solution/project files or download the original WebException code. Ok, now you have the files :) -FWIW I can accept no responsibility for any of the files or the code, I just zipped them!

    How to do I use the WebException class?

    I’m now using a slightly modified version of the code to enable error reporting within AJAX (see: Reporting errors from AJAX using the WebException Class) which I’ll try and upload later but whichever version of the code you choose the use is pretty much the same.

    Once you have referenced the DLL in your project (see: Importing/Referencing DLLs in Visual Studio) you will be able to use the WebException. As I’ve covered what you need to do to use the code from within an AJAX application in another post (see: Reporting errors from AJAX using the WebException Class) I’ll just cover how to use it to report global errors. To capture and respond to all application errors you will need to place this code within the global.asax, your project should automatically have one, if it doesn’t then you will need to add one.

    Using the global.asax file, the first thing you need to do is add a reference to the DLL at the top of your code (this will allow you to call the methods and access the properties):

    <%@ Import Namespace="ErrorReporting" %>

    Next locate the Application_Error event handler, this is the method that handles all errors within the application (with exception of those thrown from within an AJAX application, read this post to report errors from within an AJAX application). Now replace your Application_Error and Application_PreRequestHandlerExecute handlers with (for more information on what I'm doing here see: ASP.Net WebException and Error Reporting useful code):

    void Application_Error(object sender, EventArgs e)
    {
        bool reportErrors = Convert.ToBoolean(System.Configuration.ConfigurationManager.AppSettings["SendErrors"]);

        if (reportErrors)
        {
            Exception currentError = Server.GetLastError();

            #region Deal with 404's

            //Redirect the user to a friendly page
            if(CheckForErrorType(currentError, "FileNotFound"))
                RedirectToFriendlyUrl("");

            #endregion
            #region Deal with Spambots

            if (CheckForErrorType(currentError, "System.FormatException"))
            {
                if (HttpContext.Current.Request.Form.Count > 0)
                {
                    foreach (string key in HttpContext.Current.Request.Form)
                    {
                        if (key.IndexOf("_VIEWSTATE") > 0 && HttpContext.Current.Request.Form[key].ToString().IndexOf("Content-Type") > 0)
                            return;
                    }
                }
            }

            #endregion

            //Enable the trace for the duration of the error handling
            TraceContext t = HttpContext.Current.Trace;
            bool bCurrentState = t.IsEnabled;
            t.IsEnabled = true;

            #region Handle the Exception

            ErrorHandling.WebException WE = new ErrorHandling.WebException();
            WE.CurrentException = Server.GetLastError();
            WE.MailFrom = "you@yourdomain.com";
            WE.MailTo = "you@yourdomain.com";
            WE.MailAdmin = "you@yourdomain.com";
            WE.Site = "Your Site's Name or URL";
            WE.SmtpServer = "localhost";
            WE.FloodCount = 10;
            WE.FloodMins = 5;

            #endregion
            #region Choose what you're interested in

            WE.ReturnCache = true;
            WE.DrillDownInCache = true;
            WE.IncludeApplication = true;
            WE.IncludeBrowser = true;
            WE.IncludeEnvironmentVariables = true;
            WE.IncludeForm = true;
            WE.IncludeProcess = true;
            WE.IncludeQueryString = true;
            WE.IncludeRequestCookies = true;
            WE.IncludeRequestHeader = true;
            WE.IncludeResponseCookies = true;
            WE.IncludeServerVariables = true;
            WE.IncludeSession = true;
            WE.IncludeTrace = true;
            WE.IncludeVersions = true;
            WE.IncludeAuthentication = true;

            #endregion

            WE.Handle();

            //Return the trace to its original state
            t.IsEnabled = bCurrentState;

            //Redirect the user to a friendly page
            RedirectToFriendlyUrl("");
        }
    }

    protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e)
    {
        if (Context.Handler is IRequiresSessionState || Context.Handler is IReadOnlySessionState)
            ErrorReporting.SessionTracker.AddRequest("Pre Request Handler Execute"truetruefalse);
    }

    private bool CheckForErrorType(Exception ex, string errorText)
    {
        if (ex != null)
        {
            //Check the exception
            if (ex.GetType().ToString().IndexOf(errorText) > 0)
                return true;
            else
                return CheckForErrorType(ex.InnerException, errorText);
        }
        else
        {
            return false;
        }
    }

    private void RedirectToFriendlyUrl(string Url)
    {
        if (!String.IsNullOrEmpty(Url) && (Request.Url.Host.IndexOf("localhost") < 0))
            Response.Redirect(Url);
    }

    This will create a new instance of the WebException object, assign the various properties accordingly (you will need to configure these) and then finally handle the error.

    That’s it! That’s all you really need to do to have super error reporting instantly installed in your application! If that wasn't enough it's overloaded with a couple of filters for you :). I recommend you read one of my previous posts I’ve added which overviews a few simple tips and tricks when using the WebException class to that improves on its functionality (see: ASP.Net WebException and Error Reporting useful code).

    All that's left to do is to test it works (see below).

    What should I get from it?

    That’s the million dollar question! Once the WebException class has been added to your application you should receive an email every time the application throws an error (which of course means you’ll never get an email from the system!)

    View an example of the email you’ll get with all outputs set to true.

    More tips/Warnings!

    Ok so it’s installed and you’re getting no errors through (because your codes perfect) but there are a couple of other little tweaks I would make to the WebException class to make it a little more useable.

    Create a centralised class for it

    A while ago I posted a set of “useful” tips for reducing the number of spambot related emails, redirecting the user etc (see: ASP.Net WebException and Error Reporting useful code). That’s fine until you start including the WebException class into multiple projects, managing tweaks to the codebase gets a little cumbersome (i.e. adding the spambot check to all our projects that use the WebException meant a couple of hours of copying and pasting). The work around for me was to wrap it all up into a central static method (see: Reporting errors from AJAX using the WebException Class). I did this rather than fiddling with Phil’s WebException class itself incase he ever got around to releasing another version which would mean a bunch of changes etc.

    Limit the page request log

    If you have a site where every user is likely to have a high page visit count with most of the pages involving some form of form submission then it may be worth limiting the number of request’s stored as we have found that without limiting these we start receiving very large emails (some topping 10MB).

    The reason this is happening is because the session tracker logs all the form elements for the request so if you had i.e. a CMS that submits a page of content every other page request all that data will be stored in the tracker, sticking with the idea of a CMS, your typical text word is around 10bytes (see: How many bytes for...), so say the user writes 500 words per page (which isn’t really a lot) that’s 4.9Kb per form submission plus on the re-display of the page you've got ViewState... That’s just the data submitted by the user, around that, you’ve got all the form fields, field names, session info, query string etc, see how it starts to add up?

    The solution is fairly straight forward, what you need to do is alter SessionTracker.cs1:

    1I thought I'd done this in a project already but cannot find the source so this may not work.

    public class SessionTracker
    {
        public static void AddRequest(string Comments, bool DoForm, bool DoQueryString, bool DoCookies)
        {
            Request R = new Request();
            R.Time = DateTime.Now;
            R.Comments = Comments;
            
            if (System.Web.HttpContext.Current != null)
            {
                R.Path = System.Web.HttpContext.Current.Request.Path.ToString();
                if (System.Web.HttpContext.Current.Request.UrlReferrer != null)
                {
                    R.Referrer = System.Web.HttpContext.Current.Request.UrlReferrer.ToString();
                }
                if (DoForm)
                {
                    R.Form = System.Web.HttpContext.Current.Request.Form;
                }
                if (DoQueryString)
                {
                    R.QueryString = System.Web.HttpContext.Current.Request.QueryString;
                }
                if (DoCookies)
                {
                    R.Cookies = System.Web.HttpContext.Current.Request.Cookies;
                }
            }

            if (System.Web.HttpContext.Current.Session["RequestCollection"] != null)
            {
                RequestCollection RC = ((RequestCollection)System.Web.HttpContext.Current.Session["RequestCollection"]);
                RC.Add(R);
                if(RC.Count > 10)
                    RC.RemoveAt(0);
                System.Web.HttpContext.Current.Session["RequestCollection"] = RC;
            }
            else
            {
                RequestCollection RC = new RequestCollection();
                RC.Add(R);
                System.Web.HttpContext.Current.Session["RequestCollection"] = RC;
            }
        }

        public static void AddRequest(string Comments)
        {
            Request R = new Request();
            R.Time = DateTime.Now;
            R.Comments = Comments;
            
            if (System.Web.HttpContext.Current != null)
            {
                R.Path = System.Web.HttpContext.Current.Request.Path.ToString();
                if (System.Web.HttpContext.Current.Request.UrlReferrer != null)
                {
                    R.Referrer = System.Web.HttpContext.Current.Request.UrlReferrer.ToString();
                }
                R.Form = System.Web.HttpContext.Current.Request.Form;
                R.QueryString = System.Web.HttpContext.Current.Request.QueryString;
                R.Cookies = System.Web.HttpContext.Current.Request.Cookies;
            }

            if (System.Web.HttpContext.Current.Session["RequestCollection"] != null)
            {
                RequestCollection RC = ((RequestCollection)System.Web.HttpContext.Current.Session["RequestCollection"]);
                RC.Add(R);
                if (RC.Count > 10)
                    RC.RemoveAt(0);
                System.Web.HttpContext.Current.Session["RequestCollection"] = RC;
            }
            else
            {
                RequestCollection RC = new RequestCollection();
                RC.Add(R);
                System.Web.HttpContext.Current.Session["RequestCollection"] = RC;
            }
        }

        public static void AddRequest()
        {
            Request R = new Request();
            R.Time = DateTime.Now;
            
            if (System.Web.HttpContext.Current.Session["RequestCollection"] == null)
            {
                RequestCollection RC = ((RequestCollection)System.Web.HttpContext.Current.Session["RequestCollection"]);
                RC.Add(R);
                if (RC.Count > 10)
                    RC.RemoveAt(0);
                System.Web.HttpContext.Current.Session["RequestCollection"] = RC;
            }
            else
            {
                RequestCollection RC = new RequestCollection();
                RC.Add(R);
                System.Web.HttpContext.Current.Session["RequestCollection"] = RC;
            }
        }

        public SessionTracker()
        {
        }
    }

    Outputting the Trace with the WebException Class

    I know this is something I’ve posted about in the past but since moving to version 4 of the code and .Net 2.0 I was no longer getting the trace in my lovely error reports, after a little digging I’ve found a solution, in addition to the code that I posted earlier about enabling the trace using C#, the web.config needs to be set as follows:

    <trace enabled="true" requestLimit="100" pageOutput="false" traceMode="SortByTime" localOnly="true" />

    Storing the WebException code in App_Code Dir

    If you use the WebException class in an ASP.Net 2.0 site, be careful you don’t do what we did and throw the site online uncompiled with a compilation error as it won’t get reported. Luckily I found this issue on a test site but it’s still worth noting.

    Personally I wouldn’t put the error reporting code in the App_Code directory as this means you’ll end up needing to maintain a plethora of files throughout various projects. Instead compile a separate DLL and include that in your projects, then if like me you find a nice addition to the error reporting code you can easily update all sites to the latest version!

    Setup a simple generic test page

    Nothing fancy, just a button that throws an exception will do:

    TestErrorPage.aspx

    <%@ Page Language="C#" %>


    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <script runat="server">
        protected void btnError_Click(object sender, EventArgs e)
        {
            throw new ArgumentException("Test Error");
        }
    </script>

    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Test Error Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
            <p><asp:Button runat="server" ID="btnError" Text="Throw Error" OnClick="btnError_Click" /></p>
        </div>
        </form>
    </body>
    </html>

    Happy Error Reporting :) -I'm hoping this is the last time I need to blog about this code but what's the betting another post is around the corner ;)