1、注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author * @program * @description 忽略统一响应注解定义* @packagename * @date **/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {/*** 模块*/public String title() default "";public BusinessType businessType() default BusinessType.SELECT;/*** 是否保存请求的参数*/public boolean isSaveRequestData() default true;/*** 是否保存响应的参数*/public boolean isSaveResponseData() default true;}
2、接口类型
public enum BusinessType {/*** 保存**/SAVE,/*** 删除**/DELETE,/*** 查询**/SELECT;}
3、日志实体
@Data
@TableName("user_log")
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "用户日志", description = "用户日志")
public class UserLog implements Serializable {private static final long serialVersionUID = 1L;/*** 自增主键id*/@TableId(value = "id", type = IdType.AUTO)@ApiModelProperty(value = "主键")private Long id;/*** 用户编号*/@ApiModelProperty(value = "用户编号")private Long userNo;/*** 用户账号*/@ApiModelProperty(value = "用户账号")private String userAccount;@ApiModelProperty(value = "客户端ID")private String clientId;/*** 用户名称*/@ApiModelProperty(value = "用户名称")private String userName;/*** 操作方法*/@ApiModelProperty(value = "操作方法")private String callMethod;/*** 操作结果*/@ApiModelProperty(value = "操作结果")private String callResult;/*** 操作时间*/@ApiModelProperty(value = "操作时间")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")private Date operationTime;/*** 操作内容*/@ApiModelProperty(value = "操作内容")private String operationContent;/*** 操作IP*/@ApiModelProperty(value = "操作IP")private String operationIp;private String operationParam;private String operationResult;private String operationMsg;/*** 操作客户端*/@ApiModelProperty(value = "操作客户端")private String operationClient;/*** 操作地理位置*/@ApiModelProperty(value = "操作地理位置")private String operationAddress;}
4、日志记录
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hierway.tool.utils.AddressUtils;
import com.hierway.tool.utils.RequestHeaderUtils;
import com.hierway.user.bo.OauthInfo;
import com.hierway.user.entity.UserLog;
import com.hierway.user.feign.IUserLogClient;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*** @author * @program * @description 接口日志记录* @packagename * @date **/
@Aspect
@Component
@Slf4j
public class LogHandlerAspect {@Value("${spring.log-filter.enabled:true}")private Boolean enabled;@Value("${spring.log-filter.type:SAVE,DELETE}")private List<String> filterType;@Resourceprivate IUserLogClient userLogClient;private ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 20, 60, TimeUnit.MINUTES,new SynchronousQueue<Runnable>(), new ThreadPoolExecutor.DiscardPolicy());/*** 切点*//*** 处理完请求后执行** @param joinPoint 切点*/@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult){try {if(!enabled||!filterType.contains(controllerLog.businessType().toString())){return;}UserLog userLog = new UserLog();userLog.setOperationTime(new Date());String className = joinPoint.getTarget().getClass().getName();String methodName = joinPoint.getSignature().getName();userLog.setCallMethod(className + "." + methodName);userLog.setOperationContent(controllerLog.title());// 是否需要保存request,参数和值if (controllerLog.isSaveRequestData()){userLog.setOperationParam("");// 获取参数的信息,传入到数据库中。Object[] params = joinPoint.getArgs();for (int i = 0; i < params.length; i++) {Object param = params[i];userLog.setOperationParam(userLog.getOperationParam()+"参数"+i+":"+JSON.toJSONString(param)+";");}}// 是否需要保存response,参数和值if (controllerLog.isSaveResponseData() && jsonResult!=null){userLog.setOperationResult(JSON.toJSONString(jsonResult));}userLog.setCallResult("成功");userLog.setOperationIp(RequestHeaderUtils.getIpAddr());userLog.setOperationClient(RequestHeaderUtils.getClient());userLog.setOperationAddress(AddressUtils.getAddresses(userLog.getOperationIp()));executor.execute(new Runnable() {@Overridepublic void run() {try {userLogClient.create(userLog);}catch (Exception ex){log.error("保存用户日志失败",ex);}}});}catch (Exception ex){log.error("请求日志失败",ex);}}/*** 拦截异常操作** @param joinPoint 切点* @param e 异常*/@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e){try {if(!enabled||!filterType.contains(controllerLog.businessType().toString())){return;}UserLog userLog = new UserLog();userLog.setOperationTime(new Date());String className = joinPoint.getTarget().getClass().getName();String methodName = joinPoint.getSignature().getName();userLog.setCallMethod(className + "." + methodName);userLog.setOperationContent(controllerLog.title());// 是否需要保存request,参数和值if (controllerLog.isSaveRequestData()){userLog.setOperationParam("");// 获取参数的信息,传入到数据库中。Object[] params = joinPoint.getArgs();for (int i = 0; i < params.length; i++) {Object param = params[i];userLog.setOperationParam(userLog.getOperationParam()+"参数"+i+":"+JSON.toJSONString(param)+";");}}userLog.setOperationMsg(e.getMessage());userLog.setCallResult("失败");userLog.setOperationIp(RequestHeaderUtils.getIpAddr());userLog.setOperationClient(RequestHeaderUtils.getClient());userLog.setOperationAddress(AddressUtils.getAddresses(userLog.getOperationIp()));executor.execute(new Runnable() {@Overridepublic void run() {try {userLogClient.create(userLog);}catch (Exception ex){log.error("保存用户日志失败",ex);}}});}catch (Exception ex){log.error("请求日志失败",ex);}}}
5、请求头获取类
public class RequestHeaderUtils {public static String getClient(){HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String agent = request.getHeader("user-agent");StringTokenizer st = new StringTokenizer(agent,";");//得到用户的浏览器名st.nextToken();return agent;}public static String getIpAddr(){HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();if (request == null){return null;}String ip = null;// X-Forwarded-For:Squid 服务代理String ipAddresses = request.getHeader("X-Forwarded-For");if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)){// Proxy-Client-IP:apache 服务代理ipAddresses = request.getHeader("Proxy-Client-IP");}if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)){// WL-Proxy-Client-IP:weblogic 服务代理ipAddresses = request.getHeader("WL-Proxy-Client-IP");}if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)){// HTTP_CLIENT_IP:有些代理服务器ipAddresses = request.getHeader("HTTP_CLIENT_IP");}if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)){// X-Real-IP:nginx服务代理ipAddresses = request.getHeader("X-Real-IP");}// 有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IPif (ipAddresses != null && ipAddresses.length() != 0){ip = ipAddresses.split(",")[0];}// 还是不能获取到,最后再通过request.getRemoteAddr();获取if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)){ip = request.getRemoteAddr();}return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;}
}