【系统操作日志的实现】

系统操作日志的实现

  • springboot
  • 自定义注解
  • 切面
  • 线程池
  • 对请求参数、返回值、异常等操作
  • nginx反向代理获取真实IP
  • 操作内容自定义拼接

直接上代码

  • 1、自定义操作日志记录注解
/**
 * 自定义操作日志记录注解
 *
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperateLog {

    /**
     * 方法名称
     */
    String MethodName() default SysOperationType.NO;

    /**
     * 操作类别
     */
    String OperationType() default SysOperationType.OTHER;

    /**
     * 操作内容
     */
    String OperationContent() default SysOperationType.NO;

}
  • 自定义系统操作类型
/**
 * 操作类型
 */
public interface SysOperationType {
    String OTHER = "其他";
    String NO = "无";
    String UPDATE_MOBILE = "修改手机号";
    String RECHARGE = "充值";
    String DEDUCTION = "扣费";
    String UPDATE_USER = "变更所属人";
    String EXPORT_ORDER_DATA = "订单导出";
    String EXPORT_FUND_DATA = "资金明细导出";
    String PUBLISH = "发布";
}
  • 操作内容实体
@Data
@TableName("sys_operation_log")
public class SysOperationLog {

    @ApiModelProperty("主键id")
    @TableId(type = IdType.AUTO)
    private Long id;

    @ApiModelProperty("操作类型")
    private String operationType;

    @ApiModelProperty("操作对象")
    private String operationTarget;

    @ApiModelProperty("方法名称")
    private String methodName;

    @ApiModelProperty("操作内容详情")
    private String operationContent;

    @ApiModelProperty("操作状态 0失败,1成功")
    private Integer status;

    @ApiModelProperty("操作者IP")
    private String ip;

    @ApiModelProperty("IP归属地")
    private String ipAddress;

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

    @ApiModelProperty("所属主体")
    private Integer tenantCode;

    @ApiModelProperty("创建人编码")
    private Long createBy;

    @ApiModelProperty("创建时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;

    public Date getCreateTime() {
        if (createTime == null) {
            return new Date();
        }
        return createTime;
    }
}
  • 注解解析
/**
 * 注解解析
 */

@Aspect
@Component
@Slf4j
public class OperateLogAspect {

    @Resource
    private HandleLogAspect handleLogAspect;

    @Pointcut(value = "@annotation(com.cloudapi.common.annotation.OperateLog)")
    public void logPointCut() {

    }

    /**
     * 处理完请求前执行
     *
     * @param joinPoint 切点
     */
    @Before(value = "logPointCut()")
    public void before(JoinPoint joinPoint) {
        log.info("before");
    }

    /**
     * 获取请求头中登录用户信息
     *
     * @param request 请求信息
     * @return 登录用户信息
     */
    private LoginUser getLoginUser(HttpServletRequest request) {
        // 获取当前的用户
        LoginUser loginUser = null;
        String mobile = request.getHeader(AUTHORIZATION);
        if (StrUtil.isNotBlank(mobile)) {
            // 解析jwt
            // 查询用户缓存信息
            String key = Constants.WEB_USER_PREFIX + mobile;
            UserVo userVo = JSONObject.parseObject(RedisUtils.StringOps.get(key), UserVo.class);
            if (null != userVo) {
                loginUser = userVo.getUser();
            }
        }
        return loginUser;
    }


    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
        log.info("doAfterReturning");
        // 当前线程请求头信息
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert sra != null;
        HttpServletRequest request = sra.getRequest();
        // 获取用户信息
        LoginUser loginUser = getLoginUser(request);
        handleLogAspect.handleLog(joinPoint, null, jsonResult, loginUser, request);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        log.info("doAfterThrowing");
        // 当前线程请求头信息
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert sra != null;
        HttpServletRequest request = sra.getRequest();
        // 获取用户信息
        LoginUser loginUser = getLoginUser(request);
        handleLogAspect.handleLog(joinPoint, e, null, loginUser, request);
    }
}
  • 操作日志处理
