首页 > 其他分享 >操作日志记录

操作日志记录

时间:2025-01-06 16:33:56浏览次数:7  
标签:null log 记录 操作 日志 operationLog public 请求

操作日志

一、基础准备

(存储操作日志建议不要存储数据库,用户量和访问量大对性能影响很大,使用 ``logback-spring`把日志写进文件已经够用了,日志输出尽量详细点,直接下载日志文件就可以了)

使用的操作记录日志表的 建表SQL

DROP TABLE IF EXISTS `t_operation_log`;
CREATE TABLE `t_operation_log`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `operation_user_id` int(11) NULL DEFAULT NULL COMMENT '操作人ID',
  `operation_username` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作人名称',
  `operation_module` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '操作模块',
  `operation_events` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '具体操作事件',
  `operation_url` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '操作url',
  `operation_data` varchar(3048) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作附带数据',
  `operation_status` tinyint(1) NOT NULL COMMENT '操作是否正常,1正常操作, 0 操作异常',
  `operation_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作所在IP',
  `operation_result` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作结果',
  `add_time` datetime(0) NOT NULL COMMENT '操作时间',
  `deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '1 删除,0 未删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 32 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '操作日志表' ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;  -- 开启外键约束 --

在项目中编写其实体类

在这个实体类之中,写了私有化的构造函数,不允许 new 来创建对象。

可以 减少了数据冲突和不一致的可能性。 (目前就了解这么多,嘿嘿!)

@Data
@TableName("t_operation_log")
@ApiModel(value = "OperationLog对象", description = "操作日志表")
public class OperationLog implements Serializable,Cloneable {

    private static final long serialVersionUID = 1L;

    /**
     * 实现 Cloneable 克隆拷贝
     * 创建一个 默认 对象,用于作为克隆的源数据
     */
    private static final OperationLog log = new OperationLog();

    /**
     * 获取克隆对象, 避免new的方式创建
     * @return {@link OperationLog}
     */
    public static OperationLog getInstance(){
        try {
            return (OperationLog) log.clone();
        } catch (CloneNotSupportedException e) {
            return new OperationLog();
        }
    }

    /**
     * 重写克隆方法
     * @return {@link OperationLog}
     */
    public OperationLog clone() throws CloneNotSupportedException {
        return (OperationLog) super.clone();
    }
    /**
     * 私有化构造函数,不允许 new
     */
    private OperationLog(){
        this.deleted = false;
    }

    @TableId(type = IdType.AUTO)
    private Integer id;

    @ApiModelProperty("操作人ID")
    private Integer operationUserId;

    @ApiModelProperty("操作人名称")
    private String operationUsername;


    @ApiModelProperty("操作模块")
    private String operationModule;

    @ApiModelProperty("具体操作事件")
    private String operationEvents;

    @ApiModelProperty("操作Url")
    private String operationUrl;

    @ApiModelProperty("操作附带数据")
    private String operationData;

    @ApiModelProperty("操作是否正常,1正常操作, 0 操作异常")
    private Boolean operationStatus;

    @ApiModelProperty("操作结果")
    private String operationResult;

    @ApiModelProperty("操作所在IP")
    private String operationIp;

    @ApiModelProperty("操作时间")
    private LocalDateTime addTime;

    @ApiModelProperty("1 删除,0 未删除")
    private Boolean deleted;


}

二、创建日志注解

这里就简单创建了一个注解,注解的值的部分比较简单,在学习和复习的时候,可以学习 若以框架的注解,里面的内容比较丰富和完善。但是丰富完善的内容就需要在 切面类中,进行一些情况上面的判断。

@Target({ElementType.METHOD}) //注解在方法上
@Retention(RetentionPolicy.RUNTIME) //运行时 ,该注解生效
public @interface OperationLogDesc {

    /**
     * 操作模块
     */
    String module();

    /**
     * 操作事件
     */
    String events();

}

若依框架自定义日志注解

/**
 * 自定义操作日志记录注解
 * 
 * @author ruoyi
 *
 */
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
    /**
     * 模块
     */
    public String title() default "";

    /**
     * 功能
     */
    public BusinessType businessType() default BusinessType.OTHER;

    /**
     * 操作人类别
     */
    public OperatorType operatorType() default OperatorType.MANAGE;

    /**
     * 是否保存请求的参数
     */
    public boolean isSaveRequestData() default true;

    /**
     * 是否保存响应的参数
     */
    public boolean isSaveResponseData() default true;

    /**
     * 排除指定的请求参数
     */
    public String[] excludeParamNames() default {};
}

三、使用注解

	@RequestMapping("/info")
    @OperationLogDesc(module = "测试——学生信息查询" , events = "学生信息-查询")  //!!!!!!!!!!!!!!
    public R info(String id) {
        Students stu = studentService.getinfo(id);
        return R.success(stu);
    }

四、切面实现

因为我们要使用的是 aop 的切面类来实现相关功能,所以,要引入 aop 的依赖

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

下面就是最最最最最最最重要的部分了!!!!!!!!!

1、代码总览

@Aspect //声明是 aspect 切面类
@Component //声明是 spring 容器中的 bean
@Slf4j  // 日志
public class LoggerAspect {

    @Autowired
    public OperationLogService operationLogService;

    /**
     * FastThreadLocal 依赖于  netty,如果不想用netty,可以使用jdk自带的 ThreadLocal
     */
    final ThreadLocal<OperationLog> logThreadLocal = new ThreadLocal<>();
    final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");

    /**
     * 切点
     * 所有 com.*.*.controller 包下的所有方法
     */
    @Pointcut("execution(* com.*.*.controller.*.*(..))") // 切点 所有 com.*.*.controller 包下的所有方法
    public void logPointcut() {
    }


    /**
     * 请求前置通知
     * 切点 执行前
     *
     * @param joinPoint 切点
     */
    @Before("logPointcut()")
    public void beforeLog(JoinPoint joinPoint) throws NoSuchMethodException {

        // 获取请求参数
        //将方法调用时传入的参数 joinPoint 转换为字符串形式
        String params = Arrays.toString(joinPoint.getArgs());

        // 鉴权会话获取 当前登录的用户信息,我用的是 shiro,根据情况改变
//        Subject currentUser = SecurityUtils.getSubject();
//        User user = (User)currentUser.getPrincipal();
//        Integer userId = null;
//        String userName = null;
//        if(User!= null){
//            userId = User.getId();
//            userName = User.getUsername();
//        }else{
//            /*
//              因为登录接口没有登录会话状态,无法获取到用户信息,从 登录请求参数中获取 登录用户名
//              @see 例如:登录请求肯定是 post请求,上方 "params" 参数已经获取到 请求参数信息,只要判断里面是否有用户名信息 验证是否为登录接口,然后字符串截取获取用户名。。这个方法是我能想到最快捷的
//              示例登录接口参数:我的登录 请求json [LoginDTO(username=1001010, password=132456,code='A5C5')]
//             */
//            if(params.contains("username=")){
//                userName = params.substring(params.indexOf("username=") + 9, params.indexOf(", password="));
//                // 登录参数密码 简单脱密一下
//                params = params.replace("password=", "changshayueluqu_"); //将password= 替换为 changshayueluqu_
//            }
//        }
        LocalDateTime now = LocalDateTime.now();
        log.info("--------请求前置日志输出开始--------");
        /**RequestContextHolder 用于获取当前请求的上下文信息
         * getRequestAttributes()方法返回一个RequestAttributes对象,这里通过强制类型转换将其转换为ServletRequestAttributes。
         * ServletRequestAttributes是RequestAttributes的一个实现类,专门用于处理基于 Servlet 的请求上下文。
         */
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        /**
         * 由于getRequestAttributes()方法可能返回null(例如在非 Web 请求的上下文中调用)
         * 这里使用Objects.requireNonNull()方法确保attributes不为null
         * 如果attributes不为null,则调用其getRequest()方法获取HttpServletRequest对象
         * 这个对象包含了与当前 HTTP 请求相关的所有信息。
         */
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        log.info("请求访问时间: {}", dateTimeFormatter.format(now));
        // 获取请求url
        String requestUrl = request.getRequestURL().toString();
        log.info("请求url: {}", requestUrl);
        // 获取method
        log.info("请求方式: {}", request.getMethod());
        log.info("请求参数列表: {}", params);
        log.info("操作人ID: {}", 33333);


        // 验证请求方法是否带有操作日志注解
        /**
         * joinPoint.getSignature() 获取与当前连接点(被拦截的方法调用)相关的签名信息。
         * MethodSignature 是一个接口,它表示一个方法签名。
         * 获取实际调用的 method 对象,该对象包括了 方法名、参数列表、返回类型等。
         */
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        /**
         * method.getAnnotation(OperationLogDesc.class) 获取当前方法上带有 OperationLogDesc 注解的实例。
         */
        OperationLogDesc operationLogDesc = method.getAnnotation(OperationLogDesc.class);
        if (operationLogDesc != null) {

            // 操作日志记录
            OperationLog operationLog = OperationLog.getInstance();
            operationLog.setAddTime(now);
            operationLog.setOperationModule(operationLogDesc.module());
            operationLog.setOperationEvents(operationLogDesc.events());
            operationLog.setOperationData(params);
            operationLog.setOperationUrl(requestUrl);
            // 操作人ID
            operationLog.setOperationUserId(666);
            operationLog.setOperationUsername("666");

            // IP地址
            operationLog.setOperationIp(IpUtil.getIpAddr(request));
            logThreadLocal.set(operationLog);
        }
    }

    /**
     * 请求后置通知,请求完成会进入到这个方法
     *
     * @param result 响应结果json
     */
    @AfterReturning(value = "logPointcut()", returning = "result")
    public void afterReturningLogger(Object result) {
        // 程序运时间(毫秒)
        log.info("请求结束时间: {}", dateTimeFormatter.format(LocalDateTime.now()));
        log.info("--------后台管理请求后置日志输出完成--------");
        // 保存操作日志
        OperationLog operationLog = logThreadLocal.get();
        if (operationLog != null) {
            operationLog.setOperationStatus(true);
            // 用的 是 阿里巴巴的 fastjson
            operationLog.setOperationResult(JSONObject.toJSONString(result));
            // 调用具体的 service 保存到数据库中
            operationLogService.save(operationLog);
            // 移除本地线程数据
            logThreadLocal.remove();
        }
    }





    /**
     * 异常通知,请求异常会进入到这个方法
     */
    @AfterThrowing(value = "logPointcut()", throwing = "throwable")
    public void throwingLogger(Throwable throwable) {

        log.error("ErrorMessage:请根据异常产生时间前往异常日志查看相关信息");
        log.error("--------后台管理请求异常日志输出完成--------");
        // 保存操作日志
        OperationLog operationLog = logThreadLocal.get();
        if (operationLog != null) {
            operationLog.setOperationStatus(false);
            String throwableStr = throwable.toString();
            if(throwableStr.contains(":")){
                throwableStr = throwableStr.substring(throwableStr.indexOf(":") + 1);
            }
            operationLog.setOperationResult(throwableStr);
            // 调用具体的 service 保存到数据库中
            operationLogService.save(operationLog);
            // 移除本地线程数据
            logThreadLocal.remove();
        }

    }


}

2、理解解析

首先就是定义一个类,表明他是切面类,加入到 spring 容器之中,加上日志注解,可以提供看到日志信息

@Aspect //声明是 aspect 切面类
@Component //声明是 spring 容器中的 bean
@Slf4j  // 日志
public class LoggerAspect {

}

插入到数据库的操作,因为我使用的是 mybatisplus,也可以直接引用 自带的方法插入数据库,可以直接引入 mapper 层

    @Autowired
    public OperationLogService operationLogService;

使用 ThreadLocal<>() 来存储 OperationLog 对象,保证 日志对象的独立性

   /**
     * FastThreadLocal 依赖于  netty,如果不想用netty,可以使用jdk自带的 ThreadLocal
     */
    final ThreadLocal<OperationLog> logThreadLocal = new ThreadLocal<>();

创建了一个时间模板对象

    final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");

定义切点,这里是所有的controller中的方法

    /**
     * 切点
     * 所有 com.*.*.controller 包下的所有方法
     */
    @Pointcut("execution(* com.*.*.controller.*.*(..))") // 切点 所有 com.*.*.controller 包下的所有方法
    public void logPointcut() {
    }

前置通知

请求的前置通知,就是在方法执行前,起作用

JoinPoint 就指代目标方法执行过程中的连接点,它提供了一系列方法来获取与当前被拦截方法相关的信息

 /**
     * 请求前置通知
     * 切点 执行前
     *
     * @param joinPoint 切点
     */
    @Before("logPointcut()")
    public void beforeLog(JoinPoint joinPoint) throws NoSuchMethodException {

    }

利用 joinPoint.getArgs() 获取得到目标方法的参数,并将其转换为字符串数组

        // 获取请求参数
        //将方法调用时传入的参数 joinPoint 转换为字符串形式
        String params = Arrays.toString(joinPoint.getArgs());

获取当前登录用户信息的方法,也判断了未登录时的状况

获取当前时间

        // 鉴权会话获取 当前登录的用户信息,我用的是 shiro,根据情况改变
//        Subject currentUser = SecurityUtils.getSubject();
//        User user = (User)currentUser.getPrincipal();
//        Integer userId = null;
//        String userName = null;
//        if(User!= null){
//            userId = User.getId();
//            userName = User.getUsername();
//        }else{
//            /*
//              因为登录接口没有登录会话状态,无法获取到用户信息,从 登录请求参数中获取 登录用户名
//              @see 例如:登录请求肯定是 post请求,上方 "params" 参数已经获取到 请求参数信息,只要判断里面是否有用户名信息 验证是否为登录接口,然后字符串截取获取用户名。。这个方法是我能想到最快捷的
//              示例登录接口参数:我的登录 请求json [LoginDTO(username=1001010, password=132456,code='A5C5')]
//             */
//            if(params.contains("username=")){
//                userName = params.substring(params.indexOf("username=") + 9, params.indexOf(", password="));
//                // 登录参数密码 简单脱密一下
//                params = params.replace("password=", "changshayueluqu_"); //将password= 替换为 changshayueluqu_
//            }
//        }
        LocalDateTime now = LocalDateTime.now();
        log.info("--------请求前置日志输出开始--------");

获取 HTTP 请求的方法 (自己看看吧)

然后操作人id ,因为没有登录相关,自己给了一个

/**
 * RequestContextHolder 用于获取当前请求的上下文信息
 * getRequestAttributes()方法返回一个RequestAttributes对象,这里通过强制类型转换将其转换为ServletRequestAttributes。
 * ServletRequestAttributes是RequestAttributes的一个实现类,专门用于处理基于 Servlet 的请求上下文。
 */
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
 /**
  * 由于getRequestAttributes()方法可能返回null(例如在非 Web 请求的上下文中调用)
  * 这里使用Objects.requireNonNull()方法确保attributes不为null
  * 如果attributes不为null,则调用其getRequest()方法获取HttpServletRequest对象
  * 这个对象包含了与当前 HTTP 请求相关的所有信息。
  */
HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
log.info("请求访问时间: {}", dateTimeFormatter.format(now));
// 获取请求url
String requestUrl = request.getRequestURL().toString();
log.info("请求url: {}", requestUrl);
// 获取method
log.info("请求方式: {}", request.getMethod());
log.info("请求参数列表: {}", params);
log.info("操作人ID: {}", 33333);

首先就是通过 joinPoint.getSignature() 得到方法签名信息 然后得到方法,再通过方法获取 注解,看看注解有无 自定义注解 OperationLogDesc

有的话,就进行操作信息的记录 ( IpUtil 是一个工具类,在本文最后的备注里

并放进线程中 !!!!!!!!!

		// 验证请求方法是否带有操作日志注解
        /**
         * joinPoint.getSignature() 获取与当前连接点(被拦截的方法调用)相关的签名信息。
         * MethodSignature 是一个接口,它表示一个方法签名。
         * 获取实际调用的 method 对象,该对象包括了 方法名、参数列表、返回类型等。
         */
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        /**
         * method.getAnnotation(OperationLogDesc.class) 获取当前方法上带有 OperationLogDesc 注解的实例。
         */
        OperationLogDesc operationLogDesc = method.getAnnotation(OperationLogDesc.class);
        if (operationLogDesc != null) {

            // 操作日志记录
            OperationLog operationLog = OperationLog.getInstance();
            operationLog.setAddTime(now);
            operationLog.setOperationModule(operationLogDesc.module());
            operationLog.setOperationEvents(operationLogDesc.events());
            operationLog.setOperationData(params);
            operationLog.setOperationUrl(requestUrl);
            // 操作人ID
            operationLog.setOperationUserId(666);
            operationLog.setOperationUsername("666");

            // IP地址
            operationLog.setOperationIp(IpUtil.getIpAddr(request));
            logThreadLocal.set(operationLog);
        }

后置通知

请求方法执行后,执行此方法

    /**
     * 请求后置通知,请求完成会进入到这个方法
     *
     * @param result 响应结果json
     */
    @AfterReturning(value = "logPointcut()", returning = "result")
    public void afterReturningLogger(Object result) {
        
    }

记录一个请求的结束时间

通过线程得到 operationLog 对象 保证对象的唯一性和统一 !!!!!

记录操作正常

操作结果 将 JSON 转换成字符串 (方便看)

保存到数据库之中

最后移除线程

// 程序运时间(毫秒)
log.info("请求结束时间: {}", dateTimeFormatter.format(LocalDateTime.now()));
log.info("--------后台管理请求后置日志输出完成--------");
// 保存操作日志
OperationLog operationLog = logThreadLocal.get();
if (operationLog != null) {
    operationLog.setOperationStatus(true);
    // 用的 是 阿里巴巴的 fastjson
    operationLog.setOperationResult(JSONObject.toJSONString(result));
    // 调用具体的 service 保存到数据库中
    operationLogService.save(operationLog);
    // 移除本地线程数据
    logThreadLocal.remove();
}

异常通知

请求异常就会进入这个方法

    /**
     * 异常通知,请求异常会进入到这个方法
     */
    @AfterThrowing(value = "logPointcut()", throwing = "throwable")
    public void throwingLogger(Throwable throwable) {
        
    }

通过先程得到 OperationLog 对象 保证唯一性

给定 失败的 (false)状态

处理异常信息,如果异常信息包括 :(冒号),只截取异常信息,不要异常类型

存数据库,移除线程

        log.error("ErrorMessage:请根据异常产生时间前往异常日志查看相关信息");
        log.error("--------后台管理请求异常日志输出完成--------");
        // 保存操作日志
        OperationLog operationLog = logThreadLocal.get();
        if (operationLog != null) {
            operationLog.setOperationStatus(false);
            String throwableStr = throwable.toString();
            if(throwableStr.contains(":")){
                throwableStr = throwableStr.substring(throwableStr.indexOf(":") + 1);
            }
            operationLog.setOperationResult(throwableStr);
            // 调用具体的 service 保存到数据库中
            operationLogService.save(operationLog);
            // 移除本地线程数据
            logThreadLocal.remove();
        }

备注

所需依赖

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

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.31</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.16</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1.tmp</version>
        </dependency>

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

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

        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.6.6</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83_noneautotype</version>
        </dependency>

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

IpUtil

public class IpUtil {
    private static final Logger log = LoggerFactory.getLogger(IpUtil.class);

    private IpUtil() {
    }

    public static String getIpAddr(HttpServletRequest request) {
        String ipAddress;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }

            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }

            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if ("127.0.0.1".equals(ipAddress)) {
                    InetAddress inet = null;

                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException var4) {
                        log.error(var4.getMessage(), var4);
                    }

                    ipAddress = ((InetAddress)Objects.requireNonNull(inet)).getHostAddress();
                }
            }

            if (ipAddress != null && ipAddress.length() > 15 && ipAddress.indexOf(",") > 0) {
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
            }
        } catch (Exception var5) {
            ipAddress = "";
        }

        return ipAddress;
    }

    public static String getIpAddr() {
        return getV4OrV6IP();
    }

    public static String getV4OrV6IP() {
        String ip = null;
        String test = "http://test.ipw.cn";
        StringBuilder inputLine = new StringBuilder();
        BufferedReader in = null;

        try {
            URL url = new URL(test);
            HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
            in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8));

            String read;
            while((read = in.readLine()) != null) {
                inputLine.append(read);
            }

            ip = inputLine.toString();
        } catch (Exception var16) {
            log.error("获取网络IP地址异常,这是具体原因: ", var16);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException var15) {
                    var15.printStackTrace();
                }
            }

        }

        if (ip == null) {
            ip = "127.0.0.1";
            log.info("获取网络IP地址异常, 赋值默认ip: 【{}】", ip);
        }

        return ip;
    }
}

ip问题

数据库记录为 0:0:0:0:0:0:0:1

原因
0:0:0:0:0:0:0:1是属于ipv6,但是本机又没有设置ipv6,后来我又进行另一台电脑做测试,发现这种情况只有在服务器和客户端都在同一台电脑上才会出现(例如用localhost访问的时候才会出现),原来是hosts配置文件的问题 windows的hosts文件路径:C:\Windows\System32\drivers\etc\hosts linux的host路径:/etc/hosts

解决措施
注释掉文件中的 # ::1 localhost 这一行即可解决问题。不过不起作用。 最有效的方式就是改变请求的ip,不要使用localhost:8080 使用127.0.0.1:8080或者ip:8080。百分百管用

标签:null,log,记录,操作,日志,operationLog,public,请求
From: https://www.cnblogs.com/FangZongRi/p/18655632

相关文章

  • Bash Shell的操作环境
    目录1、路径与指令搜寻顺序2、bash的进站(开机)与欢迎信息:/etc/issue,/etc/motd(1)/etc/issue(2)/etc/motd 3、bash的环境配置文件(1)login与non-loginshell(2)/etc/profile(loginshell才会读取)(3) ~./bash_profile(loginshell才会读取)(4)source:读入环境配置文件的指令(5)~/.ba......
  • 省选训练赛 #17 题目 D 补题记录
    具有一定Educational意义。题意:一张无向图,将其分解为若干组基环树森林,求至少需要分解多少组。\(n,m\le2000,\\sumn,\summ\le2\times10^4\)充分利用基环树森林的性质:若为内向基环树,那么每个点的出边至多只有一条。转化:我们相当于给图中的边定向,使得所有点出边数量......
  • MongoDB的部署和操作
    注:本博文展示部署哥操作步骤和命令,具体报告及运行截图可通过上方免费资源绑定下载一.数据库的部署与配置1.单MongoDB服务器的部署(1)将服务器、客户端和工具安装包上传到hadoop1的/export/software目录(2)使用解压缩命令:·tar-xzf/export/software/mongodb-linux-x86_64-rhel......
  • Neo4j的部署和操作
    注:本博文展示部署哥操作步骤和命令,具体报告及运行截图可通过上方免费资源绑定下载一.数据库的部署与配置在单个节点上对进行数据库的单机部署(1)上传neo4j-community-3.5.30-unix.tar.gz到hadoop1的/export/software目录(2)解压缩neo4j:$tar-xzf/export/software/neo4j-commun......
  • 【Linux】squid代理http和https方式上网的操作记录
    需求说明:公司IDC机房有一台服务器A,只有内网环境:192.168.1.150现在需要让这台服务器能对外访问,能正常访问http和https请求(即80端口和443端口)操作思路:在IDC机房里另找其他两台有公网环境的服务器B(58.68.250.8/192.168.1.8)和服务器C(58.68.250.5/192.168.1.5),且这两台服务器和内网......
  • 浏览器 IndexedDB 操作库介绍及对比
    浏览器IndexedDB操作库对比以下是几个常见的IndexedDB库的介绍及对比,方便开发者根据需求选择适合的工具。1.Dexie.js简介Dexie.js是一个高性能的IndexedDB操作库,提供了简化和强大的API,适合复杂查询和事务操作。特点简化IndexedDB操作:避免原生IndexedDB......
  • 在 Windows 中,配置单元文件(Registry Hive)是 Windows 操作系统注册表的核心组成部分。W
    在Windows中,配置单元文件(RegistryHive)是Windows操作系统注册表的核心组成部分。Windows注册表是一个存储系统配置和应用程序设置的数据库,而配置单元(Hive)是这些数据库的一个逻辑分区。每个Hive存储一组与系统或用户相关的数据,结构上可以视为一个分区或区域。什么是配置单......
  • 记一次GBase 8s 安装部署数据库初始化故障问题分析及处理记录
    一、前言最近南大通用第十期GBase8s免费培训开始了,因为今年以来一直在进行去O的调研,看GBase官网介绍GBase8sV8.8兼容Oracle,GBase8s是在informix源码基础上改造的产品,学习GBase8s刚好可以了解informix和测试迁移到Oracle。于是报名参加了本次的培训。学习GBase8s,首先......
  • 《操作系统原理》总结
    一、概述(一)定义与角色操作系统是管理计算机硬件与软件资源、控制程序执行、改善人机交互界面,并为计算机系统中的其他软件和用户提供服务的系统软件。它充当了硬件与用户以及应用程序之间的桥梁,使得计算机系统能够高效、有序且安全地运行。例如,用户打开软件、读写文件、使用外部......
  • docker之学习操作记录,跟我一起练
    我没有使用高权限,所以后边都要加sudo,你如果和我一起练习的话,注意一下。1.查看状态sudosystemctlstatusdocker2.查看镜像kt@kt-SYS-4028GR-TR2:~$sudodockerimagesREPOSITORYTAGIMAGEIDCREATEDSIZEkt@kt-SYS-4028GR-TR2:~$sudodockerimages-......