简介
在 Java 领域里存在着多种日志框架,如 JCL、SLF4J、Jboss-logging、jUL、log4j、log4j2、logback 等等。今天就介绍几种比较常用的日志框架。
日志作用
##### 调试
在Java项目调试时,通过查看日志信息我们可以方便地知道当前程序的运行状态是否符合预期。
##### 错误定位
项目能正确跑起来并不能让我们高枕无忧,稳定性同样重要。项目在运行一段时候后,可能由于数据问题,网络问题,内存问题等出现异常。这时日志可以帮助开发或者运维人员快速定位错误位置,提出解决方案。要知道,生产环境往往是不允许debug的,我们一般都是通过日志来分析和排查生产环境的问题的。
##### 数据分析
大数据的兴起,使得大量的日志分析成为可能,ELK也让日志分析门槛降低了很多。日志中蕴含了大量的用户数据,包括点击行为,兴趣偏好等,用户画像对于公司下一步的战略方向有一定指引作用。
日志的使用和框架介绍
(一)传统日志输出的局限性
日志缺乏持久化手段。System.out
和System.err
等方式都是只能在控制台上输出,但实际部署项目的时候我们不可能一直盯着控制台的输出来观察是否有错误产生,所以这种缺乏持久化的日志输出方式是不符合我们实际的运维需求的。
日志输出源、输出方式单一。上述方式只能满足我们简单的控制台输出,实际上我们还希望能对日志进行文件的持久化,数据库的保存或者是通过JMS进行推送等其他操作。
日志信息缺少级别分层。没有根据信息的重要程度进行分级,不便于我们日后对日志进行分析。
(二)市面主流日志框架介绍
主流的日志框架分为以下两类:
日志实现(日志实现框架):JUL(java util logging)、logback、log4j、log4j2
日志门面(日志标准接口框架):JCL(Jakarta Commons Logging)、slf4j(Simple Logging Facade for Java)
日志门面通过接口(和抽象类)的方式,定义了日志框架应该具备的各种规范,而日志实现则实现了日志门面的规范,是具体日志输出的执行者。现在比较主流的日志组合是SLF4F
+log4j2
。
log4j日志框架使用和实现
Log4j的核心组件
日志的输出需求基本就是可定义的日志级别、可定义的输出源、可定义的日志格式。所以Log4j提供的核心组件如下:
Loggers(日志输出的入口类,可以指定日志级别)
Loggers组件在此系统中被分为五个级别:DEBUG、INFO、WARN、ERROR和FATAL。这五个级别是有顺序的,DEBUG<INFO<WARN<ERROR<FATAL,分别用来指定这条日志信息的重要程度
Log4J中有一个特殊的logger叫做“root”,他是所有logger的根,也就意味着其他所有的logger都会直接或者间接地继承自root。rootlogger可以用Logger.getRootLogger()方法获取。JUL是不是也有一个名为.的根。
Appenders(指定数据源)
Appenders用来指定日志输出到哪个地方,可以同时指定日志的输出目的地。常使用的类如下:
org.apache.log4j.ConsoleAppender(控制台)
org.apache.log4j.FileAppender(文件)
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
Layouts(指定日志输出格式)
Log4j可以在Appenders的后面附加Layouts来完成这个功能。Layouts提供四种日志输出样式,如根据HTML样式、自由指定样式、包含日志级别与信息的样式和包含日志时间、线程、类别等信息的样式。
常使用的类如下:
org.apache.log4j.HTMLLayout(以HTML表格形式布局)
org.apache.log4j.PatternLayout(可以灵活地指定布局模式)
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等信息)
环境配置
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
@Test
public void normalTest(){
//传入当前类名,获取一个Logger对象,我们将使用这个对象来进行日志输出
Loggerlogger=Logger.getLogger(Log4jTest.class);
//设置logger对象的日志级别,为ALL/OFF表示开启/关闭所有级别的日志
logger.setLevel(Level.ALL);
//定义一个appender数据源,我们这里选择的是控制台
ConsoleAppenderconsoleAppender=newConsoleAppender();
//设置输出格式,具体占位符的含义将在下文给出
consoleAppender.setLayout(newPatternLayout("%d[%t]%m%n"));
//设置一个outputstream流
consoleAppender.setWriter(newPrintWriter(System.out));
//为logger对象设置appender输出源,让logger知道将日志输出到哪里
logger.addAppender(consoleAppender);
//以下为日志输出
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.trace("trace");
logger.debug("debug");
logger.fatal("fatal");
}
(三)Logger的继承性
Log4J中有一个特殊的logger叫做“root”,他是所有logger的根,也就意味着其他所有的logger都会直接或者间接地继承自root。rootlogger可以用Logger.getRootLogger()方法获取
@Test
public void parentLoggerTest(){
LoggerparentLogger=Logger.getLogger("com.log4j");
parentLogger.addAppender(newConsoleAppender(newPatternLayout("%d[%t]%m%n")));
parentLogger.setLevel(Level.WARN);
LoggersonLogger=Logger.getLogger("com.log4j.sonLogger");
parentLogger.warn("parentLoggerwarn");
parentLogger.info("parentLoggerinfo");
sonLogger.warn("sonLoggerwarn");
sonLogger.info("sonLoggerinfo");
//判断是否真的继承了
System.out.println("sonLogger的父Logger对象"+sonLogger.getParent());
System.out.println("parentLogger对象输出:"+parentLogger);
}
(四)通过配置文件来加载日志参数
配置文件的组成
(1)配置根Logger(后续所有Logger对象都会继承根Logger的配置)
log4j.rootLogger=[level],appenderName1,appenderName2
1.level:设定日志记录的最低级别,可设的值有OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者自定义的级别,Log4j建议只使用中间四个级别。通过在这里设定级别,您可以控制应用程序中相应级别的日志信息的开关,比如在这里设定了INFO级别,则应用程序中所有DEBUG级别的日志信息将不会被打印出来。
2.appenderName:就是指定日志信息的输出源。可以同时指定多个输出源,多个输出源之间用逗号隔开。
例如:log4j.rootLogger=INFO,A1,B2,C3
2、配置日志信息输出目的地(appender)
log4j.appender.appenderName=className
appenderName:自定义appderName,在log4j.rootLogger设置中使用;
className:可设值如下:
(1)org.apache.log4j.ConsoleAppender(控制台)
(2)org.apache.log4j.FileAppender(文件)
(3)org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
(4)org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
(5)org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
3、配置日志信息的输出格式(Layout)
log4j.appender.appenderName.layout=className
className:可设值如下:
(1)org.apache.log4j.HTMLLayout(以HTML表格形式布局)
(2)org.apache.log4j.PatternLayout(可以灵活地指定布局模式)
(3)org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)
(4)org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
对于PatternLayout,我们可以通过下面的占位符来格式化日志:
%p:输出日志信息的优先级,即DEBUG,INFO,WARN,ERROR,FATAL。
%d:输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,
如:%d{yyyy/MM/ddHH:mm:ss,SSS}。
%r:输出自应用程序启动到输出该log信息耗费的毫秒数。
%t:输出产生该日志事件的线程名。
%l:输出日志事件的发生位置,相当于%c.%M(%F:%L)的组合,包括类全名、方法、文件名以及在代码中的行数。例如:test.TestLog4j.main(TestLog4j.java:10)。
%c:输出日志信息所属的类目,通常就是所在类的全名。
%M:输出产生日志信息的方法名。
%F:输出日志消息产生时所在的文件名称。
%L::输出代码中的行号。
%m::输出代码中指定的具体日志信息。
%n:输出一个回车换行符,Windows平台为"\r\n",Unix平台为"\n"。
%x:输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像javaservlets这样的多客户多线程的应用中。
%%:输出一个"%"字符。
(2)使用配置文件的方式实现一个小案例
步骤一:在resources目录下配置log.properties
文件
#指定日志的输出级别与输出端
log4j.rootLogger=INFO,Console,sizeRollFile
#自定义Appender,控制台输出配置
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d[%t]%-5p[%c]-%m%n
步骤二:在代码中输出日志:
@Test
public void simpleLog(){
Loggerlogger=Logger.getLogger(Log4jTest.class);
logger.fatal("fatal");
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
步骤三:运行代码,观察输出
日志门面
日志门面是规范,其他的实现按照规范实现各自的日志框架即可,我们程序员基于日志门面编程即可。
常见的日志门面:JCL、SLF4J
常见的日志实现:JUL、log4j、logback、log4j2
上述日志框架出现的历史顺序: log4j -->JUL-->JCL--> slf4j --> logback --> log4j2