接口日志:储存到MySQL数据库

1、定义一个日志注解,把模块的接口调用日志储存到数据库中。
2、后续可能会产生性能问题,但对于当前快速扩张的业务而言,这种过渡性的功能,还是可以接受的。

用法:

在这里插入图片描述

一、自定义注解对象

package com.pkg.modelname.anno;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogOperation {

	String value() default "";
}

二、自定义切面处理类

@Aspect
@Component
@Slf4j
public class OperationAspect {

    @Resource
    ApiAopSaveComp logUtil;

    @Pointcut("@annotation(com.pkg.modelname.anno.LogOperation)")
    public void logPointCut() {

    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        return logUtil.onAround(point);
    }
}

三、日志保存组件

@Component
@Slf4j
@Setter
@Getter
public class ApiAopSaveComp {

    /**
     * 日志默认模块名
     */
    private String modelName = "def-model";

    @Resource
    ApiLogService apiLogService;

    /**
     * 默认日志保存
     */
    private InsertLog defInsert = (joinPoint, time, status, result) -> {
        try {
            // 默认保存日志
            saveLogDef(joinPoint, time, status, result);
        } catch (NoSuchMethodException | IOException e) {
            log.error("保存日志异常: ", e);
        }
    };

    /**
     * 保存日志
     * 重设此对象以实现自己的保存日志方法
     */
    private InsertLog insertLog = defInsert;

    /**
     * 环切
     *
     * @param point 切入点
     * @return 返回
     * @throws Throwable 错误
     */
    public Object onAround(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        Object result;
        try {
            // 执行方法
            result = point.proceed();
            // 保存日志
            onSuccess(point, result, beginTime);
            return result;
        } catch (Exception e) {
            // 执行时长(毫秒)
            long cost = cost(beginTime);
            // 保存日志 返回异常
            saveLog(point, cost, false, ExceptionUtils.getStackTrace(e));
            throw e;
        }
    }

    /**
     * 成功的日志
     *
     * @param point     切入点
     * @param result    返回结果
     * @param beginTime 开始时间
     */
    private void onSuccess(ProceedingJoinPoint point, Object result, long beginTime) {
        try {
            // 执行时长(毫秒)
            long cost = cost(beginTime);
            // 返回
            String data = "";
            if (null != result) {
                data = result.toString();
            }
            // 保存日志
            saveLog(point, cost, true, data);
        } catch (Exception e) {
            log.error("保存日志异常", e);
        }
    }

    /**
     * 计算耗时
     *
     * @param beginTime 开始时间
     * @return 间隔时间
     */
    private long cost(long beginTime) {
        return System.currentTimeMillis() - beginTime;
    }

    /**
     * 保存日志
     *
     * @param joinPoint 切入点
     * @param time      耗时
     * @param status    请求状态
     * @param result    返回参数
     */
    private void saveLog(ProceedingJoinPoint joinPoint, long time, boolean status, String result) {
        try {
            insertLog.insertLog(joinPoint, time, status, result);
        } catch (Exception e) {
            log.error("保存日志失败", e);
        }
    }

    /**
     * 默认的保存日志方法
     *
     * @param joinPoint 切入点
     * @param time      耗时
     * @param status    请求状态
     * @param result    返回参数
     * @throws NoSuchMethodException 异常
     */
    private void saveLogDef(ProceedingJoinPoint joinPoint, long time, boolean status, String result) throws NoSuchMethodException, IOException {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = joinPoint.getTarget().getClass().getDeclaredMethod(signature.getName(), signature.getParameterTypes());
        LogOperation annotation = method.getAnnotation(LogOperation.class);
        // 组装日志
        WebApiLogEntity entity = new WebApiLogEntity();
        entity.setLogTime(new Date());
        entity.setModuleName(modelName);
        if (annotation != null) {
            //注解上的描述
            entity.setApi(annotation.value());
        }
        // 耗时
        entity.setCostTime(time);
        // 状态
        entity.setStatus(status);
        //请求相关信息
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        if (null != request) {
            entity.setIpAddr(HttpContextUtils.getRealIp(request));
            String header = HttpContextUtils.getAllHeader(request);
            entity.setHeader(limitLen(header));
            entity.setRequestUri(limitLen(request.getRequestURI()));
            entity.setMethod(request.getMethod());
        }
        // 请求参数
        try {
            String args = getArgs(joinPoint);
            entity.setArgs(limitLen(args, 2048));
            log.info("args : {}", args);
        } catch (Exception e) {
            log.error("参数序列错误:", e);
            e.printStackTrace();
        }
        // 返回参数
        if (null != result) {
            entity.setResult(limitLen(result, 2048));
            log.info("result : {}", result);
        }
        apiLogService.saveLog(entity);
    }

    /**
     * 限制长度
     *
     * @param header 头部
     * @return 限制后的长度
     */
    private static String limitLen(String header) {
        int maxLen = 500;
        return limitLen(header, maxLen);
    }

