error_catch.py 8.0 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
    
    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):
    """
51 52 53
    ProductErrCode is to record business error codes.
    the ProductErrCode  number ranges from 51 to 99
    product developers can directly add error code into this class. 
F
felixhjh 已提交
54
    """
F
felixhjh 已提交
55 56
    pass

F
felixhjh 已提交
57 58

class CustomException(Exception):
F
felixhjh 已提交
59 60 61 62 63 64 65 66 67 68
    """
    An self-defined exception class
    
    Usage : raise CustomException(CustomExceptionCode.exceptionCode, errorMsg, isSendToUser=False)
    Args  :  
           exceptionCode : CustomExceptionCode or ProductErrCode
           errorMsg : string message you want to describe the error
           isSendToUser : whether send to user or just record in errorlog
    Return : An string of error_info 
    """
69
    def __init__(self, exceptionCode, errorMsg, isSendToUser=False):
F
felixhjh 已提交
70 71 72
        super().__init__(self)
        self.error_info = "\n\texception_code: {}\n"\
                          "\texception_type: {}\n"\
73
                          "\terror_msg: {}\n"\
F
felixhjh 已提交
74 75
                          "\tis_send_to_user: {}".format(exceptionCode.value,
                          CustomExceptionCode(exceptionCode).name, errorMsg, isSendToUser)
F
felixhjh 已提交
76 77 78 79
    
    def __str__(self):
        return self.error_info

F
felixhjh 已提交
80 81


F
felixhjh 已提交
82
class ErrorCatch():
F
felixhjh 已提交
83 84
    """
    An decorator class to catch error for method or function.
F
felixhjh 已提交
85

F
felixhjh 已提交
86 87 88 89 90 91
    Usage : @ErrorCatch
    Args  : None
    Returns: tuple(res, response)
             res is the original funciton return 
             response includes erro_no and erro_msg
    """
F
felixhjh 已提交
92 93 94 95 96
    def __call__(self, func):
        if inspect.isfunction(func) or inspect.ismethod(func):
            @functools.wraps(func)
            def wrapper(*args, **kw):
                try:
97
                    res = func(*args, **kw)
F
felixhjh 已提交
98
                except CustomException as e:
F
felixhjh 已提交
99 100 101 102 103 104
                    if "log_id" in kw.keys():
                        log_id = kw["log_id"]
                    elif "logid_dict" in kw.keys() and "data_id" in kw.keys():
                        log_id = kw["logid_dict"].get(kw["data_id"])
                    else:
                        log_id = 0
105
                    resp = pipeline_service_pb2.Response()
F
felixhjh 已提交
106
                    _LOGGER.error("\nLog_id: {}\n{}Classname: {}\nFunctionName: {}\nArgs: {}".format(log_id, traceback.format_exc(), func.__qualname__, func.__name__, args))
F
felixhjh 已提交
107
                    split_list = re.split("\n|\t|:", str(e))
108
                    resp.err_no = int(split_list[3])
109
                    resp.err_msg = "Log_id: {}  Raise_msg: {}  ClassName: {}  FunctionName: {}".format(log_id, split_list[9], func.__qualname__ ,func.__name__ )
F
felixhjh 已提交
110 111
                    is_send_to_user = split_list[-1].replace(" ", "")
                    if is_send_to_user == "True":
112
                         return (None, resp)
F
felixhjh 已提交
113
                    else:
H
huangjianhui 已提交
114
                        print("Erro_Num: {} {}".format(resp.err_no, resp.err_msg))
115
                        print("Init error occurs. For detailed information. Please look up pipeline.log.wf in PipelineServingLogs by log_id.")
H
huangjianhui 已提交
116
                        kill_stop_process_by_pid("kill", os.getpgid(os.getpid()))
117
                except Exception as e:
F
felixhjh 已提交
118 119 120 121 122 123
                    if "log_id" in kw.keys():
                        log_id = kw["log_id"]
                    elif "logid_dict" in kw.keys() and "data_id" in kw.keys():
                        log_id = kw["logid_dict"].get(kw["data_id"])
                    else:
                        log_id = 0
124
                    resp = pipeline_service_pb2.Response()
H
huangjianhui 已提交
125
                    _LOGGER.error("\nLog_id: {}\n{}Classname: {}\nFunctionName: {}".format(log_id, traceback.format_exc(), func.__qualname__, func.__name__))
F
felixhjh 已提交
126
                    resp.err_no = CustomExceptionCode.UNKNOW.value
127
                    resp.err_msg = "Log_id: {}  Raise_msg: {}  ClassName: {}  FunctionName: {}".format(log_id, str(e).replace("\'", ""), func.__qualname__ ,func.__name__ )
128 129 130
                    return (None, resp)
                else:
                    resp = pipeline_service_pb2.Response()
F
felixhjh 已提交
131
                    resp.err_no = CustomExceptionCode.OK.value
132 133
                    resp.err_msg = ""
                    return (res, resp)
F
felixhjh 已提交
134

F
felixhjh 已提交
135 136
            return wrapper

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
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 已提交
160
            raise CustomException(CustomExceptionCode.INPUT_PARAMS_ERROR, "invalid arg list: {}".format(invalid_argument_list))
161 162 163 164 165

        # check the result.
        result = function(*args, **kwargs)
        checker = inspect.signature(function).return_annotation
        if not check('return', result, checker, function):
F
felixhjh 已提交
166
            raise CustomException(CustomExceptionCode.INPUT_PARAMS_ERROR, "invalid return type")
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204

        # 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

205 206 207 208 209 210 211 212 213 214 215 216 217 218
    @staticmethod
    def check_feed_dict(feed_dict, feed_list):
        if not isinstance(feed_dict, dict):
            return False
        # read model config, try catch and 
        feed_dict_key_size = len(feed_dict.keys())
        if len(feed_dict.keys()) != len(feed_list):
            return False
        for key in feed_list:
           if key in feed_dict.keys():
               return False
        return True 


F
felixhjh 已提交
219
ErrorCatch = ErrorCatch()