error_catch.py 6.7 KB
Newer Older
F
felixhjh 已提交
1 2 3 4 5
import sys 
import enum 
import os 
import logging 
import traceback
6
#from paddle_serving_server.pipeline import ResponseOp
F
felixhjh 已提交
7 8 9 10 11 12
import threading
import inspect
import traceback
import functools
import re
from .proto import pipeline_service_pb2_grpc, pipeline_service_pb2
F
felixhjh 已提交
13
from .util import ThreadIdGenerator
H
huangjianhui 已提交
14
from paddle_serving_server.util import kill_stop_process_by_pid
F
felixhjh 已提交
15 16 17 18 19 20

_LOGGER = logging.getLogger(__name__) 

class CustomExceptionCode(enum.Enum): 
    """
    Add new Exception
F
felixhjh 已提交
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
    
    0           Success
    50 ~ 99     Product error
    3000 ~ 3999 Internal service error
    4000 ~ 4999 Conf error 
    5000 ~ 5999 User input error
    6000 ~ 6999 Timeout error
    7000 ~ 7999 Type Check error
    8000 ~ 8999 Internal communication error
    9000 ~ 9999 Inference error
    10000       Other error
    """
    OK = 0
    PRODUCT_ERROR = 50

    NOT_IMPLEMENTED = 3000
    CLOSED_ERROR = 3001
    NO_SERVICE = 3002
    INIT_ERROR = 3003
    CONF_ERROR = 4000
    INPUT_PARAMS_ERROR = 5000
    TIMEOUT = 6000
    TYPE_ERROR = 7000
    RPC_PACKAGE_ERROR = 8000 
    CLIENT_ERROR = 9000
    UNKNOW = 10000


class ProductErrCode(enum.Enum):
    """
    ProductErrCode is a base class for recording business error code. 
    product developers inherit this class and extend more error codes. 
F
felixhjh 已提交
53
    """
F
felixhjh 已提交
54 55
    pass

F
felixhjh 已提交
56 57

class CustomException(Exception):
58
    def __init__(self, exceptionCode, errorMsg, isSendToUser=False):
F
felixhjh 已提交
59 60 61
        super().__init__(self)
        self.error_info = "\n\texception_code: {}\n"\
                          "\texception_type: {}\n"\
62
                          "\terror_msg: {}\n"\
F
felixhjh 已提交
63 64
                          "\tis_send_to_user: {}".format(exceptionCode.value,
                          CustomExceptionCode(exceptionCode).name, errorMsg, isSendToUser)
F
felixhjh 已提交
65 66 67 68
    
    def __str__(self):
        return self.error_info

F
felixhjh 已提交
69 70


F
felixhjh 已提交
71
class ErrorCatch():
F
felixhjh 已提交
72 73 74 75 76 77
    def __init__(self):
        self._id_generator = ThreadIdGenerator(
                     max_id=1000000000000000000,
                     base_counter=0,
                     step=1)

F
felixhjh 已提交
78 79 80 81 82
    def __call__(self, func):
        if inspect.isfunction(func) or inspect.ismethod(func):
            @functools.wraps(func)
            def wrapper(*args, **kw):
                try:
83
                    res = func(*args, **kw)
F
felixhjh 已提交
84 85
                except CustomException as e:
                    log_id = self._id_generator.next()
86
                    resp = pipeline_service_pb2.Response()
F
felixhjh 已提交
87
                    _LOGGER.error("\nLog_id: {}\n{}Classname: {}\nFunctionName:{}\nArgs:{}".format(log_id, traceback.format_exc(), func.__qualname__, func.__name__, args))
F
felixhjh 已提交
88
                    split_list = re.split("\n|\t|:", str(e))
89
                    resp.err_no = int(split_list[3])
F
felixhjh 已提交
90 91 92
                    resp.err_msg = "Log_id: {}  ErrNo: {}  Error_msg: {}  ClassName: {}  FunctionName: {}".format(log_id, resp.err_no, split_list[9], func.__qualname__ ,func.__name__ )
                    is_send_to_user = split_list[-1].replace(" ", "")
                    if is_send_to_user == "True":
