日志
前言
我是一名后台程序员,接触后台只有一年时间,在这期间一共做过四个项目,分别是:
- 工作室招新系统
- 视频学习网站
- 创客网站
- 打印机项目
由于之前做项目的时候没有好好重视日志,所以导致在开发与维护项目出现了很多问题,现在分享分享我的惨痛经历:
做第一个项目的时候,这个是我第一次开始学习后台时做的第一个项目,使用最原始的 servlet + jsp 技术,当时没有任何经验,没有把系统的需求和实现方式思考清楚就开始动工,做的过程遇到很多问题,但今天的主题不在这些问题上,而是关于日志,当时我们并没有在项目使用日志技术记录日志信息,因为该项目没有进行上线发布,所以后面做完了大部分功能之后就没继续完善和维护,所以没有意识到日志的重要性。
做第二个项目与第三个项目时,这是一个商业项目,由于第一次没有意识到日志的重要性,所以开发时是没有打日志的,等到项目开发完后,我们需要上线测试,部署到服务器时,发现出现了很多个 bug,但以前我们找 bug 都是通过调试或者在控制台输出错误信息来查看,部署到服务器上根本调试不了也看不到错误信息,这就导致一直找不到导致出现 bug 的地方。后面在项目添加了,但只是在 servlet 层后面添加一个全局的try catch,再记录错误信息而已,但这并没起多大作用。
做第四个项目时,当时有要打日志的想法,但是由于之前没做过,所以对于要如何打日志根本不理解,应该在什么地方记录日志,应该使用哪种日志级别。所以最后在项目的时候就随便乱打日志,导致最后输出的日志信息时,也是一团乱。
在基于前面的经历,觉得有必要去好好学习如何使用日志机制。
为什么要使用日志机制
就如我之前所说的,开发以后应用服务时,如果你没有使用日志机制,那么当你将服务部署上去时,系统出 bug 了,因为没有错误信息可以查看,所以你找不到错误出现的地方,这样会不方便测试与维护。使用日志及时记录错误的信息,会更方便我们定位到错误的地方以及错误的原因,方便我们修复系统。
日志机制
日志信息级别
日志信息一般有五种级别,分别是:
DEBUG
DEBUG 级别是最低的级别,一般是为了用于测试应用程序而输出日志信息,它只能用于开发环境与测试环境,不能用于线上环境。INFO
INFO 级别比 DEBUG 级别要高,一般用此级别来记录服务正在开启、从请求接收的信息、响应返回的信息等等WARN
WARN 级别 比 INFO 级别要高,一般是用于记录客户端与服务端连接丢失,数据库连接丢失、Scoket 连接达到限制等一些表示 server 运行时的状态信息ERROR
ERROR 级别 比 WARN 级别要高,一般是用于记录系统出现错误的异常错误信息,在开发时候,通过记录 ERROR 级别的信息,可以方便地定位到系统出错的地方,从而更方便地修复系统FATAL
FATAL 级别的信息一般是用于记录非常严重的错误事件信息,该事件可能会导致应用崩溃或者停止
如何使用日志
- 在输出 DEBUG 级别的日志信息前加一层 isDebugEnabled() 的判断,因为这是为了避免系统发布到运行环境上之后输出过多的日志信息,也避免需要为了发布而删掉或注释掉输出的 DEBUG 信息,因为这会影响日后的系统维护
- 认真考虑好哪些信息要用哪种级别去记录日志。如果在发布环境上记录太多日志信息会影响性能,因为这会产生很多文件 IO ;但如果不记录重要的信息如接收的信息、响应的信息,将会非常困难找出错误的问题和异常在何处
- 使用配置文件来加载日志的信息,这会方便地改变系统的日志记录级别,从而避免重启应用
- 对于记录的日志信息,需要格式化;在高并发的系统时,需要记录当前的线程名以及当前处理类的完全限定名;记录的日志格式应一致、有用,必要的时候还要输出时间
- 使用一系列后缀来区分不同的层次的信息。比如与数据库有关,就记录 DB_LOG,与 Session 有关,记录 SESSION_LOG,这样有利于通过后缀查找匹配出那个层次的所有日志信息,方便管理和差错
- 如果没有为一个日志指定级别,应该让他从他的父类继承,所以我们一般通过配置文件为根日志配置级别为 DEBUG
- 没有日志或过多的日志都是非常不好的,所以在性能和维护差错方面做好一个平衡,仔细选择要用哪些级别记录哪些信息
- 记录的日志信息应当是简单、易懂并且对于团队来说是有效的
Log4j
为系统性能考虑,使用 Log4j 注意下列几点:
- 避免输出 ‘%C’, ‘%f’, ‘%L’, ‘%M’ 等位置信息
- 尽量使用异步
- 为每个模块设置单独的输出文件
- 每次调用前检查 if(logger.isDebugEnabled()) { logger.debug(……) }
解析:
- 避免输出 ‘%C’, ‘%f’, ‘%L’, ‘%M’ 等位置信息
当配置文件中的配置项包含 Location 信息会非常昂贵- %C 输出类名
- %F 输出文件名
- %L 输出行号
- %M 输出函数名
注:当配置为异步输出的时候,以上位置信息可能会显示不出来,因为实在另外的一个线程记录的调用信息。此时,我们可以使用下面的方法来获取类名和函数名:
StackTraceElement se = Thread.currentThread().getStackTrace()[2];
String msg = se.getClassName() + "-[" + se.getMethodName() + "] " + errorMessage;
使用异步
要使用异步在于要在配置文件中配置 appender,为什么要使用异步来记录日志,前面说过记录日志信息会产生文件 IO,这会影响一定的性能,使用异步可以避免因为需要同步记录日志而产生的等待时间。为每个模块设置单独的输出文件
根据模块来区分日志文件,在调试和维护某个模块时,可以更方便。每次调用前检查 if(logger.isDebugEnabled()) { logger.debug(……) }
前面已说过
总结
日志机制对于开发来说是非常重要的,它关系到调试与维护,在开发一个项目前期,应该提前部署好日志环境。同时,在开发的过程中,要想好如何去记录日志信息,要用哪种级别去记录,权衡性能与维护,选择一种较好的日志实施方案。
本文只是个人观点,如有不同意,可以说出来大家一起讨论讨论。之后再补充一篇使用 slf4j + logback 搭建日志机制的博文。