一.日志系统基本常识
1.日志系统作用:将日志信息输出到控制台和文本文件,以追踪代码运行信息。
2.日志系统操作的是什么?日志系统打印信息,也是调用日志系统的log.Info(),log.Warn()这些方法的,调用方法就要消耗cpu和栈,尤其日志系统的输入参数和输出参数都是字符串,日志系统输出的都是字符串信息,否则怎么显示在控制台和保存在文本文件中嘛,所以就更消耗cpu了和内存了。因为涉及很多字符串的拼接操作,在log的info和warn()入参中。且,在log的info和warn()入参中,我们还经常调用其他方法,所以日志系统的输出方法,是很消耗cpu和内存的。
3.那么应该如何避免日志系统影响程序运行效率呢?
常用方法:1.避免log.info(),debug()入参提前生成,免去无用功,通过加入判断日志系统级别的代码,即if(logger.isInfoEnabled()).避免日志输出方法参数的提前生成。因为logger.info方法内部有判断输出级别的代码。但是在进入logger.info函数之前,("User " + userId + " is using app " + appId) 这个表达式已经通过运算拼接成了一个字符串;而如果事先使用 if (logger.isInfoEnabled())进行判断,那么当log级别在INFO以上时,就能省去上述的字符串操作,在高并发和复杂log信息拼接的情况下,使用这种标准的方法输出log能够省去不小的系统开销。另外,如果构造log信息的过程需要大量字符串操作,建议使用StringBuilder来完成字符串拼接。---防止更高日志输出级别时,判断了低级别但不输出。
2.java正确声明实例化new logger。尽量将所有logger是声明为static静态的保证内存中只声明一份。否则多次创建同一个对象,就白白创建了多个logger实例。
3.log.info(),log.debug()这些传参中,尽量避免一些复杂的调用,比如Log.info()里再连接数据库读取user的名字。
4.日志系统是已经写好了的功能代码:是一段JDK里或者其他JAR包里已经写好的代码,是代码,才能执行输入输出嘛。JAVA有自己实现的日志系统,就在java.util包里,叫java.util.logging。这是JDK自带的日志系统,还有常用的Apache log4j,第三方日志工具。
5.我们常用的日志工具有三种:jdk自带的日志工具,第三方日志工具Apache log4J,和第三方SLF4J。Apache log4J要引用jar包到buildpath。这里重点讲一下slf4j,这个才是最常用的。
6. slf4j并不能真正提供打印功能,它只是其他日志工具的封装,将不同的日志工具接口抽象出来。提供真正打印功能的还是log4j或者jdk日志工具。Slf4j的使用,使我们可以跨越和随意更换不同的具体日志实现工具,而不必更改代码和jar包import引用语句。
用途:比如,你想写一个jar包供其他人使用,jar包里的类和方法要打印一些信息,因为你不知道具体系统究竟用什么日志工具,所以在你jar包里,就可以完全使用slf4j打印。等他人系统调用你jar包时,你jar包里的打印方法会由具体的本地系统所有的日志工具实现。
如果你开发的是类库或者供其他人使用的组件,那么就应该考虑采用SLF4J,因为不可能影响最终用户选择哪种日志系统。
7.使用SLF4J项目的日志结构,具体项目,就要有log4j日志实现工具jar包,和slf4j实现工具jar包。
8.日志工具的几个输出级别:日志系统有5个输出级别,debug,info,warn,error,fatal,一个等级比一个等级高。
Debug,最低级别,一般在生产环境,是不进行输出的,只在测试环境输出,任何在调试代码时,一些必要的信息,输出来都行。
Info输出级别:info输出的信息是可以给使用人员顾客看的,在生产环境中是肯定要输出info级别的日志的。某种程度来讲,info级别的日志输出,是可以当做软件的一部分的。
Warn,error,fatal:这三个级别都是很高的级别。
9.日志级别规则:低级别的日志输出,会一并输出比目前级别高的输出。比如,日志输出级别定为debug,debug是最低级别,所以一定会打印出info,warn,error,fatal都会输出。
答:因为 Info 的级别是如此之低,所以为了让用户能够看到有效的输出信息,必须将日志级别开放到 Info 级别。但是 Warn 的级别比 Info 要高,所以用户不得不被迫看到一些 Warn 的信息。而我们其实已经假定,Warn 信息其实并不影响系统的正常运行,这一般只代表系统中存在一些还没有被发现或者修改的小 Bug。这些 Warn 信息会让最终用户困惑甚至恐慌,系统发出警告了,该怎么办?
10.日志系统只有1个:因为进行控制台输出嘛,相当于打印机一样,多个logger只能按序输出。多线程调用日志系统时候,两行日志输出之间有可能会被插入其他线程的日志记录,不会按照我们的意愿顺序输出。
11.判断日志界别,避免生成无效入参。这完全是防止当目前日志级别log输出所用级别高时,做的无效入参形成。记住,是防止防止当目前日志级别log输出所用级别高时。因为判断日志级别的代码,是在log,info(),debug()这里方法里的代码,参数已经被形成了。
二.在代码哪里使用日志,及怎么使用?
1.一定要日志对象logger,声明为static静态的,保证一个类只有一个logger对象,避免多次创建对象时多次创建logger,造成内存浪费。
private static org.slf4j.Logger logger = LoggerFactory.getLogger(APPMain.class);
2.一定要加入日志级别判断java代码,logger.isInfoEnabled(),避免入参无效形成,做无用功。提高效率。(在调用任何方法前,都是先执行和生成方法的入参,再去执行方法里的代码,这涉及到创建栈,一个方法被调用时,就创建一个新的栈空间,就会先创建方法入参。)---防止更高日志输出级别时,判断了低级别但不输出。
3. 系统启动的参数、配置、环境变量、System.Properties等信息一定要打印日志:对于软件的正常运行至关重要,这些信息的输出有助于安装配置人员通过日志快速定位问题,所以程序有必要在启动过程中把使用到的关键参数、变量在日志中输出出来。
4. 关键步骤增删改查一定要打印日志:比如,增删改查地方一定要添加打印。因为这涉及到数据是否更改成功。用于提示程序员。
5.捕获异常处一定要打印日志且不要再向外抛出异常,打印内容是stacktrace堆栈信息。堆栈信息描述了发生异常时候的瞬间堆栈信息。会指出报错位置和地方。既然你已经打印了日志信息,就不要再抛出异常了。异常处直接用looger.error()方法输出。
catch (Exception e) {
if(logger.isErrorEnabled()) {
logger.error("init um2UserService error", e);
}
三.怎样配置使用日志工具?
1.配置Log4j:Log4j配置文件,就是个properties文件,里面是keyvalue键值对,等号相连。Appender 为日志输出目的地,Log4j提供的appender有以下几种:
能够输出到控制台,文件,每日输出文件,文件到指定大小时候产生新文件,发送日志。
org.apache.log4j.ConsoleAppender(控制台),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
2)为不同的 Appender 设置日志输出级别:
### 输出到日志文件 ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG ## 输出DEBUG级别以上的日志
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 保存异常信息到单独文件 ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = logs/error.log ## 异常日志文件名
log4j.appender.D.Append = true
log4j.appender.D.Threshold = ERROR ## 只输出ERROR级别以上的日志!!!
log4j.appender.D.layout = org.apache.log4j.PatternLayout
og4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
2.项目中怎样启动Log4j?
答:将jar包引入buildpath,并配置好log4j.properties配置文件后。就可以直接使用logger了,logger类的第一次加载时候,会自动初始化lo4j的配置文件,通过static代码块的方式。
注:但凡自动读取配置文件,全都是用static初始化块来调用的,初始化快用来执行配置文件的读取。