自定义注解实现日志收集

在日常开发过程中,需要记录日志,但是每次都要写日志收集的代码就会很繁琐,那么如何优雅的实现日志收集呢?利用自定义注解可以很好的实现,下面就来说一说如何利用切面实现日志的收集

1、先自定义一个切点

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

/** @Author: best_liu
 * @Description:
 * @Date: 11:21 2022/11/23
 * @Param 
 * @return 
 **/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface RequiredLog {

    String methodType() default "";

}

2、再实现一个切面类,再该类里面写日志收集的方法

import com.mes.material.domain.MaterialEamLog;
import com.mes.material.service.IMaterialEamLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: best_liu
 * @Description:
 * @Date Create in 9:11 2022/11/23
 * @Modified By:
 */
@Aspect
@Component
@Slf4j
public class SysLogAspect {


    @Autowired
    private IMaterialEamLogService materialEamLogService;

    private static Object result = "";

    /**
     * @Pointcut 注解用于描述或定义一个切入点
     *  切入点的定义需要遵循spring中指定的表达式规范
    例如:("bean(sysMenuServiceImpl)")为切入点表达式
    的一种定义方式。
     */
    @Pointcut("@annotation(com.mes.material.annotation.log.RequiredLog)")
    public void logPointCut() {}

    /**
     * @Around 注解描述的方法为一个环绕通知方法,
     * 在此方法中可以添加扩展业务逻辑,可以调用下一个
    切面对象或目标方法
     * @param jp 连接点(此连接点只应用@Around描述的方法)
     * @return
     * @throws Throwable
     */
    @Around("logPointCut()")
    public Object aroundAdvice(ProceedingJoinPoint jp)
            throws Throwable{
        long start=System.currentTimeMillis();
        log.info("start:"+start);
        try {
            //调用下一个切面或目标方法
            result = jp.proceed();
        }finally {
            long end=System.currentTimeMillis();
            log.info("end:"+end);
            //记录日志(用户行为信息)
            saveLog(jp,result,String.valueOf(end-start));
        }
        return result;
    }

    @AfterThrowing(value = "logPointCut()",throwing = "t")
    public void afterThrowing(Throwable t) {
        result = t;
    }

    //日志记录
    private void saveLog(ProceedingJoinPoint jp,Object result,String time) {
        //1.获取用户行为日志(ip,username,operation,method,params,time,createdTime)
        //获取类的字节码对象,通过字节码对象获取方法信息
        Class<?> targetCls=jp.getTarget().getClass();
        //获取方法签名(通过此签名获取目标方法信息)
        MethodSignature ms=(MethodSignature)jp.getSignature();
        //获取目标方法名(目标类型+方法名)
        String targetClsName=targetCls.getName();
        String targetObjectMethodName=targetClsName+"."+ms.getName();
        //获取请求参数
        String targetMethodParams= Arrays.toString(jp.getArgs());
        String url = "";
        String ipAddr = "";
        if(RequestContextHolder.getRequestAttributes() != null){
            //获取请求url
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            url = request.getRequestURL().toString();
            //ipAddr = request.getRemoteHost();
            ipAddr = getIpAddress(request);
        }
        //2.封装用户行为日志(SysLog)
        //插入日志--start
        MaterialEamLog log = new MaterialEamLog();
        log.setReqParam(targetMethodParams);
        log.setMethod(targetObjectMethodName);
        //获取注解中的参数
        Map<String, Object> annotationArgs = this.getAnnotationArgs(jp);
        log.setMethodType(String.valueOf(annotationArgs.get("methodType")));
        log.setResult(String.valueOf(result));
        log.setUrl(url);
        log.setIp(ipAddr);
        log.setTime(time);
        //3.调用业务层对象方法(saveObject)将日志写入到数据库
        materialEamLogService.insert(log);
    }

    /**
     * 获取锁参数
     *
     * @param proceeding
     * @return
     */
    private Map<String, Object> getAnnotationArgs(ProceedingJoinPoint proceeding) {
        Class target = proceeding.getTarget().getClass();
        Method[] methods = target.getMethods();
        String methodName = proceeding.getSignature().getName();
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Map<String, Object> result = new HashMap<String, Object>();
                RequiredLog requiredLog = method.getAnnotation(RequiredLog.class);
                result.put("methodType", requiredLog.methodType());
                return result;
            }
        }
        return null;
    }

/**
     * 获取用户真实IP地址,不使用request.getRemoteAddr()的原因是有可能用户使用了代理软件方式避免真实IP地址,
     * 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值
     *
     * @return ip
     */
    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        log.info("x-forwarded-for ip: " + ip);
        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值,第一个ip才是真实ip
            if( ip.indexOf(",")!=-1 ){
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
            log.info("Proxy-Client-IP ip: " + ip);
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
            log.info("WL-Proxy-Client-IP ip: " + ip);
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
            log.info("HTTP_CLIENT_IP ip: " + ip);
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            log.info("HTTP_X_FORWARDED_FOR ip: " + ip);
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
            log.info("X-Real-IP ip: " + ip);
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
            log.info("getRemoteAddr ip: " + ip);
        }
        log.info("获取客户端ip: " + ip);
        return ip;
    }

}

这样一个自定义注解就完成了,在需要收集日志的地方用@RequiredLog(methodType="")注解即可

@Service
public class MesProvideRomaServiceImpl implements MesProvideRomaService {

   

    @Override
    @RequiredLog(methodType="2")
    public AjaxResult materialInfoRomaToMes(MaterialRequestVo materialRequestVo) throws IllegalAccessException, IntrospectionException, InstantiationException {
        
    }
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值