异常与日志

码出高效

  1. 处理异常程序需要处理三个问题
  1. 哪里发生异常?
  2. 谁来处理异常?
  3. 如何处理异常?
  1. 哪里发生异常: 代码分为
  1. 稳定代码
  2. 非稳定代码: 这里是异常捕获针对的地方
  1. 谁来处理异常
  1. throw: 方法内部抛出具体异常类对象的关键字
  2. throws: 用在方法signature上, 表示方法调用者可以通过此方法声明向上抛出异常对象
  3. 当前被捕获的异常是否需要自己处理
    3.1 直接捕获异常并做相应处理: 在当前方法的处理能力范围之内, 且没有必要对外透出
    3.2 向上抛出, 由上层方法或者框架来处理: 在当前方法的处理能力范围之外, 有必要对外透出
  1. 如何处理异常
  1. 无论采用哪种方式处理异常, 严禁捕获异常后什么都不做或打印一行日志了事
    1.1 方法内部处理异常: 根据不同的业务场景进行定制处理, 如重试, 回滚等操作
    1.2 向上抛出异常: 需要在异常对象中添加上下文参数, 局部变量, 运行环境等信息, 这样有助于排查问题

1. 异常分类

  1. 异常分类结构

在这里插入图片描述
所有异常都是Throwable的子类
1 Error
2 Exception

  1. exception
  1. checked异常: 需要在代码中显式处理的异常, 否则会编译报错. 如果自行处理则可以在当前方法中捕获异常, 如果无法处理, 则继续向调用方抛出异常对象
    1.1 无能为力型, 引起注意型: 针对此类异常, 程序无法处理, 如字段超长等导致的SQLException, 即使做再多的重试对解决异常也没有任何帮助
    1.2 力所能及, 坦然处置型: 如发生未授权异常, 程序可跳转至权限申请页面
  2. unchecked异常: 运行时异常, 它们都继承自RuntimeException, 不需要程序进行显示的捕捉和处理
    2.1 可预测异常: 如IndexOutOfBoundsException, NullPointerException. 基于对代码的性能和稳定性要求, 此类异常不应该被产生或者抛出, 而应该提前做好边界检查, 空指针判断等处理
    2.2 需捕捉异常: 如RPC调用
    产生的远程服务超时异常, 此类异常是客户端必须显示处理的异常, 不能因服务端的异常导致客户端不可用, 此事处理方案可以使重试或者降级处理
    2.3 可透出异常: 主要指框架或系统产生的且会自行处理的异常, 而程序无需关心.
  1. 异常处理

根据当前场景自定义具有业务含义的异常, 为了避免异常泛滥, 可以优先使用业界或者团队已定义过的异常

2. try代码块

try代码块与锁的关系

lock方法可能会抛出unchecked异常, 如果把加锁的方法放在try代码块中, 必然触发finally中的unlock方法执行. 虽然是因为加锁失败而造成程序中断的, 但是真正加锁失败的原因可能会被后者覆盖. 所以要在try代码块之前调用lock()方法

3. 异常的抛与接

  1. 信号量

在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用

  1. 推荐异常抛
  1. 对外提供的开放接口: 使用错误码
  2. 公司内部跨应用远程服务调用: 优先考虑使用Result对象来分装错误码, 错误描述信息
  3. 应用内部: 直接抛出异常对象
  1. 为什么使用Result对象封装异常信息
  1. 如果使用抛异常的返回方式: (1) 一旦调用方没有捕获, 就会产生运行时错误, 导致程序中断; (2) 如果抛出异常中不添加栈信息, 只是new自定义异常并加入自定义的错误信息, 对于调用端解决问题的帮助不会太大. (3) 如果加了栈信息, 在频繁调用出错的情况下, 信息序列化和传输的性能损耗也是问题

4. 日志

  1. 记录应用日志主要原因
  1. 记录操作轨迹: 可以数据化地分析用户偏好, 有助于优化业务逻辑, 为用户提供个性化的服务; 例如, 通过access.log记录用户的操作频度和跳转链接, 有助于分析用户的后续行为
  2. 监控系统运行状况: 全面有效的日志系统有助于建立完善的应用监控体系, 由此工程师可以实时监控系统运行状况, 及时预警, 避免故障发生
  3. 回溯系统故障
  1. 监控系统运行状况
  1. 服务器使用状态: 内存, CPU的使用情况
  2. 应用运行情况: 响应时间, QPS等交互状态
  3. 应用错误信息: 空指针, SQL异常等的监控
  4. 举例: 在CPU使用率大于60%, 四核服务器中load大于4时发出报警, 提醒工程师及时处理, 避免发生故障

4.1 日志规范

  1. 推荐的日志命名方式

appName_logType_logName.log

  1. logType为日志类型, 推荐分类: (1) stats, (2). monitor, (3). visit
  2. logName为日志描述
  1. 日志文件需要保存多久

至少保存15天, 可以根据日志文件的重要程度, 文件大小以及磁盘空间再自行延长保存时间

  1. 日志级别, 按照重要程度由低到高排序
  1. DEBUG级别日志: 记录对调试程序有帮助的信息
  2. INFO级别日志: 用来记录程序运行现场
  3. WARN级别日志: 可以用来记录程序运行现场, 但是更偏向于表名此处有出现了潜在错误的可能
  4. ERROR级别日志: 当前程序运行发生了错误, 需要被关注.
  5. FATAL级别日志: 当前程序运行出现了严重的错误时间, 并且将会导致应用程序中断

4.1.1 日志处理方式

不同级别的日志优先级和重要性不同, 因此在打印日志时针对不同的日志级别要有不同的日志处理方式

  1. 预先判断日志级别

DEBUG, INFO级别的日志: 使用条件输出或者使用占位符的方式打印
原因: 避免系统资源的浪费

  1. 避免无效日志打印
  1. 生产环境禁止输出DEBUG日志且有选择地输出INFO日志
  2. 使用INFO,WARN级别来记录业务行为信息时 , 一定要控制日志输出量, 以免磁盘空间不足.
  3. 为日志文件设置合理的生命周期, 及时清理过期的日志
  4. 避免重复打印, 务必在日志配置文件中设置additivity=false
  1. 区别对待错误日志

ERROR级别: 只记录系统逻辑错误, 异常或者违反重要的业务规则
WARN级别: 除了ERROR级别的都归入WARN级别

  1. 保证记录内容完整
  1. 记录异常时一定要输出异常堆栈
  2. 日志中如果输出对象实例, 要确保实例类重写了toString方法, 否则只会输出对象的hashCode值, 没有实际意义.
  1. 记录日志时思考的问题
  1. 日志是否有人看
  2. 看到这条日志能做什么
  3. 能不能提升问题排查效率

4.2 日志框架

  1. 日志框架

日志门面
日志适配器
日志库

  1. 门面设计模式
  2. logback

日志库, 是log4j的同一个作者, 本身实现了log4j的接口, 是log4j的升级版

  1. 日志门面适配器

门面规范是后来提出的, 例如log4j没有实现, 就找个门面适配

  1. 日志库适配器

老工程直接使用了日志库来打印, 日志库到日志门面的适配

  1. logger被定义为static变量

因为这个logger与当前类绑定, 避免每次都new一个新对象, 造成资源浪费.

  1. 防止日志冲突

避免依赖的jar包, 间接地引入其他日志库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值