0

I am implementing a custom Handler with Python. Naturally, I need to override emit(self, record) to do that. An example:

from logging import Handler, LogRecord class FooHandler(Handler): def emit(self, record: LogRecord): # handler logic 

As you can see, everytime I log something with a Logger instance, this will provide a LogRecord to emit method.

I see the current implementation of LogRecord from CPython source, you can also see it from here.

Assuming I have Logger instance called logger. Later somewhere in the code, I do these:

# either this logger.exception(Exception("foo")) # it does not have to be an instance of Exception, it's for the sake of simplicity # or this logger.error("foo", exc_info=True) # this also provides a stack trace on the console handler 

Since @thebjorn has commented about traceback module, I think I can work around from that. However I have three questions right now:

  1. How do I get the exception from LogRecord instance?
  2. If I do logger.error("message", exc_info=True), then I do not pass any exception instance. In this case, how do I get the traceback since I do not simply have any exception instance?

Thanks in advance.

Environment

  • Python 3.5 and above
3
  • 1
    the traceback module..? Commented May 15, 2019 at 17:50
  • Actually, SteamHandler implementation has given me a bit of insight but I still might need an explanation about handleError. Commented May 15, 2019 at 17:51
  • @thebjorn, that was what I was looking for. I didn't know such a thing existed in standard library. Commented May 15, 2019 at 17:53

3 Answers 3

1

I am the OP and this may not be the best answer, in practice or more Pythonic way but, for Googlers, there is a method.

As @thebjorn stated in the comments of the question, you need traceback built-in module.

Then you need to make sure that the exception you are targeting is the latest exception your software has raised. Then you can simply call:

traceback.print_last() 

If there is no exception, then you will get below as a string:

Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib64/python3.7/traceback.py", line 173, in print_last raise ValueError("no last exception") ValueError: no last exception 

In other case, you will get the latest exception's traceback:

raise Exception("foo") traceback.print_last() # will return a string similar to below Traceback (most recent call last): File "/foo/bar/baz.py", line 3296, in run_code exec(code_obj, self.user_global_ns, self.user_ns) File "/foo/bar/biz.py", line 1, in <module> raise Exception("foo") Exception: foo 

Hope this helps to Googlers.

Things to Be Aware Of

As I've mentioned, you will need to make sure that the last exception that has got raised is the exception that you are targeting. This might not be a viable solution

  • for multithreaded codebase because you must be extra careful about which thread your code is running on or
  • a codebase that is structured upon a framework such as Django because the exception handling of such frameworks might be quite complex and you might get a different Exception rather than the one you were excepting to get
Sign up to request clarification or add additional context in comments.

1 Comment

If there's not any answer with detail and with different approach in two days, I will accept this answer as valid.
1

The LogRecord object has an exc_text attribute that looks to be identical to the text provided in a traceback. It returns None when there isn't an exception.

So I think the following would get the OP what they were originally requesting:

from logging import Handler, LogRecord class FooHandler(Handler): def emit(self, record: LogRecord): print(record.exc_text) # other handler logic 

Comments

1

Expanding upon sazerac's code, exec_text didn't have any value when I ran it with a sample try except block, but exc_info WAS populated. This is what I used to work around my scenario.

from logging import Handler, LogRecord import traceback class FooHandler(Handler): def emit(self, record: LogRecord): if record.exc_info: errType, errValue, errTraceback = record.exc_info msg = "".join(traceback.format_exception(errType, errValue, errTraceback)) else: msg = '' print(msg) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.