Python、tkinter 和导入的类:记录未捕获的异常 [英] Python, tkinter and imported classes: logging uncaught exceptions

查看:43
本文介绍了Python、tkinter 和导入的类:记录未捕获的异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一些我想与我的团队分享的脚本,所以我一直在构建一堆日志记录,以便在他们在某处遇到崩溃时更容易调试,从那时起我可以看到究竟是什么崩溃了.

I am writing some scripts that I want to share with my team, so I have been building in a bunch of logging so that it will be easier to debug if they encounter a crash somewhere since then I can see what exactly crashed.

一般记录到文件没有问题,但我遇到了未捕获异常的问题.我尝试了各种方法来让它工作,例如参见 this这个.似乎 sys.excepthook 没有被调用,无论是当我从 IDLE 还是从命令提示符运行它时.

General logging to a file is no issue, but I have an issue with uncaught exceptions. I have tried various things to get it to work, see for example this and this. It seems that the sys.excepthook is just not getting called, either when I run it from IDLE or from the Command Prompt.

昨天如果在主模块中发生异常,它会正确记录,但如果在导入的类中发生异常则不会.现在根本没有记录异常,我不知道我更改了什么(所以我今天安装了 Git :-P)

Yesterday it log correctly if the exception occurred in the main module, but not if an exception occurred in an imported class. Now the exceptions don't get logged at all, I have no clue what I changed (so I installed Git today :-P)

这是我试图开始工作的代码,主模块:

Here is the code that I am trying to get to work, the main module:

import tkinter as tk
import sys
import traceback
import logging
import datetime

import exception_logging_test_imported_class as impclass

# Main Class
class ExceptMain(tk.Frame):
    def __init__(self, parent):
        logging.info('Start main')
        tk.Frame.__init__(self, parent, relief='groove', bd=4)
        self.parent = parent
        self.pack()
        tk.Label(self, text='This is the main class', bg='white').pack()
        tk.Button(self, text='Start subframe', command=self.run).pack()
        tk.Button(self, text='Throw main exception', command=self.throwex).pack()
        tk.Button(self, text='Start imported class', command=self.start_import).pack()

    # Function to start another frame, from this same file
    def run(self):
        logging.info('Run main function')
        subclass = ExceptSubclass(self)
        subclass.pack()

    # Function to start an imported frame class
    def start_import(self):
        imported_class = impclass.ExtraClass(self)
        imported_class.pack()

    # Throw an exception
    def throwex(self):
        raise ValueError("Main is burning down")

#Another class in this file
class ExceptSubclass(tk.Frame):
    def __init__(self, parent):
        logging.info('Start subframe')
        tk.Frame.__init__(self, parent, relief='groove', bd=4)
        self.parent = parent
        self.pack()
        tk.Label(self, text='This is the subclass', bg='white').pack()
        tk.Button(self, text='run sub function', command=self.script).pack()
        tk.Button(self, text='Throw sub exception', command=self.throwexsub).pack()

    # Run something
    def script(self):
        logging.info('Run subfunction')
        tk.Label(self, text='Script has run').pack()

    # Throw an exception
    def throwexsub(self):
        thing = []
        thing[1]

# Use a logger object or just logging
logger_not_just_logging = False
if logger_not_just_logging:
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    logger.addHandler(logging.FileHandler("ZZ_logging_test.log"))
else:
    logging.basicConfig(filename='ZZ_logging_test.log', level=logging.DEBUG)

logging.info('<<<<< STARTING NEW INSTANCE >>>>>')

# This function needs to be called somehow when an uncaught exception happens
def exception_handler(etype, value, tb):
    logging.exception("Uncaught exception: {0}".format(str(value)))
    with open('ZZ_exception_test.log', 'a') as file:
        file.write("<<< Exception occurred at " + str(datetime.datetime.now()) + " >>>")
        traceback.print_exception(etype, value, tb, file=file)

# Install the hook  
sys.excepthook = exception_handler

# Run the app
root = tk.Tk()
app = ExceptMain(root)
root.mainloop()

还有一个子模块:

import tkinter as tk
import logging

# This is an imported frame
class ExtraClass(tk.Frame):
    def __init__(self, parent):
        logging.info('Start imported frame')
        tk.Frame.__init__(self, parent, relief='groove', bd=4)
        self.parent = parent
        self.pack()
        tk.Label(self, text='This is an imported frame', bg='white').pack()
        tk.Button(self, text='run imported frame function', command=self.script).pack()
        tk.Button(self, text='Throw imported exception', command=self.throwexsub).pack()

    # Imported frame has a function
    def script(self):
        logging.info('Run imported frame function')
        tk.Label(self, text='Imported script has run').pack()

    # Imported frame throws an exception
    def throwexsub(self):
        thing = []
        thing[1]

我期望得到的是一个包含所有常规日志记录的文件和一个包含异常和堆栈跟踪的单独文件.

What I expect to get is one file with all the general logging and a separate file with the exceptions and stacktraces.

如果你能帮我解决这个问题,我将永远感激不尽!

I will be eternally grateful if you can help me figure this out!

推荐答案

啊哈,我找到了问题所在.以上述方式安装的 exceptionhook 仅在更高级别上工作.当我注意到我的类抛出的异常仍在控制台中记录而键盘中断确实根据我的需要记录到文件时,我发现了这一点.

Aha, I have found the issue. The excepthook that is installed in the aforementioned way is only working on a higher level. I figured that out when I noticed that the exceptions that my class was throwing were still logging in the console while a keyboard interrupt did log in the file as I wanted.

接下来我发现在 Python 中使用线程时可能会遇到类似的问题.主线程仍会根据需要记录,但另一个线程中的异常不会.然后我认为类可以覆盖我对异常处理的覆盖.由于我的所有代码都在 tkinter 主循环中运行,因此我可能需要告诉该位执行我希望它执行的操作.这种见解使我this SO 答案,并用一行代码修复了一切,一直到主类结束:

Next thing I found was that you can have similar issues when using threads in Python. The main thread would still log as wanted but the exceptions in the other thread would not. Then I figured that classes can override my overriding of what to do with exceptions. Since all of my code is running in the tkinter mainloop I might need to tell that bit to do what I want it to do. This insight led me to this SO answer and with one line of code everything was fixed, all the way at the end of the main class:

# Run the app
root = tk.Tk()
root.report_callback_exception = exception_handler # this here.
app = ExceptMain(root)
root.mainloop()

这篇关于Python、tkinter 和导入的类:记录未捕获的异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