一文搞懂Java开发中混乱的日志体系

一、日志框架

市面上的日志框架有JUL、JCL、Jboss-logging、logback、Log4j、log4j2、slf4j....

日志门面(日志的抽象层) 日志实现
jcl(Jakarta Commons Logging)、SLF4J(Simple Logging facade for Java)、jboss-loggin(使用场景少) Log4j、JUL(java.utils.logging)、Log4j2、Logback

左边选一个门面(抽象层)、右边来选一个实现。其中jcl最后更新时间为2014年后面就没再更新了,jboss-loggin使用场景很少。Log4j由于有性能问题,原作者写了个新的框架Logback,考虑到以后可能会有更多的框架,于是作者写了一个日志门面SLF4J。使用上日志门面使用 SLF4J,日志实现的话由于Log4j有了更好的替代者Logback,所以不考虑使用Log4j。JUL是Java为了抢占市场份额写的一个日志框架,不考虑。log4j2是借log4j之名由Apache重新写的日志框架,但是很多框架都还没适配起来,不考虑。所以选择使用Logback。
spring框架默认使用JCL(抽象层,具体实现不同版本不一样)。而SpringBoot由于底层是spring框架,springboot对日志进行了一次包装,选用SLF4j和logback

二、日志的使用
2.1 Log4j的使用

添加依赖:


  log4j
  log4j
  1.2.17

添加一个配置文件:

log4j.rootLogger=info,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{yyyy-MM-dd HH:mm:ss} %p [%t] %C.%M(%L) | %m%n

测试:

import org.apache.log4j.Logger;
public class Log4JTest {
    public static void main(String[] args) {
        Logger logger = Logger.getLogger("log4j");
        logger.info("log4j");
    }
}
2.2 JUL的使用

这是java自带的日志框架,无需添加依赖即可直接使用:

import java.util.logging.Logger;
public class JULTest {
    public static void main(String[] args) {
        Logger logger = Logger.getLogger("jul");
        logger.info("jul");
    }
}
2.3 JCL的使用

jcl是一个抽象层的框架,不直接记录日志,它是使用第三方实现(Log4j、JUL等等)记录日志,框架中有谁就用谁,都有的时候会按照默认顺序选择(下文会讲到),添加JCL依赖:


  commons-logging
  commons-logging
  1.2

测试:

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class JCLTest {
    public static void main(String[] args) {
        Log log = LogFactory.getLog("jcl");
        log.info("jcl");
    }
}

可以看到此时JCL打印结果格式和JUL一样。通过断点我们看到log的类型,Jdk14Logger是JCL用来操作JUL的实现类:

一文搞懂Java开发中混乱的日志体系_第1张图片

如果加上Log4j的依赖,输出格式变成了和Log4j一样:

断点看下log类型,其中Log4JLogger是JCL提供的用来操作Log4j日志的实现类:

一文搞懂Java开发中混乱的日志体系_第2张图片

JCL框架不直接记录日志,提供了记录日志的抽象方法即接口(info、debug、error等),底层通过一个数组存放具体的日志框架的类名,然后循环数组依次去匹配这些类名是否在app中被依赖了,如果找到被依赖的则直接使用,所以他有先后顺序:

一文搞懂Java开发中混乱的日志体系_第3张图片
一文搞懂Java开发中混乱的日志体系_第4张图片

底层会循环该数组,通过class.forname的形式如果能找到某个类就会跳出循环去创建这个类。否则遍历完这个数组。可以理解为jcl不提供日志实现,只是提供了一组记录日志的抽象方法即接口,好处在于我们的上层调用代码是JCL提供的,是固定的,不需要随着日志的实现而修改这部分代码,很好的解除了日志框架切换的耦合性,我们只需要选择具体的日志版本实现:增加或删除依赖即可实现日志框架切换。但是jcl已经不更新,并且通过源码我们知道这个框架对Logback、Log4j2是不支持的。我们有更好的替代者:slf4j

