springboot使用拦截器记录请求日志

本文介绍如何在Spring Boot项目中使用MyBatis Plus代码生成器创建控制器、映射器、服务和实体类,同时实现HandlerInterceptor接口记录请求日志,包括请求参数、IP地址解析及日志持久化。
摘要由CSDN通过智能技术生成

1.创建数据库

2.引入相关依赖

<?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 https://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.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboothandlerIntercptor</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-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.0.5</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.72</version>
        </dependency>
        <!--解析IP为中国|华南|广东省|深圳市|-->
        <dependency>
            <groupId>org.lionsoul</groupId>
            <artifactId>ip2region</artifactId>
            <version>1.7.2</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
        <!--解析IP为中国|华南|广东省|深圳市|-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <!--尤其要注意:----后面在com.atguigu的包下,想用mapper.xml,要加这个不然会报错-->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3.去网上下载这个文件(必须要才能解析IP为省|市|县)https://github.com/lionsoul2014/ip2region/tree/master/data

4.利用mybatisplus代码生成器生成controller、mapper、service 、entity(代码生成器不了解可以看我博客前一章)

5.实现HandlerInterceptor接口,来对请求记录日志

package com.atguigu.loghandlerintercptor.intercptor;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.atguigu.loghandlerintercptor.entity.LoggerInfos;
import com.atguigu.loghandlerintercptor.service.impl.LoggerInfosServiceImpl;
import com.atguigu.loghandlerintercptor.util.RequestUtils;
import com.atguigu.loghandlerintercptor.util.SpringContextUtil;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;



public class LoggerIntercptor implements HandlerInterceptor {
    //请求开始时间标识
    private static final String LOGGER_SEND_TIME="send_time";
    //请求日志实体标识
    private static final String LOGGER_ENTITY="_logger_entity";
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("pre");
        //创建日志实体类
        LoggerInfos loggerInfos=new LoggerInfos();
        //获取session
//        HttpSession session=request.getSession();
//        String sessionid=session.getId();
        String sessionId = request.getRequestedSessionId();

