Custom sys.excepthook in Ignition

I have been looking for a way to log local variable values from the current function as well as further down the call stack under certain circumstances in my code. I looked at using sys.exc_info() to get a traceback object, then logging the locals from each stack frame using that object. I read in the docs that sys.exc_info() is not thread safe. This leads me to my first question:

Will Ignition's threading potentially cause sys.exc_info() to return information about an exception that happened between the exception that I care about and the call to sys.exc_info()?

Because of the thread safety concerns I did some more digging and found out about the ability to override the code that gets executed when an uncaught exception is raised using sys.excepthook. My thought was that since this would be called for each exception, it would alleviate the thread safety issues posed by sys.exc_info(). I was thinking about overriding it like this:

def customExceptHook(cls, inst, tb):
    if ... # logic to determine if this is an exception that I want to log stack locals for
        logTb = tb
        while logTb:
            log(logTb.tb_frame.f_locals)
            logTb = logTb.tb_next
    # call the default excepthook to keep default behavior when an exception occurs.
    sys.__excepthook__(cls, inst, tb)

Since it would call the default except hook after logging the information I want, it seems like it shouldn't break any functionality that already happens when an exception occurs.
This leads me to my last two questions:

Are there any implications with overriding sys.excepthook that I may have missed including Ignition specific things, or Jython specific things?

If I were to implement this, where should I do the override to make sure it is overridden through any gateway restarts, scripting restarts, etc? (I was thinking in the gateway on update script)

edit: realized a problem with my function. It needs to pass the original tb to the sys.__excepthook__ function

1 Like

Hmm. Quite the rabbit hole. Please report what you find.

1 Like

@pturmel I was praying you would be the one with the answers to these questions :wink:

Anyways,
I did some testing, and I wasn't able to get my custom exception hook function to run when an exception was thrown. I tried overriding sys.excepthook at the beginning of the script that I was using for testing, as well as in the gateway update script, and it seemed to still use the default code in both cases.

I did a not-so-extensive test to try to answer my first question, which involved throwing an exception in one tag value changed script, then throwing a distinguishable exception in another script between when the first script threw its exception and when it called sys.exc_info(). The sys.exc_info() call still returned the exception that was thrown in script #1, which is the behavior I was hoping for. So for now I am going to use the exception information from that function to get a snapshot of the stack local variables.

There still could be cases where threading causes the wrong exception information to be returned depending on where and when the code is executed, I guess. One place that comes to mind that might be problematic is in gateway timer scripts that have the threading option set to "shared". To be honest I don't feel like testing this out at the moment, but if I do I will post my results on this thread.

2 Likes