2.4 SLF4J的使用

项目里面导入门面slf4j的依赖:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}

此时运行项目就会出现以下提示信息:

这并不是错误信息,并不是日志框架出错,只是这样不会有任何日志输出,如果需要输出日志我们需要绑定一个日志具体的实现框架,具体如下:

一文搞懂Java开发中混乱的日志体系_第5张图片

所有的应用程序应该面向SLF4J编程,导入具体的日志实现,最终SLF4J会调用具体的日志实现去记录日志。
空实现:如上图,如果项目中只导入了SLF4J那么不会有任何日志输出。
使用logback:logback就是对slf4j的实现,只需要导入logback的jar(有两个),底层调用logbackAPI去记录日志。
log4j:由于log4j出现比较早,当时还没有SLF4J,因此log4j在设计的时候没考虑到SLF4J,因此要使用就需要一个适配层(slf4j-log412:包含了log4j):向上实现了SLF4J的具体方法,向下真正需要记录日志的时候会去调用具体的日志实现的API
jul:同log4j
simple:slf4j默认的简单的日志实现
no-operation:没有什么操作的实现包
每一个日志的实现框架都有自己的配置文件。使用slf4j以后,配置文件还是做成日志实现框架自己本身的配置文件。
slf4j+logback的使用只需要在pom文件中添加logback的两个依赖即可:


  org.slf4j
  slf4j-api
  1.7.26


  ch.qos.logback
  logback-classic
  1.2.3


  ch.qos.logback
  logback-core
  1.2.3

项目开发中产生的日志遗留问题
比如有一个项目A开发的时候使用SLF4J+Logback,但是开发中需要使用的Spring(commons-logging)、Hibernate(jboss-logging)、MyBatis……不同的框架使用了不同的日志实现,如何实现统一日志记录,即使是别的框架也使用slf4j进行输出?

一文搞懂Java开发中混乱的日志体系_第6张图片

如上图所示,假设应用程序使用的Slf4j+logback,引入的其它框架包含了Commons-logging、log4j、juc等日志实现,如果要实现日志统一,我们需要使用对应的替换包(jcl-over-slf4j、log4j-over-slf4j、jul-to-slf4j)去取代原来的日志实现,这样把原来的实现排除掉了,但是替换包的功能和原来的具体实现包一样,原来的实现包中有什么类替换的包中也有,这样可以保证在排除了框架中依赖的日志类的时候就不会报错,但是此时调用替换的类去记录日志的时候,其实是这个类去调用slf4j,最后slf4j去调用我们应用程序中的具体实现logback。说白了就是替换包模拟原来个框架依赖的日志的各个类,框架最终使用的就是这些模拟的类,模拟的类就可以去操作我们应用程序的日志。
如果上面我们的应用程序需要使用Slf4j+log4j/jcl,其它框架的转换不变,但是应用程序在使用的时候由于在slf4j和log4j之间需要加一个适配层。
总结:如何让系统中所有的日志都统一到slf4j
1、将系统中其他日志框架先排除出去;
2、用中间包来替换原有的日志框架;
3、看情况导入slf4j其他的实现(logback)或适配层(比如slf4j-log412)

三、spring4与spring5日志的区别
1.spring4的日志

spring4默认使用了jcl(commons-logging)作为抽象层,使用方式同上面介绍过的jcl使用方式一样:


  org.springframework
  spring-context
  4.3.14.RELEASE

spring4的日志结构:

一文搞懂Java开发中混乱的日志体系_第7张图片

测试:

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class SpringLogTest {
    public static void main(String[] args) {
        Log log = LogFactory.getLog("spring");
        log.info("test spring");
    }
}

此时没有添加其它日志实现框架,默认使用的是jul框架去打印日志:

如果加上log4j依赖就会使用log4j去打印日志:

2.spring5的日志

spring5对commons-logging做了修改(spring-jcl),默认使用jcl去绑定jul打印日志。如果要使用其他日志实现框架需要让对应的日志框架去绑定slf4j,即使用方式同slf4j一样。


  org.springframework
  spring-context
  5.1.8.RELEASE

