1.搭建一个SpringBoot web环境

image-20231009211711212

maven依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--工具类包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>

2.自定义注解

1
2
3
4
5
6
7
8
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
String module() default ""; //模块名称

String operator() default "";//描述
}

3.切面和切点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
@Slf4j
@Aspect
@Component
public class LogAspect {

//切点
@Pointcut("@annotation(com.yang.aop.Log)")
public void pointcut() {
}

//环绕通知
@Around("pointcut()")
public Object log(ProceedingJoinPoint pjp) {
Object result = null;
long begin = System.currentTimeMillis();
try {
result = pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
long time = System.currentTimeMillis() - begin;

//方法返回结果
log.info("result===>:{}",result);

recordLog(pjp, time);

return result;
}

/**
* 记录日志
*
* @param pjp
* @param time
*/
private void recordLog(ProceedingJoinPoint pjp, long time) {
MethodSignature signature = (MethodSignature) pjp.getSignature(); //返回方法的签名
Method method = signature.getMethod();
log.info("=====================log start================================");
//得到注解
Log annotation = method.getAnnotation(Log.class);
//日志记录注解信息
log.info("module:{}", annotation.module());
log.info("operator:{}", annotation.operator());

//获取目标对象
Object target = pjp.getTarget();

//获取方法名称
String methodName = signature.getName();
log.info("request method:{}", target.getClass() + "." + methodName + "()");

//获取请求参数
Object[] args = pjp.getArgs();

//获取方法参数名称的工具类
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
String[] parameterNames = u.getParameterNames(method);
if (args != null && parameterNames != null) {
StringBuilder params = new StringBuilder();
for (int i = 0; i < args.length; i++) {
params.append(" ").append(parameterNames[i]).append(": ").append(args[i]);
}
log.info("params:{}", params.toString());
}

//获得请求信息
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
log.info("ip:{}", IpUtils.getIpAddr(request));

log.info("excute time : {} ms",time);
log.info("=====================log end================================");


}
}

两个工具类

HttpContextUtils

1
2
3
4
5
6
7
8
9
10
/**
* HttpServletRequest
*
*/
public class HttpContextUtils {

public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
}

IpUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
* 获取Ip
*
*/
@Slf4j
public class IpUtils {

/**
* 获取IP地址
* <p>
* 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = null, unknown = "unknown", seperator = ",";
int maxLength = 15;
try {
ip = request.getHeader("x-forwarded-for");
if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
} catch (Exception e) {
log.error("IpUtils ERROR ", e);
}

// 使用代理,则获取第一个IP地址
if (StringUtils.isEmpty(ip) && ip.length() > maxLength) {
int idx = ip.indexOf(seperator);
if (idx > 0) {
ip = ip.substring(0, idx);
}
}

return ip;
}

/**
* 获取ip地址
*
* @return
*/
public static String getIpAddr() {
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
return getIpAddr(request);
}
}

4.测试

LogRecordController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@RestController
public class LogRecordController {


@Log(module = "日志记录模块",operator = "One")
@GetMapping("/one")
public void methodOne(String name) { }

@Log(module = "日志记录模块",operator = "Two")
@GetMapping("/two")
public void methodTwo() throws InterruptedException {
Thread.sleep(2000);
}

@Log(module = "日志记录模块",operator = "Three")
@GetMapping("/three")
public void methodThree(String name, String age) { }


@Log(module = "日志记录模块",operator = "Four")
@GetMapping("/four")
public List<String> methodFour(String name, String age) {
List<String> list = new ArrayList<>();
list.add(name);
list.add(age);
return list;

}
}

启动项目访问 http://localhost:8086/four?name=11&age=12

查看日志输出

image-20231009213520491

最终项目工程结构图

image-20231009213800925