93
                         return (None, resp)
F
felixhjh 已提交
94
                    else:
H
huangjianhui 已提交
95 96 97
                        print("Erro_Num: {} {}".format(resp.err_no, resp.err_msg))
                        print("Init error occurs. For detailed information, Please look up log by log_id.")
                        kill_stop_process_by_pid("kill", os.getpgid(os.getpid()))
98
                except Exception as e:
F
felixhjh 已提交
99
                    log_id = self._id_generator.next()
100
                    resp = pipeline_service_pb2.Response()
F
felixhjh 已提交
101 102 103
                    _LOGGER.error("\nLog_id: {}\n{}Classname: {}\nFunctionName: {}\nArgs: {}".format(log_id, traceback.format_exc(), func.__qualname__, func.__name__, args))
                    resp.err_no = CustomExceptionCode.UNKNOW.value
                    resp.err_msg = "Log_id: {}  ErrNo: {}  Error_msg: {}  ClassName: {}  FunctionName: {}".format(log_id, resp.err_no, str(e).replace("\'", ""), func.__qualname__ ,func.__name__ )
104 105 106 107
                    return (None, resp)
                    # other exception won't be sent to users.
                else:
                    resp = pipeline_service_pb2.Response()
F
felixhjh 已提交
108
                    resp.err_no = CustomExceptionCode.OK.value
109 110
                    resp.err_msg = ""
                    return (res, resp)
F
felixhjh 已提交
111

F
felixhjh 已提交
112 113
            return wrapper

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
def ParamChecker(function):
    @functools.wraps(function)
    def wrapper(*args, **kwargs):
        # fetch the argument name list.
        parameters = inspect.signature(function).parameters
        argument_list = list(parameters.keys())

        # fetch the argument checker list.
        checker_list = [parameters[argument].annotation for argument in argument_list]

        # fetch the value list.
        value_list =  [inspect.getcallargs(function, *args, **kwargs)[argument] for argument in inspect.getfullargspec(function).args]

        # initialize the result dictionary, where key is argument, value is the checker result.
        result_dictionary = dict()
        for argument, value, checker in zip(argument_list, value_list, checker_list):
            result_dictionary[argument] = check(argument, value, checker, function)

        # fetch the invalid argument list.
        invalid_argument_list = [key for key in argument_list if not result_dictionary[key]]

        # if there are invalid arguments, raise the error.
        if len(invalid_argument_list) > 0:
F
felixhjh 已提交
137
            raise CustomException(CustomExceptionCode.INPUT_PARAMS_ERROR, "invalid arg list: {}".format(invalid_argument_list))
138 139 140 141 142

        # check the result.
        result = function(*args, **kwargs)
        checker = inspect.signature(function).return_annotation
        if not check('return', result, checker, function):
F
felixhjh 已提交
143
            raise CustomException(CustomExceptionCode.INPUT_PARAMS_ERROR, "invalid return type")
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181

        # return the result.
        return result
    return wrapper


def check(name, value, checker, function):
    if isinstance(checker, (tuple, list, set)):
        return True in [check(name, value, sub_checker, function) for sub_checker in checker]
    elif checker is inspect._empty:
        return True
    elif checker is None:
        return value is None
    elif isinstance(checker, type):
        return isinstance(value, checker)
    elif callable(checker):
        result = checker(value)
        return result

class ParamVerify(object):
    @staticmethod
    def int_check(c, lower_bound=None, upper_bound=None):
        if not isinstance(c, int):
            return False
        if isinstance(lower_bound, int) and isinstance(upper_bound, int):
            return c >= lower_bound and c <= upper_bound
        return True

    @staticmethod
    def file_check(f):
        if not isinstance(f, str):
            return False
        if os.path.exist(f):
            return True
        else:

            return False

F
felixhjh 已提交
182
ErrorCatch = ErrorCatch()