Easy Error Management

aspTired of hunting down mysterious bugs which users have reported (perhaps incorrectly) within your application? Having a problem with VBScript’s IsNumeric() function? These few tricks will serve to make your application more user-friendly, but more importantly help you prevent, locate, and fix errors even if no one reports the bug!

Chances are, if you are reading this article, you plan to script or have scripted one or more ASP application(s). To follow this stating-the-obvious form of logic, it can be safely stated that you will run into some of the very same problems that I have, which will more or less occur as follows:

[italic]You:
I have created the ultimate ASP application!!!

User: I found a bug!

You:
1. Where did you find it?
2. What was the error message?
3. What were you doing?

User:
1. I don’t know.
2. I don’t remember exactly what the message was, something like ‘type mismatch’…I think.
3. I was just (with one of the following): logging on/off; saving/generating a report; I don’t remember, etc…

You:
oh.[/italic]

This is followed by hours of you trying to mimic the actions of the user, within their permission level, performing countless actions within the application, all in hopes of finding the mysterious bug. You throw in countless lines of “Response.Write( variable & “ - “ & TypeName( variable ) )“ to locate the offending variable.

Upon determining the cause of the error, and the cause of the cause, you realize some degree of error-checking is necessary. The following three tricks will speed up the debugging of you applications, save you many, many lines of code, and are easily reusable. {mospagebreak title=First Trick: Checking for Truly Numeric Values} VBScript has many built-in functions to verify data types, such as IsEmpty(), IsDate(), and so forth. One issue you may encounter in your ASP application involves the IsNumeric() function. This is because VBScript evaluates Hex numbers, numbers in scientific notation, numbers with double notation, and currency all as numeric. This could be a problem because you have coded expecting only whole integers, no d’s, no e’s, and definitely no $’s. So we are therefore called upon to be a little more specific, and this can be accomplished with the following function:
[code] ‘=================================================== Function isTrulyNumeric( intToCheck ) ‘=================================================== intToCheck = Trim( intToCheck ) If Len( intToCheck ) = 0 OR IsEmpty( intToCheck ) Then isTrulyNumeric = False Else Dim c, i isTrulyNumeric = True For i = 1 To Len( intToCheck ) c = Asc( Mid( intToCheck, i, 1 ) ) If c <= 44 OR c > 57 OR c = 47 Then isTrulyNumeric = False Exit For End If Next End If End Function [/code]
Explanation: This function iterates through each character in the given parameter ( intToCheck ), checking them against their ASCII value. The only permitted characters are 0-9, ‘.’, and ‘-‘ (for negatives).

Using this function and VBScript’s other functions, along with some careful planning and analyzing of where you need which data types, will save you endless frustration and the headaches that generally accompany it! {mospagebreak title=Second Trick: User-Friendly Error Reporting} The second trick involves two steps. The first step is the oh-so-vital “friendly” error message. This is a necessity because, generally speaking, when one sees “Microsoft VBScript runtime error,” there is no obvious plan of action associated with it, leaving both the user and programmer stranded. The second step, admittedly an optional one, is to take the responsibility of reporting the error off of the user, by having the error emailed to you, tracked in a database, or both.

Step 1 – Placate the User

So, we have to stop users from being scared off by ASP’s inherent, and somewhat cryptic, error messages. This can be accomplished with some soothing color, and a calming explanation of what on earth is wrong. Take a look at the following:
[code] ‘========================================================= Sub errorMessage( strMSG ) ‘========================================================= dim blOptionalInfo blOptionalInfo = false ‘=== you can hard-code to true/false, or have it depending on permissions Response.Write( “
“) Response.Write( “

You have encountered an error!

” ) Response.Write( strMSG ) If blOptionalInfo Then Response.Write( “

Debug Info:
” ) Response.Write( “URL: ” & Request.ServerVariables(“URL”) & “
” ) Response.Write( “QUERY_STRING: ” & Request.ServerVariables(“QUERY_STRING”) & “
” ) Response.Write( “RU: ” & Request.ServerVariables(“REMOTE_USER”) & “
” ) Response.Write( “RA: ” & Request.ServerVariables(“REMOTE_ADDR”) & “
” ) End if Response.Write( Now() ) Response.Write( “

Go back
“) Response.Write( “Home” ) Response.Write( “

” ) Response.End() End Sub [/code]
Explanation: Here we are telling the user in plain English what has just happened, where they are, how they got there, and where to go from here. Here’s an example:
[code] If Not isTrulyNumeric( intUserID ) Then Call errorMessage(“The user id you have supplied is invalid! (“ & intUserID & “)” ) Else ‘proceed End If [/code]
This method of reporting will make your programming life much easier and your users happier, guaranteed or your money back!

