2020:0626---04--SpringBoot与日志

574 阅读11分钟

主要内容

1.日志框架
2.日志配置

1、日志框架

1.1 背景

小张开发一个大型系统;

1、System.out.println("");将关键数据打印在控制台;去掉?写在一个文件?

2、框架来记录系统的一些运行时信息;日志框架 ;  zhanglogging.jar;

3、高大上的几个功能?异步模式?自动归档?xxxx?  zhanglogging-good.jar?

4、将以前框架卸下来?换上新的框架,重新修改之前相关的API;zhanglogging-prefect.jar;

5、JDBC---数据库驱动;

    写了一个统一的接口层;日志门面(日志的一个抽象层);logging-abstract.jar;

    给项目中导入具体的日志实现就行了;我们之前的日志框架都是实现的抽象层;

1.2 市面上的日志框架

JUL、JCL、Jboss-logging、logback、log4j、log4j2、slf4j....
日志门面 (日志的抽象层) 日志实现
JCL(Jakarta Commons Logging) SLF4j(Simple Logging Facade for Java) jboss-logging Log4j JUL(java.util.logging) Log4j2 Logback
左边选一个门面(抽象层)、右边来选一个实现;

日志门面:  SLF4J;

日志实现:Logback;



SpringBoot:底层是Spring框架,Spring框架默认是用JCL;

==SpringBoot选用 SLF4j和logback;==

2、SLF4J使用

2.1 如何在系统中使用SLF4J

    以后开发的时候,日志记录方法的调用,不应该直接调用日志的实现类,而是调用日志抽
象层里面的方法。它会自动调用实现类中的方法。
    
    默认用的是Logback的实现jar,应该给系统中导入slf4j的jar和logback的实现jar。
    
    当然也可以用其他的实现jar,毕竟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");
      }
    }

2.2 图示:

第一列:只导了slf4j抽象层的jar,没有导入具体日志实现。
    结果只能在打出一些空的信息。   /dev/null
    
第二列:导入slf4j的jar和logback的jar
    有抽象接口框架,也有具体的日志实现。
    这就是使用slf4j的方式

第三列:日志实现选择log4j
    因为log4j出现较早,没有考虑到日志抽象层的情况。
    所以要想用lof4j日志实现那么中间要加一层适配层。
 
 
每一个日志的实现框架都有自己的配置文件。
    
使用slf4j后,配置文件还是用日志实现框架的本身的配置文件。

2.3 遗留问题

        假设我们开发了一个a系统,日志框架采用(slf4j+logback)。而且a系统用到了
    Spring,Hibernate,Mybatis....等等框架。这些框架很可能底层也用到了框架,而且
    各不相同。
        比如,Spring底层用的是(commons-logging)做的日志,Hibernate用的是(jboss-logging)
        
    那么就出现了问题:
        同一个系统出现了各种各样的日志框架。
    
    统一日志记录:
        即使是别的框架也统一使用slf4j进行输出。

2.4 解决遗留问题:

    项目中用到的所有日志框架统一使用slf4j+logback进行日志记录
        
    具体做法:统一使用slf4j+logback
    
    1.  我们项目选用slf4j+logback
        应用程序面向slf4j(接口层)编程,真正的实现我们用的是logback。
    
    2.  发现该程序编写时依赖其他的框架,而其他的框架底层用不同的日志框架记录信息。
        比如Spring中的commons-logging
        
        我们想要这些日志框架都能使用到slf4j。
    
    3.  我们可以将这些日志框架的jar(依赖)删掉。
        但是删掉后,该框架显然不能在使用了。比如Spring底层用commons-logging来记录
    信息。
        那么我们可以考虑用一个类似的jar来代替原来jar。
        
    4.  替换jar
        
        比如我们用jcl-over-slf4j.jar来代替commons-logging.jar
        
        这个代替的jar的功能和commons-logging.jar一样,该有的类,API一样。
        
        即Spring框架要用的commons-logging.jar里面的类,方法在jcl-over-slf4j.jar
    都有。那么Spring框架就不会报错了。
    
        但是新的包真正的实现是slf4j,slf4j的实现就又调到了logback。
        
        相当于中间又有了一个适配层,包装层。

    5.  如果项目想用slfj+log4j来做日志框架
        
        统一日志框架也用上面的方法。

3. SpringBoot日志关系

    我们新建一个项目,来看一下SpringBoot中的依赖。

    1. spring-boot-starter是SpringBoot项目最基本的依赖,每个场景都会依赖。
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter</artifactId>
          <version>2.3.1.RELEASE</version>
          <scope>compile</scope>
        </dependency>
    spring-boot-starter又会依赖:
        spring-boot-autoconfigure
        spring-boot-starter-logging

    2. 我们来看一下日志的依赖
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-logging</artifactId>
          <version>2.3.1.RELEASE</version>
          <scope>compile</scope>
        </dependency>
    这个日志依赖底层又依赖了许多jar,来将用到的日志都统一成slf4j。

    这就是SpringBoot底层日志依赖关系。
    
    3. 总结:
        1.SpringBoot底层也是使用slf4j+logback的方式进行日志记录。
        2.SpringBoot也把其他的日志都替换成了slf4j
        3.中间替换包实现的是slf4j
        4.如果我们要引入其他框架,一定要把框架的默认依赖移除掉。
    
    4.  这也是SpringBoot的做法。
    
        所以SpringBoot能自动适配所有的日志,而且底层使用的是slf4j+back的方式记录,
    引入其他框架的时候,只需要把它的日志框架的依赖排除掉,不用再做其他的配置。

4. SpringBoot日志的使用

