java-框架logback

介绍

Logback Manual

 三个模块

  1. logback-core模块为其他两个模块奠定了基础
  2. logback-classic模块可以被同化为log4j的显着改进版本。logback-classic本身实现了SLF4J API,因此您可以在logback和其他日志框架(如log4j或java.util.logging(JUL))之间来回切
  3. logback-access模块与Servlet容器(如Tomcat和Jetty)集成,以提供HTTP访问日志功能可以在logback-core之上轻松构建自己的模块

核心对象

  1. Logger:日志的记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。Logger对象一般多定义为静态常量.
  2. Appender:用于指定日志输出的目的地,目的地可以是控制台、文件、远程套接字服务器、 MySQL、 PostreSQL、Oracle和其他数据库、邮箱、 JMS和远程UNIX Syslog守护进程等。
  3. Layout:负责把事件转换成字符串,格式化的日志信息的输

pom

 springBoot

默认引入了 logback框架

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

传统

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.3.0-alpha4</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>1.3.0-alpha4</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-access</artifactId>
    <version>1.3.0-alpha4</version>
</dependency>

配置

模板

<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://ch.qos.logback/xml/ns/logback"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback
        https://raw.githubusercontent.com/enricopulatzo/logback-XSD/master/src/main/xsd/logback.xsd">
    <property name="SERVICE_NAME" value="metadataServer"/>
    <property name="BASE_LOG_PATH" value="logs" />
    <property name="pattern" value="%d{HH:mm:ss.SSS} [%t] %-3le %c{20} - [%M,%L] - %m%n %ex{full}" />
    <property name="pattern-color" value="[%X{REQUEST_ID}] %yellow(%d{HH:mm:ss.SSS}) [%t] %highlight(%-2le) %green(%c{50}) - [%M,%L] - %highlight(%m) %n %ex{full}"/>
    <property name="CHARSET" value="utf-8"/>
    <property name="MAX_FILE_SIZE" value="200Mb"/>
    <property name="MAX_HISTORY" value="90"/>

<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>${pattern-color}}</pattern>
        <charset>UTF-8</charset>
    </encoder>
</appender>

<appender name="info" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <encoder>
        <pattern>${pattern}</pattern>
        <charset>${CHARSET}</charset>
    </encoder>
    <file>${BASE_LOG_PATH}/${SERVICE_NAME}Info/${SERVICE_NAME}-info.log</file>

    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>${BASE_LOG_PATH}/${SERVICE_NAME}Info/${SERVICE_NAME}-info-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
        <maxFileSize>200MB</maxFileSize>
        <maxHistory>90</maxHistory>
        <totalSizeCap>5GB</totalSizeCap>
    </rollingPolicy>
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>INFO</level>
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
</appender>

<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>${pattern}</pattern>
            <charset>${CHARSET}</charset>
        </encoder>
        <file>${BASE_LOG_PATH}/${SERVICE_NAME}Error/${SERVICE_NAME}-error.log</file>

        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${BASE_LOG_PATH}/${SERVICE_NAME}Error/${SERVICE_NAME}-error-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <maxFileSize>200MB</maxFileSize>
            <maxHistory>90</maxHistory>
            <totalSizeCap>5GB</totalSizeCap>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
</appender>
<!-- additivity =
这里配置不会经过root-->
<logger name="com.du.controller" level="info" additivity="false">
    <appender-ref ref="console"/>
    <appender-ref ref="info"/>
    <appender-ref ref="error"/>
</logger>

<root level="info">
    <appender-ref ref="console"/>
    <appender-ref ref="info"/>
    <appender-ref ref="error"/>
</root>

</configuration>

文件位置

logback-spring.xml

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

如果使用logback.xml作为日志配置文件,还要使用profile功能,会有以下错误

no applicable action for [springProfile]

 <configuration>

Chapter 3: Configuration

<configuration scan="true" scanPeriod="60 seconds" debug="false"> 
    <!--其他配置省略--> 
</configuration> 
  1. scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
  2. scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟
  3. debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false

property 定义公共的属性

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
   <property name="SERVICE_NAME" value="metadataServer"/>
   <property name="BASE_LOG_PATH" value="logs" />
   <property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
   <property name="pattern-color" value="%yellow(%d{yyyy-MM-dd HH:mm:ss.SSS}) [%thread] %highlight(%-5level) %green(%logger{50}) - [%method,%line]  - %highlight(%msg) %n"/>
   <property name="CHARSET" value="utf-8"/>
   <property name="MAX_FILE_SIZE" value="10Mb"/>
   <property name="MAX_HISTORY" value="1"/>
