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 filterType;
@Resource
private IUserLogClient userLogClient;
private ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 20, 60, TimeUnit.MINUTES,
new SynchronousQueue(), 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() {
@Override
public 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() {
@Override
public 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为客户端的真实IP
if (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;
}
}