一、自定义日志注解
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" ; @Autowiredprivate 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( )) ) { //根据不同的请求类型存入请求内容:POSTString requestBody = WebLogUtil.buildPostParam( point) ; webApiLogParam.setRequestBody( requestBody) ; log.info( "Request Args :" + WebLogUtil.buildPostParam( point)) ; } else if ( GET.equalsIgnoreCase( request.getMethod( )) ) { //GETwebApiLogParam.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_webtry {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> {
}