一、自定义日志注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Description 日志监控自定义注解
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface WebLog {
WebApiLogType logType() default WebApiLogType.NULL;
String desc() default "";
String name() default "";
}
二、定义AOP逻辑
import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.util.Date;
import java.util.Objects;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
public class WebLogApiAspect {
private static final Logger log = LoggerFactory.getLogger(WebLogApiAspect.class);
public final static String POST ="POST", GET = "GET";
@Autowired
private LogService logService;
/**
* 采用异步逻辑处理日志保存,降低整体调用的时间
* <p>只开一个线程处理日志保存逻辑,防止流量洪峰压垮保存逻辑侧</p>
* <p>拒绝策略采用直接拒绝而非交给主线程处理,则当前的监控日志只能承担1001的TPS</p>
*/
public static Executor EXECUTOR = new ThreadPoolExecutor(1, 1, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000), new ThreadPoolExecutor.AbortPolicy());
@Pointcut("@annotation(com.common.aop.WebLog) ")
public void webLogPoint() {
}
@Around("webLogPoint()")
public Object webLogRecord(ProceedingJoinPoint point) throws Throwable {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (Objects.isNull(requestAttributes)){
return point.proceed();
}
HttpServletRequest request = requestAttributes.getRequest();
Object result=null;
Exception e = null;
try {
result= point.proceed();
} catch (Exception exception) {
e = exception;
throw exception;
} finally {
try {
save(request,point,result,e);
} catch (Exception aopException) {
log.error("webLogRecord error", aopException);
}
}
return result;
}
private void save(HttpServletRequest request, ProceedingJoinPoint point, Object result, Exception e) {
LogParam webApiLogParam = new LogParam();
if (POST.equalsIgnoreCase(request.getMethod())){
//根据不同的请求类型存入请求内容:POST
String requestBody = WebLogUtil.buildPostParam(point);
webApiLogParam.setRequestBody(requestBody);
log.info("Request Args :"+ WebLogUtil.buildPostParam(point));
} else if (GET.equalsIgnoreCase(request.getMethod())) {
//GET
webApiLogParam.setRequestBody(request.getQueryString());
log.info("get Args : "+ request.getQueryString());
} else {
log.info("接口监控只支持REST风格的GET/POST方法,当前方法为 {}", request.getMethod());
return;
}
//请求参数组装
buildRequestParam(webApiLogParam,request,point);
//响应结果过组装
buildResultParam(webApiLogParam, result);
//异常信息组装
if (null!=e) {
buildExceptionParam(webApiLogParam, e);
}
EXECUTOR.execute(() -> {
//日志model发送到api_web
try {
logService.getBaseMapper().insert(BeanUtil.copyProperties(webApiLogParam, WebApiLogParam.class));
} catch (Exception exception) {
log.error("日志监控保存失败",exception);
}
});
}
private void buildExceptionParam(LogParam webApiLogParam, Exception exception) {
webApiLogParam.setException(1);
webApiLogParam.setExceptionMessage(exception.toString());
webApiLogParam.setErr(exception.toString());
}
private void buildRequestParam(LogParam webApiLogParam, HttpServletRequest request, ProceedingJoinPoint point) {
//反射获取自定义注解
WebLog webLog = ((MethodSignature) point.getSignature()).getMethod().getAnnotation(WebLog.class);
webApiLogParam.setUrl(request.getRequestURL().toString());
webApiLogParam.setMethod(request.getMethod());
webApiLogParam.setRetry(0);
webApiLogParam.setApi(point.getSignature().toString());
if (StringUtils.isNotEmpty(request.getServletPath())){
webApiLogParam.setSourceCode(Objects.requireNonNull(webLog.name()));
webApiLogParam.setSourceType(Objects.requireNonNull(webLog.desc()));
}
webApiLogParam.setCreator(IpUtils.getIpAddr(request));
webApiLogParam.setCreateTime(new Date());
webApiLogParam.setIsDeleted(0);
}
public void buildResultParam(LogParam webApiLogParam,Object result) {
try {
if (result instanceof ChannelResult) {
ChannelResult<?> modelResult = (ChannelResult<?>) result;
if (ChannelResultUtil.isSuccess(modelResult)) {
webApiLogParam.setResult(0);
}else {
webApiLogParam.setResult(1);
}
webApiLogParam.setResponseBody(JSON.toJSONString(modelResult));
} else if (result instanceof ChannelResult) {
ChannelResult<?> modelResult = (ChannelResult<?>) result;
if (ChannelResultUtil.isSuccess(modelResult)) {
webApiLogParam.setResult(0);
}else {
webApiLogParam.setResult(1);
}
webApiLogParam.setResponseBody(JSON.toJSONString(modelResult));
}else {
webApiLogParam.setResult(0);
webApiLogParam.setResponseBody(JSON.toJSONString(result));
}
} catch (Exception exception) {
log.error("组装响应结果失败", exception);
}
}
}
三.日志类和工具
import com.alibaba.fastjson.JSON;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Objects;
/**
* @ClassName WebLogUtil
*/
@Component
public class WebLogUtil {
public static String buildPostParam(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
Annotation[] parameterAnnotation = parameterAnnotations[i];
if (null != parameterAnnotation) {
for (Annotation annotation : parameterAnnotation) {
Objects.equals(RequestBody.class, annotation.getClass());
if (args[i] == null) return null;
return JSON.toJSONString(args[i]);
}
}
}
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
if (parameter.getType() != HttpServletRequest.class
&& parameter.getType() != HttpServletResponse.class
&& parameter.getType() != ServletRequest.class
&& parameter.getType() != ServletResponse.class) {
if (args[i] == null) return null;
return JSON.toJSONString(args[i]);
}
}
return null;
}
}
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* @Description 日志监控模型
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class LogParam implements Serializable {
private Long id;
/**
* 来源单号
*/
private String sourceCode;
/**
* 来源类型
*/
private String sourceType;
/**
* 请求方式
*/
private String method;
/**
* 请求入参
*/
private String requestBody;
/**
* 响应参数
*/
private String responseBody;
/**
* 接口推送结果
* 0成功1失败
*/
private Integer result;
/**
* 错误信息
*/
private String err;
/**
* 有无异常
* 0有1没有
*/
private Integer exception;
/**
* 异常信息
*/
private String exceptionMessage;
/**
* 重试标志
* 0是1否
*/
private Integer retry;
/**
* 接口地址
*/
private String url;
/**
* 请求方法
*/
private String api;
private Date createTime;
private Date modifyTime;
private Integer isDeleted;
private String creator;
private Long modifier;
}
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
public class IpUtils {
private static Logger logger = LoggerFactory.getLogger(IpUtils.class);
public IpUtils() {
}
public static String getIpAddr(HttpServletRequest request) {
String unknown = "unknown";
String ip = null;
try {
ip = request.getHeader("x-forwarded-for");
if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
} catch (Exception var4) {
logger.error("IPUtils ERROR ", var4);
}
return ip;
}
}
import com.baomidou.mybatisplus.extension.service.IService;
import com.dal.log.entity.WebApiLogParam;
public interface LogService extends IService<WebApiLogParam> {
}