日志系统的起源发展,常见搭配就不过多赘述了,直接进入正题。
企业级的应用系统是必定会包含日志的。
读日志和debug可以解决开发人员在项目中遇到的99%的问题。也是一个开发人员必备的基础素质,一看到成千上万行日志不要发憷熟悉日志系统和规范日志系统的记录,可以迅速定位问题。
在项目中引入log4j以后,会配置一个名为log4j.properties的配置文件
### 设置能打印的几种级别 ###
log4j.rootLogger = debug,stdout,D,E
### 输出信息到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss,SSS} [%-5p] %l %n%m
###[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n %M方法名 %L行数
### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} %5p --- [%5t] %C %M(%L) : %m%n
### %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %l %m%n
### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} %5p --- [%5t] %C %M(%L) : %m%n
### 加拦截器,还有切面编程自动加日志
可以看到
Log4j由三个重要的组件构成:日志信息的优先级,日志信息的输出目的地,日志信息的输出格式。日志信息的优先级从高到低有ERROR、WARN、 INFO、DEBUG,分别用来指定这条日志信息的重要程度;日志信息的输出目的地指定了日志将打印到控制台还是文件中;而输出格式则控制了日志信息的显 示内容。
输出时候一定注意格式,把线程(很多问题是由于线程间调用和同步引起的)全路径加类名方法名等都
一般项目中,每个模块,每个级别都有对应的日志文件目录,还会对单个文件过大的日志分片。
影响到程序正常运行、当前请求正常运行的异常情况:
多数都是ERROR级别的日志,先查看最近的error日志,查看启动失败的原因,例如数据库初始化配置,配置文件不存在或者格式错误,新引入的组件配置失败引起整个项目无法启动,第三方加载出错,定位到错误之后就很好解决了。
不应该出现但是不影响程序、当前请求正常运行的异常情况:
一般是许可文件不存在,失效,引用一些过期方法,配置文件不存在但自动创建,容错机制的时候出现的错误情况,接近临界值的时候,例如:缓存池占用达到警告线
业务异常的记录,比如:当接口抛出业务异常时,应该记录此异常
Service方法中对于系统/业务状态的变更,代码走进不同的逻辑分支。
记录调用方法,存储过程,外部接口的参数与返回值
生产情况下DEBUG,需要使用开关进行管理,一般处于关闭状态。
示例:
logger.debug("Processing trade with id:[{}] and symbol : [{}] ", id, symbol);
当你遇到问题的时候,只能通过debug功能来确定问题,你应该考虑打日志,良好的系统,是可以通过日志进行问题定为的。
当你碰到if…else 或者 switch这样的分支时,要在分支的首行打印日志,用来确定进入了哪个分支
经常以功能为核心进行开发,你应该在提交代码前,可以确定通过日志可以看到整个流程
基本格式
必须使用参数化信息的方式:
logger.debug("Processing trade with id:[{}] and symbol : [{}] ", id, symbol);
对于debug日志,必须判断是否为debug级别后,才进行使用:
if (logger.isDebugEnabled()) {
logger.debug("Processing trade with id: " +id + " symbol: " + symbol);
}
不要进行字符串拼接,那样会产生很多String对象,占用空间,影响性能。
反例(不要这么做):
logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
使用[]进行参数变量隔离
如有参数变量,应该写成如下写法:
logger.debug("Processing trade with id:[{}] and symbol : [{}] ", id, symbol);
这样的格式写法,可读性更好,对于排查问题更有帮助。
和代码一样,日志系统可能会由于开发人员的习惯,积累越来越多的冗余部分,这时候就要对日志进行瘦身精简(无调用的代码,注释的代码不是后续功能的,大胆的删掉,让代码保持清晰,本来逻辑很复杂,再掺杂进去各种无用误导的东西,对后续开发梳理和维护极其不友好),及时删除无用的日志打印动作,对于打印的日志一定按照规范设计的等级,参数化格式打印,打印出关键信息还要注意日志的显示退格。
对待自己的代码,还有代码打印出来的日志,一定要抱着一种洁癖的心态去雕琢,清爽的日志内容和格式,可以节省很多定位问题的时间,看起来也赏心悦目,更重要的是,也可以帮助一线的兄弟,减少工作量,在项目出问题的时候更快速的定位解决问题,节约成本挽回损失。