Step 2 – Obtaining Omniscience

Imagine you were the one contacting a user about an error they encountered, not vice versa. Imagine that not only were you able to explain the error quickly, but also explain that you’ve already fixed it! Imagine the enormous amounts of trust and respect (not to mention $$) that would be heaped upon you!

This is fairly easy to accomplish. All you need to do, is create another subprocedure for emailing/logging the error, and call it from the errorMessage() subprocedure. You’ll want to modify the code below with the all variants you wish to be tracked.
[code] ‘========================================================= Sub reportError( strMSG ) ‘========================================================= dim arVars, objMail, strBody, i redim arVars( # of items to track ) arVars( 0 ) = “error:” & strMSG arVars( 1 ) = “time:” & now() arVars( 2 ) = “user id:” & intUserID arVars( 3 ) = “page:” & Request.ServerVariables(“URL”) arVars( 4 ) = “querystring:” & Request.QueryString() arVars( 5 ) = “form data:” & Request.Form() arVars( # ) = etc… set objMail = Server.CreateObject(“CDONTS.NewMail”) objMail.To = “youremail@domain.com” objMail.Subject = “error report” strBody = “the following error was encountered” & vbCrLf & vbCrLf For i = 0 To UBound( arVars ) strBody = strBody & arVars( i ) & vbCrLf Next objMail.Body = strBody objMail.Send() Set objMail = Nothing arVars = Null ‘followed by optional tracking in a database End Sub [/code]
So there you have it! With good planning, you can be made aware of 99% of all the errors that occur within your application, without depending on the user to notify you. Lastly, let’s just look at one handy trick to save time with our debugging. {mospagebreak title=Third Trick: The Quick Debugger} If you’re at all like me, you will quickly be annoyed by typing and re-typing the same lines of code to read your offending variables, their type, size, and so forth. So, it’s always handy to have a subprocedure do all the work for you. Here’s a simple one:
[code] ‘========================================================= Sub debug( var ) ‘========================================================= Response.Write(““) Response.Write(“Type = ” & typeName( var ) & “
“) If Not typeName( var ) = “Variant()” Then Response.Write( “Size = ” & len(var) & “
” ) Response.Write( “Value

” ) Response.Write( Replace( var, vbCrLf, “
“) ) Else Dim intDims, i, j intDims = CountDims( var ) if intDims < 3 then For i = 0 to UBound( var, 1 ) If intDims = 1 then Response.Write("var( " & i & " ): " ) Response.Write( typename( var( i ) ) & " = " ) Response.Write( var( i ) & "
” & vbCrLf ) Else For j = 0 to UBound( var, 2 ) Response.Write(“var( ” & i & “, ” & j & ” ): ” ) Response.Write( typename( var( i, j ) ) & ” = ” ) Response.Write( var( i, j ) & “
” & vbCrLf ) Next End If Next Else Response.Write( “array has “ & intDims & “ dimensions.” ) End If End If Response.Write(“
“) Response.End() End Sub [/code]
For this to work on arrays, you’ll need the function below. Currently the debugger works with up to two dimensions, but could be modified to do more if that’s necessary within your application.
[code] [/code]
Basically, you could use the subprocedure debug() anywhere you want to see what a variable is in gory detail. What I have started doing is inserting it – commented out – in places where I could foresee errors occurring, such as when picking up Query String elements, or with SQL SELECT statements. Example:
[code] Dim intUserID, strSQL intUserID = Trim( Request.QueryString(“intUserID”) ) ‘debug( intUserID ) strSQL = “SELECT * FROM users WHERE id = “ & intUserID ‘debug( strSQL ) [/code]
This way, when I need to do some quick debugging, I simply un-comment the desired line, and I’m well on my way to finding the cause of the error.

Conclusion

I hope you find these techniques as useful as I have. I can honestly say that using this method of error reporting has saved me hours of frustrated searching. I also believe that using the debugger has prevented carpal tunnel syndrome brought on by typing millions of Response.Write()- Response.End()’s.
[gp-comments width="770" linklove="off" ]