调试技巧-添加属性Hook
调试的问题
接手的一个项目中用到了 mongoengine
,跟踪其中一个 bug。某个 Document
子类的对象在调用 to_json
时,偶尔会漏掉其中一个字段。
检查 Document
源代码发现,to_json
实际上是用到了 _fields_ordered
属性来生成 json
,添加日志发现子类的 _fields_ordered
属性被修改了,但是代码中找不到修改的地方。
这时候这个问题就难调试了,因为修改的是类的属性,所以一个进程修改了这个属性之后,属性值就变了。但是一个进程要处理很多 http
请求,所以有可能是之前某一个请求修改的属性。可能性很多,很难排查。
添加属性Hook
为了解决这个问题,只有在修改类属性的时候添加日志,这样就比较容易定位到修改类属性的地方。
class Meta(type):
def __setattr__(cls, name, value):
print("setattr", name, value, cls.__name__)
super(Meta, cls).__setattr__(name, value)
class Test(object, metaclass=Meta):
_test = ("xxxxx", )
print("before", Test._test)
Test._test = ("xxxxx", "yyyyy")
print("after", Test._test)
输出
before ('xxxxx',)
setattr _test ('xxxxx', 'yyyyy') Test
after ('xxxxx', 'yyyyy')
结果
最后定位到问题在于 pickle.loads
了老版本的二进制数据,Document
类定了 __setstate__
方法,在 loads
根据数据改变了 _fields_ordered
的值。
def __setstate__(self, data):
...
if '_fields_ordered' in data:
setattr(type(self), '_fields_ordered', data['_fields_ordered'])
...