</configuration>

Appender

Chapter 4: Appenders

负责写日志的组件,它有两个必要属性name和class。name指定appender名称,class指定appender的全限定名

ConsoleAppender 

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
   <property name="pattern-color" value="%yellow(%d{yyyy-MM-dd HH:mm:ss.SSS}) [%thread] %highlight(%-5level) %green(%logger{50}) - [%method,%line]  - %highlight(%msg) %n"/>
   <property name="CHARSET" value="utf-8"/>

   <!-- 控制台输出 -->
   <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
      <encoder>
         <pattern>${pattern-color}</pattern>
         <charset>${CHARSET}</charset>
      </encoder>
   </appender>
   <!--系统操作日志-->
    <root level="debug">
        <appender-ref ref="console" />
    </root>
</configuration>

FileAppender

  1. <file>:被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值
  2. <append>:如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true
  3. <prudent>:如果是 true,日志会被安全的写入文件,即使其他的FileAppender也在向此文件做写入操作,效率低,默认是 false
<configuration> 
  <appender name="FILE" class="ch.qos.logback.core.FileAppender"> 
    <file>testFile.log</file> 
    <append>true</append> 
    <encoder> 
       <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> 
    </encoder> 
  </appender> 
 <root level="DEBUG"> 
    <appender-ref ref="FILE" /> 
 </root> 
</configuration>

RollingFileAppender

滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件

  1. <file>:被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值
  2. <append>:如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true
  3. <rollingPolicy>:当发生滚动时,决定RollingFileAppender的行为,涉及文件移动和重命名。属性class定义具体的滚动策略类
SizeAndTimeBasedRollingPolicy
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${BASE_LOG_PATH}/${SERVICE_NAME}Info/${SERVICE_NAME}-info.log</file>
   <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
      <fileNamePattern>${BASE_LOG_PATH}/${SERVICE_NAME}Info/${SERVICE_NAME}-info.log.%d{yyyy-MM-dd}.log.%i.gz</fileNamePattern>
      <maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
      <maxHistory>${MAX_HISTORY}</maxHistory>
      <totalSizeCap>100MB</totalSizeCap>
   </rollingPolicy>
   <encoder>
      <pattern>${pattern}</pattern>
      <charset>${CHARSET}</charset>
   </encoder>
   <filter class="ch.qos.logback.classic.filter.LevelFilter">
           <level>INFO</level>
           <onMatch>ACCEPT</onMatch>
           <onMismatch>DENY</onMismatch>
       </filter>
</appender>
ch.qos.logback.core.rolling.TimeBasedRollingPolicy

fileNamePattern:日志归档文件名

maxHistory:日志存活时间,大于这个时间的日志都将会删除 单位为天

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

   <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>${BASE_LOG_PATH}/${SERVICE_NAME}Info/${SERVICE_NAME}-info.log</file>
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
         <fileNamePattern>${BASE_LOG_PATH}/${SERVICE_NAME}Info/${SERVICE_NAME}-info.log.%d{yyyy-MM-dd}.log.%i.gz</fileNamePattern>
          <maxHistory>${MAX_HISTORY}</maxHistory>
         <totalSizeCap>100MB</totalSizeCap>
         <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> 
                <maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
         </timeBasedFileNamingAndTriggeringPolicy>
      </rollingPolicy>
      <encoder>
         <pattern>${pattern}</pattern>
         <charset>${CHARSET}</charset>
      </encoder>
      <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
   </appender>
</configuration>

logger

这里配置不会经过root-->

<logger name="com.du.controller" level="info" additivity="false">
  <appender-ref ref="console"/>
  <appender-ref ref="info"/>
  <appender-ref ref="error"/>
</logger>

 Encoder

对日志进行格式化

<encoder>
	<pattern>${pattern-color}</pattern>
	<charset>${CHARSET}</charset>
</encoder>

root

<root level="info">
  <appender-ref ref="console"/>
  <appender-ref ref="info"/>
  <appender-ref ref="error"/>
</root>

AsyncAppender

<configuration>
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>myapp.log</file>
    <encoder>
      <pattern>%logger{35} -%kvp -%msg%n</pattern>
    </encoder>
  </appender>

  <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="FILE" />
  </appender>

  <root level="DEBUG">
    <appender-ref ref="ASYNC" />
  </root>
