views:

111

answers:

3

Theoretically, the end user should never see internal errors. But in practice, theory and practice differ. So the question is what to show the end user. Now, for the totally non-technical user, you want to show as little as possible ("click here to submit a bug report" kind of things), but for more advanced users, they will want to know if there is a work around, if it's been known for a while, etc. So you want to include some sort of info about what's wrong as well.

The classic way to do this is either an assert with a filename:line-number or a stack trace with the same. Now this is good for the developer because it points him right at the problem; however it has some significant downsides for the user, particularly that it's very cryptic (e.g. unfriendly) and code changes change the error message (Googling for the error only works for this version).

I have a program that I'm planning on writing where I want to address these issues. What I want is a way to attach a unique identity to every assert in such a way that editing the code around the assert won't alter it. (For example, if I cut/paste it to another file, I want the same information to be displayed) Any ideas?

One tack I'm thinking of is to have an enumeration for the errors, but how to make sure that they are never used in more than one place?

(Note: For this question, I'm only looking at errors that are caused by coding errors. Not things that could legitimately happen like bad input. OTOH those errors may be of some interest to the community at large.)

(Note 2: The program in question would be a command line app running on the user's system. But again, that's just my situation.)

(Note 3: the target language is D and I'm very willing to dive into meta-programming. Answers for other languages more than welcome!)

(note 4: I explicitly want to NOT use actual code locations but rather some kind of symbolic names for the errors. This is because if code is altered in practically any way, code locations change.)

+2  A: 

Interesting question. A solution I have used several times is this: If it's a fatal error (non-fatal errors should give the user a chance to correct the input, for example), we generate a file with a lot of relevant information: The request variables, headers, internal configuration information and a full backtrace for later debugging. We store this in a file with a generated unique filename (and with the time as a prefix).

For the user, we present a page which explains that an unrecoverable error has occurred, and ask that they include the filename as a reference if they would like to report the bug. A lot easier to debug with all this information from the context of the offending request.

In PHP the debug_backtrace() function is very useful for this. I'm sure there's an equivalent for your platform.

Also remember to send relevant http headers: Probably: HTTP/1.1 500 Internal Server Error

Given a sensible format of the error report file, it's also possible to analyze the errors that users have not reported.

rebra
Also, consider things like an in-memory logger which goes into this file.
S.Lott
Unfortunately, I don't have a trace system in the language :(
BCS
+1  A: 

Write a script to grep your entire source tree for uses of these error codes, and then complain if there are duplicates. Run that script as part of your unit tests.

Ned Batchelder
A good fall back, but I'd rather have this kind of error break the build or faster feedback.
BCS
@BCS: then run the check as part of the build instead of part of the unit tests :D
Jonathan Leffler
+1  A: 

I know nothing about your target language, but this is an interesting question that I have given some thought to and I wanted to add my two cents.

My feeling has always been that messages for hard errors and internal errors should be as useful as possible for the developer to identify the problem & fix it quickly. Most users won't even look at this error message, but the highly sophisticated end users (tech support people perhaps) will often get a pretty good idea what the problem is and even come up with novel workarounds by looking at highly detailed error messages. The key is to make those error messages detailed without being cryptic, and this is more an art than a science.

An example from a Windows program that uses an out-of-proc COM server. If the main program tries to instantiate an object from the COM server and fails with the error message:

"WARNING: Unable to Instantiate UtilityObject: Error 'Class Not Registered' in 'CoCreateInstance'"

99% of users will see this and think it is written in Greek. A tech support person may quickly realize that they need ro re-register the COM server. And the developer will know exactly what went wrong.

In order to associate some contextual information with the assertion, in my C++ code I will often use a simple string with the name of the method, or something else that makes it clear where the error occured (I apologize for answering in a language you didn't ask about):

int someFunction()
{
  static const std::string loc = "someFunction";
  :  :
  if( somethingWentWrong )
  {
    WarningMessage(loc.c_str(), "Unable to Instantiate UtilityObject:  Error 'Class Not
    Registered' in 'CoCreateInstance);
  }
}

...which generates:

WARNING [someFunction] : Unable to Instantiate UtilityObject: Error 'Class Not Registered' in 'CoCreateInstance

John Dibling
Excellent points!
BCS