Friday, January 30, 2009

Getting the Call Stack from eScript

I've been working on some routines for exception logging; basically recording some important data when an exception condition occurs. Right now it records things like the error code, error message, application name, active view name, user id, primary position id, organization id, server name, and a bunch of other stuff. You get the idea.

Well today, I decided to add a call stack for those times the exception occurs in server script.

Where did I get the call stack from? I noticed that the call stack is displayed when an exception is thrown and we push it to the screen using TheApplication().RaiseErrorText(e), where e is the exception object. So I knew the call stack was available somewhere in that exception object.

Let's hack the exception object and see what it contains. A quick run of this code in the business service simulator was very revealing:
try
{
  TheApplication ().RaiseErrorText ("Hello, World!");
}
catch (e)
{
  for (var key in e)
    Outputs.SetProperty (key, e[key]);
}
Here are the values that came out:
PropertyValue
errTextHello, World!
errCode22709
nameError
message SiebelError: Hello, World!***RAISE ERROR TEXT*** Error near no filename:159 [RaiseErrorText()]. from no filename:159 [Service_PreInvokeMethod()]

So I can see that the message property of the exception object houses the message I threw along with the call stack (at least when I threw that exception using RaiseErrorText()). So I can use that to my advantage.

Actually, the construct was something like this:
"SiebelError:" + <error message> + "***RAISE ERROR TEXT***" + <call stack>
I didn't really have a need for anything before the call stack, so I stripped out everything up to and including the ***RAISE ERROR TEXT*** bit. So finally, here's the function I wrote to get the call stack.
function GetCallStack ()
{
  try
  {
    TheApplication ().RaiseErrorText ("");
  }
  catch (e)
  {
    return (e.message.replace (/^.*\*{3}RAISE ERROR TEXT\*{3}/, "");
  }
}
If that return line looks Chinese to you it's because I am part Chinese. Allow me to translate...

I stripped out the junk prefix by using the string.replace() method to replace unwanted text with an empty string. The unwanted text was expressed as a regular expression, which basically reads, "Everything from the beginning of the line until ***RAISE ERROR TEXT***" So pretty straightforward logically.

Well there you have it! I'll save the primer on regular expressions for another day.