推荐的方式 存储Python异常是什么? - 以结构化方式允许访问该异常的不同部分 - 在Django模型中
设计一个记录“事件”或“尝试做foo”的Django模型是很常见的;要记录的部分信息是“......结果是这个错误”。然后它成为模型上的一个字段。
在数据库中记录错误的一个重要方面是以各种结构化方式查询错误:什么是异常类型?具体信息是什么?异常实例的其他参数是什么?什么是完整的追溯?
(我知道可以让我通过网络向他们传输日志记录或错误的服务;这不这个问题是什么。我需要项目中的结构化异常数据& #39;自己的数据库,直接从同一数据库中的其他模型进行参考。)
Python exception object是一个包含所有这些部分的数据结构 - 异常类型,异常实例的参数,“原因”和“上下文”,回溯 - 单独提供,作为属性宾语。这个问题的目标是将Python异常信息存储在数据库模型中,以便可以以等效的结构化方式查询它们。
因此,使用自由格式的TextField来记录异常的字符串表示是不够的。需要结构化字段或字段集;甚至可能是一个用于存储异常实例的完整单独模型。
当然,我可以自己设计这样的东西,并花时间犯同样的错误,无数人在我面前毫无疑问地试图实现这样的事情。相反,我想从解决这个问题的现有艺术中学习。
在Django模型中存储结构化Python异常数据的一般模式是什么,最好是在PyPI中的现有成熟通用库,我的项目可以用于其模型?
答案 0 :(得分:0)
我不确定很多人都在寻找一种存储异常数据的自定义方式
最终,你必须知道你需要什么。在我迄今为止工作的所有项目中,回溯文本包含了足够的信息来挖掘错误源。 (有时,甚至是天真的,以便TB多次逃脱,仍然足以“反向逃避”它,并且只用一个错误实例来修复源。)
一些调试工具在发生异常时在每个执行帧中提供实时交互式自我检测 - 但必须是“实时”,因为您通常不能序列化Python执行帧并将其存储在数据库中。
也就是说,如果回溯文本不够,您可以通过调用sys.exc_info()[2]
来获取回溯对象。这允许您内省每个帧并自己知道文件,行号,本地和全局变量作为词典。如果希望变量值在数据库上可用,则必须对它们进行序列化,但并非所有变量值都可以轻松序列化。因此,在此过程中,您可以随时了解“足够时”。
大多数现代数据库允许使用JSON字段,并将异常信息序列化为JSON字段,将数据限制为字符串,数字,bool和None可能就足够了。
一种方法是手动运行f_globals和f_locals dict上每个键的每个键,并尝试json.dumps键的值,并在JSON序列化的异常上,使用对象的repr
代替。或者,您可以自定义JSON序列化程序,该序列化程序可以存储日期时间和日期,打开文件,套接字等的自定义相关数据 - 只有您可以了解自己的需求。
TL; DR:使用JSON字段,在except子句中通过调用sys.last_traceback()
获取traceback对象,并使用自定义函数将您想要的内容序列化为JSON字段。
或者,只需使用traceback.format_tb
来保存回溯文本 - 无论如何它都可能足够了。
答案 1 :(得分:0)
大多数人将这样的任务委派给RollBar或LogEntries等第三方服务,或委派给在诸如Elastic LogStash的VPC中运行的中间件服务。
我建议Django ORM不太适合这种类型的存储。 ORM的大多数优点是,您无需复杂的SQL即可加入关系表,并通过迁移管理架构更改和参照完整性。这些是Django专为“ CRUD”应用程序所遇到的问题-具有用户帐户,首选项,通知收件箱等的Web应用程序。ORM旨在管理具有更多读写功能的 mutable 数据。
您的问题(存储在生产中发生的Python异常)完全不同。您的架构需求几乎不会改变。您要存储的数据一经写入就永远不会被修改,并且所有写入操作都是严格追加的。您的数据不包含外键或其他在迁移中可能会更改的字段。您几乎总是会查询历史上的最新数据,而很少在离线/批量分析之外读取。
如果您真的想将此信息存储在Django中,建议您只存储一个滚动窗口,您可以定期将其滚动为磁盘上的压缩日志。否则,您将在几乎不需要的数据中维护昂贵的索引。在这种情况下,您应该考虑构建自己的自定义Django模型,以提取所需的Exception元数据。您还可以按照@jsbueno的建议,将此信息放入存储为字符串的JSON字段中,但这会牺牲索引选择。
(请注意,Python异常不能直接序列化为JSON或进行腌制。有一个名为tblib的项目可以启用腌制,而该项目又可以存储为Django中的BLOB字段,但是我不知道其性能是合理的。我猜这是不值得的。)
近年来,有许多替代DBMS产品用于具有分析查询模式的类似日志的仅附加存储。但是,大多数进展都太过新了,而且太“网络规模”,无法与Django进行现成的集成,后者专注于更小,更传统的CRUD应用程序。您应该寻找可以更广泛地与Python集成的解决方案,因为对于Django而言,复杂的日志记录/事件存储几乎是不适用的。