Hi i've seen other threads recommend providing error reporting using code like; system.gui.errorBox("%s, %s"% (sys.exc_info()[0],sys.exc_info()[1]))
But that doesn't provide offending line number which can be very handy.
For example the following code:
try:
# some code
except:
import sys
logger.error(str(sys.exc_info()))
outputs:
12:58:53.082 [Thread-31] ERROR com.inductiveautomation.factorypmi.application.script.builtin.WindowUtilities - <type 'exceptions.TypeError'>, getValueAt(): 1st arg can't be coerced to int
But if i don't have any try/catch code, i get the following output from the Error popup (compact details):
Traceback (most recent call last):
File "", line 58, in calcQnty
TypeError: getValueAt(): 1st arg can't be coerced to int
I have come up with the following approach for 'catch all' error handling of unexpected errors using this as reference:
import sys
import traceback
logger = system.util.getLogger("ScriptDebugging")
# Some identifier for script name. 'calcQnty' is a custom method on a button component
scriptName = self.name + ".calcQnty"
try:
# Some code
except:
exType, ex, tb = sys.exc_info()
logger.error("ERROR in '%s' line %d: %s, %s" % (scriptName,traceback.tb_lineno(tb), exType, ex))
Provides the following output which i think provides all the info I would need to debug:
13:47:12.268 [Thread-39] ERROR ScriptDebugging - ERROR in 'Button Calc Volume.calcQnty' line 61: <type 'exceptions.TypeError'>, getValueAt(): 1st arg can't be coerced to int
You might want to look at my Java wrapper for python exceptions that is part of later.py. You can pass it to a logger to expand into a java-style traceback.
Java wrapper for Jython exceptions to preserve the python stack trace.
class PythonAsJavaException(java.lang.Exception):
def __init__(self, pyexc, tb=None):
super(PythonAsJavaException, self).__init__(repr(pyexc), None, True, True)
traceElements = []
if tb is None:
tb = sys.exc_info()[2]
while tb:
code = tb.tb_frame.f_code
vnames = code.co_varnames
if vnames and vnames[0] in ('cls', 'self'):
ref = tb.tb_frame.f_locals[vnames[0]]
if vnames[0] == 'self':
className = fullClassName(ref.__class__)
else:
className = fullClassName(ref)
else:
className = '<global>'
traceElements.append(StackTraceElement(className, code.co_name, code.co_filename, tb.tb_lineno))
tb = tb.tb_next
traceElements.reverse()
self.setStackTrace(traceElements)
Here is my quick and dirty way:
from pturmel.later import PythonAsJavaException
def log_full_exception(logger, message, exception):
"""
Logs a Python exception as a Java-style traceback using the system.util.getLogger instance.
Args:
logger (system.util.getLogger): Logger instance to log the exception.
message (str): Additional context message.
exception (Exception): Python exception to log.
"""
try:
raise PythonAsJavaException(exception)
except PythonAsJavaException as java_exc:
logger.error("{}: {}".format(message, exception), java_exc)
# Set up the logger
my_logger = system.util.getLogger("Test")
try:
# Simulate an exception
1/0
except Exception as e:
log_full_exception(my_logger, "An error occurred", e)
Here is an example of the output:
ERROR Test -- An error occurred: integer division or modulo by zero
org.python.proxies.later$PythonAsJavaException$3: ZeroDivisionError('integer division or modulo by zero',)
at <global>.<module>(<input>:25)