/**
 * @description: 操作日志处理类
 * @date : 2023/3/29
 */
@Component
@Slf4j
public class HandleLogAspect {

    @Resource
    private SysOperationLogDao sysOperationLogDao;

    @Async("operationLogThreadPoolExecutor")
    public void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult, LoginUser loginUser, HttpServletRequest request) {
        try {
            // 获得注解
            OperateLog controllerLog = getAnnotationLog(joinPoint);
            if (Objects.isNull(controllerLog)) {
                return;
            }
            // 数据库日志
            SysOperationLog sysOperationLog = new SysOperationLog();
            // 处理注解上的参数、方法参数、登录信息,ip信息,异常信息,返回结果
            getControllerMethodDescription(joinPoint, controllerLog, sysOperationLog, request, loginUser, e, jsonResult);
            // 保存数据库
            int insert = sysOperationLogDao.insert(sysOperationLog);
            log.info("操作日志落库:{}", insert);
        } catch (Exception exp) {
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }


    /**
     * 处理注解上的参数、方法参数、登录信息,ip信息,异常信息,返回结果
     *
     * @param operateLog      日志
     * @param sysOperationLog 操作日志
     */
    public void getControllerMethodDescription(JoinPoint joinPoint,
                                               OperateLog operateLog,
                                               SysOperationLog sysOperationLog,
                                               HttpServletRequest request,
                                               LoginUser loginUser,
                                               Exception e,
                                               Object jsonResult) throws Exception {
        // 初始状态
        sysOperationLog.setStatus(CommonConstant.YES);
        // 登录信息
        if (Objects.nonNull(loginUser)) {
            sysOperationLog.setOperationName(loginUser.getRealName() != null ? loginUser.getRealName() : loginUser.getId().toString());
            sysOperationLog.setCreateBy(loginUser.getId());
            sysOperationLog.setTenantCode(loginUser.getTenantCode());
        } else {
            sysOperationLog.setCreateBy(0L);
            sysOperationLog.setOperationName("system");
            sysOperationLog.setTenantCode(0);
        }
        if (Objects.nonNull(e)) {
            sysOperationLog.setStatus(CommonConstant.NO);
        }
        // 设置action动作
        sysOperationLog.setOperationType(operateLog.OperationType().trim());
        // 获取请求参数
        String nameAndValue = getNameAndValue(joinPoint);
        // 操作对象
        sysOperationLog.setOperationTarget(nameAndValue);
        // 设置操作内容
        String str = operateLog.OperationContent();
        String format;
        if (!StringUtils.isEmpty(nameAndValue) && str.contains("{")) {
            format = MessageFormat.format(str, nameAndValue);
        } else {
            format = str;
        }
        sysOperationLog.setOperationContent(format);
        // 方法名称
        sysOperationLog.setMethodName(operateLog.MethodName());
        // 获取ip
        String realRequestIp = getRealRequestIp(request);
        // 获取ip归属地
        String ipAddress = getAddress(realRequestIp);
        sysOperationLog.setIp(realRequestIp);
        sysOperationLog.setIpAddress(ipAddress);
    }

    /**
     * 是否存在注解,如果存在就获取
     */
    private OperateLog getAnnotationLog(JoinPoint joinPoint) throws Exception {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            return method.getAnnotation(OperateLog.class);
        }
        return null;
    }

    /**
     * 获取参数中操作者
     *
     * @param joinPoint 入参
     * @return 请求参数
     */
    private String getNameAndValue(JoinPoint joinPoint) {
        Object[] paramValues = joinPoint.getArgs();
        if (paramValues != null && paramValues.length > 0) {
            String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
            for (int i = 0; i < paramNames.length; i++) {
                Object paramValue = paramValues[i];
                if (paramValue != null) {
                    try {
                        String str = JSON.toJSONString(paramValue);
                        JSONObject jsonObject = JSON.parseObject(str);
                        Object operationTarget = jsonObject.get("operationTarget");
                        if (operationTarget != null) {
                            return operationTarget.toString();
                        }
                    } catch (Exception e) {
                        log.error("参数解析异常");
                    }
                }
            }
        }
        return "";
    }

    /**
     * 处理完请求中执行
     *
     * @param point 切点
     */
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) {
        log.info("around");
        //获取方法名称
        Signature methodName = point.getSignature();
        long l1 = System.currentTimeMillis();
        Object obj = null;
        try {
            obj = point.proceed(point.getArgs());
        } catch (Throwable e) {
            e.printStackTrace();
        }
        log.info(methodName + "bye" + "\t耗时 " + (System.currentTimeMillis() - l1));
        return obj;
    }

    /**
     * 真实请求IP
     *
     * @param request 请求
     * @return 真实ip
     */
    public static String getRealRequestIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (null != ip && ip.contains(",")) {
            ip = ip.substring(0, ip.indexOf(","));
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            InetAddress address = null;
            try {
                address = InetAddress.getLocalHost();
            } catch (UnknownHostException e) {
                log.info("获取本地IP异常");
            }
            if (address != null) {
                log.info("本地IP:{}", address.getHostAddress());
                ip = address.getHostAddress();
            }
        }
        return ip;
    }

    /**
     * 通过调用接口根据ip获取归属地
     */
    public String getAddress(String ip) {
        if (!StringUtils.isEmpty(ip)) {
            try {
                String encode = URLEncoder.encode(ip, "utf-8");
                URL realUrl = new URL("https://whois.pconline.com.cn/ipJson.jsp?ip=" + encode + "&json=true");
                HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
                conn.setRequestProperty("contentType", "GBK");
                conn.setRequestMethod("GET");
                conn.setUseCaches(false);
                conn.setReadTimeout(6000);
                conn.setConnectTimeout(6000);
                conn.setInstanceFollowRedirects(false);
                int code = conn.getResponseCode();
                String ipaddr = "";
                if (code == 200) {
                    InputStream inputStream = conn.getInputStream();
                    StringBuilder sb = new StringBuilder();
                    String readLine;
                    BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "GBK"));
                    BufferedReader responseReader = new BufferedReader(in);
                    while ((readLine = responseReader.readLine()) != null) {
                        sb.append(readLine);
                    }
                    ipaddr = sb.substring(sb.indexOf("addr") + 7, sb.indexOf("regionNames") - 3);
                    responseReader.close();
                    in.close();
                    inputStream.close();
                }
                // 断开连接,释放资源
                conn.disconnect();
                return ipaddr;
            } catch (Exception e) {
                log.error("获取ip归属地异常:{}", ip);
                e.printStackTrace();
                return null;
            }
        }
        return null;
    }
}
  • 开启多线程