spring5的日志结构:

一文搞懂Java开发中混乱的日志体系_第8张图片

测试:

此时如果我们也加上log4j依赖:

一文搞懂Java开发中混乱的日志体系_第9张图片

可以看到此时不论我们的应用中是否添加了其它日志实现框架,打印日志的log类型始终是LogAdapter$JavaUtilsLog。我们看一下spring5中这个获取日志对象的源码:

public static Log createLog(String name) {
    switch (logApi) {
        case LOG4J:
            return LogAdapter.Log4jAdapter.createLog(name);
        case SLF4J_LAL:
            return LogAdapter.Slf4jAdapter.createLocationAwareLog(name);
        case SLF4J:
            return LogAdapter.Slf4jAdapter.createLog(name);
        default:
            // Defensively use lazy-initializing adapter class here as well since the
            // java.logging module is not present by default on JDK 9. We are requiring
            // its presence if neither Log4j nor SLF4J is available; however, in the
            // case of Log4j or SLF4J, we are trying to prevent early initialization
            // of the JavaUtilLog adapter - e.g. by a JVM in debug mode - when eagerly
            // trying to parse the bytecode for all the cases of this switch clause.
            return LogAdapter.JavaUtilAdapter.createLog(name);
    }
}

其中logApi的值在类加载的时候就会初始化:

private static final String LOG4J_SPI = "org.apache.logging.log4j.spi.ExtendedLogger";
private static final String LOG4J_SLF4J_PROVIDER = "org.apache.logging.slf4j.SLF4JProvider";
private static final String SLF4J_SPI = "org.slf4j.spi.LocationAwareLogger";
private static final String SLF4J_API = "org.slf4j.Logger";
private static final LogAdapter.LogApi logApi;
static {
    if (isPresent(LOG4J_SPI)) {
        if (isPresent(LOG4J_SLF4J_PROVIDER) && isPresent(SLF4J_SPI)) {
            // log4j-to-slf4j bridge -> we'll rather go with the SLF4J SPI;
            // however, we still prefer Log4j over the plain SLF4J API since
            // the latter does not have location awareness support.
            logApi = LogAdapter.LogApi.SLF4J_LAL;
        }
        else {
            // Use Log4j 2.x directly, including location awareness support
            logApi = LogAdapter.LogApi.LOG4J;
        }
    }
    else if (isPresent(SLF4J_SPI)) {
        // Full SLF4J SPI including location awareness support
        logApi = LogAdapter.LogApi.SLF4J_LAL;
    }
    else if (isPresent(SLF4J_API)) {
        // Minimal SLF4J API without location awareness support
        logApi = LogAdapter.LogApi.SLF4J;
    }
    else {
        // java.util.logging as default
        logApi = LogAdapter.LogApi.JUL;
    }
}

可以看到,静态代码块中针对使用不同的日志框架做了一系列的判断,定义的常量字符串代表的类都是各个日志实现框架中的类,会根据这个去查找是否引入了对应的依赖。例如这里的“org.slf4j.spi.LocationAwareLogger”是slf4j中的类,我们如果要使用log4j框架和slf4j使用log4j一样操作即可(在这个测试中我们不需要spring自带的日志,可以剔除,但是需要注意,如果使用了AnnotationConfigApplicationContext剔除以后需要添加jcl的转换包,否则会报错,使用在下面“4.2 MyBatis整合Spring时日志”介绍时pom.xml中会有说明):


  org.springframework
  spring-context
  5.1.8.RELEASE
  
    
      org.springframework
      spring-jcl
    
  


  org.slf4j
  slf4j-api
  1.7.26


  org.slf4j
  slf4j-log4j12
  1.7.26

此时“org.slf4j.spi.LocationAwareLogger”就会在项目中存在,在静态代码块的判断过程中logApi的值就会变成“SLF4J_LAL”,创建log对象的时候就会创建log4j的:

