T31Java异常处理和日志
Java异常体系Java异常机制使用异常、日志为系统保驾护航道路千万条,安全第一条日志不规范,排查两行泪异常应当描述导致当前异常发生的原因根据异常栈快速定位倒异常发生的位置结合异常描述和异常栈解决异常日志记录规约系统应依赖使用日志框架(SLF4J、JCL)的API而不是具体日志库中的APISLF4j绑定具体日志框架的过程系统应依赖使用日志框架(SLF4J、JCL)的API而不是具体日志库中的API
Java异常体系
Java异常机制
使用异常、日志为系统保驾护航
道路千万条,安全第一条
日志不规范,排查两行泪
异常应当描述导致当前异常发生的原因
根据异常栈快速定位倒异常发生的位置
结合异常描述和异常栈解决异常
日志记录规约
系统应依赖使用日志框架(SLF4J、JCL)的API而不是具体日志库中的API
SLF4j绑定具体日志框架的过程
- 系统应依赖使用日志框架(SLF4J、JCL)的API而不是具体日志库中的API
- 在日志输出时,字符串变量之间的拼接使用占位符的方式
- 日志打印时禁止直接用JSON工具将对象转换为String
- 尽量用英文来描述日志错误信息
logback框架使用之核心配置对象及属性分析
logback日志框架使用之配置文件解析
根节点-configuration及其通用属性的配置示例
Appender节点配置示例
logback日志框架使用之关键类图分析
异步Appender配置示例
logback日志记录线程模型分析
异常错误日志实时通知
将错误异常日志发送至邮箱与企业微信
整合Sentry将错误日志发送至钉钉消息
日志输出规约
日志级别开关判断
异常信息要完整
避免重复打印日志
扩展日志设计与规约
T31日志设计标准
基于以上我们的日志设计将按以下标准来设计
1、系统应依赖使用日志框架(SLF4J、JCL)的API而不是具体日志库中的API,这样有利于哦我们维护和统一各个类的日志处理方式
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(App.class);
2、为了检查异常错误是否是在应用运行期间周期性发送,我们的日志文件起码要保存两周以上。
3、应用中的扩展日志的命名方式应该统一 :
appName_logType_logName.log。logType:日志类型,推荐分类有
stats/desc/monitor/visit 等;logName:日志描述。这种命名的好处:通过文件名就可知道日志文件属于什么应用,什么类型,什么目的,也有利于归类查找
例如:
T31server应用中余票监控时区转换异常,如:
T31server_monitor_timeZoneConvert.log
我们为了方便查看和便于通过日志对系统进行及时监控,应该对日志进行分类,比如将错误日志和业务日志分开存放
4、级别日志输出(trace/debug/info)必须使用条件输出形式或者使用占位符的方式。
说明:logger.debug("Processing trade with id: " + id + " symbol: " + symbol);如果日志级别是 warn,上述日志不会打印,但是会执行字符串拼接操作,如果 symbol 是对象, 会执行 toString()方法,浪费了系统的计算资源,就算执行了上述操作,可日志却也没有打印。
例如:
使用条件输出形式
if (logger.isDebugEnabled()) {
logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
}
使用占位符的方式
logger.debug("Processing trade with id: {} symbol : {} ", id, symbol);
5、为了避免重复打印日志,造成磁盘IO浪费,所以一定要做log4j.xml中设置additivity=false。
<logger name="com.T31.dubbo.config" additivity="false">
6、异常信息必须包括两类信息:系统异常现场信息和异常堆栈信息。如果不处理,那就要关键字 throws 往上抛出。
logger.error(各类参数或者对象 toString + "_" + e.getMessage(), e);
7、对于日志的使用要谨慎。生产环境禁止输出debug日志;对于info日志要有选择的输出;warn级别的日志要注意日志输出量的问题,避免把服务器磁盘占满,及时删除这些观察日志。
错误码设计文档
错误码的功能和应用
- 错误码暂定都是5位数字,并配有相应的英文解释
- 错误码为 0 表示成功,其他都表示错误
- 错误码按模块按功能场景分级分段,前三位错误码表示模块,第四位表示模块下的功能。举例,商城系统里有交易模块和商品模块,则可以这样划分:401开头的表示交易模块,402开头的表示商品模块,4011开头的表示交易模块里的下单场景需要用到的错误码,4021表示商品模块下的添加商品场景里需要用到的错误码。如果某个场景功能下需要的比较多的错误码,则可以使用其他未被使用的码段,即该场景功能可以拥有多个码段,然后通过添加注释等方式让人理解即可。
- 数字 1 开头的错误码表示系统级别的错误,比如缺少某种字符集,连不上数据库之类的,系统级的错误码不需要分模块,可以按照自增方式进行添加
- 数字 4 开头的错误码表示API参数校验失败,比如 “交易模块下单场景中,订单金额参数不能为空” 可以用 40111 错误码来表示
- 数字 5 开头的错误码表示后台业务校验失败,比如 “交易模块下单场景中,该用户没有下单权限” 可以用 50111 错误码来表示
- 数字 4 开头的错误码与数字 5 开头的错误码对应的模块分类需要保持一致,即 4011 表示交易模块下单场景的API错误,5011 表示交易模块下单场景的业务错误
- 错误码按需分配,逐步增加,灵活扩展
错误码规约
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A71o5mmJ-1635754163694)(D:\大数据\笔记\孤尽\T31.assets\image-20211101152244310.png)]
T31错误码设计文档
错误码举例
一、授权/令牌请求接口返回码
描述应用发起授权请求或令牌请求时,开放平台的返回码。
错误码 | 错误描述 | Error Description |
---|---|---|
10000 | 非法的请求参数 | Invalid request |
10001 | 用户认证失败 | Invalid client |
10002 | 非法的授权信息 | Invalid grant |
10003 | 应用没有被授权,无法使用所指定的grant_type | Unauthorized client |
10004 | grant_type字段超过定义范围 | Unsupported grant_type |
10005 | scope信息无效或超出范围 | Invalid scope |
10006 | 提供的更新令牌已过期 | Expired token |
10007 | redirect_uri字段与注册应用时所填写的不匹配 | Redirect_uri mismatch |
10008 | response_type参数值超过定义范围 | Unsupported response type |
10009 | 用户或授权服务器拒绝授予数据访问权限 | Access denied |
二、API通用返回码
描述API接口的共性返回码
错误码 | 错误描述 | Error Description |
---|---|---|
0 | 成功 | Success |
1 | 未知错误 | Unknown error |
2 | 服务暂不可用 | Service temporarily unavailable |
3 | 未知的方法 | Unsupported openapi method |
4 | 接口调用次数已达到设定的上限 | Open api request limit reached |
5 | 请求来自未经授权的IP地址 | Unauthorized client IP address |
6 | 无权限访问该用户数据 | No permission to access user data |
7 | 来自该refer的请求无访问权限 | No permission to access data for this referer |
100 | 请求参数无效 | Invalid parameter |
101 | api key无效 | Invalid API key |
104 | 无效签名 | Incorrect signature |
105 | 请求参数过多 | Too many parameters |
106 | 未知的签名方法 | Unsupported signature method |
107 | timestamp参数无效 | Invalid/Used timestamp parameter |
109 | 无效的用户资料字段名 | Invalid user info field |
110 | 无效的access token | Access token invalid or no longer valid |
111 | access token过期 | Access token expired |
210 | 用户不可见 | User not visible |
211 | 获取未授权的字段 | Unsupported permission |
212 | 没有权限获取用户的email | No permission to access user email |
800 | 未知的存储操作错误 | Unknown data store API error |
801 | 无效的操作方法 | Invalid operation |
802 | 数据存储空间已超过设定的上限 | Data store allowable quota was exceeded |
803 | 指定的对象不存在 | Specified object cannot be found |
804 | 指定的对象已存在 | Specified object already exists |
805 | 数据库操作出错,请重试 | A database error occurred. Please try again |
900 | 访问的应用不存在 | No such application exists |
异常处理设计文档
1、在Controller层统一捕获异常
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0pleq66Q-1635754163695)(D:\大数据\笔记\孤尽\T31.assets\image-20211101153800619.png)]
2、全局异常处理组件GlobalExceptionHandler 的定义和使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Koq11W4e-1635754163696)(D:\大数据\笔记\孤尽\T31.assets\image-20211101153818178.png)]
3、API层异常设计实践
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jK1TKGbE-1635754163697)(D:\大数据\笔记\孤尽\T31.assets\image-20211101153829115.png)]
4、Service层异常设计实
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IlJDBN2T-1635754163697)(D:\大数据\笔记\孤尽\T31.assets\image-20211101153903878.png)]
5、DAO数据处理层异常、日志实践
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6xm25Xpf-1635754163698)(D:\大数据\笔记\孤尽\T31.assets\image-20211101153925106.png)]
6、使用MDC实现轻量级调用链路追踪
分布式链路追踪
将一次分布式请求还原成调用链路,将一次分布式请求的调用 情况集中展示,比如各个服务节点上的耗时、请求具体到达哪 台机器上、每个服务节点的请求状态等等
链路追踪的主要功能
- 故障快速定位
- 链路性能可视化
- 链路分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-REGFoRtV-1635754163698)(D:\大数据\笔记\孤尽\T31.assets\image-20211101153946485.png)]
6、使用MDC实现轻量级调用链路追踪
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oQaH10zi-1635754163699)(D:\大数据\笔记\孤尽\T31.assets\image-20211101154112792.png)]
7、用有限的异常类处理业务中复杂多变的无限可能
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yQKOtIEO-1635754163700)(D:\大数据\笔记\孤尽\T31.assets\image-20211101154129400.png)]
8、降低系统的维护难度与过度设计、冗余的手段
通用模块的单独拆分
合理的领域划分
合适的工程结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JhK3yY0k-1635754163700)(D:\大数据\笔记\孤尽\T31.assets\image-20211101154159342.png)]
T31异常处理设计文档
1、防止 NPE(空指针异常,Java中常见难处理的异常)
NPE 产生的场景:
1)返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE。
例如:
public int f()
{ return Integer 对象}
如果为 null,自动解箱抛 NPE。 数据库的查询结果可能为 null。
特殊NPE场景及其处理对策,级联调用时容易产生NPE
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8rCqryfx-1635754163701)(D:\大数据\笔记\孤尽\T31.assets\image-20211101155510827.png)]
3) 集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null。
4) 远程调用返回对象时,一律要求进行空指针判断,防止 NPE。
5) 对于 Session 中获取的数据,建议 NPE 检查,避免空指针。
6) 级联调用,如下
obj.getA().getB().getC();
这样的一连串调用,易产生 NPE。
我们使用 JDK8 的 Optional 类来防止 NPE 问题。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jxskds82-1635754163702)(D:\大数据\笔记\孤尽\T31.assets\image-20211101155636854.png)]
2、RuntimeException
RuntimeException 可以通过预先检查进行规避,而不应该 通过 catch 来处理,
例如:
IndexOutOfBoundsException,NullPointerException
能判断一定要判断,不要无脑使用try catch
if (obj != null) {...}
这样的 try catch 没有意义
try { obj.method() } catch (NullPointerException e) {...}
3、finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。 如果 JDK7 及以上,可以使用 try-with-resources 方式,向我们现在开发常用的Java8就可以用这种方法。
4、不能在 finally 块中使用 return,finally 块中的 return 返回后方法结束执行,不 会再执行 try 块中的 return 语句。
5、foreach遍历集合的异常
- 不要在foreach循环里进行元素的remove/add操作
- foreach循环会自动跳过遍历空集合,如果对于有null值的集合,碰到null时需要注意NPE
更多推荐
所有评论(0)