异常处理与日志规范
- 底层原始异常不要捕获直接向外抛出,一直往外抛出,直到 Service 或者 Controller;
- 自定义的 BusinessException 异常统一在 Controller 层进行汇总处理;
- 其他异常需要首先在 Service 层进行 error 级别日志记录,之后向外层抛出 BusinessException 并调用 setCause 设置造成原因(注意 Service 只能向外抛出 BusinessException,其他异常需要进行异常转换);
- Controller 层不要再向外抛出异常,需要在 Controller 层进行捕获操作
- 如果返回的是 RespResult,在 catch 中返回
RespResult.fail
- 如果返回的是页面,在 catch 中返回
"redirect:/500"
- Controller 层捕获异常的时候不需要打印错误日志
- 系统只处理受检异常,运行时异常不要捕获也不要抛出,属于Bug,应当解决;
- 进行异常转换时都要进行 error 级别日志记录,并且调用 initCause 方法设置错误原因;
- Service 层向外抛出自定义的 BusinessException 异常,由调用此 Service 的 Controller 或者定时任务捕获处理;
- 抛出定义的 CdnHuaweiException 或 BusinessException 是可以使用链式调用的方式快捷打印日志,示例如下:
throw new CdnHuaweiException("XXXX发生错误!错误原因:{}", e.getMessage()).setCause(e).log();
- 其他特殊类(Bean、Component、Interceptor…)的异常处理和日志打印规则可自行处理;
程序示例
DomainConfigureApi.java
public static JSONObject getDomainConfigs(String domainName) throws CdnHuaweiException { try { Request request = HuaweiRequest.getRequest(CdnConst.GET_DOMAIN_CONFIGS.replace("{domain_name}", domainName), "GET"); Response response = HuaweiRequest.doRequest(request); return HuaweiRequest.dealResponse(response); } catch (Exception e) { log.error("查询域名配置接口失败!错误原因:{}", e.getMessage()); throw new CdnHuaweiException("查询域名配置接口失败!错误原因:{}", e.getMessage()).setCause(e); // 如果打印的日志和报错的信息一样,那么上面两行代码也可以使用下面代替 // throw new CdnHuaweiException("查询域名配置接口失败!错误原因:{}", e.getMessage()).setCause(e).log(); } }
CdnDomainService.java
public JSONObject getDomainConfig(String domainName) throws BusinessException { JSONObject jsonObject = null; try { jsonObject = DomainConfigureApi.getDomainConfigs(domainName); } catch (CdnHuaweiException e) { log.error("获取域名的详细配置失败,域名:{}", domainName); throw new CdnHuaweiException("获取域名的详细配置失败!错误原因:{}", e.getMessage()).setCause(e); } JSONObject configs = jsonObject.getJSONObject("configs"); // 解析主源站 JSONArray sourcesArray = configs.getJSONArray("sources"); for (int i = 0; i < sourcesArray.size(); i++) { JSONObject source = sourcesArray.getJSONObject(i); if (source.getInteger("priority") == SourcePriority.MAIN) { configs.put("main_source", source); } if (source.getInteger("priority") == SourcePriority.BACK) { configs.put("back_source", source); } } return configs; }
DomainSettingsPageController.java
@GetMapping("/domain-setting-basic") public String domainBasicSettings(Long id, Map<String, Object> map) { if (Assert.isEmpty(id)) { return "redirect:/400"; } CdnDomainVo cdnDomainVo = cdnDomainService.getCdnDomainVoById(id); if (Assert.isEmpty(cdnDomainVo)) { return "redirect:/404"; } // 获取域名的详细配置 try { JSONObject domainConfig = cdnDomainService.getDomainConfig(cdnDomainVo.getDomainName()); map.put("domainConfig", domainConfig); } catch (BusinessException e) { return "redirect:/500"; } // 查询所有的业务类型 List<CdnBusinessType> businessTypes = cdnBusinessTypeService.queryAll(); // 查询所有的业务范围 List<CdnServiceArea> serviceAreas = cdnServiceAreaService.queryAll(); // 查询所有源站类型 List<CdnOriginType> originTypes = cdnOriginTypeService.queryAll(); map.put("businessTypes", businessTypes); map.put("serviceAreas", serviceAreas); map.put("originTypes", originTypes); map.put("domain", cdnDomainVo); return "admin/domain/domain-setting-basic"; }