@Configuration
@Slf4j
public class ThreadPoolConfiguration {

    /**
     * 操作日志线程池
     * @return 线程池
     */
    @Bean(name = "operationLogThreadPoolExecutor", destroyMethod = "shutdown")
    public ThreadPoolExecutor systemCheckPoolExecutorService() {
        return new ThreadPoolExecutor(3, 10, 60, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(10000),
                new ThreadFactoryBuilder().setNameFormat("operationLog-executor-%d").build(),
                (r, executor) -> log.error("操作日志线程已满! "));
    }
}
  • springboot启动类增加开启多线程注解
@EnableAsync
  • 在方法上增加注解(依次:方法名,操作类型,自定义操作内容占位符{0})
@OperateLog(MethodName = "subtractBalance", OperationType = SysOperationType.DEDUCTION, OperationContent = "给【{0}】公司扣费")
  • nginx代理获取真实IP增加配置
proxy_set_header        X-Real-IP       $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

# 例:
server {  
    listen 80;  
    server_name localhost;  
    location / { 
         proxy_set_header        X-Real-IP       $remote_addr;
         proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_pass http://xxxx;
    }

  • @Async注解失效原因:
1、注解@Async的方法不是public方法
2、注解@Async的返回值只能为void或者Future
3、注解@Async方法使用static修饰也会失效
4、spring无法扫描到异步类,没加注解@Async  或 @EnableAsync注解
5、调用方与被调方不能在同一个类
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值