</configuration>

  1. 记录异步日志撑爆内存;
  2. 记录异步日志出现日志丢失;
  3. 记录异步日志出现阻塞。

默认队列大小为 256,达到 80% 容量后开始丢弃 <=INFO 级别的日志后

queueSize 设置得特别大,就可能会导致 OOM。

queueSize 设置得比较小(默认值就非常小),且 discardingThreshold 设置为大于 0 的值(或者为默认值),队列剩余容量少于 discardingThreshold 的配置就会丢弃 <=INFO 的日志。这里的坑点有两个。一是,因为 discardingThreshold 的存在,设置queueSize 时容易踩坑。比如,本例中最大日志并发是 1000,即便设置 queueSize 为 1000 同样会导致日志丢失。二是,discardingThreshold 参数容易有歧义,它不是百分比,而是日志条数。对于总容量 10000 的队列,如果希望队列剩余容量少于 1000 条的

时候丢弃,需要配置为 1000。

neverBlock 默认为 false,意味着总可能会出现阻塞。如果 discardingThreshold 为0,那么队列满时再有日志写入就会阻塞;如果 discardingThreshold 不为 0,也只会丢弃 <=INFO 级别的日志,那么出现大量错误日志时,还是会阻塞程序。

可以看出 queueSize、discardingThreshold 和 neverBlock 这三个参数息息相关,务必按需进行设置和取舍,到底是性能为先,还是数据不丢为先:

如果考虑绝对性能为先,那就设置 neverBlock 为 true,永不阻塞。

如果考虑绝对不丢数据为先,那就设置 discardingThreshold 为 0,即使是 <=INFO 的级别日志也不会丢,但最好把 queueSize 设置大一点,毕竟默认的 queueSize 显然太小,太容易阻塞。

如果希望兼顾两者,可以丢弃不重要的日志,把 queueSize 设置大一点,再设置一个合理的 discardingThreshold。

 MDC

Mapped Diagnostic Context,可以粗略的理解成是一个线程安全的存放诊断日志的容器

接口

  1. put
  2. get
  3. remove
  4. clear

用途

  1. 在WEB应用中,如果想在日志中输出请求用户IP地址、请求URL、统计耗时等等
  2. MDC基本都能支撑可以在MDC中填充REQUEST ID追踪单个请求的执行轨迹
  3. 微服务场景下,使用MDC埋点,做到链路跟踪
  4. 最好是有日志收集工具、将多实例、多系统的日志实现收集,再根据MDC埋点,进行grep,就可以打印一个完整的微服务请求链路

微服务日志链路

分布式日志链路

  1. 搞清楚当前工程的对于普通的web应用来说,给每个请求添加一条标识符(例如UUID),可以快速的grep到某一个请求的完整链路
  2. 对于分布式应用来说,一个链路会经过多个系统,此时也可以利用MDC实现多个系统日志的“拼接”

MDC-使用

启用

日志配置文件

[%X{REQUEST_ID}]

<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder charset="UTF-8">
	<pattern>[%X{REQUEST_ID}] %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %L - %msg%n</pattern>
</encoder>
</appender>

拦截器

public class TraceIdInterceptor implements HandlerInterceptor {
    private static final String FLAG = "REQUEST_ID";
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        // 清理之前的请求 id
        MDC.clear();
        String traceId = request.getHeader(FLAG);
        if (StrUtil.isEmpty(traceId)) {
            if (null == MDC.get(FLAG)) {
                MDC.put(FLAG, IdUtil.fastSimpleUUID());
            }
        } else {
            MDC.put(FLAG, traceId);
        }
        return true;
    }
}
@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TraceIdInterceptor()).addPathPatterns("/**").order(0);
    }
}

restTemplate配置

@Configuration
public class MyRestTemplateConfig {
    /**
     * 请求链路的id
     */
    private static final String FLAG = "REQUEST_ID";

    /**
     * 远程调用,添加请求id,形成请求链路可追溯
     * @return RestTemplate
     */
    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        List<ClientHttpRequestInterceptor> list = new ArrayList<>();
        list.add(((request, body, execution) -> {
            String traceId = MDC.get(FLAG);
            if (StrUtil.isNotEmpty(traceId)) {
                request.getHeaders().add(FLAG, traceId);
            }
            return execution.execute(request, body);
        }));
        restTemplate.setInterceptors(list);
        return restTemplate;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值