Cloud
Cloud

Reputation: 19333

Auto-generate full stack trace to STDERR on thread crash

Problem

I have a library which I must use, written in Python 2.7. There are several bugs in it, and one of them occasionally causes the calling thread to crash (rarely, however). I would like to generate the stack trace so I can determine which thread is dying when the library crashes. I get a trace dumped to STDERR of what went wrong in the library, i.e.:

A problem occurred in a Python script.  Here is the sequence of
function calls leading up to the error, in the order they occurred.

I've also tried GDB (which works wonders for my C/C++ projects), using a guide I found on StackOverflow to get "Python plus GDB" working (so I can attach to a running Python application). However, I don't see anything helpful that relates to the (now dead) thread.


Question

Is it possible, in Python 2.7, to force a thread (when it crashes) to report a full stack trace to STDOUT, STDERR, or a log file, when this sort of issue (i.e. a library call crashing the calling thread) occurs?

Thank you.

Upvotes: 0

Views: 405

Answers (1)

gbtimmon
gbtimmon

Reputation: 4322

If you have access to the thread definition -- you can write a wrapper thread

import logger 
log = logger.getLogger(__name__)

class WrapperThread(threading.Thread):
    def __init__(self, innerThread):

        self.innerThread = innerThread

    def start(self):
        try:
            # run the thread in the current context with run. 
            self.innerThread.run()
        except Exception as e:
            log.error("%s has crashed.", self, exc_info=True) #Exec info makes loggin print the stack trace. 

Depending on the library you are using you may be able to apply a decorator to the thread definition. Though I don't recommend code like this ever being included in released code

import threading
import logging

logging.basicConfig()

def loggingThread(clazz, logger):
    class _Thread(clazz):
        def __init__(self, *args, **kwargs):
            clazz.__init__(self, *args, **kwargs)

        def run(self):
            try:
               clazz.run(self)
            except Exception as e:
                logger.error("%s has Crashed!", self, exc_info=True)

    return _Thread

threading.Thread = loggingThread(threading.Thread, logging)

import random 

def ohNo(range1, range2):
    for x in xrange(1, range1):
        if x % random.randint(1, range2) == 0:
            raise ValueError("Oh no. %d is an illeagal value!" % (x,))

test = threading.Thread(target=ohNo, args=(500,100))
test.start()

Upvotes: 1

Related Questions