Adobe Suite Error Logging

Hey Everyone,

Recently I’ve been working a lot with After Effects and Photoshop script development and now have a need to start looking at error logging of some kind. We’ve started to get odd exceptions that we’re having a hard time debugging and error logging in this case would help immensely.

The problem is that I’m not to sure on how to approach developing a solution to help handle this problem. The only way I can think of to log errors is to use a try/catch in any area that might be expected to fail. But to go through and add many try/catches just seems like a bad way to approach this. I’m curious if anyone has any ideas on how to implement this within our current tool set. Keep in mind we’re not using the Photoshop/After Effects SDK at this time and only using what the DOM & javascript has to offer.

I’m also curious to hear how you handle errors in any application whether its a DCC tool or an external app as I’ve had limited experience with logging previously.

Thanks,
Ryan

For some of our tools, the production versions are called through a function that implements bug reporting and wraps the main entry point for the tool in a try statement.

In the catch, we report the error, user, environment vars, version of the tool, version of the os, version of the app, etc etc etc. These particular tools are written in python, so we also dump the traceback. Given the introspection available in Adobe’s implementation of JavaScript, I’d be surprised if you can back-trace, though the exception should give you the source and the line number of the error.

Yup, the way this is usually done is to launch tools from a high-level system that takes care of all that. Usually you also have some sort of logging in your tools/libraries. At the point your getting crashes, you just want to put a try/catch as high up as possible, and do the error reporting in the catch, and then fail.

But like Bronwen said, I imagine all best practices and advice are out the window when dealing with Adobe JS- you’re going to have to dumb down your solutions until you get something that works.

I do the same as Bronwen in Illustrator (java scripting).
Wrap everything inside Main() into a try statement and dump any error out with whatever info could be helpful. I work in a small team, so usually I just print it out to screen with ‘alert’, but you could just as easily dump it to a txt file.

Another thing that I do sometimes that can be help is to have a ‘global’ txt variable in in which I put little bits of info while functions are called.
So for example, if I know a certain function can be unstable or heavy, I put something in the text file like ‘calling function xyz from abc …etc’ and dump that txt variable in the error output too. It can help identify where things go wrong.

So instead of needing a try-catch in every function, you just have 1 try catch and one txt variable that tells you what the last functions were that are called.
Seems to work ok.

Thanks for the help! I’ve got a pretty nice solution working as I type this - I’ve taken your wisdom and wrapped everything into a try/catch. So far I’m able to pull out everything Bronwen has listed and then some, including a stack trace, however that trace doesn’t look like it works correctly. More investigation to be done there.

The Error object seems highly undocumented in Adobe’s JS as their object model viewer is missing about 10 properties under the Error class, so if anyone else is looking for the same info I suggest running the following line to help you out a bit as this is where much of the info I needed comes from:

alert(error.reflect.properties);

Thanks again everyone. :):

How are you doing the stack trace?

I’m using what Adobe provides in their $ (debugging) object. Using “$.stack” works, however it’s not really returning a trace it’s just returning the function in which the exception resides. I was expecting to get an actual call trace up to the point of the exception.

To show this, I used the following code to see the result of $.stack:

function functionA()
{
	functionB();
}

function functionB()
{
	functionC();
}

try
{
	functionA();
}
catch(error)
{
	writeLogEntry(error);
}

$.stack is being called within writeLogEntry() and returns the following:

[Logging.jsx]
writeLogEntry([Error:ReferenceError: functionC is not a function])

Now my guess is in most languages you get a physical trace, something like:

fileName, line ##, ran functionA(),
fileName, line ##, ran functionB(),
fileName, line ##, error functionC()

I could very well be using $.stack incorrectly as there isn’t too much documentation on it - but according to the docs it returns the current stack trace as a string. Also using the reflect object reveals no hidden properties or methods, so it makes me think more design limitation instead of incorrect use.

Your stack trace is always going to be current, so if you only log it in your catch statement, your stack will be the stack from the catch statement. In other words, you’d be too late. It actually has already dumped the stack trace from the state you were in when you got the error.

You could create your own stack trace, if you aren’t above using a decorator pattern to modify all your existing functions.

I’ve modified your example to create a simple version of this. If you decide to implement something along these lines, you’d have to limit stack trace depth (especially if any of your functions are recursive) to avoid out-of-memory errors. And obviously you’ll want to log somewhere other than the console.

I’m decorating two functions here that each have a different argument list length, just to show the usage of the arguments property.

var customStack = new Array();

function stackRecord( func ) {
    return function() {
        stackData = func.name + "(";
        for ( i = 0; i < arguments.length; i++ )
        {
            stackData += " " + arguments[i] + ( i == arguments.length - 1 ? " " : "," );
        }
        stackData += ")"
        customStack.push( stackData  );
        func( arguments );
        customStack.pop();
    };
}

functionA = stackRecord( function functionA( arg1, arg2, arg3 )
{
    functionB()
} );

functionB = stackRecord( function functionB()
{
    functionC()
} );

try
{
    functionA( 0, 1, 2 );
}
catch(error)
{
    $.writeln( "File:" + $.fileName )
    $.writeln( "Error occurred at line " + error.line );
    $.writeln( "
Traceback:" )
    for ( i in customStack )
    {
        $.writeln( customStack[i] );
    }
    $.writeln( "
" + error );
}

This outputs the following:

File:(Script1)
Error occurred at line 24

Traceback:
functionA( 0, 1, 2 )
functionB()

ReferenceError: functionC is not a function

Wow thats awesome Bronwen! I appreciate you going the extra mile and showing an example of this, very helpful. I’ll be looking into incorporating this in the near future.