一文搞懂Java开发中混乱的日志体系_第10张图片

此时控制台输出的日志就是通过log4j的框架去实现的:

spring5日志使用总结:
1、默认使用jcl去依赖java自带的jul
2、如果要使用其它日志框架实现需要通过对应的日志实现去绑定slf4j(就是slf4j的使用方式)

四、MyBatis以及和Spring整合时的日志
4.1 MyBatis单独使用时的日志操作

MyBatis自带了日志抽象工厂,具体的实现由SLF4J、Apache Commons Logging、Log4j 2、Log4j、JDK logging完成。当项目中存在多个实现时会根据上面的几个框架从前到后这个顺序依次查找,找到了就会优先使用该日志实现。看一下MyBatis中日志源码即可知晓原因:


一文搞懂Java开发中混乱的日志体系_第11张图片
一文搞懂Java开发中混乱的日志体系_第12张图片
一文搞懂Java开发中混乱的日志体系_第13张图片
一文搞懂Java开发中混乱的日志体系_第14张图片

在org.apache.ibatis.logging.LogFactory的静态代码块中,会根据上面说过的顺序拿到指定的日志实现类的类字节码创建对象,如果失败则继续尝试,只要找到一个就不会再往下去创建。
在很多情况下项目中会自带日志框架,比如Tomcat中自带了Commons Logging,如果我们想要使用log4j作为日志框架,我们需要引入log4j的jar,并且还要指定使用的日志框架:
方式一:通过在 MyBatis 配置文件 mybatis-config.xml 里面添加一项 setting 来选择别的日志工具


  
    ...
    
    ...
  

其中logImpl 可选的值有:SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING、NO_LOGGING,或者是实现了接口 org.apache.ibatis.logging.Log 的,且构造方法是以字符串为参数的类的完全限定名
方式二:调用如下任一方法来使用日志工具
org.apache.ibatis.logging.LogFactory.useSlf4jLogging();
org.apache.ibatis.logging.LogFactory.useLog4JLogging();
org.apache.ibatis.logging.LogFactory.useJdkLogging();
org.apache.ibatis.logging.LogFactory.useCommonsLogging();
org.apache.ibatis.logging.LogFactory.useStdOutLogging();
需要注意的是:要调用以上某个方法,必须在调用其它 MyBatis 方法之前调用它。另外,仅当运行时类路径中存在该日志工具时,调用与该日志工具对应的方法才会生效,否则 MyBatis 一概忽略。如你环境中并不存在 Log4J,你却调用了相应的方法,MyBatis 就会忽略这一调用,转而以默认的查找顺序查找日志工具
使用示例之Log4J打印日志
maven项目中添加以下依赖:


  org.mybatis
  mybatis
  3.4.6



  mysql
  mysql-connector-java
  5.1.46


  log4j
  log4j
  1.2.17

创建Dao接口:

public interface UserMapper {
    List> findAllUser();
    List> findUserByCondition(String strId);
}

在resources目录下创建同包名同类名的xml文件:



    
    

核心配置文件mybatis-config.xml:




    
        
            
            
                
                
                
                
            
        
    
    
        
    

log4j.properties:

log4j.rootLogger=debug,stdout
#这句控制输出SQL语句
log4j.logger.com.mobei.dao=TRACE
#输出到控制台
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{yyyy-MM-dd HH:mm:ss} %p [%t] %C.%M(%L) | %m%n

测试类:

public static void main(String[] args) throws Exception {
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory sessionFactory = builder.build(is);
    SqlSession sqlSession = sessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    List> allUser = mapper.findAllUser();
    System.out.println(allUser);
    List> conditionUser = mapper.findUserByCondition("1");
    System.out.println(conditionUser);
    
    sqlSession.close();
    is.close();
}

根据前面的分析我们知道,由于此时类路径下包含了log4j日志和jdk自带的jul日志,由于log4j优先于jul所以肯定会使用log4j框架输出日志:

