SpringBoot_day_03

SpringBoot的日志框架与配置

日志框架

小张,开发一个大型系统;
1、System.out.println(“”);将关键数据打印在控制台;去掉?写在一个文件?
2、框架来记录系统的一些运行时信息;日志框架 ; zhanglogging.jar;
3、高大上的几个功能?异步模式?自动归档?xxxx? zhanglogging-good.jar?
4、将以前框架卸下来?换上新的框架,重新修改之前相关的API;zhanglogging-prefect.jar;
5、JDBC—数据库驱动;
写了一个统一的接口层;日志门面(日志的一个抽象层);logging-abstract.jar;
给项目中导入具体的日志实现就行了;我们之前的日志框架都是实现的抽象层;


市面上的日志框架

JUL、JCL、Jboss-logging、logback、log4j、log4j2、slf4j….

左边选一个门面(抽象层)、右边来选一个实现;

日志门面: SLF4J;

日志实现:Logback;

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

SpringBoot选用 SLF4j 和 logback.

SLF4j使用

如何在系统中使用SLF4j

SLF4j参考文档

以后开发的时候,日志记录方法的调用,不应该来直接调用日志的实现类,而是调用日志抽象层里面的方法;

给系统里面导入 slf4j的jarlogback的实现jar

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");
  }
}

图示:

每一个日志的实现框架都有自己的配置文件。使用slf4j以后,配置文件还是要用日志实现框架自己本身的配置文件;

比如使用slf4j 和 logback,就用logback的配置文件,使用slf4j 和 log4j,就使用log4j的配置文件。而不是使用slf4j的配置文件。

遗留问题

a系统(slf4j+logback): Spring(commons-logging)、Hibernate(jboss-logging)、MyBatis、xxxx

我们自己的a系统想要使用slf4j+logback做日志框架,但是我们的a系统是基于Spring、Hibernate等其他框架的,而这些其他框架底层使用的是其他日志框架,Spring使用的是commons-logging,Hibernate使用的是jboss-logging。

现在,我们想统一日志记录,即:使别的框架和我一起统一使用slf4j进行输出,能做到吗?

slf4j官网给出了答案,参考答案

如何让系统中所有的日志都统一到slf4j?

1、将系统中其他日志框架的日志jar包先排除出去;

2、用中间包来替换原有的日志框架的jar;

3、导入slf4j其他的实现。

SpringBoot日志关系

创建一个新的Springboot的应用,只需要Web模块即可。

现在,我们来查看一下Springboot的依赖关系。

查看依赖关系

1)使用idea的自带的maven管理器工具

这样不够直观。

2)查看依赖图

在pom文件中,右键点击Diagrams -> show dependencies

首先查看spring‐boot‐starter,因为这是Springboot的应用都必须依赖的jar包。

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

然后发现,在spring‐boot‐starter下,有spring‐boot‐starter‐logging。

说明SpringBoot使用它来做日志功能

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

再来看spring-boot-starter-logging底层依赖:

总结:

1)、SpringBoot底层也是使用 slf4j + logback 的方式进行日志记录

2)、SpringBoot也把其他的日志都替换成了slf4j;

3)、中间替换包?

@SuppressWarnings("rawtypes")
public abstract class LogFactory {
    static String UNSUPPORTED_OPERATION_IN_JCL_OVER_SLF4J =
"http://www.slf4j.org/codes.html#unsupported_operation_in_jcl_over_slf4j";
    static LogFactory logFactory = new SLF4JLogFactory();

其实这些jar包,本身还是原来的日志框架的jar包,只是在使用的过程中,偷换成了slf4j的日志框架,就和适配器模式很像。

这些操作,Springboot已经帮我们做好了,我们只需要在引入其他框架的时候,移除掉他们本身的日志框架就行了。

4)、如果我们要引入其他框架?一定要把这个框架的默认日志依赖移除掉?

   我们挑Spring来做一个列子:

   Spring框架用的是commons-logging;

<dependency>        
    <groupId>org.springframework</groupId>            
    <artifactId>spring‐core</artifactId>            
    <exclusions>            
         <exclusion>                
            <groupId>commons‐logging</groupId>                    
            <artifactId>commons‐logging</artifactId>                    
         </exclusion>                
    </exclusions>            
</dependency>

发现,Springboot也帮我们剔除掉了,Springboot真方便!!!

SpringBoot能自动适配所有的日志,而且底层使用slf4j+logback的方式记录日志,引入其他框架的时候,只需要
把这个框架依赖的日志框架排除掉即可;

日志使用

默认配置

直接运行,我们刚刚创建的项目:

