Getting error line number from sys.exc_info

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

Ignition v7.9.9 (b2018081621)
Java: Oracle Corporation 1.8.0_101

Is there anyway to get the line number out when writing exception handling?
Thank very much!

1 Like

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

1 Like

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.

Thanks Phil,
Had a look but i think its a bit over my head to use that wrapper!

I know its been years, but for anyone looking for a complete answer here is what worked for me (as always thank you Phill!):

snipped from later.py from Phill

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)

2 Likes

Pssst! One "L".