一文搞懂Java开发中混乱的日志体系_第15张图片

org.apache.ibatis.logging.LogFactory.useXXXLogging()测试
此时如果我们想要切换成STDOUT_LOGGING,则需要在使用之前(测试发现必须在SqlSessionFactory 产生之前)调用org.apache.ibatis.logging.LogFactory.useStdOutLogging():

public static void main(String[] args) throws Exception {
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

    org.apache.ibatis.logging.LogFactory.useStdOutLogging();

    SqlSessionFactory sessionFactory = builder.build(is);
    SqlSession sqlSession = sessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List> allUser = mapper.findAllUser();
    System.out.println(allUser);
    List> conditionUser = mapper.findUserByCondition("1");
    System.out.println(conditionUser);
    sqlSession.close();
    is.close();
}

此时的日志:

一文搞懂Java开发中混乱的日志体系_第16张图片

使用JUL打印sql日志
我们将pom.xml中的log4j日志去除掉,此时类路径下就只有JDK自带的JUL日志记录包了,我们测试:

public static void main(String[] args) throws Exception {
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory sessionFactory = builder.build(is);
    SqlSession sqlSession = sessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List> allUser = mapper.findAllUser();
    System.out.println(allUser);
    List> conditionUser = mapper.findUserByCondition("1");
    System.out.println(conditionUser);
    sqlSession.close();
    is.close();
}
一文搞懂Java开发中混乱的日志体系_第17张图片

发现此时没有任何sql相关的日志打印,经过我们前面的分析,此时应该默认会使用了JUL去打印日志才对,为什么会没有呢?这其实跟JUL默认的日志级别有关,我们断点调试即可知道JUL默认的日志级别是FINE(500):

一文搞懂Java开发中混乱的日志体系_第18张图片

这里的log对象类型是Jdk14LoggingImpl,我们找到该方法:

一文搞懂Java开发中混乱的日志体系_第19张图片

可以看到JUL底层默认设置的日志级别为FINE(500),是小于INFO(800)的,所以不会输出日志,如果我们想要修改这个日志级别,可以进行扩展:
自定义一个实现Log的类,在类中声明JUL的logger字段(这个类在这里仅为了测试输出log的,其它级别的日志如果需要还要继续去完善):

import org.apache.ibatis.logging.Log;
import java.util.logging.Logger;
public class MyLog implements Log {
    private Logger logger;
    public MyLog(String clazz) {
        logger = Logger.getLogger("my log");
    }
    @Override
    public boolean isDebugEnabled() {
        return true;
    }
    @Override
    public boolean isTraceEnabled() {
        return false;
    }
    @Override
    public void error(String s, Throwable e) {
    }
    @Override
    public void error(String s) {
    }
    @Override
    public void debug(String s) {
        logger.info(s);
    }
    @Override
    public void trace(String s) {
    }
    @Override
    public void warn(String s) {
    }
}

将该日志注册到核心配置文件mybatis-config.xml中:


    

此时测试即可看到sql日志打印:

一文搞懂Java开发中混乱的日志体系_第20张图片

log4j调整打印sql粒度:指定只打印某个方法对应的sql
修改配置文件,只打印findAllUser方法的sql:log4j.logger.com.mobei.dao.UserMapper.findAllUser=TRACE,同样可以指定某个类中所有方法和某个包下所有类中的所有方法,具体可以看官网

log4j.rootLogger=info,stdout
#输出到控制台
log4j.logger.com.mobei.dao.UserMapper.findAllUser=TRACE
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{yyyy-MM-dd HH:mm:ss} %p [%t] %C.%M(%L) | %m%n

输出结果(记得切换成使用log4j日志),可以看到此时findUserByCondition方法的sql并没有打印:

一文搞懂Java开发中混乱的日志体系_第21张图片
4.2 MyBatis整合Spring时日志

