1. 程式人生 > >springboot中過濾器的使用(以logback-日誌記錄為例)

springboot中過濾器的使用(以logback-日誌記錄為例)

過濾器和攔截器的區別,過濾器和攔截器的用途,面試經常被問到:https://blog.csdn.net/qq_36411874/article/details/53996873,這篇部落格寫的很詳細。

使用攔截器記錄日誌的案例:https://blog.csdn.net/qq_31289187/article/details/83513290

本案例主要是實際開發中使用過濾器進行日誌記錄

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.1.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.cn.dl</groupId>
	<artifactId>springbootfilterdemo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>springbootfilterdemo</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

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

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

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.47</version>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

1、CommonConfig

package com.cn.dl.common;

/**
 * Created by Tiger on 2018/10/29.
 */
public interface CommonConfig {
    String START_TIME = "startTime";
    String IP = "ip";
    String CONSUME_TIME = "consumeTime";
    String REQ_PATH = "reqPath";
    String RES_BODY = "resBody";
    String LOG_PREFIX = "logData==";
    String TRACE_ID = "tarceId";
    String LOG_TYPE = "logType";
    String START = "start";
    String END = "end";
    String MODULE = "module";
    String SESSION_ID = "sessionId";
    String OPERATE_RESULT = "operateResult";
}

2、WebFilterDemo

package com.cn.dl.filter;

import com.cn.dl.common.CommonConfig;
import com.cn.dl.logtrace.LogTrace;
import org.jboss.logging.MDC;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.util.UUID;

/**
 * Created by yanshao on 2018/12/26.
 */
// TODO: 2018/12/26 如果不使用這個註解,可以用@Configuration中做一下配置
@WebFilter(urlPatterns = "/*")
public class WebFilterDemo implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("過濾器init>>>>>>>>>>>>>>>>>");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("過濾器doFilter>>>>>>>>>>>>>>>>>開始");

        LogTrace logTrace = new LogTrace();
        logTrace.generateTraceId();
        logTrace.printPreLog(servletRequest);
        filterChain.doFilter(servletRequest, servletResponse);
        logTrace.printAfterLog(servletRequest);

        System.out.println("過濾器doFilter>>>>>>>>>>>>>>>>>結束");
    }

    @Override
    public void destroy() {
        System.out.println("過濾器destroy>>>>>>>>>>>>>>>>>");
    }

}

3、LogTrace

package com.cn.dl.logtrace;

import com.alibaba.fastjson.JSONObject;
import com.cn.dl.common.CommonConfig;
import com.cn.dl.utils.NetUtils;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.scheduling.annotation.Async;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.UUID;

/**
 * Created by yanshao on 2018/12/26.
 */
public class LogTrace {

    private Logger logger = LoggerFactory.getLogger("outToFile");
    /**
     * 請求前日誌列印
     * @param request
     * */
    public void preLogTrace(HttpServletRequest request){
        Long startTime = System.currentTimeMillis();
        JSONObject json = new JSONObject();
        //使用者ip
        json.put(CommonConfig.IP, NetUtils.getClientIpAddress(request));
        //請求路徑
        json.put(CommonConfig.REQ_PATH,request.getRequestURI());
        //請求引數
        Map<String,String[]> map = request.getParameterMap();
        map.forEach((key,value) -> {
            json.put(key,request.getParameter(key));
        });
        //記錄請求開始時間
        request.setAttribute(CommonConfig.START_TIME,startTime);
        //traceId
        json.put(CommonConfig.TRACE_ID, MDC.get(CommonConfig.TRACE_ID));

        json.put(CommonConfig.LOG_TYPE,CommonConfig.START);

        logger.info(CommonConfig.LOG_PREFIX + json.toJSONString());
    }
    /**
     * 請求後日志列印
     * @param request
     * */
    public void afterLogTrace(HttpServletRequest request){
        JSONObject json = new JSONObject();
        Long startTime = (Long) request.getAttribute(CommonConfig.START_TIME);
        //請求耗時
        json.put(CommonConfig.CONSUME_TIME,System.currentTimeMillis() - startTime);
        //traceId
        json.put(CommonConfig.TRACE_ID,MDC.get(CommonConfig.TRACE_ID));
        //響應Data
        json.put(CommonConfig.RES_BODY,request.getAttribute(CommonConfig.RES_BODY));
        //日誌型別
        json.put(CommonConfig.LOG_TYPE,CommonConfig.END);

        logger.info(CommonConfig.LOG_PREFIX + json.toJSONString());
    }
    /**
     * 列印請求日誌
     * @param servletRequest
     * */
    @Async
    public void printPreLog(ServletRequest servletRequest){
        if(servletRequest instanceof HttpServletRequest){
            preLogTrace((HttpServletRequest) servletRequest);
        }
    }
    /**
     * 列印響應日誌
     * @param servletRequest
     * */
    @Async
    public void printAfterLog(ServletRequest servletRequest){
        if(servletRequest instanceof HttpServletRequest){
            afterLogTrace((HttpServletRequest) servletRequest);
        }
    }
    /**
     * 記錄traceId以及模組名
     * */
    public void generateTraceId(){
        org.jboss.logging.MDC.put(CommonConfig.TRACE_ID, UUID.randomUUID().toString().replace("-",""));
        //// TODO: 2018/12/26 模組名
        org.jboss.logging.MDC.put(CommonConfig.MODULE,"filterDemo");
    }
}

4、WebConfig

package com.cn.dl.config;

import com.cn.dl.filter.WebFilterDemo;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/**
 * Created by yanshao on 2018/12/26.
 */
@Configuration
public class WebConfig{

    @Bean
    public FilterRegistrationBean WebFilterDemo(){
        //// TODO: 2018/12/26 配置了過濾器
        FilterRegistrationBean frBean = new FilterRegistrationBean();
        frBean.setFilter(new WebFilterDemo());
        frBean.addUrlPatterns("/*");
        frBean.setName("webFilterDemo");
        return frBean;
    }
}

5、ControllerReponseAdvice

package com.cn.dl.logtrace;

import com.alibaba.fastjson.JSON;
import com.cn.dl.common.CommonConfig;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.servlet.http.HttpServletRequest;

/**
 * Created by Tiger on 2018/10/29.
 */
@ControllerAdvice
// TODO: 2018/12/19 控制器增強,在響應返回之前,可以獲取RequestMapping註解方法的返回結果
public class ControllerReponseAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(@Nullable Object obj, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        if (obj != null) {
            try {
                // TODO: 2018/12/26 在這裡可以修改返回引數
                HttpServletRequest servletRequest = ((ServletServerHttpRequest) serverHttpRequest).getServletRequest();
                servletRequest.setAttribute(CommonConfig.RES_BODY, JSON.toJSONString(obj));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return obj;
    }
}

6、NetIpUtils

package com.cn.dl.utils;

import javax.servlet.http.HttpServletRequest;

/**
 * 獲取使用者真實IP
 * Created by yanshao on 2018/10/29.
 */
public class NetIpUtils {
    private static final String[] HEADERS_TO_TRY = {
            "X-Forwarded-For",
            "Proxy-Client-IP",
            "WL-Proxy-Client-IP",
            "HTTP_X_FORWARDED_FOR",
            "HTTP_X_FORWARDED",
            "HTTP_X_CLUSTER_CLIENT_IP",
            "HTTP_CLIENT_IP",
            "HTTP_FORWARDED_FOR",
            "HTTP_FORWARDED",
            "HTTP_VIA",
            "REMOTE_ADDR"
    };
    /**
     * 獲取使用者真實IP地址
     * @param request
     * @return
     */
    public static String getClientIpAddress(HttpServletRequest request) {
        String rip = request.getRemoteAddr();
        for (String header : HEADERS_TO_TRY) {
            String ip = request.getHeader(header);
            if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
                rip = ip;
                break;
            }
        }
        int pos = rip.indexOf(',');
        if (pos >= 0) {
            rip = rip.substring(0, pos);
        }
        return rip;
    }
}

7、WebControllerDemo

package com.cn.dl.controller;

import com.alibaba.fastjson.JSONObject;
import com.cn.dl.common.CommonConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

import java.util.UUID;

/**
 * Created by yanshao on 2018/12/26.
 */
@RestController
@RequestMapping("/filter_demo")
public class WebControllerDemo {

    private Logger logger = LoggerFactory.getLogger("outToFile");


    @PostMapping("/register")
    public JSONObject register(@RequestParam("name") String name,@RequestParam("mobile") String mobile){
        System.out.println("註冊成功");
        JSONObject json = new JSONObject();
        json.put(CommonConfig.SESSION_ID,UUID.randomUUID().toString().replace("-",""));
        json.put(CommonConfig.OPERATE_RESULT,true);
        json.put("mobile",mobile);
        return json;
    }

}

8、logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds">
  <property name="logData" value="./logDatas" />
  <statusListener class="ch.qos.logback.core.status.NopStatusListener" />
  <appender name="CONSOLE_OUT" class="ch.qos.logback.core.ConsoleAppender">
    <layout class="ch.qos.logback.classic.PatternLayout">
      <pattern>%-15(%d{HH:mm:ss.SSS}) [%thread] %-5level %logger{80}[%line] -%msg%n</pattern>
    </layout>
  </appender>

  <appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
  <discriminator>
    <key>module</key>
    <defaultValue>module</defaultValue>
  </discriminator>
  <sift>
  <appender name="LOG_DEMO" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${logData}/logDataDemo-${module}.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${logData}/logDataDemo-%d{yyyy-MM-dd}.log</fileNamePattern>
      <maxHistory>30</maxHistory>
    </rollingPolicy>
    <encoder>
      <pattern>
        %-20(%d{yyyy-MMM-dd HH:mm:ss} [%thread]) %-5level %logger{80} [%line] -%msg%n
      </pattern>
      <charset>UTF-8</charset>
    </encoder>
  </appender>
  </sift>
  </appender>

  <root level="info">
    <appender-ref ref="CONSOLE_OUT" />
  </root>
  <!--輸出到指定日誌檔案-->
  <logger name="outToFile" level="INFO" additivity="false">
    <appender-ref ref="SIFT"/>
  </logger>
</configuration>

最後啟動,調介面127.0.0.1:8080/filter_demo/register,檢視列印日誌

19:50:24.701    [http-nio-8080-exec-10] INFO  org.springframework.web.servlet.DispatcherServlet[524] -Initializing Servlet 'dispatcherServlet'
19:50:24.707    [http-nio-8080-exec-10] INFO  org.springframework.web.servlet.DispatcherServlet[546] -Completed initialization in 6 ms
過濾器doFilter>>>>>>>>>>>>>>>>>開始
註冊成功
過濾器doFilter>>>>>>>>>>>>>>>>>結束
過濾器doFilter>>>>>>>>>>>>>>>>>開始
註冊成功
過濾器doFilter>>>>>>>>>>>>>>>>>結束
過濾器doFilter>>>>>>>>>>>>>>>>>開始
註冊成功
2018-十二月-26 19:49:32 [http-nio-8080-exec-10] INFO  outToFile [45] -logData=={"logType":"start","ip":"127.0.0.1","name":"tiger","mobile":"13299998888","reqPath":"/filter_demo/register","tarceId":"b28318c158414898b7c86cc9acc210e0"}
2018-十二月-26 19:49:32 [http-nio-8080-exec-10] INFO  outToFile [63] -logData=={"logType":"end","consumeTime":147,"resBody":"{\"operateResult\":true,\"mobile\":\"13299998888\",\"sessionId\":\"f627f02c7feb44dd9e5b1627c37587cc\"}","tarceId":"b28318c158414898b7c86cc9acc210e0"}
2018-十二月-26 19:49:33 [http-nio-8080-exec-7] INFO  outToFile [45] -logData=={"logType":"start","ip":"127.0.0.1","name":"tiger","mobile":"13299998888","reqPath":"/filter_demo/register","tarceId":"e8ce0c87651e4ea2a7a7e9ba7aaf99e6"}
2018-十二月-26 19:49:33 [http-nio-8080-exec-7] INFO  outToFile [63] -logData=={"logType":"end","consumeTime":4,"resBody":"{\"operateResult\":true,\"mobile\":\"13299998888\",\"sessionId\":\"7513c6fd73f24f8fbf264371d528cd27\"}","tarceId":"e8ce0c87651e4ea2a7a7e9ba7aaf99e6"}
2018-十二月-26 19:49:34 [http-nio-8080-exec-9] INFO  outToFile [45] -logData=={"logType":"start","ip":"127.0.0.1","name":"tiger","mobile":"13299998888","reqPath":"/filter_demo/register","tarceId":"ffb9285fdac94ca8a254da72e6ad4fd3"}
2018-十二月-26 19:49:34 [http-nio-8080-exec-9] INFO  outToFile [63] -logData=={"logType":"end","consumeTime":3,"resBody":"{\"operateResult\":true,\"mobile\":\"13299998888\",\"sessionId\":\"8eb5f0a104a54de4bbc3ae652a26a3ff\"}","tarceId":"ffb9285fdac94ca8a254da72e6ad4fd3"}
2018-十二月-26 19:49:34 [http-nio-8080-exec-8] INFO  outToFile [45] -logData=={"logType":"start","ip":"127.0.0.1","name":"tiger","mobile":"13299998888","reqPath":"/filter_demo/register","tarceId":"8775eb7639334967ae190373200dfcf6"}
2018-十二月-26 19:49:34 [http-nio-8080-exec-8] INFO  outToFile [63] -logData=={"logType":"end","consumeTime":2,"resBody":"{\"operateResult\":true,\"mobile\":\"13299998888\",\"sessionId\":\"9b41ca860ed9443d9e242fa8b6eb975f\"}","tarceId":"8775eb7639334967ae190373200dfcf6"}
2018-十二月-26 19:49:34 [http-nio-8080-exec-6] INFO  outToFile [45] -logData=={"logType":"start","ip":"127.0.0.1","name":"tiger","mobile":"13299998888","reqPath":"/filter_demo/register","tarceId":"f69bed589de64b518626db6e325f4d6a"}
2018-十二月-26 19:49:35 [http-nio-8080-exec-6] INFO  outToFile [63] -logData=={"logType":"end","consumeTime":5,"resBody":"{\"operateResult\":true,\"mobile\":\"13299998888\",\"sessionId\":\"31087530caea47edb02bb69a05277499\"}","tarceId":"f69bed589de64b518626db6e325f4d6a"}
2018-十二月-26 19:49:35 [http-nio-8080-exec-5] INFO  outToFile [45] -logData=={"logType":"start","ip":"127.0.0.1","name":"tiger","mobile":"13299998888","reqPath":"/filter_demo/register","tarceId":"b30d3f60994245ae8a5fe59331f3096b"}
2018-十二月-26 19:49:35 [http-nio-8080-exec-5] INFO  outToFile [63] -logData=={"logType":"end","consumeTime":4,"resBody":"{\"operateResult\":true,\"mobile\":\"13299998888\",\"sessionId\":\"2037904c9d6e4c0093e038e4b0e5866f\"}","tarceId":"b30d3f60994245ae8a5fe59331f3096b"}
2018-十二月-26 19:49:35 [http-nio-8080-exec-4] INFO  outToFile [45] -logData=={"logType":"start","ip":"127.0.0.1","name":"tiger","mobile":"13299998888","reqPath":"/filter_demo/register","tarceId":"23124e37cc0d4d1b83684fd9c0526f4e"}
2018-十二月-26 19:49:35 [http-nio-8080-exec-4] INFO  outToFile [63] -logData=={"logType":"end","consumeTime":2,"resBody":"{\"operateResult\":true,\"mobile\":\"13299998888\",\"sessionId\":\"d9d2e90f524c4ae0b721efbdb2e18b4d\"}","tarceId":"23124e37cc0d4d1b83684fd9c0526f4e"}