记录feign调用日志

通过AOP 实现记录feign调用日志

feign client aop

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
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.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
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.Date;
import java.util.Objects;

/**
 * @description: api audit log
 * @author: kailiwang
 * @date: 11/08/2021
 */
@Aspect
@Component
@Slf4j
public class ApiLogAspect {

    @Autowired
    SystemService systemService;

    @Pointcut("@within(org.springframework.cloud.openfeign.FeignClient)")
    public void feignClientAspect() {
        // Do nothing as it's an injection
    }

    @Around("feignClientAspect()")
    public Object doAround(ProceedingJoinPoint jp) throws Throwable {
        log.info("getTarget:{}", jp.getTarget());

        ApiAuditLogEntity apiAuditLog = getApiAuditLog(jp);
        Object proceed = null;
        String errorMsg = null;
        try {
            proceed = jp.proceed();
            return proceed;
        } catch (Throwable ex) {
            log.error("ApiLogAspect exception,", ex);
            errorMsg = ex.getMessage();
            throw ex;
        } finally {
            if (Objects.nonNull(apiAuditLog)) {
                apiAuditLog.setResponseTime(new Date());
                // async save log
                systemService.asyncSaveApiAuditLog(apiAuditLog, proceed, errorMsg);
            }
        }
    }


    private ApiAuditLogEntity getApiAuditLog(JoinPoint jp){
        try {
            if (!(jp.getSignature() instanceof MethodSignature)) {
                return null;
            }
            MethodSignature methodSignature = (MethodSignature) jp.getSignature();
            Method method = methodSignature.getMethod();
            RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
            if(requestMapping == null){
                return null;
            }
            ApiAuditLogEntity apiAuditLogEntity = new ApiAuditLogEntity();

            apiAuditLogEntity.setMethod(ArrayUtils.isNotEmpty(requestMapping.method()) ? requestMapping.method()[0].name() : "");

            String url = getUrl(jp.getTarget());
            String path = ArrayUtils.isNotEmpty(requestMapping.value()) ? requestMapping.value()[0] : "";

            String name = requestMapping.name();
            if (StringUtils.isNotBlank(path)) {
                String pathName = path.startsWith("/") ? path.substring(1).replaceAll("/", "_") : path;
                apiAuditLogEntity.setApiName(StringUtils.isNotBlank(name) ? name : pathName);
            }
            apiAuditLogEntity.setPath(StringUtils.isNotBlank(url) ? url + path : path);

            apiAuditLogEntity.setCreatedBy(getCurrentUser());
            apiAuditLogEntity.setRequestTime( new Date());
            apiAuditLogEntity.setRequest(Arrays.toString(jp.getArgs()));

            return apiAuditLogEntity;
        } catch (Exception e) {
            log.error("build api audit log entity error,", e);
            return null;
        }
    }

    private String getUrl(Object target){
        String url = null;
        if (Objects.isNull(target)) {
            return url;
        }
        String targetStr = target.toString();
        if (StringUtils.isBlank(targetStr)) {
            return url;
        }
        int beginIndex = targetStr.indexOf("(") + 1;
        int endIndex = targetStr.lastIndexOf(")");
        if (beginIndex < endIndex) {
            targetStr = targetStr.substring(beginIndex, endIndex);
        }
        String[] split = targetStr.split(", ");
        if (ArrayUtils.isEmpty(split)) {
            return url;
        }
        for (int i = 0; i < split.length; i++) {
            if (split[i].contains("url=")) {
                String[] strings = split[i].split("=");
                url = strings.length > 1 ? strings[1] : null;
                break;
            }
        }
        return url;
    }

    private String getCurrentUser(){
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            return UserUtil.getUserName(request);
        } catch (Exception e) {
            return null;
        }
    }

}

entity

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

@Entity
@Table(name = "tb_api_audit_log")
@Getter
@Setter
public class ApiAuditLogEntity implements Serializable {