    /**
     * 限制长度
     *
     * @param str 字符
     * @param len 长度,不包含
     * @return 限制后的长度
     */
    private static String limitLen(String str, int len) {
        if (null == str) {
            return null;
        }
        return str.length() > len ? str.substring(0, len) : str;
    }

    /**
     * 获取请求参数
     *
     * @param point 切入点
     * @return 参数
     */
    public static String getArgs(ProceedingJoinPoint point) {
        Object[] args = point.getArgs();
        if (null != args && args.length > 0) {
            Map<Integer, String> map = new HashMap<>();
            for (int i = 0; i < args.length; i++) {
                map.put(i, args[i].toString());
            }
            return new Gson().toJson(map);
        }
        return null;
    }

}

日志打印接口

默认实现一个接口,支持其它模块再次实现,提升扩展性

public interface InsertLog {
    /**
     * 插入日志
     *
     * @param joinPoint 切入点
     * @param time      耗时
     * @param status    请求结果状态
     * @param result    返回参数
     */
    void insertLog(ProceedingJoinPoint joinPoint, long time, boolean status, String result);
}
日志储存实体对象
@Data
@NoArgsConstructor
@ToString
@TableName("tb_web_api_log")
public class WebApiLogEntity {

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

    private Date logTime;

    /**
     * 模块名
     */
    private String moduleName;

    private String api;

    /**
     * 耗时
     */
    private Long costTime;

    private boolean status;

    private String ipAddr;

    private String header;

    private String requestUri;

    private String method;

    private String args;

    private String result;

}

实体表

SQL代码

CREATE TABLE `tb_web_api_log`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `log_time` datetime NULL DEFAULT NULL,
  `module_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `api` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `cost_time` int(11) NULL DEFAULT NULL,
  `status` tinyint(4) NULL DEFAULT NULL,
  `ip_addr` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `header` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `request_uri` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `method` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `args` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `result` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'api日志表' ROW_FORMAT = Compact;

图例:
在这里插入图片描述

使用到的第三方包

ExceptionUtils.getStackTrace(e)

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.9</version>
</dependency>

HTTP上下文工具类

public class HttpContextUtils {

    /**
     * 获取当前请求
     * @return 请求
     */
    public static HttpServletRequest getHttpServletRequest() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return null;
        }
        return ((ServletRequestAttributes) requestAttributes).getRequest();
    }

    /**
     * 获取真正的ip
     * @param request 请求
     * @return
     */
    public static String getRealIp(HttpServletRequest request) {
        //
        String ip = request.getHeader("X-Real-IP");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            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();
        }
        // 处理多IP的情况(只取第一个IP)
        if (ip != null && ip.contains(",")) {
            String[] ipArray = ip.split(",");
            ip = ipArray[0];
        }
        return ip;
    }

    /**
     * 获取所有头
     *
     * @param request 请求
     * @return 所有请求头
     */
    public static String getAllHeader(HttpServletRequest request) {
        Enumeration<String> eHeaders = request.getHeaderNames();
        Map<String,String> map =new HashMap<>();
        while (eHeaders.hasMoreElements()){
            String name = eHeaders.nextElement();
            String val = request.getHeader(name);
            map.put(name,val);
        }
        return JSONObject.toJSONString(map);
    }

}

四、扩展用法

在这里插入图片描述

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,如果您需要将操作过程日志存储数据库,可以按照以下步骤进行: 1. 创建数据库表,用于存储日志数据。可以根据需要定义表的字段,例如 log_id、log_time、log_content 等。 2. 在代码使用数据库连接库连接数据库,并获取数据库游标,以执行 SQL 语句。 3. 在代码,将需要记录的操作过程信息记录到字符串变量,例如: ```python log_content = "执行了某个操作,操作参数为:xxxx" ``` 4. 在代码,执行插入操作,将操作过程信息插入到数据库,例如: ```python import pymysql # 建立数据库连接 conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='123456', db='test', charset='utf8') cursor = conn.cursor() # 执行插入操作 log_content = "执行了某个操作,操作参数为:xxxx" insert_sql = "INSERT INTO log_table (log_time, log_content) VALUES (now(), %s)" cursor.execute(insert_sql, (log_content,)) # 提交事务并关闭游标和数据库连接 conn.commit() cursor.close() conn.close() ``` 在上述示例代码,我们首先建立了一个 MySQL 数据库连接,并获取了数据库游标。然后,我们定义了一个 log_content 变量,用于记录操作过程信息。接着,我们执行了一个插入操作,将当前时间和 log_content 变量的值插入到 log_table 表。最后,我们提交了事务,并关闭了游标和数据库连接。 需要注意的是,如果需要记录多条操作日志,可以在循环执行插入操作,每次插入一条日志记录。在插入时,可以根据需要使用参数化查询,以避免 SQL 注入攻击。同时,为了避免日志数据过大,可以根据需要设置日志过期时间,并定期删除过期日志

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

*crzep

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值