发现,Springboot已经帮我们创建好了默认的配置。默认情况下,Spring Boot会用Logback来记录日志,并用INFO级别输出到控制台。在运行应用程序和其他例子时,你应该已经看到很多INFO级别的日志了。

从上图可以看到,日志输出内容元素具体如下:

  • 时间日期:精确到毫秒
  • 日志级别:ERROR, WARN, INFO, DEBUG or TRACE
  • 进程ID
  • 分隔符:--- 标识实际日志的开始
  • 线程名:方括号括起来(可能会截断控制台输出)
  • Logger名:通常使用源代码的类名
  • 日志内容

使用:

在测试类中:

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBoot03LoggingApplicationTests {

    //日志记录器
    Logger logger = LoggerFactory.getLogger(getClass());

    @Test
    public void contextLoads() {

        //System.out.println();  我们不再使用这种方法来记录了

        //日志的级别,trace < debug < info < warn < error
        //只会打印本级别和比本级别更高的日志
        logger.trace("这是trace日志···");
        logger.debug("这是debug日志···");
        //Springboot默认的级别是info级别
        logger.info("这是info日志···");
        logger.warn("这是warn日志···");
        logger.error("这error日志···");
    }

}

运行测试方法:

只打印了info级别以及以上的。说明Springboot默认的日志级别是info。

1) 修改日志的级别。

在application.properties文件中。

logging.level.com.liuzhuo=trace

再次运行测试方法:

格式为:logging.level.* = LEVEL

  • logging.level:日志级别控制前缀,*为包名或Logger名
  • LEVEL:选项 TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF

举例:

  • logging.level.com.liuzhuo=DEBUG:com.liuzhuo包下所有class以DEBUG级别输出
  • logging.level.root=WARN:root日志以WARN级别输出

2) 日志文件的输出

默认情况下,Spring Boot将日志输出到控制台,不会写到日志文件。如果要编写除控制台输出之外的日志文件,则需在application.properties中设置logging.file或logging.path属性。

  • logging.file,设置文件,可以是绝对路径,也可以是相对路径。如:logging.file=my.log
  • logging.path,设置目录,会在该目录下创建spring.log文件,并写入日志内容,如:logging.path=/var/log

logging.file=xxx.log,会在项目的根目录下生成xxx.log文件
logging.file=G:/xxx.log,会在G盘下生xxx.log文件

logging.path=/aaa/bbb,会在项目的磁盘下,创建aaa/bbb文件夹,然后生成spring.log日志文件,日志文件的名字都是spring.log

注:二者不能同时使用,如若同时使用,则只有logging.file生效

默认情况下,日志文件的大小达到10MB时会切分一次,产生新的日志文件,默认级别为:ERROR、WARN、INFO

3) 日志输出格式的设置

# 在控制台输出的日志的格式
logging.pattern.console=%d{yyyy‐MM‐dd} [%thread] %‐5level %logger{50} ‐ %msg%n
# 指定文件中日志输出的格式
logging.pattern.file=%d{yyyy‐MM‐dd} === [%thread] === %‐5level === %logger{50} ==== %msg%n
日志输出格式:

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

    ‐‐>

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

指定配置

在使用我们自己定义的日志配置文件之前,我们先来看看,为啥我们不用配置日志文件,Springboot中也能使用日志功能?

1) 打开org.springframework.boot:spring-boot:1.5.17.RELEASE
2) 打开org.springframework.boot
3) 打开logging、logback、base.xml

<?xml version="1.0" encoding="UTF-8"?>

<!--
Base logback configuration provided for compatibility with Spring Boot 1.1
-->

<included>
    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
    <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>
    <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
    <include resource="org/springframework/boot/logging/logback/file-appender.xml" />
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
    </root>
</included>

我们发现,Springboot已经帮我们默认配置好了。

我们看到root下的 level的等于INFO,所以Springboot默认是info级别。

再看<include resource="org/springframework/boot/logging/logback/defaults.xml" />

<?xml version="1.0" encoding="UTF-8"?>

<!--
Default logback configuration provided for import, equivalent to the programmatic
initialization performed by Boot
-->