    private static final long serialVersionUID = 625948518839067473L;

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "api_name")
    private String apiName;

    @Column(name = "method")
    private String method;

    @Column(name = "path")
    private String path;

    @Column(name = "uid")
    private String uid;

    @Column(name = "reference_no")
    private String referenceNo;

    @Column(name = "created_by")
    private String createdBy;

    @Column(name = "created_date", insertable = false, updatable = false)
    private Date createdDate;

    @Column(name = "description")
    private String description;

    @Column(name = "request")
    private String request;

    @Column(name = "request_time")
    private Date requestTime;

    @Column(name = "response")
    private String response;

    @Column(name = "response_time")
    private Date responseTime;

    @Column(name = "status_code")
    private String statusCode;
}

dao

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

/**
 * @description: description
 * @author: kailiwang
 * @date: 12/08/2021
 */
@Repository
@Transactional
public interface ApiAuditLogRepository extends JpaRepository<ApiAuditLogEntity, Long> {

    @Modifying
    @Query("DELETE FROM ApiAuditLogEntity WHERE createdDate < :expireDate")
    Integer deleteByCreatedDateLessThan(Date expireDate);
}

async save log

    @Async("asyncExecutor")
    public void asyncSaveApiAuditLog(ApiAuditLogEntity apiAuditLog, Object proceed, String desc){
        if (Objects.isNull(apiAuditLog)) {
            return;
        }
        if (StringUtils.isNotBlank(desc)) {
            apiAuditLog.setDescription(desc);
            apiAuditLog.setStatusCode("ERROR");
        } else {
            apiAuditLog.setStatusCode("SUCCESS");
        }
        if (Objects.nonNull(proceed)) {
            try {

                apiAuditLog.setResponse(JsonUtil.toPlainJson(proceed));
            } catch (Exception e) {
                log.error("ApiLogAspect updateApiResp to json error,", e);
            }
        }
        try {
            apiAuditLogRepository.save(apiAuditLog);
            log.info("saveApiAuditLog end.");
        } catch (Exception e) {
            log.error("saveApiAuditLog entity:{}, error,", JsonUtil.toPlainJson(apiAuditLog), e);
        }
    }

scheduled task

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.Date;

@Slf4j
@Component
public class ScheduledTask {

    @Autowired
    ApiAuditLogRepository apiAuditLogRepository;

    @Value("${spring.scheduled.clearApiLogRunIp}")
    private String clearApiLogRunIp;

    /**
     * clear expire api audit log every month
     */
    @Scheduled(cron="${spring.scheduled.clearApiLogCron:0 0 0 * * SUN}")
    public void syncClearExpireApiAuditLog(){
        if (!getlockForRun()) {
            log.info("ClearExpireApiAuditLog sync job end due to other app is run.");
            return;
        }
        log.info("ClearExpireApiAuditLog sync job start.");
        // before 30 day datetime
        Date expireDate = DateUtils.addDays(new Date(), -30);
        apiAuditLogRepository.deleteByCreatedDateLessThan(expireDate);
        log.info("ClearExpireApiAuditLog sync job end.");

    }

    private boolean getlockForRun() {
        log.info("Get lock for run config ip:{}", clearApiLogRunIp);
        String host = null;
        try {
            host = Inet4Address.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            log.error("Error when get ip", e);
            return false;
        }

        if (host.equalsIgnoreCase(clearApiLogRunIp)) {
            return true;
        } else {
            return false;
        }
    }
}

sql script

DROP TABLE IF EXISTS `tb_api_audit_log`;
CREATE TABLE IF NOT EXISTS `tb_api_audit_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `api_name` varchar(255)  DEFAULT NULL,
  `method` varchar(20)  DEFAULT NULL,
  `path` varchar(255)  DEFAULT NULL,
  `uid` varchar(100)  DEFAULT NULL,
  `reference_no` varchar(255)  DEFAULT NULL COMMENT 'APPLICATION REFERENCE NUMBER',
  `created_by` varchar(255)  DEFAULT NULL,
  `created_date` datetime NOT NULL DEFAULT current_timestamp(),
  `description` longtext  DEFAULT NULL,
  `request` mediumtext  DEFAULT NULL,
  `request_time` datetime DEFAULT NULL,
  `response` mediumtext  DEFAULT NULL,
  `response_time` datetime DEFAULT NULL,
  `status_code` mediumtext  DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE='utf8_general_ci';
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值