def __start_server_thread_fun(self, tid): """ 启动服务处理主线程,本线程结束就代表服务停止 @param {int} tid - 线程id """ _result = CResult(code='00000') # 成功 with ExceptionTool.ignored_cresult( result_obj=_result, logger=self._logger, self_log_msg='[%s-STARTING][NAME:%s]%s: ' % (self._server_log_prefix, self._server_name, _('start service error'))): # 统一的异常处理 self._logger.log( self._log_level, '[%s-STARTING][NAME:%s]%s' % (self._server_log_prefix, self._server_name, _('service starting'))) # 执行服务启动处理,执行通过则代表启动成功tid start_result = self._start_server_self(tid) self.__last_start_result = start_result if start_result.code != '00000': # 启动失败,登记了日志,修改状态为未启动,退出 self._logger.log( logging.ERROR, ('[%s-STARTING][NAME:%s][USE:%ss]%s: %s - %s' % (self._server_log_prefix, self._server_name, str((datetime.datetime.now() - self.__server_begin_time ).total_seconds()), _('start service error'), start_result.code, start_result.msg))) self._server_status_change(EnumServerRunStatus.Stop, start_result) return # 启动成功,更新状态 self._logger.log( self._log_level, '[%s-STARTED][NAME:%s][USE:%ss]%s' % (self._server_log_prefix, self._server_name, str((datetime.datetime.now() - self.__server_begin_time ).total_seconds()), _('start service sucess'))) self._server_status_change(EnumServerRunStatus.Running, _result) # 开始进入循环处理 while True: if self.__server_run_status == EnumServerRunStatus.WaitStop: # 收到指令等待停止 while True: if self.__server_run_status == EnumServerRunStatus.ForceStop: # 过程中又被要求强制退出 break # 执行预停止处理函数,例如关闭已打开的子线程 stop_predeal_result = self._stop_server_predeal_self( tid, start_result.server_info) if stop_predeal_result.code == '00000' and not stop_predeal_result.is_finished: # 预处理未完成,需要循环处理 RunTool.sleep(0.1) continue else: # 预处理已完成,退出 break break elif self.__server_run_status == EnumServerRunStatus.ForceStop: # 收到指令马上停止 break else: # 正常执行一次服务处理函数 run_result = self._server_run_self( tid, start_result.server_info) # 继续下一个循环处理 if not run_result.is_finished: continue # 线程结束就代表服务已关闭,执行结束处理函数 self._stop_server_end_self(tid) self._server_status_change(EnumServerRunStatus.Stop, _result) self._logger.log( self._log_level, '[%s-STOPED][NAME:%s][USE:%ss]%s' % (self._server_log_prefix, self._server_name, str((datetime.datetime.now() - self.__server_stop_time ).total_seconds()), _('service stoped')))
def __server_connect_thread_fun(self, thread_id, server_opts, net_info): """ 调用外围传入的网络连接处理线程的封装函数 该函数的主要目的是屏蔽调用程序的网络连接处理函数的异常 @param {int} thread_id - 线程ID @param {object} server_opts - 网络服务的启动参数 @param {object} net_info - 网络的接入参数(例如socket对象) """ with ExceptionTool.ignored_all( logger=self._logger, self_log_msg='[%s][NAME:%s]%s: ' % (self._server_log_prefix, self._server_name, _( 'net service connect deal threading error')), force_log_level=logging.ERROR ): self.__server_connect_deal_fun(thread_id, server_opts, net_info, self.self_tag) # 结束处理 self.__server_connect_thread_end(thread_id)
def stop_server(self, is_wait=True, overtime=0): """ 关闭服务,设置服务为WaitStop-等待停止状态或ForceStop-强制停止状态 @param {bool} is_wait=True - 是否等待服务器所有线程都处理完成后再关闭,True-等待所有线程完成处理,False-强制关闭 @param {float} overtime=0 - 等待超时时间,单位为秒,0代表一直等待 @returns {CResult} - 停止结果,result.code:'00000'-成功,'21402'-服务停止失败-服务已关闭, '31005'-执行超时,29999'-其他系统失败 """ _result = CResult(code='00000') # 成功 with ExceptionTool.ignored_cresult( _result, logger=self._logger, self_log_msg='[%s-STOPING][NAME:%s]%s: ' % (self._server_log_prefix, self._server_name, _('stop service error')), force_log_level=logging.ERROR): self.__server_run_status_lock.acquire() try: _status = EnumServerRunStatus.WaitStop if not is_wait: _status = EnumServerRunStatus.ForceStop self.__server_stop_time = datetime.datetime.now() if self.__server_run_status == EnumServerRunStatus.Running: # 运行状态,处理设置等待关闭状态 self._logger.log( self._log_level, '[%s-STOPING][NAME:%s]%s' % (self._server_log_prefix, self._server_name, _('service stoping'))) self._server_status_change(_status, _result) elif self.__server_run_status == EnumServerRunStatus.WaitStop \ and _status == EnumServerRunStatus.ForceStop: self._logger.log( self._log_level, '[%s-STOPING][NAME:%s]%s' % (self._server_log_prefix, self._server_name, _('service force stoping'))) self._server_status_change(_status, _result) else: # 不属于运行状态,不能处理 _temp_result = CResult(code='21402') # 服务停止失败-服务已关闭 self._logger.log( self._log_level, '[%s-STOPING][NAME:%s]%s' % (self._server_log_prefix, self._server_name, _temp_result.msg)) return _temp_result finally: self.__server_run_status_lock.release() # 等待服务关闭 _begin_time = datetime.datetime.now() # 记录等待开始时间 while is_wait: if self.__server_run_status == EnumServerRunStatus.Stop: break if overtime > 0 and (datetime.datetime.now() - _begin_time).total_seconds() > overtime: _result = CResult(code='31005') # 执行超时 break RunTool.sleep(0.1) # 返回结果 return _result
def _graph_cmd_dealfun(self, message='', cmd='', cmd_para='', prompt_obj=None, **kwargs): """ 生成APP网络关系图 @param {string} message='' - prompt提示信息 @param {string} cmd - 执行的命令key值 @param {string} cmd_para - 传入的命令参数(命令后的字符串,去掉第一个空格) @param {PromptPlus} prompt_obj=None - 传入调用函数的PromptPlus对象,可以通过该对象的一些方法控制输出显示 @param {kwargs} - 传入的主进程的初始化kwargs对象 @returns {CResult} - 命令执行结果,可通过返回错误码10101通知框架退出命令行, 同时也可以通过CResult对象的 print_str属性要求框架进行打印处理 """ _ok_result = CResult(code='00000') try: # 获取参数及处理参数 _execute_path = self._console_global_para['execute_file_path'] _run_para = { 'file': '', 'formatter': 'col-list', 'comment': 'graph', 'outformat': 'pdf', 'direction': 'forward', 'trace_id': '', 'display_no_trace': 'false', 'down_flow_first': 'true', 'trace_relations': None, 'save': '', 'temp': os.path.join(_execute_path, 'temp'), 'view': 'true', 'cleanup': 'false', 'graph_config': os.path.join(_execute_path, 'conf/graph_config.xml'), 'template': 'default' } _in_para = self._cmd_para_to_dict(cmd_para, name_with_sign=False) _run_para.update(_in_para) if '{para}1' not in _run_para.keys(): prompt_obj.prompt_print(_('you must give the $1 para!', 'file')) return CResult(code='20999') _run_para['file'] = _run_para['{para}1'] if _run_para['save'] == '': _run_para['save'] = 'graph.pdf' # 样式模板信息处理 _style_xml = SimpleXml(_run_para['graph_config'], encoding='utf-8') _style_dict = _style_xml.to_dict()['graphviz']['templates'].get( _run_para['template'], { 'graph': {}, 'node': {}, 'edge': {}, 'edge_color': {}, 'trace': {} } ) # 关联关系线颜色 _name_mapping = _style_dict['edge_color'].get('name_mapping', {}) _use_round_color = _style_dict['edge_color'].get('use_round_color', False) _round_color = _style_dict['edge_color'].get( 'round_color', '').replace(' ', '').split(',') # 生成关系字典 _graph_json = self._formatter_col_list(_run_para['file'], prompt_obj=prompt_obj) # 处理生成调用链的情况 _display_no_trace = _run_para['display_no_trace'] == 'true' if _run_para['trace_id'] != '': # 参与追踪的关系名 if _run_para['trace_relations'] is None: # 全部关系 _run_para['trace_relations'] = _graph_json['relation_name'] else: _run_para['trace_relations'] = _run_para['trace_relations'].split(',') # 获取追踪信息字典 _trace_info = self._get_trace_info( _graph_json, _run_para['trace_id'], _run_para['down_flow_first'] == 'true', _run_para['direction'], _run_para['trace_relations'] ) else: _trace_info = { 'trace_id': '', 'up_nodes': list(), 'down_nodes': list(), 'up_edges': list(), 'down_edges': list() } # 调用链的颜色配置 _attr_center_node = _style_dict.get('trace', {}).get('center_node', {}) _attr_up_node = _style_dict.get('trace', {}).get('up_node', {}) _attr_down_node = _style_dict.get('trace', {}).get('down_node', {}) _attr_up_edge = _style_dict.get('trace', {}).get('up_edge', {}) _attr_down_edge = _style_dict.get('trace', {}).get('down_edge', {}) # 处理关系线条的颜色映射 _relation_color = dict() _index = 0 for _relation_name in _graph_json['relation_name']: if _relation_name in _name_mapping: _relation_color[_relation_name] = _name_mapping[_relation_name] elif _use_round_color: _color_index = _index % len(_round_color) _relation_color[_relation_name] = _round_color[_color_index] _index += 1 else: _relation_color[_relation_name] = '' # 开始处理画图 _dot = Digraph( comment=_run_para['comment'], format=_run_para['outformat'], encoding='utf-8', directory=_run_para['temp'], graph_attr=_style_dict['graph'], node_attr=_style_dict['node'], edge_attr=_style_dict['edge'] ) # 开始画节点 _added_rank = list() # 已添加的分组 for _id, _info in _graph_json['list'].items(): if _info['rank'] != '': # 有分组处理 if _info['rank'] not in _added_rank: _added_rank.append(_info['rank']) with _dot.subgraph() as _sub_dot: _sub_dot.attr(rank='same') for _sub_id in _graph_json['rank'][_info['rank']]: # 节点颜色设置 if _sub_id == _trace_info['trace_id']: # 中心节点 _attr = _attr_center_node elif _sub_id in _trace_info['up_nodes']: _attr = _attr_up_node elif _sub_id in _trace_info['down_nodes']: _attr = _attr_down_node elif _display_no_trace and _trace_info['trace_id'] != '': # 不在链中且不应显示 continue else: _attr = {} _sub_dot.node( _sub_id, label=_graph_json['list'][_sub_id]['name'], **_attr ) else: # 节点颜色设置 if _id == _trace_info['trace_id']: # 中心节点 _attr = _attr_center_node elif _id in _trace_info['up_nodes']: _attr = _attr_up_node elif _id in _trace_info['down_nodes']: _attr = _attr_down_node elif _display_no_trace and _trace_info['trace_id'] != '': # 不在链中且不应显示 continue else: _attr = {} _dot.node(_id, label=_info['name'], **_attr) # 开始画关联线 for _id, _info in _graph_json['list'].items(): for _key, _list in _info.items(): if _key in ('name', 'rank'): continue # 判断颜色 _edge_color = _relation_color[_key] for _temp_id in _list: if _temp_id == '': continue if _run_para['direction'] == 'reverse': _head_id = _temp_id _tail_id = _id else: _head_id = _id _tail_id = _temp_id # 处理颜色 _findstr = '%s&&%s' % (_head_id, _tail_id) if _findstr in _trace_info['up_edges']: _attr = _attr_up_edge elif _findstr in _trace_info['down_edges']: _attr = _attr_down_edge elif _display_no_trace and _trace_info['trace_id'] != '': continue else: _attr = {'color': _edge_color} _dot.edge(_head_id, _tail_id, **_attr) # 保存图片 _dir, _filename = os.path.split(os.path.abspath(_run_para['save'])) _filename = FileTool.get_file_name_no_ext(_filename) _dot.render( filename=_filename, directory=_dir, format=_run_para['outformat'], view=(_run_para['view'] == 'true'), cleanup=(_run_para['cleanup'] == 'true') ) except Exception as e: _prin_str = '%s (%s):\n%s' % ( _('execution exception'), str(e), traceback.format_exc() ) prompt_obj.prompt_print(_prin_str) return CResult(code='20999') # 结束 return _ok_result
def _formatter_col_list(self, file: str, prompt_obj=None, **kwargs) -> dict: """ col-list格式化文档 @param {str} file - 要处理的文件路径 @param {PromptPlus} prompt_obj=None - 传入调用函数的PromptPlus对象,可以通过该对象的一些方法控制输出显示 @returns {dict} - 返回的关系字典, list为以id为key的系统清单, rank为分组清单(显示在同一行) 具体格式如下: { 'list': { 'id': { 'name': '系统或模块名', 'rank': '分组名', '关系名1': ['id1', 'id2', 'id3', ...], '关系名2': [...], ... }, ... }, 'relation_name': ['关系名1', '关系名2', ...], 'rank': { 'rank_name1': ['id1', 'id2', ...], ... } } """ _default_key = ('id', 'name', 'rank') # 打开文件开始逐个处理 _wb = xlrd.open_workbook(filename=file) _sheet = _wb.sheets()[0] _nrows = _sheet.nrows # 总行数 _ncols = _sheet.ncols # 总列数 # 处理标题行 _head = dict() # 标题行字典,key为标题名, value为位置 for _index in range(0, _ncols): _head[str(_sheet.cell(0, _index).value)] = _index # 处理关系名 _relation_name = list() # 关系名列表 for _name in _head.keys(): if _name not in _default_key: _relation_name.append(_name) # 遍历每行生成id和name的对照字典 _name_dict = dict() # key为name, value为id _relation = dict() # 系统清单字典 _rank = dict() # 分组清单字典 for _row in range(1, _nrows): _id = str(_sheet.cell(_row, _head['id']).value).strip() _name = _id if 'name' in _head: _name = str(_sheet.cell(_row, _head['name']).value).strip() _rank_name = '' if 'rank' in _head: _rank_name = str(_sheet.cell(_row, _head['rank']).value).strip() # 处理分组 if _rank_name != '': if _rank_name in _rank: _rank[_rank_name].append(_id) else: _rank[_rank_name] = [_id, ] # 处理系统清单 _name_dict[_name] = _id _relation[_id] = {'name': _name, 'rank': _rank_name} for _head_str, _index in _head.items(): if _head_str not in _default_key: _list_str = str(_sheet.cell(_row, _index).value).strip() if _list_str != '': _relation[_id][_head_str] = _list_str.split(',') # 处理关系数组中的name映射 for _id, _para in _relation.items(): for _key, _value in _para.items(): if _key not in _default_key: try: for _index in range(len(_value)): _item = _value[_index].strip() if _item != '' and _item not in _relation: # 需要通过name转换为id _value[_index] = _name_dict[_item] else: _value[_index] = _item except: _prin_str = _( 'System [$1] relationship [$2] with not define system name [$3]!', _id, _key, _item ) + '\n' prompt_obj.prompt_print(_prin_str) # 重新抛出异常 raise # 返回处理后的标准字典 return { 'list': _relation, 'relation_name': _relation_name, 'rank': _rank }
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)
def __server_connect_deal_fun_http(self, thread_id, server_opts, net_info, self_tag): """ Http服务自有的服务处理函数,当server_http_deal_fun被传入的时候使用 @param {[type]} thread_id - 线程ID @param {[type]} server_opts - 服务的启动参数 @param {[type]} net_info - 具体实现的连接信息(例如Socket对象) @param {[type]} self_tag - 用于发起端传入自身的识别标识 """ while True: # 判断是否要断开服务器 if self.server_run_status != EnumServerRunStatus.Running: # 服务器状态不是运行,直接断开连接 self._logger.log( self._log_level, '[LIS-HTTP][NAME:%s][IP:%s][PORT:%s]%s' % ( self_tag, str(net_info.raddr[0]), str(net_info.raddr[1]), _('close remote connection because servie shutdown') ) ) self.close_connect(net_info) return # 获取报文信息 _result = self.recv_data(net_info, {}) if not _result.is_success(): self._logger.log( logging.ERROR, '[LIS-HTTP][NAME:%s][IP:%s][PORT:%s][EX:%s]%s: %s - %s\n%s' % ( self_tag, str(net_info.raddr[0]), str(net_info.raddr[1]), str(type(_result.error)), _('recv data from remote error'), _result.code, _result.msg, _result.trace_str ) ) self.close_connect(net_info) return _proto_msg = _result.data[0] _msg = _result.data[1] # 写日志 if self._is_print_msg_log: self._logger.log( self._log_level, '[INF-RECV][NAME:%s][IP:%s][PORT:%s]\n%s' % ( self_tag, str(net_info.raddr[0]), str(net_info.raddr[1]), self.get_print_str(_proto_msg, _msg) ) ) _is_close = True _rproto_msg = None _rmsg = None try: (_is_close, _rproto_msg, _rmsg) = self._server_http_deal_fun(net_info, _proto_msg, _msg) except Exception as e: self._logger.log( logging.ERROR, '[LIS-HTTP][NAME:%s][IP:%s][PORT:%s][EX:%s]%s\n%s' % ( self_tag, str(net_info.raddr[0]), str(net_info.raddr[1]), str(type(e)), _('execute server_http_deal_fun error'), traceback.format_exc() ) ) # 组织一个异常的返回报文 _rproto_msg = MsgHTTP('%s 500 Internal Server Error' % (_proto_msg.ver), obj_type=EnumMsgObjType.String) # 组织回包 if _rproto_msg is not None: _result = self.send_data(net_info, (_rproto_msg, _rmsg), {}) if not _result.is_success(): self._logger.log( logging.ERROR, '[LIS-HTTP][NAME:%s][IP:%s][PORT:%s][EX:%s]%s: %s - %s\n%s' % ( self_tag, str(net_info.raddr[0]), str(net_info.raddr[1]), str(type(_result.error)), _('send data to remote error'), _result.code, _result.msg, _result.trace_str ) ) self.close_connect(net_info) return # 写日志 if self._is_print_msg_log: self._logger.log( self._log_level, '[INF-RET][NAME:%s][IP:%s][PORT:%s]\n%s' % ( self_tag, str(net_info.raddr[0]), str(net_info.raddr[1]), self.get_print_str(_rproto_msg, _rmsg) ) ) # 判断是否断开连接 if _is_close: self.close_connect(net_info) return # 睡眠一下,继续处理下一个请求 RunTool.sleep(0.001)