线上遇到一个问题,服务器返回500错误,但是没有任何日志输出 我们使用的是loguru 0.5.3版本,打印日志,同时输出到文件和标准输出流,当然标准输出流是没法看了,直接重定向/dev/null 了 怀疑是loguru出了问题 在内网复现后,把服务器放到前台,标准输出流果然有日志出现
xmlrpc.client.Fault: <Fault 1: "<class 'xml.parsers.expat.ExpatError'>:not well-formed (invalid token): line 12, column 79">
--- Logging error in Loguru Handler [#2](https://laidback.montaigne.io/tags/2) ---
Record was: None
Traceback (most recent call last):
File "/usr/local/python3/lib/python3.8/site-packages/loguru/_handler.py", line 272, in _queued_writer
message = queue.get()
File "/usr/local/python3/lib/python3.8/multiprocessing/queues.py", line 358, in get
return _ForkingPickler.loads(res)
TypeError: __init__() missing 2 required positional arguments: 'faultCode' and 'faultString'
--- End of logging error ---
这看起来有些费解。我们是使用了loguru的 非同步打印日志的方式,用于多进程的情况下,它会把日志放到一个队列里,然后有个单独线程从这个队列里取到这个值,这中间python底层是用到pickle来做序列化。 xmlrpc.client.Fault定义如下
class Fault(Error):
"""Indicates an XML-RPC fault package."""
def __init__(self, faultCode, faultString, **extra):
Error.__init__(self)
self.faultCode = faultCode
self.faultString = faultString
def __repr__(self):
return "<%s %s: %r>" % (self.__class__.__name__,
self.faultCode, self.faultString)
查了些资料后发现 python - Cannot unpickle Exception subclass - Stack Overflow (python3.5 前的一个类似错误) https://github.com/python/cpython/blob/main/Objects/exceptions.c#L144 (BaseException的reduce方法)
static PyObject *
BaseException_reduce(PyBaseExceptionObject *self, PyObject *Py_UNUSED(ignored))
{
if (self->args && self->dict)
return PyTuple_Pack(3, Py_TYPE(self), self->args, self->dict);
else
return PyTuple_Pack(2, Py_TYPE(self), self->args);
}
Fault继承于Exception, Exception继承于BaseException,BaseException有个属性叫args 问题就出在Fault的init函数里 Error.init(self) 这一行,等于args属性最后是空的, 反序列化的时候会调用 reduce 会返回Fault类和空的args,然后用它们实例化Fault类,那就出错了, 因为Fault需要两个位置参数 faultCode, faultString 也就是我们开始看到的错误。 回到开始的问题,作者已经修复了无法反序列化这个问题, https://github.com/Delgan/loguru/pull/963 只要升级loguru到0.7.2就行