From 30017fa381db25499f9700792df9898ed4934ad1 Mon Sep 17 00:00:00 2001 From: chenqi <1535985458@qq.com> Date: Mon, 16 Nov 2020 20:27:06 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20aop=E5=88=87=E9=9D=A2=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E6=96=B0=E5=A2=9Ejson=E5=8F=82=E6=95=B0=E6=89=93?= =?UTF-8?q?=E5=8D=B0=E5=92=8C=E9=AB=98=E5=B9=B6=E5=8F=91=E5=9C=BA=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 打印json类型的参数以及参数名和参数值 2. 高并发场景多线程情况下, 将日志封装到对象中一起打印 --- demo-log-aop/pom.xml | 6 + .../com/xkcoding/log/aop/aspectj/AopLog.java | 162 ++++++++++++++---- .../log/aop/controller/TestController.java | 20 +++ 3 files changed, 151 insertions(+), 37 deletions(-) diff --git a/demo-log-aop/pom.xml b/demo-log-aop/pom.xml index 7113002aa..a01664fbf 100644 --- a/demo-log-aop/pom.xml +++ b/demo-log-aop/pom.xml @@ -23,6 +23,12 @@ + + + com.google.guava + guava + + org.springframework.boot spring-boot-starter-web diff --git a/demo-log-aop/src/main/java/com/xkcoding/log/aop/aspectj/AopLog.java b/demo-log-aop/src/main/java/com/xkcoding/log/aop/aspectj/AopLog.java index 487a35a57..d39c093de 100644 --- a/demo-log-aop/src/main/java/com/xkcoding/log/aop/aspectj/AopLog.java +++ b/demo-log-aop/src/main/java/com/xkcoding/log/aop/aspectj/AopLog.java @@ -1,16 +1,28 @@ package com.xkcoding.log.aop.aspectj; +import cn.hutool.core.util.ArrayUtil; import cn.hutool.json.JSONUtil; +import com.google.common.collect.Maps; import eu.bitwalker.useragentutils.UserAgent; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.JoinPoint; 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.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Collections; import java.util.Map; import java.util.Objects; @@ -20,14 +32,13 @@ *

* * @author yangkai.shen + * @author chen qi * @date Created in 2018-10-01 22:05 */ @Aspect @Component @Slf4j public class AopLog { - private static final String START_TIME = "request-start"; - /** * 切入点 */ @@ -36,27 +47,6 @@ 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 parameterMap = request.getParameterMap(); - log.info("【请求参数】:{},", JSONUtil.toJsonStr(parameterMap)); - Long start = System.currentTimeMillis(); - request.setAttribute(START_TIME, start); - } - /** * 环绕操作 * @@ -66,25 +56,123 @@ public void beforeLog(JoinPoint point) { */ @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(); - 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; } /** - * 后置操作 + * 获取方法参数名和参数值 + * @param joinPoint + * @return */ - @AfterReturning("log()") - public void afterReturning() { - ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); - HttpServletRequest request = Objects.requireNonNull(attributes).getRequest(); + private Map 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 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; } } diff --git a/demo-log-aop/src/main/java/com/xkcoding/log/aop/controller/TestController.java b/demo-log-aop/src/main/java/com/xkcoding/log/aop/controller/TestController.java index 0eda8962c..c261d7917 100644 --- a/demo-log-aop/src/main/java/com/xkcoding/log/aop/controller/TestController.java +++ b/demo-log-aop/src/main/java/com/xkcoding/log/aop/controller/TestController.java @@ -2,17 +2,25 @@ import cn.hutool.core.lang.Dict; 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.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import java.util.Map; + /** *

* 测试 Controller *

* * @author yangkai.shen + * @author chen qi * @date Created in 2018-10-01 22:10 */ +@Slf4j @RestController public class TestController { @@ -27,4 +35,16 @@ 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 map) { + + final String jsonStr = JSONUtil.toJsonStr(map); + log.info(jsonStr); + return Dict.create().set("json", map); + } } From d8935e6969c1d7886f0bcf9f082058d21f5d5754 Mon Sep 17 00:00:00 2001 From: chenqi <1535985458@qq.com> Date: Tue, 17 Nov 2020 10:13:33 +0800 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20=E4=BF=AE=E6=94=B9log-aop=E7=9A=84R?= =?UTF-8?q?EADME.md=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- demo-log-aop/README.md | 239 ++++++++++++++++++++++++++++------------- 1 file changed, 167 insertions(+), 72 deletions(-) diff --git a/demo-log-aop/README.md b/demo-log-aop/README.md index 526a7da29..14fea8048 100644 --- a/demo-log-aop/README.md +++ b/demo-log-aop/README.md @@ -30,6 +30,11 @@ + + com.google.guava + guava + + org.springframework.boot spring-boot-starter-web @@ -86,73 +91,149 @@ *

* * @author yangkai.shen + * @author chen qi * @date Created in 2018-10-01 22:05 */ @Aspect @Component @Slf4j 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 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 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 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 { *

* * @author yangkai.shen + * @author chen qi * @date Created in 2018-10-01 22:10 */ +@Slf4j @RestController 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 map) { + + final String jsonStr = JSONUtil.toJsonStr(map); + log.info(jsonStr); + return Dict.create().set("json", map); + } } ```