日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無(wú)先后,達(dá)者為師

網(wǎng)站首頁(yè) 編程語(yǔ)言 正文

[springboot]攔截器實(shí)現(xiàn)統(tǒng)一訪問(wèn)日志

作者:字母哥哥 更新時(shí)間: 2022-05-11 編程語(yǔ)言


文章目錄

    • 一、需求
    • 二、定義訪問(wèn)日志內(nèi)容記錄實(shí)體類(lèi)
    • 三、自定義日志攔截器
    • 四、攔截器注冊(cè)
    • 五、獲取ip訪問(wèn)地址的工具類(lèi)
    • 六、"ACCESS-LOG"的日志Logger定義

一、需求

我們本節(jié)要實(shí)現(xiàn)的需求

  • 針對(duì)當(dāng)前系統(tǒng)的每一次接口訪問(wèn),要記錄是什么人訪問(wèn)的(用戶名)、什么時(shí)間訪問(wèn)的、訪問(wèn)耗時(shí)多長(zhǎng)時(shí)間、使用什么HTTP method方法訪問(wèn)的、訪問(wèn)結(jié)果如何等。可以稱(chēng)為審計(jì)日志。
  • 將訪問(wèn)記錄審計(jì)日志,輸出到一個(gè)單獨(dú)的日志文件access.log

二、定義訪問(wèn)日志內(nèi)容記錄實(shí)體類(lèi)

@Data
public class AccessLog {
    //訪問(wèn)者用戶名
    private String username;
    //請(qǐng)求路徑
    private String url;
    //請(qǐng)求消耗時(shí)長(zhǎng)
    private Integer duration;
    //http 方法:GET、POST等
    private String httpMethod;
    //http 請(qǐng)求響應(yīng)狀態(tài)碼
    private Integer httpStatus;
    //訪問(wèn)者ip
    private String ip;
    //此條記錄的創(chuàng)建時(shí)間
    private Date createTime;
}

三、自定義日志攔截器

通過(guò)自定義攔截器的方式,記錄審計(jì)日志。

  • 攔截器的preHandle方法,可以用于攔截請(qǐng)求處理開(kāi)始。用于記錄請(qǐng)求開(kāi)始時(shí)間等信息保存到Http Request,用于后續(xù)計(jì)算請(qǐng)求時(shí)長(zhǎng)。
  • 攔截器的postHandle方法,可以用于攔截請(qǐng)求處理完成。可以從Request對(duì)象獲取開(kāi)始時(shí)間,計(jì)算本次請(qǐng)求總的處理時(shí)長(zhǎng)等信息。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;


public class AccessLogInterceptor implements HandlerInterceptor {
    //請(qǐng)求開(kāi)始時(shí)間標(biāo)識(shí)
    private static final String LOGGER_SEND_TIME = "SEND_TIME";
    //請(qǐng)求日志實(shí)體標(biāo)識(shí)
    private static final String LOGGER_ACCESSLOG = "ACCESSLOG_ENTITY";


    private static final Logger logger = LoggerFactory.getLogger("ACCESS-LOG");

    /**
     * 進(jìn)入SpringMVC的Controller之前開(kāi)始記錄日志實(shí)體
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {

        //創(chuàng)建日志實(shí)體
        AccessLog accessLog = new AccessLog();

        // 設(shè)置IP地址
        accessLog.setIp(AdrressIpUtils.getIpAdrress(request));

        //設(shè)置請(qǐng)求方法,GET,POST...
        accessLog.setHttpMethod(request.getMethod());

        //設(shè)置請(qǐng)求路徑
        accessLog.setUrl(request.getRequestURI());

        //設(shè)置請(qǐng)求開(kāi)始時(shí)間
        request.setAttribute(LOGGER_SEND_TIME,System.currentTimeMillis());

        //設(shè)置請(qǐng)求實(shí)體到request內(nèi),方便afterCompletion方法調(diào)用
        request.setAttribute(LOGGER_ACCESSLOG,accessLog);
        return true;
    }


    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) throws Exception {

        //獲取本次請(qǐng)求日志實(shí)體
        AccessLog accessLog = (AccessLog) request.getAttribute(LOGGER_ACCESSLOG);

        //獲取請(qǐng)求錯(cuò)誤碼,根據(jù)需求存入數(shù)據(jù)庫(kù),這里不保存
        int status = response.getStatus();
        accessLog.setHttpStatus(status);

        //設(shè)置訪問(wèn)者(這里暫時(shí)寫(xiě)死)
        // 因?yàn)椴煌膽?yīng)用可能將訪問(wèn)者信息放在session里面,有的通過(guò)request傳遞,
        // 總之可以獲取到,但獲取的方法不同
        accessLog.setUsername("admin");

        //當(dāng)前時(shí)間
        long currentTime = System.currentTimeMillis();

        //請(qǐng)求開(kāi)始時(shí)間
        long snedTime = Long.valueOf(request.getAttribute(LOGGER_SEND_TIME).toString());


        //設(shè)置請(qǐng)求時(shí)間差
        accessLog.setDuration(Integer.valueOf((currentTime - snedTime)+""));

        accessLog.setCreateTime(new Date());
        //將sysLog對(duì)象持久化保存
        logger.info(accessLog.toString());
    }
}

上文中LoggerFactory.getLogger("ACCESS-LOG")獲取一個(gè)日志配置中的Logger的名字,用于打印日志輸出,持久化到日志文件里面。"ACCESS-LOG"的日志Logger定義我們下文中介紹。

四、攔截器注冊(cè)

攔截器注冊(cè)就不細(xì)講了,前面的章節(jié)已經(jīng)介紹過(guò),回看。

@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {

    //設(shè)置排除路徑,spring boot 2.*,注意排除掉靜態(tài)資源的路徑,不然靜態(tài)資源無(wú)法訪問(wèn)
    private final String[] excludePath = {"/static"};

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AccessLogInterceptor()).addPathPatterns("/**").excludePathPatterns(excludePath);
    }
}

五、獲取ip訪問(wèn)地址的工具類(lèi)

上面的代碼中涉及到一個(gè)工具類(lèi),用于獲取請(qǐng)求客戶端的ip。代碼如下:

public class AdrressIpUtils {
    /**
     * 獲取Ip地址
     * @param request HttpServletRequest
     * @return ip
     */
    public static String getIpAdrress(HttpServletRequest request) {
        String Xip = request.getHeader("X-Real-IP");
        String XFor = request.getHeader("X-Forwarded-For");
        if(StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)){
            //多次反向代理后會(huì)有多個(gè)ip值,第一個(gè)ip才是真實(shí)ip
            int index = XFor.indexOf(",");
            if(index != -1){
                return XFor.substring(0,index);
            }else{
                return XFor;
            }
        }
        XFor = Xip;
        if(StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)){
            return XFor;
        }
        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("HTTP_CLIENT_IP");
        }
        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
            XFor = request.getRemoteAddr();
        }
        return XFor;
    }
}

六、"ACCESS-LOG"的日志Logger定義

下文中的配置參考以Log4J2配置為例(參考上一節(jié)的內(nèi)容進(jìn)行學(xué)習(xí))
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-y0r1Nn7P-1644633419822)(images/screenshot_1598061650386.png)]

  • LoggerFactory.getLogger("ACCESS-LOG") 代碼去配置文件里面找一個(gè)name為ACCESS-LOG的Logger配置。
  • 該Logger是一個(gè)AsyncLogger,指向的輸出目標(biāo)是ACCESS-APPENDER
  • ACCESS-APPENDER是一個(gè)日志文件輸出配置,日志文件是access-log.log

<configuration>
    <properties>
        
        <property name="LOG_HOME">D:/logsproperty>
    properties>
    <Appenders>
        
        <RollingFile name="ACCESS-APPENDER"
                     fileName="${LOG_HOME}/access.log"
                     filePattern="${LOG_HOME}/access-%d{yyyy-MM-dd}-%i.log">
            
            <PatternLayout>
                <pattern>[%d][%p][%t][%C] %m%npattern>
            PatternLayout>
            <Policies>
                
                <SizeBasedTriggeringPolicy size="100MB"/>
                <TimeBasedTriggeringPolicy/>
            Policies>
            
            <DefaultRolloverStrategy max="20"/>
        RollingFile>
    Appenders>

    <Loggers>
        <AsyncLogger name="ACCESS-LOG" level="debug" additivity="false">
            <AppenderRef ref="ACCESS-APPENDER" level="info"/>
        AsyncLogger>
    Loggers>
configuration>


原文鏈接:https://blog.csdn.net/hanxiaotongtong/article/details/122893220

欄目分類(lèi)
最近更新