经过前面那么多的分析, MyBatis整合Spring时日志的使用就变得异常简单了。以Spring5为例,Spring5默认使用jcl+jul,这里我们指定为使用log4j打印日志(其他使用方式和Spring5中日志的使用一样,都是相通的):
pom.xml:


    org.springframework
    spring-context
    5.1.8.RELEASE
    
    
        
            org.springframework
            spring-jcl
        
    



    org.slf4j
    slf4j-api
    1.7.26



    org.slf4j
    jcl-over-slf4j
    1.7.26



    org.slf4j
    slf4j-log4j12
    1.7.26



    org.springframework
    spring-jdbc
    5.1.8.RELEASE



    org.mybatis
    mybatis
    3.4.6



    mysql
    mysql-connector-java
    5.1.46



    org.mybatis
    mybatis-spring
    2.0.1

dao接口:

public interface UserMapper {
    @Select("select * from user")
    List> findAllUser();
}

service:

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    public List> findAllUser(){
        return userMapper.findAllUser();
    }
}

log4j.proprerties:

log4j.rootLogger=info,stdout
#这句控制输出SQL
log4j.logger.com.mobei.dao=TRACE
#输出到控制台
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{yyyy-MM-dd HH:mm:ss} %p [%t] %C.%M(%L) | %m%n

配置类:

@Configuration
@ComponentScan("com.mobei")
@MapperScan("com.mobei.dao")
public class AppConfig {
    @Bean
    public DataSource dataSource(){
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setUsername("root");
        ds.setPassword("123456");
        ds.setUrl("jdbc:mysql:///test");
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        return ds;
    }
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource());
        return factoryBean;
    }
}

测试:

public static void main(String[] args) {
    AnnotationConfigApplicationContext ac =
        new AnnotationConfigApplicationContext(AppConfig.class);
    UserService service = ac.getBean(UserService.class);
    List> allUser = service.findAllUser();
    System.out.println(allUser);
}
一文搞懂Java开发中混乱的日志体系_第22张图片

验证log4j生效:

log4j.appender.stdout.target=System.err
一文搞懂Java开发中混乱的日志体系_第23张图片

PS: 默认情况下的JUL日志级别配置
Spring5默认使用JCL+JUL,如果不添加任何其它日志实现框架,就是使用JUL输出日志,前面我们介绍的自定义JUL日志对象以修改日志级别,我们是将自定义的类配置在配置文件的setting标签中,对于这种配置类我们可以使用如下方式配置:

@Bean
public SqlSessionFactoryBean sqlSessionFactory() throws Exception {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();

    org.apache.ibatis.session.Configuration cfg = 
        new org.apache.ibatis.session.Configuration();
    cfg.setLogImpl(MyLog.class);
    factoryBean.setConfiguration(cfg);

    factoryBean.setDataSource(dataSource());
    return factoryBean;
}
五、springboot中的日志

    org.springframework.boot
    spring-boot-starter
    2.1.6.RELEASE

spring-boot-starter使用的日志:

    
      org.springframework.boot
      spring-boot-starter-logging
      2.1.6.RELEASE
      compile
    
一文搞懂Java开发中混乱的日志体系_第24张图片

总结:
1)、SpringBoot底层也是使用slf4j+logback的方式进行日志记录
2)、SpringBoot也把其他的日志都替换成了slf4j;
如果我们要引入其它框架,如果框架使用的日志不是logback或者版本不一致,一定要把这个框架的默认日志依赖移除掉,否则可能会出现jar包冲突。SpringBoot能自动适配所有的日志,而且底层使用slf4j+logback的方式记录日志,引入其它框架的时候把这个框架依赖的日志框架排除掉。
SpringBoot默认帮我们配置好了日志,启动应用就可以看到控制台的日志输出。我们可以根据自己的需要来使用日志。