4.1 SpringBoot日志的使用

    SpringBoot默认帮我们配置好了日志,使用方式如下。   
    @SpringBootTest
    class Day0626Springboot04LoggingApplicationTests {
    
        //获得一个日志记录器
        Logger logger = LoggerFactory.getLogger(getClass());
    
        @Test
        void contextLoads() {
           // System.out.println();
    
            //日志的级别:由低到高
            //可以调整输出的日志级别:只打印某个级别和大于其级别的内容
            logger.trace(()->"这是trace日志...");
            logger.debug(()->"这是debug日志...");
            logger.info(()->"这是Info日志...");
            logger.warn(()->"这是warn日志...");
            logger.error(()->"这是error日志...");
        }

}
    运行test方法测试一下:
    发现只打印了info及大于其级别的内容。

4.2 SpringBoot日志的调整配置

    1.在主配置文件中配置日志级别:
    
        配置com.atguigu包下的类:所有的日志级别是trace

        没有指定级别,就要SpringBoot默认规定的级别:root级别
        
    2.  logging.file.name/logging.file.path
    
        这两个属性有冲突,一般只用一个。常用logging.file.path。
        
        这两个属性不配置的话,默认将日志信息在控制台输出。
        
        1.logging.file.name
            #当前项目下生成springboot.log日志:也会打印到控制台
            logging.file.name=springboot.log
            
            #在指定路径下生成springboot.log日志
            logging.file.name=F:/springboot.log
        2. logging.file.path
# 在当前项目所在磁盘的根路径下创建: /springboot/log/springboot.log
logging.file.path=/spring/log
    3.  日志输出格式。
    
        默认是:时间+日志级别+线程id+全类名+消息        

        我们可以自己规定格式:
        默认的格式写法:

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

        日志输出格式分析:
           %d表示日期时间
           %thread表示线程名
           %-5level:级别从左显示五个字符宽度
           %logger{50} :表示logger名字最长50个字符,否则按照据点分割。
           %msg:日志消息
           %n:换行符。
           
    
    4. SpringBoot给我们默认配置好了日志。
    
        如果还想要进行配置,在类路径下放上每个日志框架对应的配置文件即可;SpringBoot就不使用他默认配置的日志配置了。
        
        logback.xml 配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
    <!-- 定义日志的根目录 -->
    <property name="LOG_HOME" value="/app/log" />
    <!-- 定义日志文件名称 -->
    <property name="appName" value="atguigu-springboot"></property>
    <!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!--
        日志输出格式:
			%d表示日期时间,
			%thread表示线程名,
			%-5level:级别从左显示5个字符宽度
			%logger{50} 表示logger名字最长50个字符,否则按照句点分割。 
			%msg:日志消息,
			%n是换行符
        -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </layout>
    </appender>

    <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->  
    <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 指定日志文件的名称 -->
        <file>${LOG_HOME}/${appName}.log</file>
        <!--
        当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
        TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。
        -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--
            滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动 
            %i:当文件大小超过maxFileSize时,按照i进行文件滚动
            -->
            <fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <!-- 
            可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,
            且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是,
            那些为了归档而创建的目录也会被删除。
            -->
            <MaxHistory>365</MaxHistory>
            <!-- 
            当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy
            -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 日志输出格式: -->     
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
        </layout>
    </appender>

    <!-- 
		logger主要用于存放日志对象,也可以定义日志类型、级别
		name:表示匹配的logger类型前缀,也就是包的前半部分
		level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
		additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,
		false:表示只用当前logger的appender-ref,true:
		表示当前logger的appender-ref和rootLogger的appender-ref都有效
    -->
    <!-- hibernate logger -->
    <logger name="com.atguigu" level="debug" />
    <!-- Spring framework logger -->
    <logger name="org.springframework" level="debug" additivity="false"></logger>



    <!-- 
    root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
    要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。 
    -->
    <root level="info">
        <appender-ref ref="stdout" />
        <appender-ref ref="appLogAppender" />
    </root>
</configuration> 
    5.  日志框架和日志配置文件的对应关系:
        logback                     :       logback.xml
        log4j2                      :       log4j2.xml
        JDK(java util logging)      :       logging.properties

        高级特性:<springProfile>只在某个环境中激活该配置文件。
        logback:xml:
            直接就被日志框架识别了。
        logback-spring.xml:
            日志框架就不直接加载了,由SpringBoot加载。那么就能用到SpringBoot一个
        非常好的功能:<springProfile>只在某个环境中激活该配置文件。
        
    建议使用logback-spring.xml来做日志配置文件。
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <springProfile name="dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ---> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
            </springProfile>
            <springProfile name="!dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} 6666 [%thread] 6666 %-5level %logger{50} - %msg%n</pattern>
            </springProfile>
        </layout>
    </appender>
    注意:使用自己写的日志配置文件时,要注解掉主配置文件中关于日志的配置,去掉有冲突的配置。

5 切换日志框架

    例如:
        我们现在用的是slf4j+logback的日志框架,怎么切换到是slf4j+log4j?
        
        此处只做举例:因为这是没意义的本来log4j就是因为作者觉得不好,才写了logback的。

    1. 我们在pom.xml打开依赖结构

    2. 再排除掉默认的logback的依赖

    3. 导入适配层的包
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </dependency>
    4. 测试一下

    5. 加一个lof4j的配置文件
    
    
可以按照slf4j的日志适配图进行相关的切换; 

5.2 转成Log4j2

转成Log4j2:
    将依赖中的starter-loggingExclude掉:
    spring-boot-starter-logging
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-starter-logging</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
    导入:oot-starter-log4j2
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-starter-logging</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>