asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search








Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology
International

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Web Application Error Handling and Logging For ASP
By Adam Tuliper
Rating: 4.3 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

    Introduction

    Let's face it. Up until .NET there was no real error handling in ASP code. Sure, you have your "On Error Resume Next" and "On Error Goto 0", but there was no good structured error handling. I can truly say there has not been one Web application I have come across of decent proportions that handles all errors that can arise. Almost every one would break if the database was down, rather than give the user a nice message. There comes a time in every developer's life when he realizes he is lacking something. Most developers I come across lack error-handling skills.

    I think that the most important aspect of an application is not its feature set, but how well it responds to the user, and this includes response to errors.

    An application can be full of features, but one or two unhandled bugs in the right spot can ruin a great application. What I am hoping to show here is a great way to handle errors in Web applications. As Web developers, we have one easy advantage that fat-client development does not usually have. We have the ability to catch all of our errors from a top level and handle them gracefully.

    How do you know if users are getting errors?

    It's the truth. Chances are if a user has a problem at your Web site, you will never know about it unless you:

    1. Have a way to know automatically when an error occurs
    2. The user is very upset and calls your company

    Wouldn't it be great to:

    1. Receive an automatic e-mail that contains details of the errors when an error occurs
    2. Get descriptive information about the error
    3. Know where in your code the error happened
    4. Log such information as the user, ip, and all form information
    5. Handle multiple applications and Web sites from one error handling page
    6. Provide a reference number and a friendly message to users
    7. Prevent evil-doers from possibly compromising your Web site

    All when an error occurs?

    This would enable you to be proactive, rather than reactive. Users who are on an intranet application who receive an error could actually be called about the error before their anger level rises against the IT staff. Your end users will be very happy. With a little bit of ASP coding, SQL, and a little help from IIS 5, this can all be accomplished. At the end of the article is a zip file that contains four files:

    • 500-100.asp - The actual error-handling page
    • ErrorLog.sql - A SQL script to create the database, table, and user for SQL Server
    • TestPost.asp - A small web page with form data that posts to test.asp
    • Test.asp - A small file that generates an error

    Prevent Crucial Information From Leaking Out

    This technique also helps make your Web site more hack proof. First I must ask one thing of anyone reading this. Please, please, please validate your input from Web pages! Assume no input is safe. There are so many applications out there on the Internet that do not validate input. Please do not become another statistic. Check all of your Web applications and make sure you handle errors not only in your application, but also at a site level as mentioned in this article. There will probably be some bug we didn't find in our application. So even with error handling on your pages, it may still be possible to raise an error in your application. These errors can give evildoers ways to get into the information in your site and do damage. As a side note, strip all invalid characters from input. One good article that outlines this is: Protecting Your Web Application Against Dangerous Requests (http://www.15seconds.com/issue/020701.htm).

    Getting Into The Details

    When an unhandled error happens in IIS, IIS performs a Server.Transfer to one of many error pages. You can see the error pages defined in IIS by

    1. Right clicking on a website and selecting properties
    2. Clicking the custom errors tab

    When looking in this window, you will notice errors are listed by error code. Some of the errors will have a semicolon after them followed by another number. This denotes a "Sub Error Code". Values from 100-199 are for informational messages. Values from 200-299 indicate the request was successful. Values from 300-399 indicate a redirection occurred on the server. Values from 400-499 indicate a client error occurred. Values from 500-599 indicate a server error occurred. Listed below are some error numbers and their descriptions (a generalized list is available at http://msdn.microsoft.com/library/en-us/act/htm/actml_ref_hsco.asp?frame=true)

    400      Bad Request
    401;1    Unauthorized - Login Failed
    401;2    Unauthorized - Login failed due to server configuration
    404      Not Found
    500      Internal Server Error
    500;12   Internal Server Error - Application Restarting
    500;13   Internal Server Error - Server Too Busy
    500;15   Internal Server Error - Direct Requests For Global.asa
    500;100  Internal Server Error - ASP Error
    
    The one we will concern ourselves with here is the 500;100 error. This error deals with the compiling or running of an ASP file and is raised automatically when there is an unhandled ASP error.

    (note: if you want to implement your own "404 page not found" page with your own look and feel, here is where you would do it. Create your own Web page and edit the properties of the 404 error to point to your new page)

    When an unhandled error occurs, IIS catches the exception and does a Server.Transfer to the error page specified in the Custom Errors configuration screen. A Server.Transfer takes all the information posted to one page and "Transfers" it to another page. This is great news for us because it means not only can we log the URL on which the error happened, but we can also log all the form data that was posted to the page causing the error. This can go a long way in helping to reconstruct the error, especially when users don't remember what they did.

    What we will want to do is to create out own error page and put it somewhere on the Web site. I suggest selecting the 500;100 error in IIS, select "URL" for the message type. The URL should be based off of your Web site's root. My recommended way is to create a folder or virtual directory off of your root Web called Errors. Place the new ASP file that we will create into this directory. In the 500;100 configuration screen, for the URL, enter in /Errors/500_100.asp and click OK.

    Once IIS redirects to the error page, we will have several treasure chests of information available to us about the error and its source. In IIS, Server.GetLastError will return an ASPError object. This object contains the following properties:

    ASPCode - Returns an error code generated by IIS. This can be empty in several cases; one being where you raise an error inside a component (Err.Raise) and your ASP code does not handle it.

    Ex. ASP 0177 - The call to Server.CreateObject failed. The request object instance Cannot be created.

    Number - Returns the standard COM error code

    Ex. -2147467238

    Source - Returns the actual source code, when available, of the line that caused the error

    Category - Indicates if the source of the error was internal to ASP, the scripting language, or an object

    File - Indicates the name of the .asp file that was being processed when the error occurred

    Ex. /global.asa

    Line - Indicates the line within the .asp file that generated the error

    Ex. 16

    Column - Indicates the column position within the .asp file that generated the error

    Ex. 5

    Description - Returns a short description of the error

    Ex. Server.CreateObject Failed

    ASPDescription - Returns a more detailed description of the error if it is an ASP-related error

    Ex. The server process could not be started because the configured identity is incorrect. Check the username and password.

    If I had a file named test.asp that contained:

    
    <% 
       Dim clsTest
       Set clsTest = Server.CreateObject("SomeInvalidName.ClassName")
    %>
    
    
    Then the ASPErr object would contain:
    ----------------------------------
    ASPCode: ASP 0177 
    Number: -2147221005 
    Source:  
    Category: Server object 
    File: /test.asp 
    Line: 3 
    Column: -1 
    Description: Server.CreateObject Failed 
    ASPDescription: Invalid ProgID. For additional information specific to this
    message please visit the Microsoft Online 
    Support site located at: http://www.microsoft.com/contentredirect.asp. 
    ----------------------------------
    
    There are also some important pieces of information in the HTTP headers. We will want to log these also. They are very simply found in the Request.ServerVariables collection.

    ------------------------------------------------
    Request.ServerVariables("ALL_HTTP") - All of the HTTP headers (useful for systems like Netegrity and other authentication systems that rely on HTTP headers to pass information around)

    Request.ServerVariables("REQUEST_METHOD") - This tells us if its a Get or Post, so we can then log the form parameters if its a post

    Request.ServerVariables("SERVER_PORT") - Generally this is 80 (regular http port) or 443 if it is a secure connection

    Request.ServerVariables("HTTPS") - This will be "off" or "on"

    Request.ServerVariables("LOCAL_ADDR") - The Server's addrss the error happened on. Crucial if you are running in an environment multiple servers that use one log file

    Request.ServerVariables("REMOTE_ADDR") - The address of the client

    Request.ServerVariables("HTTP_USER_AGENT") - Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)

    Request.ServerVariables("URL") - The url of the page the error occured on
    ------------------------------------------------

    Accessing these parameters from the error page is simple. If my error page only contained the following code:

    
    <%
    Set ASPErr = Server.GetLastError()
    Response.Write ASPErr.ASPDescription & "<br>"
    Response.Write Request.ServerVariables("URL") & "<br>"
    %>
    
    
    Then my output would be:
    -------------------------------------
    Invalid ProgID. For additional information specific to this message please visit 
    the Microsoft Online Support site located at: http://www.microsoft.com/contentredirect.asp
    /test.asp
    -------------------------------------
    
    Here is another example. If I forget a ")" on the end of my line of code then the error information will look like this:
    ASPCode  
    Number -2146827282 
    Source Response.Write (CurrentInterestRateEffectiveDate & 
    " - " & DateAdd("m",3,CurrentInterestRateEffectiveDate) 
    Category Microsoft VBScript compilation
    File /navigator55integ_Local/proposalsmain/proposals/proposalscreen.asp 
    Line 962 
    Column 106 
    Description Expected ')' 
    ASPDescription 
    
    Of course we want a little bit more information than this. We also want to log this information from a database. But, what if the error page was invoked because the database server was down? This is quite possible, and luckily it is easy to get around. In my sample code you will notice that before the information is logged to the database, it is written to a test file. Once you get an error page running on your system, test it out to make sure the file you are writing to has appropriate permissions. By default, it will need write access for the IUSR_MACHINENAME account that your Web site is running under.

    To sum everything up, here is the data we will log to a database:

    1. Session ID
    2. Request Method
    3. Server Port
    4. Was it HTTP or HTTPs
    5. The server's address
    6. The user's address
    7. The user's browser type
    8. The URL that caused the error
    9. A reference id the customer can use when talking to support
    10. All of the posted form data
    11. All of the HTTP headers
    12. ASPCode
    13. Number
    14. Source
    15. Category
    16. File
    17. Line
    18. Column
    19. Description
    20. ASPDescription
    21. The date and time the error occurred.

    Phew! This is a lot of great information. Most of this information should allow you to reconstruct an error on your system so you can zap those bugs. One idea that just came to mind, if you are having one of those killer bugs you just cannot get and you wish you had all of the prior form posted information, you could use Server.Transfer on a Web page to go to a logging page like I've described. The logging page would then log all of the form and URL information, then append a "Logged=Y" to the URL and then do a Server.Transfer to the original page. You would then have an entire log of every single bit of form data and URL information to track down an error. Cool, isn't it? (Hey.. I think so.)

    To log the information, let's first create a database into which to log it all. Open up SQL Server's query analyzer and run this script. This will create the database and a table named errors. This could easily be converted to Microsoft access.

    
    CREATE DATABASE [ErrorLog]
    GO
    
    Use ErrorLog
    
    CREATE TABLE [dbo].[Errors] (
    	[ID] [int] IDENTITY (1, 1) NOT NULL ,
    	[SessionID] [char] (12)  NULL ,
    	[RequestMethod] [char] (5) NULL ,
    	[ServerPort] [char] (5) NULL ,
    	[HTTPS] [char] (3) NULL ,
    	[LocalAddr] [char] (15) NULL ,
    	[HostAddress] [char] (15) NULL ,
    	[UserAgent] [varchar] (255) NULL ,
    	[URL] [varchar] (400) NULL ,
    	[CustomerRefID] [varchar] (20) NULL ,
    	[FormData] [varchar] (2000),
    	[AllHTTP] [varchar] (2000),
    	[ErrASPCode] [char] (10) NULL ,
    	[ErrNumber] [char] (11) NULL ,
    	[ErrSource] [varchar] (255) NULL ,
    	[ErrCategory] [varchar] (50) NULL ,
    	[ErrFile] [varchar] (255) NULL ,
    	[ErrLine] [int] NULL ,
    	[ErrColumn] [int] NULL,
    	[ErrDescription] [varchar] (1000) NULL ,
    	[ErrAspDescription] [varchar] (1000) NULL ,
    	[InsertDate] [datetime] NOT NULL 
    ) ON [PRIMARY]
    
    
    In the code that you will download, you will notice a section commented out like this:
    
    <%If False Then%>
    	<tr>
    <td>ASPCode</td><td><%=AspErr.ASPCode%>
    </td>
    	</tr>
    ....
    ....
    ....
    	<tr>
    <td>URL</td><td><%=Request.ServerVariables("U
    RL")%></td>
    	</tr>
    <%End If%>
    
    
    The reason I did this was to leave the code in it; you will want to use on your development system. It would be quite annoying if every time an error happened on your development system, you had to look at the database. So, uncomment this block by removing the "if" statement to use it on your development system. For your production systems, remove the entire block if you wish. The meat of the database logging code looks like this:

    (btw I do not recommend storing login names and passwords directly in the asp files /ever/, but it is included here just for simplicity)

    
    Sub LogErrorToDatabase()
    Dim con
    Dim rstError
    
    	
    	Set con = Server.CreateObject("ADODB.Connection")
    	Set rstError = Server.CreateObject("ADODB.Recordset")
    	
    	con.open "dsn=ErrorLog;uid=ErrorLog;pwd=secret;"
    	Set rstError.ActiveConnection = con
    	rstError.Open "Select * From Errors",,3,3 'adOpenStatic, adLockOptimistic
    	rstError.AddNew
    	
    	With rstError
    		.Fields("SessionID") = Session.SessionID
    		.Fields("RequestMethod").Value = Request.ServerVariables("REQUEST_METHOD")
    		.Fields("ServerPort").Value = Request.ServerVariables("SERVER_PORT")
    		.Fields("HTTPS").Value = Request.ServerVariables("HTTPS")
    		.Fields("LocalAddr").Value = Request.ServerVariables("LOCAL_ADDR")
    		.Fields("HostAddress").Value = Request.ServerVariables("REMOTE_ADDR")
    		.Fields("UserAgent").Value = Request.ServerVariables("HTTP_USER_AGENT")
    		.Fields("URL").Value = Request.ServerVariables("URL")
    		.Fields("CustomerRefID").Value = mstrCustRefID
    		.Fields("FormData").Value = Request.Form
    		.Fields("AllHTTP").Value = Replace(Request.ServerVariables("ALL_HTTP"),vbLf,vbCrLf)
    		.Fields("ErrASPCode").Value = AspErr.ASPCode
    		.Fields("ErrNumber").Value = AspErr.Number
    		.Fields("ErrSource").Value = AspErr.Source
    		.Fields("ErrCategory").Value = AspErr.Category
    		.Fields("ErrFile").Value = AspErr.File
    		.Fields("ErrLine").Value = AspErr.Line
    		.Fields("ErrColumn").Value = AspErr.Line
    		.Fields("ErrDescription").Value = AspErr.Description
    		.Fields("ErrAspDescription").Value = AspErr.AspDescription
    		.Fields("InsertDate").Value = Now()
    	End With
    	
    	rstError.Update
    	rstError.Close
    	con.Close
    	Set rstError = Nothing
    	Set con = Nothing
    
    End Sub
    
    

    Summary

    I cannot stress enough the importance of handling all errors that can come up in a Web application. By using a top-level error page like this, you can catch important error information while letting users know something will be done about their problem. I hope this article has tought you something important about application development. This will hopefully decrease your system's down time by providing you with crucial information for finding and fixing errors.

    To set this up on your system, simply download the zip file and:

    1. Create the errorlog database from the sql script ErrorLog.sql
    2. You created the DSN named ErrorLog to access the database created in step 1
    3. You told IIS to handle 500;100 errors to the 500-100.asp custom error handler page in the custom errors section of the website properties in IIS.
    4. If you are debugging currently in Interdev, chances are a msgbox will popup telling you an error occured. This is because your running debugger (Interdev) will catch some IIS exceptions. Stop debugging, and it should go right to the error page. I recently installed the visual studio.net on my machine and I get a prompt asking me if I want to use the selected debugger when an error occurs. Just click no and it will flow through to the error page.

    Other possibilities to add to this application might be:

    1. If your site has multiple applications on it, you could store an application variable containing an application name to log it.
    2. Log user information depending on your authentication methods.
    3. Add a trigger to the database or the Web page (components like ASPMail are good for this) to e-mail you when an error occurs.
    4. Create a Web page that simply queries these errors so you don't always have to go back to the database to check for errors.

    About the Author

    Adam Tuliper's professional career involes developing .Net, COM, VB, ASP, C, C++, and SQL Server solutions for business solutions and consumer applications. He's been developing for over six years professionaly. He is also the owner of gecko software, where they develop Internet center software, system monitoring, and security scanning software. He has a degree in CIS. He also serves as a security consultant to several companies. You can reach him at amt@gecko-software.com.

  • Rate This Article
    Not HelpfulMost Helpful
    1 2 3 4 5
    Supporting Products/Tools
    AspEncrypt
    Built around the Microsoft CryptoAPI, AspEncrypt helps you harness all major encryption and hashing algorithms such as DES, Triple-DES, RC2, RC4, RSA, MD5 and SHA1 in just a few lines of code. The component can be used in tandem with AspEmail to send encrypted and signed mail in the industry-standard S/MIME format, or with AspUpload to encrypt files as they are being uploaded. AspEncrypt can also be used to issue and manage X.509 digital certificates.
    [Top]
    AspPDF
    AspPDF is an ASP/ASP.NET component which enables generation and management of documents in PDF format. Features include advanced text formatting, font embedding, form fill-in, images, tables, content and page extraction, document stitching, encryption, digital signatures, and more.
    [Top]
    Other Articles
    Feb 3, 2005 - ASP.NET Mixed Mode Authentication
    In many web applications it is desirable for both intranet users and external parties to be able to seamlessly log onto the system. The problem this raises is that it is not easy to allow intranet users to log in via Windows integrated authentication while also allowing external parties to log in to the same application using standard forms authentication. This article will show you one way to achieve the best of both worlds when it comes to authentication.
    [Read This Article]  [Top]
    Dec 8, 2004 - Designing Role-Based Security Models for .NET
    In this article, Michele Leroux Bustamante discusses authentication, authorization and role-based security in .NET. Along the way, he provides some best practices for implementing role-based security in some typical .NET application scenarios including rich clients, Web applications, and Web services.
    [Read This Article]  [Top]
    May 11, 2004 - SharePoint Security and .NET Impersonation
    When implementing custom components that require access to restricted resources, implicit impersonation must be used. Jay Nathan shows how to create a class that makes using .NET Impersonation a snap.
    [Read This Article]  [Top]
    Mar 10, 2004 - Intellectual Property Protection and Code Obfuscation
    Learn about the execution process of CLR-based programs and how to protect your applications from being easily disassembled back into source code.
    [Read This Article]  [Top]
    Feb 24, 2004 - How to Send Secure Mail in ASP-Based E-Commerce Applications - Part II
    Businesses that utilize encrypted e-mail may find Secure Multipurpose Internet Mail Extensions (S/MIME) to be somewhat restrictive. This article shows how to use security features in PDF as an alternative to S/MIME.
    [Read This Article]  [Top]
    Feb 2, 2004 - Fighting Spambots with .NET and AI
    Bill Gates, in a recent interview, predicted the end of spam by 2006. One of the methods he mentioned involved a challenge only a real live person could handle. Adnan Masood shows how to use AI and .NET to create a user verification scheme that incorporates similar concepts Gates alluded to.
    [Read This Article]  [Top]
    Jan 21, 2004 - Configuring .NET Code Access Security
    Code Access Security (CAS) is the .NET Framework security model that grants code permission to resources based on "evidence" pertaining to the encapsulating assembly. In this article, David Myers examines CAS and explains different configuration methods.
    [Read This Article]  [Top]
    Mar 10, 2003 - Platform Neutral and Transparent Encryption of Sensitive Customer Information
    Zhenlei Cai combines an open source C++ encryption library with SQL Server extended stored procedures to create a platform neutral, transparent encryption solution that resides at the database layer.
    [Read This Article]  [Top]
    Jan 15, 2003 - Exploring Machine.Config - User Security and More
    Christopher Spann offers a .NET configuration tip that should help ease system administrators' fears of security compromise and thus assuage growing developer demand for a .NET environment.
    [Read This Article]  [Top]
    Dec 10, 2002 - Encrypting Cookie Data with ASP.NET
    You don't have to be a cryptography expert or spend lots of money on third-party components to secure sensitive data in .NET. In this article, Wayne Plourde shows just how easy it is to encrypt cookie data using encryption classes in the .NET System.Security.Cryptography namespace.
    [Read This Article]  [Top]
    Mailing List
    Want to receive email when the next article is published? Just Click Here to sign up.

    Support the Active Server Industry



    JupiterOnlineMedia

    internet.comearthweb.comDevx.commediabistro.comGraphics.com

    Search:

    Jupitermedia Corporation has two divisions: Jupiterimages and JupiterOnlineMedia

    Jupitermedia Corporate Info


    Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

    Advertise | Newsletters | Tech Jobs | Shopping | E-mail Offers

    Solutions
    Whitepapers and eBooks
    Microsoft Article: HyperV-The Killer Feature in WinServer ‘08
    Avaya Article: How to Feed Data into the Avaya Event Processor
    Microsoft Article: Install What You Need with Win Server ‘08
    HP eBook: Putting the Green into IT
    Whitepaper: HP Integrated Citrix XenServer for HP ProLiant Servers
    Intel Go Parallel Portal: Interview with C++ Guru Herb Sutter, Part 1
    Intel Go Parallel Portal: Interview with C++ Guru Herb Sutter, Part 2--The Future of Concurrency
    Avaya Article: Setting Up a SIP A/S Development Environment
    IBM Article: How Cool Is Your Data Center?
    Microsoft Article: Managing Virtual Machines with Microsoft System Center
    HP eBook: Storage Networking , Part 1
    Microsoft Article: Solving Data Center Complexity with Microsoft System Center Configuration Manager 2007
    MORE WHITEPAPERS, EBOOKS, AND ARTICLES
    Webcasts
    Intel Video: Are Multi-core Processors Here to Stay?
    On-Demand Webcast: Five Virtualization Trends to Watch
    HP Video: Page Cost Calculator
    Intel Video: APIs for Parallel Programming
    HP Webcast: Storage Is Changing Fast - Be Ready or Be Left Behind
    Microsoft Silverlight Video: Creating Fading Controls with Expression Design and Expression Blend 2
    MORE WEBCASTS, PODCASTS, AND VIDEOS
    Downloads and eKits
    Sun Download: Solaris 8 Migration Assistant
    Sybase Download: SQL Anywhere Developer Edition
    Red Gate Download: SQL Backup Pro and free DBA Best Practices eBook
    Red Gate Download: SQL Compare Pro 6
    Iron Speed Designer Application Generator
    MORE DOWNLOADS, EKITS, AND FREE TRIALS
    Tutorials and Demos
    How-to-Article: Preparing for Hyper-Threading Technology and Dual Core Technology
    eTouch PDF: Conquering the Tyranny of E-Mail and Word Processors
    IBM Article: Collaborating in the High-Performance Workplace
    HP Demo: StorageWorks EVA4400
    Intel Featured Algorhythm: Intel Threading Building Blocks--The Pipeline Class
    Microsoft How-to Article: Get Going with Silverlight and Windows Live
    MORE TUTORIALS, DEMOS AND STEP-BY-STEP GUIDES