5.1 日志级别(这里应该放到前面介绍的,懒得改了,凑合着看吧)
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SbLog1ApplicationTests {
    Logger logger = LoggerFactory.getLogger(getClass());
    @Test
    public void contextLoads() {
        //日志的级别:
        //低->高 trace

由于SpringBoot默认使用info级别(root级别),所以只会有高于info级别的日志输出:

我们可以在properties文件中设置指定目录下(或者类)的日志输出级别,比如指定com.mobei.sb_log_1目录下日志输出级别为trace:

logging.level.com.mobei.sb_log_1=trace
5.2 SpringBoot修改日志的默认配置
logging.file logging.path Example Description
(none) (none) 只在控制台输出
指定文件名 (none) my.log 输出日志到my.log文件
(none) 指定目录 /var/log 输出到指定目录的 spring.log 文件中

logging.file
不指定路径在当前项目下生成xxx.log日志

logging.file=springboot.log
一文搞懂Java开发中混乱的日志体系_第25张图片

也可以指定完整的路径:

logging.file=G:/springboot.log

一文搞懂Java开发中混乱的日志体系_第26张图片

logging.path
和logging.file是冲突设置,两者任选其一即可,如果两者都指定也只有logging.file起作用。一般使用logging.path,用于指定日志文件生成后存放的目录,其中日志文件默认叫spring.log

# 在当前磁盘的根路径(当前项目所在目录的根目录,例如这里项目在G盘下,根目录就是G:\)
# 下创建spring文件夹和里面的log文件夹:使用 spring.log 作为默认文件
logging.path=/spring/log
一文搞懂Java开发中混乱的日志体系_第27张图片
5.3 日志的格式

默认格式:

%d表示日期时间,        
%thread表示线程名,        
%‐5level:级别从左显示5个字符宽度        
%logger{50} 表示logger名字最长50个字符,否则按照句点分割。         
%msg:日志消息,        
%n是换行符        

logging.pattern.console:在控制台输出的日志的格式
修改成我们自己指定格式的:

logging.pattern.console=%d{yyyy‐MM‐dd} [%thread] %‐5level %logger{50} ‐ %msg%n

logging.pattern.file:指定文件中日志输出的格式

logging.pattern.file=%d{yyyy‐MM‐dd} === [%thread] === %-5level === %logger{50} ==== %msg%n
5.4 SpringBoot中日志的默认值所在位置
一文搞懂Java开发中混乱的日志体系_第28张图片
一文搞懂Java开发中混乱的日志体系_第29张图片
5.5 指定配置(参考官网说明)

给类路径下放上每个日志框架自己的配置文件即可,SpringBoot就不使用他默认配置的了:

一文搞懂Java开发中混乱的日志体系_第30张图片

官网说明:使用标准的文件名logback.xml在应用启动的时候被日志框架识别,绕过了SpringBoot,不能扩展高级功能,如果需要扩展,需要使用扩展名比如logback-spring.xml,这样日志框架无法识别,不会去加载,而是由SpringBoot去解析配置,可以使用到SpringBoot的高级Profile功能

一文搞懂Java开发中混乱的日志体系_第31张图片

比如我们指定某端配置只在某个环境下生效:


    
        %d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n
    
    
        %d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n
    

非生产环境:

生产环境:
激活生产环境可以在配置文件中设置:

spring.profiles.active=dev

或者在启动的时候带上命令参数:

运行结果:

如果不使用扩展名,直接使用logback.xml就会报错:

附上logback-spring.xml完整内容:




    
    
    
    
    
    
        
        
            
                %d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n
            
            
                %d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n
            
        
    

      
    
        
        ${LOG_HOME}/${appName}.log
        
        
            
            ${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log
            
            365
            
            
                100MB
            
        
             
        
            %d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n
        
    

    
    
    
    
    

    
    
        
        
    
 
5.6 切换日志框架

    org.springframework.boot
    spring-boot-starter-web
    
        
            spring-boot-starter-logging
            org.springframework.boot
        
    


    org.springframework.boot
    spring-boot-starter-log4j2

你可能感兴趣的:(一文搞懂Java开发中混乱的日志体系)