        //获取请求路径
        String uri = request.getRequestURI();
        //请求参数信息
        String paramData = JSON.toJSONString(request.getParameterMap(),
                                    SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteMapNullValue);
        //设置客户端IP
        loggerInfos.setClientId(RequestUtils.getIpAddr(request));
        //设置客户端省市
        loggerInfos.setCityInfo(RequestUtils.getCityInfo(RequestUtils.getIpAddr(request)));
        //设置请求方法
        loggerInfos.setMethod(request.getMethod());
        //设置请求类型(json|普通请求)
        loggerInfos.setType(RequestUtils.getRequestType(request));
        //设置请求参数内容json字符串
        loggerInfos.setParamData(paramData);
        //设置请求地址
        loggerInfos.setUri(uri);
        //设置sessionid
        loggerInfos.setSessionId(sessionId);
        //设置请求开始时间
        long currentTimeMillis=System.currentTimeMillis();
        loggerInfos.setTime((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")).format(currentTimeMillis).toString());
        request.setAttribute(LOGGER_SEND_TIME,currentTimeMillis);
        //设置请求实体到request中,方便afterCompletion方法调用
        request.setAttribute(LOGGER_ENTITY,loggerInfos);
        return true;

    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("after");

        //获取请求错误码
        int status = response.getStatus();
        //当前时间
        long currentTimeMillis = System.currentTimeMillis();
        //获取请求开始时间
        long time = (long)request.getAttribute(LOGGER_SEND_TIME);
        //获取请求日志实体
        LoggerInfos loggerInfos = (LoggerInfos)request.getAttribute(LOGGER_ENTITY);
        //设置请求时间差
        loggerInfos.setTimeConsuming(Integer.valueOf((currentTimeMillis-time)+""));
        //设置返回时间
        loggerInfos.setReturnTime((new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS")).format(currentTimeMillis).toString());
        //设置返回错误码
        loggerInfos.setHttpStatusCode(status+"");
        //设置返回值
        loggerInfos.setReturnData(JSON.toJSONString(request.getAttribute(RequestUtils.LOGGER_RETURN),
                                SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteMapNullValue));
        System.out.println(loggerInfos);
        //执行将日志写入数据库
        LoggerInfosServiceImpl loggerInfosServiceImpl = (LoggerInfosServiceImpl) SpringContextUtil.getBean("loggerInfosServiceImpl");
        loggerInfosServiceImpl.save(loggerInfos);
    }
}

6.工具类RequestUtils(获取ip地址以及将ip转换为省|市|县)和 SpringContextUtil(获取bean)

package com.atguigu.loghandlerintercptor.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.lionsoul.ip2region.DataBlock;
import org.lionsoul.ip2region.DbConfig;
import org.lionsoul.ip2region.DbSearcher;
import org.lionsoul.ip2region.Util;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;

@Slf4j
public class RequestUtils {

    public static final String LOGGER_RETURN="logger_return";

    private static final String UNKNOWN = "unknown";

    /**
     * 获取 IP地址
     * 使用 Nginx等反向代理软件, 则不能通过 request.getRemoteAddr()获取 IP地址
     * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,
     * X-Forwarded-For中第一个非 unknown的有效IP字符串,则为真实IP地址
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }
    public static String getRequestType(HttpServletRequest request) {
        //获取头部信息
        String xRequestedWith = request.getHeader("X-Requested-With");
        if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1) {
            return "json";
        } else {
            return "普通请求";
        }
    }


    public static String getCityInfo(String ip) {
        DbSearcher searcher = null;
        try {
            //这个/ip2region/ip2region.db文件在类路径下
            String dbPath = RequestUtils.class.getResource("/ip2region/ip2region.db").getPath();
            File file = new File(dbPath);
            if (!file.exists()) {
                //java.io.tmpdir:获取操作系统缓存的临时目录
                String tmpDir = System.getProperties().getProperty("java.io.tmpdir");
                dbPath = tmpDir + "ip.db";
                file = new File(dbPath);
                FileUtils.copyInputStreamToFile(RequestUtils.class.getClassLoader().getResourceAsStream("classpath:ip2region/ip2region.db"), file);
            }
            int algorithm = DbSearcher.BTREE_ALGORITHM;
            DbConfig config = new DbConfig();
            searcher = new DbSearcher(config, dbPath);
            Method method = null;
            switch (algorithm) {
                case DbSearcher.BTREE_ALGORITHM:
                    method = searcher.getClass().getMethod("btreeSearch", String.class);
                    break;
                case DbSearcher.BINARY_ALGORITHM:
                    method = searcher.getClass().getMethod("binarySearch", String.class);
                    break;
                case DbSearcher.MEMORY_ALGORITYM:
                    method = searcher.getClass().getMethod("memorySearch", String.class);
                    break;
            }
            DataBlock dataBlock = null;
            if (!Util.isIpAddress(ip)) {
                log.error("Error: Invalid ip address");
            }
            dataBlock = (DataBlock) method.invoke(searcher, ip);
            return dataBlock.getRegion();
        } catch (Exception e) {
            log.error("获取IP地址失败,{}", e.getMessage());
        } finally {
            if (searcher != null) {
                try {
                    searcher.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

}
---------------------------------------------------------------------------------------------------
package com.atguigu.loghandlerintercptor.util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * Spring Context 工具类
 *
 * @author MrBird
 *
 */
@Component
public class SpringContextUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextUtil.applicationContext = applicationContext;
    }

    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }
    public static <T> T getBean(Class<T> clazz){
        return applicationContext.getBean(clazz);
    }

    public static <T> T getBean(String name, Class<T> requiredType) {
        return applicationContext.getBean(name, requiredType);
    }

    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }

    public static boolean isSingleton(String name) {
        return applicationContext.isSingleton(name);
    }

    public static Class<?> getType(String name) {
        return applicationContext.getType(name);
    }

}

7.在http://localhost:8080/swagger-ui.html中向这个controller发送请求

package com.atguigu.loghandlerintercptor.controller;


import com.alibaba.fastjson.JSONObject;
import com.atguigu.loghandlerintercptor.util.RequestUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author testjava
 * @since 2020-07-09
 */
@RestController
@RequestMapping("/index")
public class LoggerInfosController {


    @GetMapping("/login")
    public JSONObject login(HttpServletRequest request, String name){
        JSONObject obj=new JSONObject();
        obj.put("msg","用户:"+name+"登录成功");
        //将返回值写入到请求中
        request.setAttribute(RequestUtils.LOGGER_RETURN,obj);
        return obj;
    }
}

结果:

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值