HiveNetCore.utils.exception_tool 源代码

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
#
# Copyright 2018 黎慧剑
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

"""
异常处理工具

@module exception_tool
@file exception_tool.py

"""

import os
import sys
import copy
import traceback
import logging
from contextlib import contextmanager
# 根据当前文件路径将包路径纳入, 在非安装的情况下可以引用到
sys.path.append(os.path.abspath(os.path.join(
    os.path.dirname(__file__), os.path.pardir, os.path.pardir)))
from HiveNetCore.generic import CResult
from HiveNetCore.i18n import _


__MOUDLE__ = 'exception_tool'  # 模块名
__DESCRIPT__ = u'异常处理工具'  # 模块描述
__VERSION__ = '0.1.0'  # 版本
__AUTHOR__ = u'黎慧剑'  # 作者
__PUBLISH__ = '2018.08.29'  # 发布日期


[文档]class ExceptionTool(object): """ 异常处理工具,提供便捷的异常处理模式 """
[文档] @staticmethod @contextmanager def ignored(expect=(), logger=None, self_log_msg='', force_log_level=None, debug=False): """ 忽略指定异常, 简化异常捕获代码, 利用该函数忽略指定的异常, 详细说明如下: 1、对于指定忽略的异常, 忽略不处理( 如果指定logger则会进行日志输出, 使用WARNING级别) 2、对于非指定的异常, 仍抛出异常( 如果指定logger则会进行日志输出, 使用ERROR级别) 3、输出的日志为self_log_msg+'\n'+trace_str @param {tuple} expect=() - 需要忽略的异常列表, 例如(ZeroDivisionError, ValueError) @param {object} logger=None - 日志对象, 如果为None代表不需要输出日志, 传入对象需满足: 1、标准logging的logger对象 2、自定义的日志类对象, 但应实现warning、error的标准方法 @param {string} self_log_msg='' - 需要输出的自定义日志信息 @param {int} force_log_level=None - 强制遇到所有异常统一按指定的日志级别输出(logging.INFO/...) @param {bool} - debug=False - 是否调试模式, 如果是调试模式, 当没有logger时使用print输出堆栈信息 @example with ignored((ZeroDivisionError, ValueError), logger, '执行XX出现异常'): count = 1 / 0 count = count + 10000 """ try: yield except expect as ex: # 匹配到指定异常, 输出日志 _log_level = logging.WARNING if force_log_level is not None: _log_level = force_log_level ExceptionTool.__print_log(logger=logger, self_log_msg='[EX:%s]%s' % (str(type(ex)), self_log_msg), trace_str=traceback.format_exc(), log_level=_log_level, debug=debug) pass except Exception as e: # 其他异常, 输出日志并抛出异常 _log_level = logging.ERROR if force_log_level is not None: _log_level = force_log_level ExceptionTool.__print_log(logger=logger, self_log_msg='[EX:%s]%s' % (str(type(e)), self_log_msg), trace_str=traceback.format_exc(), log_level=_log_level, debug=debug) raise sys.exc_info()[1]
[文档] @staticmethod @contextmanager def ignored_all(unexpect=(), logger=None, self_log_msg='', force_log_level=None, debug=False): """ 忽略除指定以外的所有异常,简化异常捕获代码, 利用该函数忽略指定以外的所有异常, 详细说明如下: 1、对于指定以外的异常, 忽略不处理( 如果指定logger则会进行日志输出, 使用WARNING级别) 2、对于指定的异常, 仍抛出异常( 如果指定logger则会进行日志输出, 使用ERROR级别) 3、输出的日志为self_log_msg+'\n'+trace_str @param {tuple} unexpect=() - 指定不能忽略的异常列表, 例如(ZeroDivisionError, ValueError) @param {object} logger=None - 日志对象, 如果为None代表不需要输出日志, 传入对象需满足: 1、标准logging的logger对象 2、自定义的日志类对象, 但应实现warning、error的标准方法 @param {string} self_log_msg='' - 需要输出的自定义日志信息 @param {int} force_log_level=None - 强制遇到所有异常统一按指定的日志级别输出(logging.INFO/...) @param {bool} - debug=False - 是否调试模式, 如果是调试模式, 当没有logger时使用print输出堆栈信息 @example with ignored_all((ZeroDivisionError, ValueError), logger, '执行XX出现异常'): count = 1 / 0 count = count + 10000 """ try: yield except unexpect as ue: # 匹配到指定异常, 输出日志并抛出异常 _log_level = logging.ERROR if force_log_level is not None: _log_level = force_log_level ExceptionTool.__print_log(logger=logger, self_log_msg='[EX:%s]%s' % (str(type(ue)), self_log_msg), trace_str=traceback.format_exc(), log_level=_log_level, debug=debug) raise sys.exc_info()[1] except Exception as e: # 其他异常, 输出日志并忽略 _log_level = logging.WARNING if force_log_level is not None: _log_level = force_log_level ExceptionTool.__print_log(logger=logger, self_log_msg='[EX:%s]%s' % (str(type(e)), self_log_msg), trace_str=traceback.format_exc(), log_level=_log_level, debug=debug) pass
[文档] @staticmethod @contextmanager def ignored_cresult(result_obj=None, error_map={}, expect=(), expect_no_log=False, expect_use_error_map=True, logger=None, self_log_msg='', force_log_level=None, i18n_obj=None, i18n_msg_paras=(), debug=False): """ 忽略异常并设置CResult对象,简化异常捕获代码, 利用该函数忽略指定的异常, 并设置传入的通用结果对象, 详细说明如下: 1、对于指定忽略的异常, 忽略不处理, 结果为成功( 如果指定logger则会进行日志输出, 使用WARNING级别) 2、对于非指定的异常, 不抛出异常, 结果为失败( 如果指定logger则会进行日志输出, 使用ERROR级别) 3、输出的日志为self_log_msg+'\n'+trace_str 4、根据error_map的映射关系设置错误码和错误信息 @param {CResult} result_obj=None - 需要设置的错误类对象(对象值会被修改) @param {dict} error_map={} - 用来设置错误类对象的映射表, 具体说明如下: 1、key为异常类, value为(code, msg)的错误码、错误描述二元组, 如果msg=None代表使用标准错误码 2、应有一个'DEFAULT'的key, 代表没有匹配上的异常映射, 默认value为('29999', None) 3、应有一个'SUCESS'的key, 代表成功的映射, 默认value为('00000', None) 注: value也可以为(code, msg, i18n_msg_paras)的错误码、错误描述、国际化替换参数三元组, i18n_msg_paras为tuple类型, 使用该模式支持CResult的国际化处理 @param {tuple} expect=() - 需要忽略的异常列表, 例如(ZeroDivisionError, ValueError) @param {bool} expect_no_log=False - 忽略异常列表是否不打印日志 @param {bool} expect_use_error_map=True - 忽略异常列表所匹配到的异常, 所返回错误码是否使用错误码映射表: 如果在映射表中匹配上则返回映射表的错误码;匹配不上则返回成功 @param {object} logger=None - 日志对象, 如果为None代表不需要输出日志, 传入对象需满足: 1、标准logging的logger对象 2、自定义的日志类对象, 但应实现warning、error的标准方法 @param {string} self_log_msg='' - 需要输出的自定义日志信息 @param {int} force_log_level=None - 强制遇到所有异常统一按指定的日志级别输出(logging.INFO/...) @param {object} i18n_obj=None - 国际化类的实例对象, 该对象需实现translate方法 @param {tuple} i18n_msg_paras=() - 与self_log_msg配套使用, 当使用国际化时, 可以传入变量, 用于替换self_log_msg中的$1占位符 @param {bool} - debug=False - 是否调试模式, 如果是调试模式, 当没有logger时使用print输出堆栈信息 @example result = CResult() with ExceptionTools.ignored_CResult(result_obj=result, error_map={},expect=(),logger=None,self_log_msg=''): i = 1/0 i = i + 1000 print(str(result)) """ _error_map = copy.deepcopy(error_map) try: # 初始化对象 if result_obj is None: result_obj = CResult(code='00000', msg=None, i18n_obj=i18n_obj) # 确保映射表中有默认值 if 'SUCESS' not in _error_map.keys(): _error_map['SUCESS'] = ('00000', None, i18n_msg_paras) if 'DEFAULT' not in _error_map.keys(): _error_map['DEFAULT'] = ('29999', None, i18n_msg_paras) # 执行with对应的脚本 yield except expect as ex: # 匹配到指定异常, 输出日志 if not expect_no_log: _log_level = logging.WARNING if force_log_level is not None: _log_level = force_log_level _self_log_msg = '' if i18n_obj is not None: _self_log_msg = i18n_obj.translate(self_log_msg, replace_para=i18n_msg_paras) else: _self_log_msg = _(self_log_msg, *i18n_msg_paras) ExceptionTool.__print_log(logger=logger, self_log_msg='[EX:%s]%s' % (str(type(ex)), _self_log_msg), trace_str=traceback.format_exc(), log_level=_log_level, debug=debug) # 按成功处理 _error = sys.exc_info() _trace_str = traceback.format_exc() if expect_use_error_map and _error[0] in _error_map.keys(): if len(_error_map[_error[0]]) < 3: result_obj.change_code( code=_error_map[_error[0]][0], msg=_error_map[_error[0]][1] ) else: result_obj.change_code( code=_error_map[_error[0]][0], msg=_error_map[_error[0]][1], i18n_msg_paras=_error_map[_error[0]][2] ) result_obj.error = str(_error[0]) result_obj.trace_str = _trace_str else: # 按成功处理 pass except Exception as e: # 其他异常, 输出日志, 获取失败信息 _error = sys.exc_info() _trace_str = traceback.format_exc() if _error[0] in _error_map.keys(): if len(_error_map[_error[0]]) < 3: result_obj.change_code( code=_error_map[_error[0]][0], msg=_error_map[_error[0]][1] ) else: result_obj.change_code( code=_error_map[_error[0]][0], msg=_error_map[_error[0]][1], i18n_msg_paras=_error_map[_error[0]][2] ) result_obj.error = str(_error[0]) result_obj.trace_str = _trace_str else: # 其他失败 if len(_error_map['DEFAULT']) < 3: result_obj.change_code( code=_error_map['DEFAULT'][0], msg=_error_map['DEFAULT'][1] ) else: result_obj.change_code( code=_error_map['DEFAULT'][0], msg=_error_map['DEFAULT'][1], i18n_msg_paras=_error_map['DEFAULT'][2] ) result_obj.error = str(_error[0]) result_obj.trace_str = _trace_str _log_level = logging.ERROR if force_log_level is not None: _log_level = force_log_level _self_log_msg = '' if i18n_obj is not None: _self_log_msg = i18n_obj.translate(self_log_msg, replace_para=i18n_msg_paras) else: _self_log_msg = _(self_log_msg, *i18n_msg_paras) ExceptionTool.__print_log(logger=logger, self_log_msg='[EX:%s]%s' % (str(type(e)), _self_log_msg), trace_str=result_obj.trace_str, log_level=_log_level, debug=debug)
# 内部函数定义 @staticmethod def __print_log(logger=None, self_log_msg='', trace_str='', log_level=logging.WARNING, debug=False): """ 内部进行日志输出处理, 调用日志对象进行日志输出处理 @param {object} logger=None - 日志对象, 如果为None代表不需要输出日志, 传入对象需满足: 1、标准logging的logger对象 2、自定义的日志类对象, 但应实现warning、error等的标准方法 @param {string} self_log_msg='' - 需要输出的自定义日志信息 @param {string} trace_str='' - 错误追踪堆栈日志, 异常时的traceback.format_exc() @param {int} log_level=logging.WARNING - 需要输出的自定义日志级别 @param {bool} - debug=False - 是否调试模式, 如果是调试模式, 当没有logger时使用print输出堆栈信息 """ if logger is not None or debug: # 要输出的日志内容 _log_str = '' if len(self_log_msg) > 0: _log_str = self_log_msg + '\n' + trace_str else: _log_str = trace_str if logger is not None: # 输出日志 logger.log(log_level, _log_str, extra={'callFunLevel': 3}) else: print(_log_str)
if __name__ == '__main__': # 当程序自己独立运行时执行的操作 # 打印版本信息 print(('模块名: %s - %s\n' '作者: %s\n' '发布日期: %s\n' '版本: %s' % (__MOUDLE__, __DESCRIPT__, __AUTHOR__, __PUBLISH__, __VERSION__)))