日志的打印在软件开发过程当中必不可少,通常分为两个大类:程序员
操做日志,主要针对的是用户,例如在Photoshop软件中会记录本身操做的步骤,便于用户本身查看。web
系统日志,主要针对的是软件开发人员(包括测试、维护人员),也就是说这部分的日志用户是看不到的,也就是咱们一般所说的debug日志。算法
在大学中所谓的实践项目或者老师布置的做用中,一般是不会在乎日志,除非在做业中有特别的须要,每每在开发过程当中直接打印控制台语句来调试程序,这是极为不专业的调试开发过程。因此这也就致使了一个问题,大学毕业和工做时衔接不上最大的问题不在于技术上的难度,而是日志打印的问题。这个看似不起眼的问题对于应届生来讲每每是“恶梦”,操做日志相对比较好理解,用户作了什么就记录什么;而打印系统日志则无从下手,每每通常有下面几个方面——3W:spring
本篇着重讲解系统日志,因此如下“日志”均为“系统日志”的简称。我将针对这几个方面对系统日志的打印作一个简要的总结。另外对Java中经常使用的日志打印框架(log4j)的几种使用方式作一个示范。apache
1.程序入口编程
在入口打印日志是由于这个时候传递进来的参数没有通过任何处理,将它打印在日志文件中能一眼就知道程序的原始数据是否符合咱们的预期,是否是传递进来的原始数据就出现 的问题。api
2.异常捕获服务器
在异常打印出详细的日志能让你快速定位错误在哪里,例如在程序抛出异常捕获时,在平时咱们常常就是直接在控制台打印出堆栈信息e.printStackTrace(),但在实际的生产环境更加艰苦,更别说有IDE来让你查看控制台信息,此时就须要咱们将堆栈信息记录在日志中,以便发生异常时咱们能准肯定位程序在哪里出错。app
3.重要信息框架
这一点可能很宽泛,由于不一样的业务逻辑重点可能并不同,例如在有的重要参数不能为空,此时就须要判断是否为空,若是为空则记录到日志中;还有的例如传递进来的参数通过一系列的算法处理事后,此时也须要打印日志来查看是否计算正确。但切记,尽可能不要直接在for循环中打印日志,特别是for循环特别大时,这样你的日志可能分分钟被冲得不见踪影,甚至带来性能上的影响。
日志打印一般有四种级别,从高到底分别是:ERROR、WARN、INFO、DEBUG。应该选用哪一种级别就是个很重要的问题。
首先明确日志级别中的优先级是什么意思,在你的系统中若是开启了某一级别的日志后,就不会打印比它级别低的日志。例如,程序若是开启了INFO级别日志,DEBUG日志就不会打印,但不打印不表明不产生,这在后面会提到。一般在生产环境中开启INFO日志。
那么应该打印什么级别的日志呢?首先咱们应该明确谁在看日志。
一般来讲,系统出了问题客户不会进到系统对着黑黢黢的控制台查看日志输出,因此日志所面对的主体对象必然是软件开发人员(包括测试测试、维护人员)。
下面咱们假设几种场景来帮助咱们理解日志级别。
首先,程序开发结束后交由给测试人员进行测试,测试人员根据测试用例发现某个用例的输出和预期不符,此时他的第一反应该是查看日志。此时的日志是INFO级别日志不会出现DEBUG级别的日志,如今就须要根据日志打印分为两种状况决定他下一步操做:
综上,INFO级别的日志应该是能帮助测试人员判断这是不是一个真正的bug,而不是本身操做失误形成的。
假设测试人员如今已经初步判断这是一个bug,而且这个bug不那么明显,此时就须要开发人员到场确认。
开发人员到达现场后,第一步应该是查看INFO日志初步做初步判断验证测试人员的见解,接着若是不能判断出问题所在则应该是将日志级别调整至DEBUG级别,打印出DEBUG级别的日志,经过DEBUG日志来分析定位bug出在哪里。
因此,DEBUG级别的日志应该是能帮助开发人员分析定位bug所在的位置。
ERROR和WARN的级别都比INFO要高,因此在设定日志级别在INFO时,这二者的日志也会被打印。根据上面INFO和DEBUG级别的区别以及适用人员能够知道,ERROR和WARN是同时给测试和开发观察的。
WARN级别称之为“警告”,这个“警告”实际上就有点含糊了,它不算错,你能够选择忽视它,但也能够选择重视它。例如,如今一个WARN日志打出这么一条日志“系统有崩溃的风险”,这个时候就须要引发足够的重视,它表明如今不会崩溃,可是它有崩溃的风险。或者出现“某用户在短期内将密码输出不少次事后才进入了系统”,这个时候是否是系统被暴力破解了呢?等等,这个级别日志如同它的字面含义,给你一个警告,你能够选择忽视,也能够重视,但至少它如今不会给系统带来其余影响。
ERROR级别称之为“错误”,这个含义就更明显了,就是系统出现了错误,须要处理。最为常见的就是捕获异常时所打印的日志。
上面咱们介绍了四种日志级别的区别,特别须要注意的是INFO级别和DEBUG级别所适用的人员。那么咱们该如何选择哪一个级别的日志输出呢?
如下是个人我的理解:
对于DEBUG级别,我认为更关心的是过程,以及更为具体的相关信息,由于帮助它的定位在于帮助开发人员定位bug,定位bug就须要较为详细的参数信息才能定位。例如对于某个具体的算法过程,可使用DEBUG打印,开发人员不只关心结果,同时在结果不正确时应该能根据DEBUG日志查询计算过程是否出现误差
某个不常走到的分支,对于常规的操做是不该该打印WARN日志的,只有在知足某个条件才能走到的分支,且这个分支引发了“警觉”,此时就应该打印WARN日志。
毫无疑问出现错误,程序不能继续运行下去就应该打印ERROR日志,这个错误并非业务上的错误。例如,新增某个用户发现已经存在时,此时虽然新增失败,但不能说程序出现错误就打印ERROR日志;在删除某个用户发现用户已经被锁定时,此时也不能说由于程序不能按照删除的逻辑继续运行下去就应该打印ERROR日志。
应该打印什么内容?打印的内容必定要从实际出发。也就是说若是在实际的生产环境中,你的用户量很大,日志在不停地刷新,如何定位某个用户的整个登陆以及后续的操做呢?固然就是根据用户名来跟踪。因此打印内容的第一要素就是要能便于定位;定位事后也许用户在好几个模板中进行操做,仍是定位,这个时候定的是模块的位;还有一点固然就是用户操做时的具体参数;最后一点就是用户干了什么。
总结就是,[id, module, params, content](关键字,模块,参数,内容)。
以上就是对日志打印的几点建议,说的不全面,抛砖引玉。下面是对日志打印框架(log4j)的非最佳实践。
Spring中使用log4j日志框架能够说是最为常见的应用场景了,咱们将结合Spring对log4j作一个简单的示范。
在IDEA中建立一个Maven构建的Web项目,项目结构以下图所示:
pom.xml中的依赖以下:
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.6.6</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.6</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</version> </dependency>
在resources(IDEA中resources就是classpath路径)中新建一个log4j.properties文件,以下所示:
1 log4j.rootLogger = INFO, stdout, logfile 2 #日志输出到控制台 3 log4j.appender.stdout = org.apache.log4j.ConsoleAppender 4 log4j.appender.stdout.layout = org.apache.log4j.PatternLayout 5 log4j.appender.stdout.ConversionLayout = %d [%t] %-5p %c - %m%n 6 #日志输出到文件 7 log4j.appender.logfile = org.apache.log4j.DailyRollingFileAppender 8 log4j.appender.logfile.File = /Users/yulinfeng/Log/log 9 log4j.appender.logfile.maxFileSize=10240KB #日志的最大容量为10M 10 log4j.appender.logfile.Append = true #是否追加写进文件 11 log4j.appender.logfile.Threshold = DEBUG #输出DEBUG级别日志到文件中 12 log4j.appender.logfile.layout = org.apache.log4j.PatternLayout 13 log4j.appender.logfile.layout.ConversionPattern = %d [%t] %-5p %c - %m%n
第1行,log4j.rootLogger=INFO, stdout, logfile。这是log4j的根配置,第一个参数表示输出什么级别的日志,后面的参数表示输出的位置,位置能够是控制台,也能够是文件,语法为log4j.rootLogger=[level], appendername……,在这里定义了两个输出位置,名字无所谓取设么,有意义便可。日志级别从高到低分别是:OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL,log4j建议只使用ERROR、WARN、INFO、DEBUG四个级别,也就是也就是在上面提到过的。
第三、7行就分别指定了stdout和logfile日志的输出位置,log4j一共提供了5个。
org.apache.log4j.ConsoleAppender(控制台)
org.apache.log4j.FileAppender(文件)
org.apache.log4j.DailyRollingFileAppender(天天产生一个日志文件)
org.apache.log4j.RollingFileAppender(文件大小达到必定大小后产生一个新的文件)
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意位置)
第4行表示日志信息的格式,一共有如下几种。
org.apache.log4j.HTMLLayout(以HTML表格输出)
org.apache.log4j.PatternLayout(灵活的自定义格式输出)
org.apache.log4j.SimpleLayout(简单的格式输出,只包括日志级别和日志信息的字符串)
org.apache.log4j.TTCCLayout(包含线程、日志级别、日志所在类和日志信息的字符串)
一般为了更为灵活的打印日志,咱们会选择PatternLayout布局的日志,同时经过ConversionPattern自定义输出格式。
按照上面的配置,咱们就能够在代码中进行日志的输出了。因为是在Spring框架下使用log4j,因此就要使用Spring对log4j进行初始化,在web.xml中对log4j进行初始化。
<web-app> <display-name>log web</display-name> <context-param> <param-name>log4jConfigurationLocation</param-name> <param-value>classpath*:log4j.properties</param-value> </context-param> <!--每隔60s扫描log4j的配置文件,这里配置的log4jRefreshInterval参数表示能不用重启web服务器就能动态更改log4j日志级别,这也是和Spring整合的一大好处--> <context-param> <param-name>log4jRefreshInterval</param-name> <param-value>60000</param-value> </context-param> <listener> <!--从spring4.2.1开始Log4jConfigListener已经被废弃,最好使用log4j2对应的org.apache.logging.log4j.web.Log4jServletContextListener .Log4jServletContextListener--> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener> </web-app>
此时在代码逻辑中加入如下代码便可根据咱们的配置输出系统日志。
private Logger log = Logger.getLogger(Test.class); log.info(“test info”);
上面是全部日志文件都输出到一个文件的状况,在实际中咱们颇有可能针对不一样的模块输出到不一样到日志文件。
修改log4j.properties:
1 log4j.rootLogger = INFO, module1, module2 2 #输出到控制台 3 log4j.appender.stdout = org.apache.log4j.ConsoleAppender 4 log4j.appender.stdout.layout = org.apache.log4j.PatternLayout 5 log4j.appender.stdout.ConversionLayout = %d [%t] %-5p %c - %m%n 6 #模块1输出的日志文件 7 log4j.appender.module1 = org.apache.log4j.DailyRollingFileAppender 8 log4j.appender.module1.File = /Users/yulinfeng/Log/module1 9 log4j.appender.module1.maxFileSize=10240KB #日志的最大容量为10M 10 log4j.appender.module1.Append = true #是否追加写进文件 11 log4j.appender.module1.Threshold = DEBUG #输出DEBUG级别日志到文件中 12 log4j.appender.module1.layout = org.apache.log4j.PatternLayout 13 log4j.appender.module1.layout.ConversionPattern = %d [%t] %-5p %c - %m%n 14 #模块2输出的日志文件 15 log4j.appender.module2 = org.apache.log4j.DailyRollingFileAppender 16 log4j.appender.module2.File = /Users/yulinfeng/Log/module2 17 log4j.appender.module2.maxFileSize=10240KB #日志的最大容量为10M 18 log4j.appender.module2.Append = true #是否追加写进文件 19 log4j.appender.module2.Threshold = DEBUG #输出DEBUG级别日志到文件中 20 log4j.appender.module2.layout = org.apache.log4j.PatternLayout 21 log4j.appender.module2.layout.ConversionPattern = %d [%t] %-5p %c - %m%n
在模块1中输出日志文件时其实就是参数不一样而已:
private Logger log = Logger.getLogger(“module1”); log.info(“test info”);
在模块2中:
private Logger log = Logger.getLogger(“module2”); log.info(“test info”);
以上就是在Spring中使用log4j日志框架的非最佳实践。
最后,还要介绍另一种打印日志的方式,上面的方式将会在每一个类中都定义一个Logger对象,这样的代码相对于业务逻辑来讲实际是不想关,此时就能够利用Spring中的AOP面向切面编程打印日志。这里可能不是全部的人都能接触到利用AOP来打印日志,这里暂时不作详细介绍。
这是一个能给程序员加buff的公众号