@@ -30,6 +30,11 @@ | |||||
</properties> | </properties> | ||||
<dependencies> | <dependencies> | ||||
<dependency> | |||||
<groupId>com.google.guava</groupId> | |||||
<artifactId>guava</artifactId> | |||||
</dependency> | |||||
<dependency> | <dependency> | ||||
<groupId>org.springframework.boot</groupId> | <groupId>org.springframework.boot</groupId> | ||||
<artifactId>spring-boot-starter-web</artifactId> | <artifactId>spring-boot-starter-web</artifactId> | ||||
@@ -86,73 +91,149 @@ | |||||
* </p> | * </p> | ||||
* | * | ||||
* @author yangkai.shen | * @author yangkai.shen | ||||
* @author chen qi | |||||
* @date Created in 2018-10-01 22:05 | * @date Created in 2018-10-01 22:05 | ||||
*/ | */ | ||||
@Aspect | @Aspect | ||||
@Component | @Component | ||||
@Slf4j | @Slf4j | ||||
public class AopLog { | public class AopLog { | ||||
private static final String START_TIME = "request-start"; | |||||
/** | |||||
* 切入点 | |||||
*/ | |||||
@Pointcut("execution(public * com.xkcoding.log.aop.controller.*Controller.*(..))") | |||||
public void log() { | |||||
} | |||||
/** | |||||
* 前置操作 | |||||
* | |||||
* @param point 切入点 | |||||
*/ | |||||
@Before("log()") | |||||
public void beforeLog(JoinPoint point) { | |||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); | |||||
HttpServletRequest request = Objects.requireNonNull(attributes).getRequest(); | |||||
log.info("【请求 URL】:{}", request.getRequestURL()); | |||||
log.info("【请求 IP】:{}", request.getRemoteAddr()); | |||||
log.info("【请求类名】:{},【请求方法名】:{}", point.getSignature().getDeclaringTypeName(), point.getSignature().getName()); | |||||
Map<String, String[]> parameterMap = request.getParameterMap(); | |||||
log.info("【请求参数】:{},", JSONUtil.toJsonStr(parameterMap)); | |||||
Long start = System.currentTimeMillis(); | |||||
request.setAttribute(START_TIME, start); | |||||
} | |||||
/** | |||||
* 环绕操作 | |||||
* | |||||
* @param point 切入点 | |||||
* @return 原方法返回值 | |||||
* @throws Throwable 异常信息 | |||||
*/ | |||||
@Around("log()") | |||||
public Object aroundLog(ProceedingJoinPoint point) throws Throwable { | |||||
Object result = point.proceed(); | |||||
log.info("【返回值】:{}", JSONUtil.toJsonStr(result)); | |||||
return result; | |||||
} | |||||
/** | |||||
* 后置操作 | |||||
*/ | |||||
@AfterReturning("log()") | |||||
public void afterReturning() { | |||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); | |||||
HttpServletRequest request = Objects.requireNonNull(attributes).getRequest(); | |||||
Long start = (Long) request.getAttribute(START_TIME); | |||||
Long end = System.currentTimeMillis(); | |||||
log.info("【请求耗时】:{}毫秒", end - start); | |||||
String header = request.getHeader("User-Agent"); | |||||
UserAgent userAgent = UserAgent.parseUserAgentString(header); | |||||
log.info("【浏览器类型】:{},【操作系统】:{},【原始User-Agent】:{}", userAgent.getBrowser().toString(), userAgent.getOperatingSystem().toString(), header); | |||||
} | |||||
/** | |||||
* 切入点 | |||||
*/ | |||||
@Pointcut("execution(public * com.xkcoding.log.aop.controller.*Controller.*(..))") | |||||
public void log() { | |||||
} | |||||
/** | |||||
* 环绕操作 | |||||
* | |||||
* @param point 切入点 | |||||
* @return 原方法返回值 | |||||
* @throws Throwable 异常信息 | |||||
*/ | |||||
@Around("log()") | |||||
public Object aroundLog(ProceedingJoinPoint point) throws Throwable { | |||||
// 开始打印请求日志 | |||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); | |||||
HttpServletRequest request = Objects.requireNonNull(attributes).getRequest(); | |||||
// 打印请求相关参数 | |||||
long startTime = System.currentTimeMillis(); | |||||
Object result = point.proceed(); | |||||
String header = request.getHeader("User-Agent"); | |||||
UserAgent userAgent = UserAgent.parseUserAgentString(header); | |||||
final Log l = Log.builder() | |||||
.threadId(Long.toString(Thread.currentThread().getId())) | |||||
.threadName(Thread.currentThread().getName()) | |||||
.ip(getIp(request)) | |||||
.url(request.getRequestURL().toString()) | |||||
.classMethod(String.format("%s.%s", point.getSignature().getDeclaringTypeName(), | |||||
point.getSignature().getName())) | |||||
.httpMethod(request.getMethod()) | |||||
.requestParams(getNameAndValue(point)) | |||||
.result(result) | |||||
.timeCost(System.currentTimeMillis() - startTime) | |||||
.userAgent(header) | |||||
.browser(userAgent.getBrowser().toString()) | |||||
.os(userAgent.getOperatingSystem().toString()).build(); | |||||
log.info("Request Log Info : {}", JSONUtil.toJsonStr(l)); | |||||
return result; | |||||
} | |||||
/** | |||||
* 获取方法参数名和参数值 | |||||
* @param joinPoint | |||||
* @return | |||||
*/ | |||||
private Map<String, Object> getNameAndValue(ProceedingJoinPoint joinPoint) { | |||||
final Signature signature = joinPoint.getSignature(); | |||||
MethodSignature methodSignature = (MethodSignature) signature; | |||||
final String[] names = methodSignature.getParameterNames(); | |||||
final Object[] args = joinPoint.getArgs(); | |||||
if (ArrayUtil.isEmpty(names) || ArrayUtil.isEmpty(args)) { | |||||
return Collections.emptyMap(); | |||||
} | |||||
if (names.length != args.length) { | |||||
log.warn("{}方法参数名和参数值数量不一致", methodSignature.getName()); | |||||
return Collections.emptyMap(); | |||||
} | |||||
Map<String, Object> map = Maps.newHashMap(); | |||||
for (int i = 0; i < names.length; i++) { | |||||
map.put(names[i], args[i]); | |||||
} | |||||
return map; | |||||
} | |||||
private static final String UNKNOWN = "unknown"; | |||||
/** | |||||
* 获取ip地址 | |||||
*/ | |||||
public static String getIp(HttpServletRequest request) { | |||||
String ip = request.getHeader("x-forwarded-for"); | |||||
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { | |||||
ip = request.getHeader("Proxy-Client-IP"); | |||||
} | |||||
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { | |||||
ip = request.getHeader("WL-Proxy-Client-IP"); | |||||
} | |||||
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { | |||||
ip = request.getRemoteAddr(); | |||||
} | |||||
String comma = ","; | |||||
String localhost = "127.0.0.1"; | |||||
if (ip.contains(comma)) { | |||||
ip = ip.split(",")[0]; | |||||
} | |||||
if (localhost.equals(ip)) { | |||||
// 获取本机真正的ip地址 | |||||
try { | |||||
ip = InetAddress.getLocalHost().getHostAddress(); | |||||
} catch (UnknownHostException e) { | |||||
log.error(e.getMessage(), e); | |||||
} | |||||
} | |||||
return ip; | |||||
} | |||||
@Data | |||||
@Builder | |||||
@NoArgsConstructor | |||||
@AllArgsConstructor | |||||
static class Log { | |||||
// 线程id | |||||
private String threadId; | |||||
// 线程名称 | |||||
private String threadName; | |||||
// ip | |||||
private String ip; | |||||
// url | |||||
private String url; | |||||
// http方法 GET POST PUT DELETE PATCH | |||||
private String httpMethod; | |||||
// 类方法 | |||||
private String classMethod; | |||||
// 请求参数 | |||||
private Object requestParams; | |||||
// 返回参数 | |||||
private Object result; | |||||
// 接口耗时 | |||||
private Long timeCost; | |||||
// 操作系统 | |||||
private String os; | |||||
// 浏览器 | |||||
private String browser; | |||||
// user-agent | |||||
private String userAgent; | |||||
} | |||||
} | } | ||||
``` | ``` | ||||
@@ -165,22 +246,36 @@ public class AopLog { | |||||
* </p> | * </p> | ||||
* | * | ||||
* @author yangkai.shen | * @author yangkai.shen | ||||
* @author chen qi | |||||
* @date Created in 2018-10-01 22:10 | * @date Created in 2018-10-01 22:10 | ||||
*/ | */ | ||||
@Slf4j | |||||
@RestController | @RestController | ||||
public class TestController { | public class TestController { | ||||
/** | |||||
* 测试方法 | |||||
* | |||||
* @param who 测试参数 | |||||
* @return {@link Dict} | |||||
*/ | |||||
@GetMapping("/test") | |||||
public Dict test(String who) { | |||||
return Dict.create().set("who", StrUtil.isBlank(who) ? "me" : who); | |||||
} | |||||
/** | |||||
* 测试方法 | |||||
* | |||||
* @param who 测试参数 | |||||
* @return {@link Dict} | |||||
*/ | |||||
@GetMapping("/test") | |||||
public Dict test(String who) { | |||||
return Dict.create().set("who", StrUtil.isBlank(who) ? "me" : who); | |||||
} | |||||
/** | |||||
* 测试post json方法 | |||||
* @param map 请求的json参数 | |||||
* @return {@link Dict} | |||||
*/ | |||||
@PostMapping("/testJson") | |||||
public Dict testJson(@RequestBody Map<String, Object> map) { | |||||
final String jsonStr = JSONUtil.toJsonStr(map); | |||||
log.info(jsonStr); | |||||
return Dict.create().set("json", map); | |||||
} | |||||
} | } | ||||
``` | ``` | ||||
@@ -23,6 +23,12 @@ | |||||
</properties> | </properties> | ||||
<dependencies> | <dependencies> | ||||
<dependency> | |||||
<groupId>com.google.guava</groupId> | |||||
<artifactId>guava</artifactId> | |||||
</dependency> | |||||
<dependency> | <dependency> | ||||
<groupId>org.springframework.boot</groupId> | <groupId>org.springframework.boot</groupId> | ||||
<artifactId>spring-boot-starter-web</artifactId> | <artifactId>spring-boot-starter-web</artifactId> | ||||
@@ -1,16 +1,28 @@ | |||||
package com.xkcoding.log.aop.aspectj; | package com.xkcoding.log.aop.aspectj; | ||||
import cn.hutool.core.util.ArrayUtil; | |||||
import cn.hutool.json.JSONUtil; | import cn.hutool.json.JSONUtil; | ||||
import com.google.common.collect.Maps; | |||||
import eu.bitwalker.useragentutils.UserAgent; | import eu.bitwalker.useragentutils.UserAgent; | ||||
import lombok.AllArgsConstructor; | |||||
import lombok.Builder; | |||||
import lombok.Data; | |||||
import lombok.NoArgsConstructor; | |||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
import org.aspectj.lang.JoinPoint; | |||||
import org.aspectj.lang.ProceedingJoinPoint; | import org.aspectj.lang.ProceedingJoinPoint; | ||||
import org.aspectj.lang.annotation.*; | |||||
import org.aspectj.lang.Signature; | |||||
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.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||
import org.springframework.web.context.request.RequestContextHolder; | import org.springframework.web.context.request.RequestContextHolder; | ||||
import org.springframework.web.context.request.ServletRequestAttributes; | import org.springframework.web.context.request.ServletRequestAttributes; | ||||
import javax.servlet.http.HttpServletRequest; | import javax.servlet.http.HttpServletRequest; | ||||
import java.net.InetAddress; | |||||
import java.net.UnknownHostException; | |||||
import java.util.Collections; | |||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Objects; | import java.util.Objects; | ||||
@@ -20,14 +32,13 @@ import java.util.Objects; | |||||
* </p> | * </p> | ||||
* | * | ||||
* @author yangkai.shen | * @author yangkai.shen | ||||
* @author chen qi | |||||
* @date Created in 2018-10-01 22:05 | * @date Created in 2018-10-01 22:05 | ||||
*/ | */ | ||||
@Aspect | @Aspect | ||||
@Component | @Component | ||||
@Slf4j | @Slf4j | ||||
public class AopLog { | public class AopLog { | ||||
private static final String START_TIME = "request-start"; | |||||
/** | /** | ||||
* 切入点 | * 切入点 | ||||
*/ | */ | ||||
@@ -36,27 +47,6 @@ public class AopLog { | |||||
} | } | ||||
/** | |||||
* 前置操作 | |||||
* | |||||
* @param point 切入点 | |||||
*/ | |||||
@Before("log()") | |||||
public void beforeLog(JoinPoint point) { | |||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); | |||||
HttpServletRequest request = Objects.requireNonNull(attributes).getRequest(); | |||||
log.info("【请求 URL】:{}", request.getRequestURL()); | |||||
log.info("【请求 IP】:{}", request.getRemoteAddr()); | |||||
log.info("【请求类名】:{},【请求方法名】:{}", point.getSignature().getDeclaringTypeName(), point.getSignature().getName()); | |||||
Map<String, String[]> parameterMap = request.getParameterMap(); | |||||
log.info("【请求参数】:{},", JSONUtil.toJsonStr(parameterMap)); | |||||
Long start = System.currentTimeMillis(); | |||||
request.setAttribute(START_TIME, start); | |||||
} | |||||
/** | /** | ||||
* 环绕操作 | * 环绕操作 | ||||
* | * | ||||
@@ -66,25 +56,123 @@ public class AopLog { | |||||
*/ | */ | ||||
@Around("log()") | @Around("log()") | ||||
public Object aroundLog(ProceedingJoinPoint point) throws Throwable { | public Object aroundLog(ProceedingJoinPoint point) throws Throwable { | ||||
// 开始打印请求日志 | |||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); | |||||
HttpServletRequest request = Objects.requireNonNull(attributes).getRequest(); | |||||
// 打印请求相关参数 | |||||
long startTime = System.currentTimeMillis(); | |||||
Object result = point.proceed(); | Object result = point.proceed(); | ||||
log.info("【返回值】:{}", JSONUtil.toJsonStr(result)); | |||||
String header = request.getHeader("User-Agent"); | |||||
UserAgent userAgent = UserAgent.parseUserAgentString(header); | |||||
final Log l = Log.builder() | |||||
.threadId(Long.toString(Thread.currentThread().getId())) | |||||
.threadName(Thread.currentThread().getName()) | |||||
.ip(getIp(request)) | |||||
.url(request.getRequestURL().toString()) | |||||
.classMethod(String.format("%s.%s", point.getSignature().getDeclaringTypeName(), | |||||
point.getSignature().getName())) | |||||
.httpMethod(request.getMethod()) | |||||
.requestParams(getNameAndValue(point)) | |||||
.result(result) | |||||
.timeCost(System.currentTimeMillis() - startTime) | |||||
.userAgent(header) | |||||
.browser(userAgent.getBrowser().toString()) | |||||
.os(userAgent.getOperatingSystem().toString()).build(); | |||||
log.info("Request Log Info : {}", JSONUtil.toJsonStr(l)); | |||||
return result; | return result; | ||||
} | } | ||||
/** | /** | ||||
* 后置操作 | |||||
* 获取方法参数名和参数值 | |||||
* @param joinPoint | |||||
* @return | |||||
*/ | */ | ||||
@AfterReturning("log()") | |||||
public void afterReturning() { | |||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); | |||||
HttpServletRequest request = Objects.requireNonNull(attributes).getRequest(); | |||||
private Map<String, Object> getNameAndValue(ProceedingJoinPoint joinPoint) { | |||||
Long start = (Long) request.getAttribute(START_TIME); | |||||
Long end = System.currentTimeMillis(); | |||||
log.info("【请求耗时】:{}毫秒", end - start); | |||||
final Signature signature = joinPoint.getSignature(); | |||||
MethodSignature methodSignature = (MethodSignature) signature; | |||||
final String[] names = methodSignature.getParameterNames(); | |||||
final Object[] args = joinPoint.getArgs(); | |||||
String header = request.getHeader("User-Agent"); | |||||
UserAgent userAgent = UserAgent.parseUserAgentString(header); | |||||
log.info("【浏览器类型】:{},【操作系统】:{},【原始User-Agent】:{}", userAgent.getBrowser().toString(), userAgent.getOperatingSystem().toString(), header); | |||||
if (ArrayUtil.isEmpty(names) || ArrayUtil.isEmpty(args)) { | |||||
return Collections.emptyMap(); | |||||
} | |||||
if (names.length != args.length) { | |||||
log.warn("{}方法参数名和参数值数量不一致", methodSignature.getName()); | |||||
return Collections.emptyMap(); | |||||
} | |||||
Map<String, Object> map = Maps.newHashMap(); | |||||
for (int i = 0; i < names.length; i++) { | |||||
map.put(names[i], args[i]); | |||||
} | |||||
return map; | |||||
} | |||||
private static final String UNKNOWN = "unknown"; | |||||
/** | |||||
* 获取ip地址 | |||||
*/ | |||||
public static String getIp(HttpServletRequest request) { | |||||
String ip = request.getHeader("x-forwarded-for"); | |||||
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { | |||||
ip = request.getHeader("Proxy-Client-IP"); | |||||
} | |||||
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { | |||||
ip = request.getHeader("WL-Proxy-Client-IP"); | |||||
} | |||||
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { | |||||
ip = request.getRemoteAddr(); | |||||
} | |||||
String comma = ","; | |||||
String localhost = "127.0.0.1"; | |||||
if (ip.contains(comma)) { | |||||
ip = ip.split(",")[0]; | |||||
} | |||||
if (localhost.equals(ip)) { | |||||
// 获取本机真正的ip地址 | |||||
try { | |||||
ip = InetAddress.getLocalHost().getHostAddress(); | |||||
} catch (UnknownHostException e) { | |||||
log.error(e.getMessage(), e); | |||||
} | |||||
} | |||||
return ip; | |||||
} | |||||
@Data | |||||
@Builder | |||||
@NoArgsConstructor | |||||
@AllArgsConstructor | |||||
static class Log { | |||||
// 线程id | |||||
private String threadId; | |||||
// 线程名称 | |||||
private String threadName; | |||||
// ip | |||||
private String ip; | |||||
// url | |||||
private String url; | |||||
// http方法 GET POST PUT DELETE PATCH | |||||
private String httpMethod; | |||||
// 类方法 | |||||
private String classMethod; | |||||
// 请求参数 | |||||
private Object requestParams; | |||||
// 返回参数 | |||||
private Object result; | |||||
// 接口耗时 | |||||
private Long timeCost; | |||||
// 操作系统 | |||||
private String os; | |||||
// 浏览器 | |||||
private String browser; | |||||
// user-agent | |||||
private String userAgent; | |||||
} | } | ||||
} | } |
@@ -2,17 +2,25 @@ package com.xkcoding.log.aop.controller; | |||||
import cn.hutool.core.lang.Dict; | import cn.hutool.core.lang.Dict; | ||||
import cn.hutool.core.util.StrUtil; | import cn.hutool.core.util.StrUtil; | ||||
import cn.hutool.json.JSONUtil; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
import org.springframework.web.bind.annotation.GetMapping; | import org.springframework.web.bind.annotation.GetMapping; | ||||
import org.springframework.web.bind.annotation.PostMapping; | |||||
import org.springframework.web.bind.annotation.RequestBody; | |||||
import org.springframework.web.bind.annotation.RestController; | import org.springframework.web.bind.annotation.RestController; | ||||
import java.util.Map; | |||||
/** | /** | ||||
* <p> | * <p> | ||||
* 测试 Controller | * 测试 Controller | ||||
* </p> | * </p> | ||||
* | * | ||||
* @author yangkai.shen | * @author yangkai.shen | ||||
* @author chen qi | |||||
* @date Created in 2018-10-01 22:10 | * @date Created in 2018-10-01 22:10 | ||||
*/ | */ | ||||
@Slf4j | |||||
@RestController | @RestController | ||||
public class TestController { | public class TestController { | ||||
@@ -27,4 +35,16 @@ public class TestController { | |||||
return Dict.create().set("who", StrUtil.isBlank(who) ? "me" : who); | return Dict.create().set("who", StrUtil.isBlank(who) ? "me" : who); | ||||
} | } | ||||
/** | |||||
* 测试post json方法 | |||||
* @param map 请求的json参数 | |||||
* @return {@link Dict} | |||||
*/ | |||||
@PostMapping("/testJson") | |||||
public Dict testJson(@RequestBody Map<String, Object> map) { | |||||
final String jsonStr = JSONUtil.toJsonStr(map); | |||||
log.info(jsonStr); | |||||
return Dict.create().set("json", map); | |||||
} | |||||
} | } |