目录
Commons Logging slf4j,logback,log4j的区别
SpringBoot 在所有内部日志中使用Commons Logging,但是默认配置也提供了对常用日志的支持,如:Java Util Logging,Log4J, Log4J2和Logback。每种Logger都可以通过配置使用控制台或者文件输出日志内容。
SLF4J类似于Commons Logging,也是一个日志接口,而Logback类似于Log4j,是一个日志的实现。
根据不同的日志系统,你可以按如下规则组织配置文件名,就能被正确加载:
Spring Boot官方推荐使用带有-spring的文件名作为你的日志配置(如logback-spring.xml,而不是logback.xml),命名为logback-spring.xml的日志配置文件,spring boot可以为它添加一些spring boot特有的配置项(下面会提到)。
默认的命名规则,并且放在 src/main/resources 下面即可
Logback:logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy
Log4j:log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xml
Log4j2:log4j2-spring.xml, log4j2.xml
JDK (Java Util Logging):logging.properties
commons-logging和slf4j都是日志门面。
Commons Logging slf4j,logback,log4j的区别
SpringBoot 在所有内部日志中使用Commons Logging,commons-logging和slf4j都是日志门面。
slf4j(Simple logging Facade for Java) 简单日志门面,日志系统的实现进行了具体的抽象化,只提供了统一的日志使用接口,没有任何日志实现,只有一个jar包(slf4j-api.jar)。
log4j和logback是具体的日志框架。这两个日志框是同一个作者开发,后者性能更高。
logbak直接实现slf4j接口,性能更高
springboot默认日子框架为logback
日志级别
SLF4J将日志分为trace、debug、info、warn、error五个级别,每个级别对应记录不同的日志,对应不同的使用场景。
日志级别从低到高分为
TRACE < DEBUG < INFO < WARN < ERROR < FATAL
如果设置为 WARN ,则低于 WARN 的信息都不会输出
一般设置为 DEBUG 就够用了,看具体的业务需求
trace
trace是最低优先级的日志,一般用来追踪详细的程序运行流,比如程序的运行过程中,运行到了哪一个方法,进入了哪一条分支。通过trace程序的运行流程,可以判断程序是否按照期望的逻辑在运行。举个例子,有以下代码段。
public class Car{
//点火
public void fire(){
log.trace("fire")
...
}
debug
debug是比trace高一级别的日志,该级别的日志就是用来debug用的。这类日志往往用在判断是否有出现bug的场景,且往往记录了代码运行的详细信息,比如方法调用传入的参数信息。
假设运行的过程中出现了问题,为了定位问题的原因,可以使用debug级别的日志来记录档位,通过记录运行时传入的gear来判断是不是因为档位输入错误而导致Car起步失败。
//挂档
public void gear(int gear){
log.trace("gear")
//记录档位
log.debug(gear)
...
}
info
info比debug高一级别,用来记录程序运行的一些关键信息,它不像trace那样记录程序运行的整个流程,也不像debug那样为了解决问题而记录详细的信息。
info记录的是整个系统的运行信息,比如系统运行到了哪一个阶段,到达了哪一个状态。还是以车启动为例子,将启动的代码修改如下
public void static main(String[] args){
Car car = new Car();
car.fire();
car.clutch();
car.gear(1);
car.looseBrake();
log.info("car is ready to run faster !");
}
可以看到,运行完looseBrake方法之后,使用info记录了车的状态,车已经完成了起步了,可以踩油门加速了。
warn
warn比info的级别更高,用来记录一些警告信息。警告信息表示,程序进入了一个特殊的状态,在该状态下程序可以继续运行,但是不建议让程序进入该状态,因为该状态可能导致结果出现问题
车启动的过程中如果以大于1档起步,就使用warn记录警告,告诉调用者不推荐这样使用,实际上2档起步也是可以的,但可能会出问题
//挂档
public void gear(int gear){
log.trace("gear")
//记录档位
log.debug(gear)
if(gear > 1){
log.warn("gear "+ gear +" is too high !")
}
...
}
error
error级别的日志是最高优先级了,用来记录运行时的错误信息,表示程序运行过程中出现了需要被解决的问题,往往是一些异常。使用error日志的时候,一般会将详细的异常出现的原因记录。
public void static main(String[] args){
Car car = new Car();
try{
car.fire();
car.clutch();
car.gear(1);
car.looseBrake();
}catch(Exception e){
log.error("run failed !",e.getCause())
}
log.info("car is ready to run faster !");
}
commons-logging
Jakarta Commons Logging (JCL)提供的是一个日志(Log)接口(interface),同时兼顾轻量级和不依赖于具体的日志实现工具。
它提供给中间件/日志工具开发者一个简单的日志操作抽象,允许程序开发人员使用不同的具体日志实现工具。用户被假定已熟悉某种日志实现工具的更高级别的细节。JCL提供的接口,对其它一些日志工具,包括Log4J, Avalon LogKit, and JDK 1.4等,进行了简单的包装,此接口更接近于Log4J和LogKit的实现。
SpringBoot 在所有内部日志中使用Commons Logging,但是默认配置也提供了对常用日志的支持,如:Java Util Logging,Log4J, Log4J2和Logback。每种Logger都可以通过配置使用控制台或者文件输出日志内容。
SLF4J类似于Commons Logging,也是一个日志接口,而Logback类似于Log4j,是一个日志的实现。
在我们的项目中,如果只单纯的依赖了commons-logging,那么默认使用的日志对象就是Jdk14Logger,默认使用的日志工厂就是LogFactoryImpl,没有使用SpringBoot
commons-logging和slf4j都是日志门面。
原理:
JCL有两个基本的抽象类: Log( 基本记录器 ) 和 LogFactory( 负责创建 Log 实例 ) 。当 commons-logging.jar 被加入到 CLASSPATH之后,它会合理地猜测你想用的日志工具,然后进行自我设置,用户根本不需要做任何设置。默认的 LogFactory 是按照下列的步骤去发现并决定那个日志工具将被使用的(按照顺序,寻找过程会在找到第一个工具时中止) :
①首先在classpath下寻找commons-logging.properties文件。如果找到,则使用其中定义的Log实现类;如果找不到,则在查找是否已定义系统环境变量org.apache.commons.logging.Log,找到则使用其定义的Log实现类;
②查看classpath中是否有Log4j的包,如果发现,则自动使用Log4j作为日志实现类;
③否则,使用JDK自身的日志实现类(JDK1.4以后才有日志实现类);
④否则,使用commons-logging自己提供的一个简单的日志实现类SimpleLog;
commons-logging接口和自身的实现类
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
接下来,在classpath下定义配置文件:commons-logging.properties:
#指定日志对象:
org.apache.commons.logging.Log = org.apache.commons.logging.impl.Jdk14Logger
#指定日志工厂:
org.apache.commons.logging.LogFactory = org.apache.commons.logging.impl.LogFactoryImpl
package com.wbf.log;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* commons-logging-1.0.4.jar,但是没有log4j-1.2.15.jar和log4j.properties
* debug<info<warn<error
* log4j.rootLogger=级别,如:warn,但是warn之下的级别debug, info的信息无法输出
* @author wbf
*/
public class TestCommonsLoggingLog4j {
private final Log log = LogFactory.getLog(TestCommonsLoggingLog4j.class);
public static void main(String[] args) {
TestCommonsLoggingLog4j testSlf4j = new TestCommonsLoggingLog4j();
testSlf4j.testLog(10);
}
private void testLog(int a) {
if (a == 10) {
log.info("log info...");
log.warn("log warn...");
log.error("log error...");
}
}
}
commons-logging接口和log4j实现类
package com.wbf.log;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* commons-logging-1.0.4.jar、log4j-1.2.15.jar和log4j.properties
* debug<info<warn<error
* log4j.rootLogger=级别,如:warn,但是warn之下的级别debug, info的信息无法输出
* @author wbf
*/
public class TestCommonsLoggingLog4j {
private final Log log = LogFactory.getLog(TestCommonsLoggingLog4j.class);
public static void main(String[] args) {
TestCommonsLoggingLog4j testSlf4j = new TestCommonsLoggingLog4j();
testSlf4j.testLog(10);
}
private void testLog(int a) {
if (a == 10) {
log.info("log info...");
log.warn("log warn...");
log.error("log error...");
}
}
}
### direct log messages to stdout ###
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{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.rootLogger=warn, stdout
修改commons-logging.properties文件:显示地指定log4j
org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
源码分析
(84条消息) 日志之commons-logging_commons-logging依赖_木村牧村的博客-CSDN博客
获取当前线程的classLoader,根据classLoader从缓存中获取LogFactroy,使用的缓存是WeakHashTable对象;如果缓存中存在,则返回,没有则进入下面流程;
读取classpath下的commons-logging.properties文件,判断其中是否设置了use_tccl属性,如果不为空则判断,该属性的值是否为false,若为false,则将baseClassLoader替换为当前类的classLoader;
接着,继续获取LogFactory对象,此步骤分为4中方式:
(1)在系统属性中查找“org.apache.commons.logging.LogFactory”属性的值,根据值生成LogFactory对象;
(2)通过资源“META-INF/services/org.apache.commons.logging.LogFactory”文件,获取的值生成LogFactory对象;
(3)通过配置文件commons-logging.properties,获取以“org.apache.commons.logging.LogFactory”为key的值,根据值生成logFactory;
(4)如果以上均不成功,则创建系统默认的日志工厂:org.apache.commons.logging.impl.LogFactoryImpl成功获取日志工厂后,根据类名获取日志对象;
主要逻辑在discoverLogImplementation方法中:
(1)检查commons-logging.properties文件中是否存在“org.apache.commons.logging.Log”属性,若存在则创建具体的日志对象;若不存在,进行下面逻辑;
(2)遍历classesToDiscover数组,该数组存有日志具体实现类的全限定类名:org.apache.commons.logging.impl.Log4JLogger、org.apache.commons.logging.impl.Jdk14Logger、org.apache.commons.logging.impl.Jdk13LumberjackLogger、org.apache.commons.logging.impl.SimpleLog;
(3)根据数组中存着的全限定类名,按照顺序依次加载Class文件,进行实例化操作,最后返回Log实例,默认为Jdk14Logger;
其中,获取日志工厂的过程,诟病最多。究其原因,主要是commons-logging在获取日志工厂的过程中使用了classLoader来寻找日志工厂实现,进而导致了其他组件,如若使用自己的classload,则不能获取具体的日志工厂对象,则导致启动失败,这样就是我们常说的–动态查找机制。
logback
spring-boot-starter
这个包就自带了 logging
的依赖包,SpringBoot会默认使用logback
作为日志框架,在生成springboot项目的时候可以直接勾选logback,那么就可以直接使用logback了。
如下配置将:
在根目录applog中每天生成一个文件夹,并生成一个天级别的日志文件。
Logger标签:作为日志的记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。
Appender主要用于指定日志输出的目的地,目的地可以是控制台、文件、远程套接字服务器、 MySQL、PostreSQL、 Oracle和其他数据库、 JMS和远程UNIX Syslog守护-进程等。
Layout 负责把事件转换成字符串,格式化的日志信息的输出
logback-spring.xml,放在 src/main/resources 下面
logback-spring.xml,放在 src/main/resources
下面
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>logback</contextName>
<!--输出到控制台-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!--按天生成日志,即一天只生成一个文件夹和一个日志文件-->
<appender name="logFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<Prudent>true</Prudent>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>
applog/%d{yyyy-MM-dd}/%d{yyyy-MM-dd}.log
</FileNamePattern>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
%d{yyyy-MM-dd HH:mm:ss} -%msg%n
</Pattern>
</layout>
</appender>
<!-- logger节点,可选节点,作用是指明具体的包或类的日志输出级别,
以及要使用的<appender>(可以把<appender>理解为一个日志模板)。
addtivity:非必写属性,是否向上级loger传递打印信息。默认是true-->
<logger name="com.framework.job" additivity="false">
<appender-ref ref="console"/>
<appender-ref ref="logFile"/>
</logger>
<!--项目的整体的日志打印级别为info-->
<root level="info">
<appender-ref ref="console"/>
<appender-ref ref="logFile"/>
</root>
</configuration>
如果没有按照命名规则,则需要手动添加logback.xml
application.yml中配置logging
#logging
logging:
config: classpath:log/logback.xml
使用以下方式,可以单独设置每个包下的日志记录级别,
在application.yml,application.properties
logging:
file: applog/sys.log
level:
com:
leshangju:
prism:
auth:
dao: debug
usershop:
dao: debug
loginuser:
dao: debug
logging.level.com.framework.job : info
测试结果
@Slf4j
public class MyJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("启动定时任务 My Job:" + LocalDateTime.now());
log.trace("进入了myjob任务");
try {
Thread.sleep(3000);
log.trace("睡了3秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("结束任务!");
System.out.println("end My Job:" + LocalDateTime.now());
}
}
slf4j
简单日志门面(Simple Logging Facade For Java) SLF4J主要是为了给Java日志访问提供一套标准、规范的API框架,其主要意义在于提供接口,具体的实现交由其他日志框架,例如log4j2和logback等。当然slf4j自己也提供了功能较为简单的实现,但是一般很少用到。对于一般的Java项目而言,日志框架会选择slf4j-api作为门面,配上具体的实现框架(log4j2、logback等),中间使用桥接器完成桥接。所以我们可以得出SLF4J最重要的两个功能就是对于日志框架的绑定以及日志框架的桥接。
<dependency>
<!--日志门面核心包 -->
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
<dependency>
<groupId>日志实现框架</groupId>
<artifactId>日志实现框架</artifactId>
</dependency>
SLF4J依赖slf4j-api包和日志实现框架包,如果不导入任何日志实现框架的话,会报错
@Slf4j的简单使用教程
很简单的就是为了能够少写两行代码,不用每次都在类的最前边写上:
private static final Logger logger = LoggerFactory.getLogger(this.XXX.class);
我们只需要在类前面添加注解@Slf4j,即可使用log日志的功能了
确保idea中有Lombok插件
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
可以直接使用log.info("");
日志实现框架:JUL,log4j,logback,log4j2
常见的日志门面:JCL,slf4j
出现顺序:log4j->JUL->JCL->slf4j->logback->log4j2
Log4j入门教程
Log4J 是 Apache 的一个开源项目(官网 http://jakarta.apache.org/log4j),通过在项目中使用 Log4J,我们可以控制日志信息输出到控制台、文件、GUI 组件、甚至是数据库中。我们可以控制每一条日志的输出格式,通过定义日志的输出级别,可以更灵活的控制日志的输出过程。方便项目的调试。
Log4J 主要由 Loggers (日志记录器)、Appenders(输出端)和 Layout(日志格式化器)组成。其中 Loggers 控制日志的输出级别与日志是否输出;Appenders 指定日志的输出方式(输出到控制台、文件等);Layout 控制日志信息的输出格式。
Appender(输出端)
Appender 用来指定日志输出到哪个地方,可以同时指定日志的输出目的地。Log4j 常用的输出目的地有以下几种:输出端 类型 作用
ConsoleAppender 将日志输出到控制台
FileAppender 将日志输出到文件中
DailyRollingFileAppender 将日志输出到一个日志文件,并且每天输出到一个新的文件
RollingFileAppender 将日志信息输出到一个日志文件,并且指定文件的尺寸,当文件大 小达到指定尺寸时,会自动把文件改名,同时产生一个新的文件
JDBCAppender 把日志信息保存到数据库中
Layout(日志格式化器)
格式化器类型 作用
HTMLLayout 格式化日志输出为HTML表格形式
SimpleLayout 简单的日志输出格式化,打印的日志格式为(info - message)
PatternLayout 最强大的格式化期,可以根据自定义格式输出日志,如果没有指定转换格式,就是用默认的转换格式
# 控制台输出配置
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
# 文件输出配置
log4j.appender.A = org.apache.log4j.DailyRollingFileAppender
log4j.appender.A.File = D:/log.txt #指定日志的输出路径
log4j.appender.A.Append = true
log4j.appender.A.Threshold = DEBUG
log4j.appender.A.layout = org.apache.log4j.PatternLayout #使用自定义日志格式化器
log4j.appender.A.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n #指定日志的输出格式
log4j.appender.A.encoding=UTF-8 #指定日志的文件编码
#指定日志的输出级别与输出端
log4j.rootLogger=DEBUG,Console,A
public class Log4JTest {
public static void main(String[] args) {
//获取Logger对象的实例
Logger logger = Logger.getLogger(Log4JTest.class);
logger.debug("这是debug");
logger.info("这是info");
logger.warn("这是warn");
logger.error("这是error");
logger.fatal("这是fatal");
}
}
并会在 D 盘生成一份 log.txt 文件。文本内容如下: