增强错误报告的XML-RPC服务器
标准库(在Python 2中是xmlrpclib
和SimpleXMLRPCServer
,在Python 3中是xmlrpc.server
)会把所有错误(包括使用上的错误)都报告为Python的异常。这种方式不太适合公共服务,因为异常信息通常对没有Python知识的人来说不太容易理解,而且可能会泄露一些敏感信息。虽然解决这个问题并不难,但我更倾向于不重复造轮子。有没有什么第三方库能提供更好的错误报告?我希望能得到清晰的错误信息,尤其是使用错误的提示,同时在报告内部错误时能隐藏内部细节(这可以通过日志记录来更好地实现)。
xmlrpclib
已经定义了一些常量来表示这些错误,比如:NOT_WELLFORMED_ERROR
(格式不正确的错误)、UNSUPPORTED_ENCODING
(不支持的编码)、INVALID_ENCODING_CHAR
(无效的编码字符)、INVALID_XMLRPC
(无效的XML-RPC)、METHOD_NOT_FOUND
(找不到方法)、INVALID_METHOD_PARAMS
(无效的方法参数)、INTERNAL_ERROR
(内部错误)。
2 个回答
1
我觉得你遇到的问题不是特定于某个库的。当你使用任何库或框架时,通常你会想要捕捉所有的错误,把它们记录下来,然后显示一个“哎呀,我们遇到问题了。你可以通过x@x.com联系我们,告诉我们错误编号100和你做了什么。”这样的提示。所以,你可以把可能出错的地方放在try/catch语句里,创建一个通用的日志记录器,然后就可以开始了……
1
看起来没有现成的库能满足我的需求,所以我最终自己实现了一个:
class ApplicationError(Fault):
def __init__(self, exc_info):
Fault.__init__(self, xmlrpclib.APPLICATION_ERROR,
u'Application internal error')
class NotWellformedError(Fault):
def __init__(self, exc):
Fault.__init__(self, xmlrpclib.NOT_WELLFORMED_ERROR, str(exc))
class UnsupportedEncoding(Fault):
def __init__(self, exc):
Fault.__init__(self, xmlrpclib.UNSUPPORTED_ENCODING, str(exc))
# XXX INVALID_ENCODING_CHAR is masked by xmlrpclib, so the error code will be
# INVALID_XMLRPC.
class InvalidRequest(Fault):
def __init__(self, message):
ault.__init__(self, xmlrpclib.INVALID_XMLRPC, message)
class MethodNotFound(Fault):
def __init__(self, name):
Fault.__init__(self, xmlrpclib.METHOD_NOT_FOUND,
u'Method %r is not supported' % name)
class WrongMethodUsage(Fault):
def __init__(self, message):
Fault.__init__(self, xmlrpclib.INVALID_METHOD_PARAMS, message)
class WrongType(Fault):
def __init__(self, arg_name, type_name):
Fault.__init__(self, xmlrpclib.INVALID_METHOD_PARAMS,
u'Parameter %s must be %s' % (arg_name, type_name))
class XMLRPCDispatcher(SimpleXMLRPCDispatcher, XMLRPCDocGenerator):
server_name = server_title = 'Personalization center RPC interface'
server_documentation = 'Available methods'
def __init__(self, methods):
SimpleXMLRPCDispatcher.__init__(self, allow_none=True, encoding=None)
self.register_instance(methods)
self.register_multicall_functions()
#self.register_introspection_functions()
def _dispatch(self, method_name, args):
if self.funcs.has_key(method_name):
method = self.funcs[method_name]
else:
method = self.instance._getMethod(method_name)
arg_names, args_name, kwargs_name, defaults = \
inspect.getargspec(method)
assert arg_names[0]=='self'
arg_names = arg_names[1:]
n_args = len(args)
if not (args_name or defaults):
if n_args!=len(arg_names):
raise WrongMethodUsage(
u'Method %s takes exactly %d parameters (%d given)' % \
(method_name, len(arg_names), n_args))
else:
min_args = len(arg_names)-len(defaults)
if len(args)<min_args:
raise WrongMethodUsage(
u'Method %s requires at least %d parameters (%d given)' % \
(method_name, min_args, n_args))
if not args_name and n_args>len(arg_names):
raise WrongMethodUsage(
u'Method %s requires at most %d parameters (%d given)' % \
(method_name, len(arg_names), n_args))
try:
return method(*args)
except Fault:
raise
except:
logger.exception('Application internal error for %s%r',
method_name, args)
raise ApplicationError(sys.exc_info())
def dispatch(self, data):
try:
try:
args, method_name = xmlrpclib.loads(data)
except ExpatError, exc:
raise NotWellformedError(exc)
except LookupError, exc:
raise UnsupportedEncoding(exc)
except xmlrpclib.ResponseError:
raise InvalidRequest('Request structure is invalid')
method_name = method_name.encode('ascii', 'replace')
result = self._dispatch(method_name, args)
except Fault, exc:
logger.warning('Fault %s: %s', exc.faultCode, exc.faultString)
return xmlrpclib.dumps(exc)
else:
try:
return xmlrpclib.dumps((result,), methodresponse=1)
except:
logger.exception('Application internal error when marshalling'\
' result for %s%r', method_name, args)
return xmlrpclib.dumps(ApplicationError(sys.exc_info()))
class InterfaceMethods:
def _getMethod(self, name):
if name.startswith('_'):
raise MethodNotFound(name)
try:
method = getattr(self, name)
except AttributeError:
raise MethodNotFound(name)
if not inspect.ismethod(method):
raise MethodNotFound(name)
return method