1.自定义注解
/**
* @Author ZhengQinfeng
* @Date 2020/10/31 16:33
* @dec
*/
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperLog {
/**
* 操作模块
*
* @return
*/
String operModule() default "";
/**
* 操作类型
*
* @return
*/
String operType() default "";
/**
* 操作描述
*
* @return
*/
String operDesc() default "";
}
2.自定义切面
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.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import qinfeng.zheng.anno.OperLog;
import qinfeng.zheng.util.IPUtil;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
/**
* @Author ZhengQinfeng
* @Date 2020/10/31 16:33
* @dec
*/
@Slf4j
@Aspect
@Component
public class OperLogAspect {
/**
* 定义日志操作切入点
*/
@Pointcut("@annotation(qinfeng.zheng.anno.OperLog)")
public void operLogPointCut() {
}
/**
* 切入点是controller包下的所有方法
*/
@Pointcut("execution(* qinfeng.zheng.controller..*.*(..))")
public void exceptionLogPointCut() {
}
/**
* 正常返回通知,如果切入点抛出异常,不会执行
*
* @param joinPoint 切入点
* @param response 返回结果
*/
@AfterReturning(value = "operLogPointCut()", returning = "response")
public void saveOperLog(JoinPoint joinPoint, Object response) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 切入点方法
Method method = signature.getMethod();
OperLog operLog = method.getAnnotation(OperLog.class);
String module = "";
String type = "";
String desc = "";
if (operLog != null) {
module = operLog.operModule();
type = operLog.operType();
desc = operLog.operDesc();
}
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = method.getName();
methodName = className + "." + methodName;
// 请求URI
String requestURI = request.getRequestURI();
// 操作ip
String operIp = IPUtil.getIpFromRequest(request);
// 请求参数
Object[] args = joinPoint.getArgs();
log.info("模块:{}, 类型:{}, 描述:{},方法:{}, 入参:{}, uri:{}, 请求ip:{}, 响应值:{}", module, type, desc, methodName, args, requestURI, operIp, response);
}
@AfterThrowing(pointcut = "exceptionLogPointCut()", throwing = "error")
public void saveException(JoinPoint joinPoint, Throwable error) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
try {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = method.getName();
methodName = className + "." + methodName;
// 请求URI
String requestURI = request.getRequestURI();
// 操作ip
String operIp = IPUtil.getIpFromRequest(request);
// 请求参数
Object[] args = joinPoint.getArgs();
// 获取异常名称
String exceptionName = error.getClass().getName();
// 异常信息
String errorMessage = stackTraceToString(exceptionName, error.getMessage(), error.getStackTrace());
log.info("方法:{}, 入参:{}, uri:{}, 请求ip:{}, 异常信息:{}", methodName, args, requestURI, operIp, errorMessage);
} catch (Exception e) {
log.error("{}", e.getMessage(), e);
}
}
public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] stackTraceElements) {
StringBuffer stringBuffer = new StringBuffer();
for (StackTraceElement traceElement : stackTraceElements) {
stringBuffer.append(traceElement).append("\n");
}
String message = exceptionName + ":" + exceptionMessage + "\n\t" + stringBuffer.toString();
return message;
}
}
3.Ip工具类
import org.apache.commons.lang.text.StrTokenizer;
import javax.servlet.http.HttpServletRequest;
import java.util.regex.Pattern;
/**
* IpV4 获取真实ip地址
*/
public class IPUtil {
public static final String _255 = "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)";
public static final Pattern pattern = Pattern.compile("^(?:" + _255 + "\\.){3}" + _255 + "$");
/**
* String类型ip转为Long类型
*
* @param longIp
* @return String
*/
public static String longToIpV4(long longIp) {
int octet3 = (int) ((longIp >> 24) % 256);
int octet2 = (int) ((longIp >> 16) % 256);
int octet1 = (int) ((longIp >> 8) % 256);
int octet0 = (int) ((longIp) % 256);
return octet3 + "." + octet2 + "." + octet1 + "." + octet0;
}
/**
* Long类型ip转为String类型
*
* @param ip
* @return Long
*/
public static long ipV4ToLong(String ip) {
String[] octets = ip.split("\\.");
return (Long.parseLong(octets[0]) << 24) + (Integer.parseInt(octets[1]) << 16)
+ (Integer.parseInt(octets[2]) << 8) + Integer.parseInt(octets[3]);
}
/**
* @param ip
* @return boolean
*/
public static boolean isIPv4Private(String ip) {
long longIp = ipV4ToLong(ip);
return (longIp >= ipV4ToLong("10.0.0.0") && longIp <= ipV4ToLong("10.255.255.255"))
|| (longIp >= ipV4ToLong("172.16.0.0") && longIp <= ipV4ToLong("172.31.255.255"))
|| longIp >= ipV4ToLong("192.168.0.0") && longIp <= ipV4ToLong("192.168.255.255");
}
public static boolean isIPv4Valid(String ip) {
return pattern.matcher(ip).matches();
}
/**
* 获取String类型真实ip地址,基于反向代理。
*
* @param request
* @return
* 在反向代理中将X-Forward-For替换为remote_addr,即,真实的IP地址。
*/
public static String getIpFromRequest(HttpServletRequest request) {
String ip;
boolean found = false;
if ((ip = request.getHeader("x-forwarded-for")) != null) {
StrTokenizer tokenizer = new StrTokenizer(ip, ",");
while (tokenizer.hasNext()) {
ip = tokenizer.nextToken().trim();
if (isIPv4Valid(ip) && !isIPv4Private(ip)) {
found = true;
break;
}
}
}
if (!found) {
ip = request.getRemoteAddr();// 获得ip地址
}
return ip;
}
}
4.测试类
import org.springframework.web.bind.annotation.*;
import qinfeng.zheng.anno.OperLog;
import qinfeng.zheng.vo.UserInfo;
import java.util.ArrayList;
import java.util.List;
/**
* @Author ZhengQinfeng
* @Date 2020/10/31 20:16
* @dec
*/
@RestController
public class TestController {
@OperLog(operModule = "测试模块", operType = "新增", operDesc = "新增用户")
@PostMapping("/index")
public List<UserInfo> index(@RequestBody UserInfo userInfo) {
List<UserInfo> rets = new ArrayList<>();
rets.add(userInfo);
rets.add(userInfo);
return rets;
}
@OperLog(operModule = "测试模块", operType = "错误信息", operDesc = "测试异常打印信息")
@GetMapping("/error/{number}")
public Integer index(@PathVariable Integer number) {
return 10 / number;
}
}
日拱一卒无有尽,功不唐捐终入海