<included>
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
    <property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{yyyy-MM-dd HH:mm:ss.SSS} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>

    <appender name="DEBUG_LEVEL_REMAPPER" class="org.springframework.boot.logging.logback.LevelRemappingAppender">
        <destinationLogger>org.springframework.boot</destinationLogger>
    </appender>

    <logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR"/>
    <logger name="org.apache.catalina.util.LifecycleBase" level="ERROR"/>
    <logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN"/>
    <logger name="org.apache.sshd.common.util.SecurityUtils" level="WARN"/>
    <logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN"/>
    <logger name="org.crsh.plugin" level="WARN"/>
    <logger name="org.crsh.ssh" level="WARN"/>
    <logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="ERROR"/>
    <logger name="org.hibernate.validator.internal.util.Version" level="WARN"/>
    <logger name="org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration" level="WARN"/>
    <logger name="org.springframework.boot.actuate.endpoint.jmx" additivity="false">
        <appender-ref ref="DEBUG_LEVEL_REMAPPER"/>
    </logger>
    <logger name="org.thymeleaf" additivity="false">
        <appender-ref ref="DEBUG_LEVEL_REMAPPER"/>
    </logger>
</included>

我们能看到

<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>

这里就是配置控制台输出日志格式的配置,我们写在application.propertiese中的logging.pattern.console=xxx.就会覆盖这个配置中的内容,如果不配置的话,就使用默认的配置,就是冒号后面的一串:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} xxxxx


现在我们已经了解到了Springboot的默认日志文件的配置了。当我们具体使用日志框架时,需要使用哪个配置文件呢?

打开Spring的官网,logging参考文献

找到:26.5 Custom log configuration

从图中,我们可以看出,使用logback日志框架,就使用logback-spring.xml、logback.xml等配置文件。

其他的日志框架,类似logback。

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

现在,我们在类路径下,即resources下,放入一个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> 

现在,我们修改一下日志的输出格式:

 <!-- 日志输出格式: -->     
<layout class="ch.qos.logback.classic.PatternLayout">
    <pattern>%d{yyyy-MM-dd} -----> [ %thread ] -----> [ %-5level ] [ %logger{50} : %line ] -----> %msg%n</pattern>
</layout>

把时间的时分秒去掉,然后把分隔符改为箭头。

启动Springboot项目:

我们的日志配置文件设置成功了。


我们注意到了,官网上面写了logback.xml和logback-spring.xml两种配置文件的书写方式。

官网推荐我们使用logback-spring.xml命名的配置文件。

logback.xml: 是直接被日志框架识别,在Spring应用启动之前。不会走Spring的流程。

logback-spring.xml: 日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot
的高级Profile功能

可以使用:在哪个环境下生效。

<springProfile name="staging">
    <!‐‐ configuration to be enabled when the "staging" profile is active ‐‐>
   可以指定某段配置只在某个环境下生效  
</springProfile>

如:

<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">
            <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} ==== [%thread] ==== %‐5level %logger{50} ‐ %msg%n</pattern>
            </springProfile>
        </layout>
    </appender>

在dev环境下:使用上面的输出格式。

在非dev环境下:使用下面的输出格式。

现在运行Springboot项目:

发现,日志框架出现错误了。因为我们的日志配置文件的名字还没有改。

现在修改logback.xml —> logback-spring.xml.

再次启动Springboot应用:

发现,启动的是非dev环境的日志控制台输出格式。

我们在配置Springboot为dev环境。

1)application.properties.

spring.profiles.active=dev

2) 配置命令行参数:
--spring.profiles.active=dev

使用上面两种方法都可以改变Springboot的环境。

然后启动Springboot应用:

切换日志框架

切换成log4j

现在,我们使用的是slf4j+logback。但是我们想换成slf4j + log4j怎么办?

这里,说明一下,就是因为log4j有问题,所以才会推出logback日志框架的,这里只是做个例子演示日志框架的切换,实际情况下,直接使用logback日志框架就行。

切换日志框架,我们直接参考slf4j的官网的那个图,就行。

现在打开我们的依赖图层:

去掉图中画红色框框的logback的依赖。

快速移除依赖:在想要移除的依赖上面,右键Exclude

移除完毕后:

打开pom.xml文件:

现在需要添加log4j的依赖。

直接在pom.xml文件中添加:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log412</artifactId>
</dependency>

启动Springboot应用:

发出警告,因为在类路径下,没有log4j.properties的配置文件。

在resources下,添加log4j.properties的配置文件

### set log levels ###
log4j.rootLogger = debug ,  stdout ,  D ,  E

### 输出到控制台 ###
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.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
#log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

再次启动应用,就会打印出日志信息了。


切换成log4j2

在官网上面,我们能发现,Springboot默认是加载logback日志框架的,我们也可以直接改成log4j2日志框架。

在pom.xml文件中:

    <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>

直接移除spring‐boot‐starter‐logging依赖。然后添加spring‐boot‐starter‐log4j2的依赖。

然后添加log4j2-spring.xml 或者 log4j2.xml 日志配置文件。


  转载请注明: 解忧杂货店 SpringBoot_day_03

  目录