Browse Source

使用 AOP 记录 web 请求日志

v-1.5.x
yangkai.shen 6 years ago
parent
commit
716a69ca54
11 changed files with 414 additions and 2 deletions
  1. +3
    -1
      README.md
  2. +1
    -1
      TODO.md
  3. +168
    -0
      spring-boot-demo-aoplog/README.md
  4. +41
    -0
      spring-boot-demo-aoplog/pom.xml
  5. +12
    -0
      spring-boot-demo-aoplog/src/main/java/com/xkcoding/springbootdemoaoplog/SpringBootDemoAoplogApplication.java
  6. +69
    -0
      spring-boot-demo-aoplog/src/main/java/com/xkcoding/springbootdemoaoplog/aspectj/AopLog.java
  7. +38
    -0
      spring-boot-demo-aoplog/src/main/java/com/xkcoding/springbootdemoaoplog/controller/IndexController.java
  8. +62
    -0
      spring-boot-demo-aoplog/src/main/java/com/xkcoding/springbootdemoaoplog/util/JsonMapper.java
  9. +3
    -0
      spring-boot-demo-aoplog/src/main/resources/application.yml
  10. +16
    -0
      spring-boot-demo-aoplog/src/test/java/com/xkcoding/springbootdemoaoplog/SpringBootDemoAoplogApplicationTests.java
  11. +1
    -0
      spring-boot-demo-parent/pom.xml

+ 3
- 1
README.md View File

@@ -1,6 +1,6 @@
# Spring Boot Demo

spring boot demo 是一个用来学习 spring boot 的项目,已经集成 actuator(监控)、logback(日志)、JPA(ORM 框架)、mybatis(ORM 框架)、redis-cache(缓存)、swagger(API 接口管理测试)、ureport2(中国式报表)模块,后续会集成activemq,email, freemarker,shiro,websocket,quartz,netty等模块。
spring boot demo 是一个用来学习 spring boot 的项目,已经集成 actuator(监控)、admin(可视化监控)、logback(日志)、aopLog(通过 AOP 记录 web 请求日志)、JPA(ORM 框架)、mybatis(ORM 框架)、redis-cache(缓存)、swagger(API 接口管理测试)、ureport2(中国式报表)模块,后续会集成activemq,email, freemarker,shiro,websocket,quartz,netty等模块。

依赖的 Spring Boot 版本:

@@ -50,6 +50,7 @@ spring boot demo 是一个用来学习 spring boot 的项目,已经集成 actu
<module>../spring-boot-demo-actuator</module>
<module>../spring-boot-demo-admin</module>
<module>../spring-boot-demo-logback</module>
<module>../spring-boot-demo-aoplog</module>
<module>../spring-boot-demo-orm-jpa</module>
<module>../spring-boot-demo-orm-mybatis</module>
<module>../spring-boot-demo-cache-redis</module>
@@ -146,6 +147,7 @@ spring boot demo 是一个用来学习 spring boot 的项目,已经集成 actu
| [spring-boot-demo-actuator](./spring-boot-demo-actuator) | spring-boot 集成 spring-boot-starter-actuator 用于监控 spring-boot 的启动和运行状态 |
| [spring-boot-demo-admin](./spring-boot-demo-admin) | spring-boot 集成 spring-boot-admin 来可视化的监控 spring-boot 程序的运行状态,可以与 actuator 互相搭配使用 |
| [spring-boot-demo-logback](./spring-boot-demo-logback) | spring-boot 集成 logback 日志 |
| [spring-boot-demo-aoplog](./spring-boot-demo-aoplog) | spring-boot 使用 AOP 切面的方式记录 web 请求日志 |
| [spring-boot-demo-orm-jpa](./spring-boot-demo-orm-jpa) | spring-boot 集成 spring-boot-starter-data-jpa 操作数据库 |
| [spring-boot-demo-orm-mybatis](./spring-boot-demo-orm-mybatis) | spring-boot 集成 [mybatis-spring-boot-starter](https://github.com/mybatis/spring-boot-starter)、[mybatis-spring-boot-starter](https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter) |
| [spring-boot-demo-cache-redis](./spring-boot-demo-cache-redis) | spring-boot 使用 Redis 做缓存 |


+ 1
- 1
TODO.md View File

@@ -7,7 +7,7 @@
- [x] ~~spring-boot-demo-actuator(对 Spring boot 的端点监控)~~
- [x] ~~spring-boot-demo-admin(对 Spring boot 可视化管控)~~
- [x] ~~spring-boot-demo-logback(集成 logback 日志)~~
- [ ] spring-boot-demo-aopLog(使用 AOP 拦截请求日志信息)
- [ ] spring-boot-demo-aoplog(使用 AOP 拦截请求日志信息)
- [ ] spring-boot-demo-exceptionHandler(统一异常处理)
- [ ] spring-boot-demo-orm-jdbcTemplate(操作 SQL 关系型数据库 - JdbcTemplate)
- [x] ~~spring-boot-demo-orm-jpa(操作 SQL 关系型数据库 - JPA)~~


+ 168
- 0
spring-boot-demo-aoplog/README.md View File

@@ -0,0 +1,168 @@
# spring-boot-demo-aoplog

依赖[spring-boot-demo-parent](../spring-boot-demo-parent)、`spring-boot-starter-aop`

### pom.xml

```xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-boot-demo-aoplog</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spring-boot-demo-aoplog</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../spring-boot-demo-parent/pom.xml</relativePath>
</parent>

<properties>
<useragent.version>1.20</useragent.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--UserAgent工具类-->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>${useragent.version}</version>
</dependency>
</dependencies>

<build>
<finalName>spring-boot-demo-aoplog</finalName>
</build>

</project>
```

### AopLog.java

```java
/**
* aop 切面记录请求日志
*
* @package: com.xkcoding.springbootdemoaoplog.aspectj
* @description:aop 切面记录请求日志
* @author: yangkai.shen
* @date: Created in 2017/11/24 上午9:43
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
@Aspect
@Component
@Slf4j
public class AopLog {
private static final String START_TIME = "start-request";

@Pointcut("execution(public * com.xkcoding.springbootdemoaoplog.controller.*Controller.*(..))")
public void log() {
}

@Before("log()")
public void beforeLog(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();

log.info("【请求 URL】:{}", request.getRequestURL());
log.info("【请求 IP】:{}", request.getRemoteAddr());
log.info("【请求类名】:{},【请求方法名】:{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
Map parameterMap = request.getParameterMap();
log.info("【请求参数】:{},", JsonMapper.obj2Str(parameterMap));
Long start = System.currentTimeMillis();
request.setAttribute(START_TIME, start);
}

@Around("log()")
public Object arroundLog(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object result = proceedingJoinPoint.proceed();
log.info("【返回值】:{}", JsonMapper.obj2Str(result));
return result;
}

@AfterReturning("log()")
public void afterReturning(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = 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);
}
}
```

### JsonMapper.java

```java
/**
* Json 转化工具类
*
* @package: com.xkcoding.springbootdemoaoplog.util
* @description:Json 转化工具类
* @author: yangkai.shen
* @date: Created in 2017/11/24 上午9:36
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
@Slf4j
public class JsonMapper {
private static ObjectMapper objectMapper = new ObjectMapper();

/**
* 对象转 json 字符串
*
* @param src 元对象
* @param <T> 类型
* @return json 字符串
*/
public static <T> String obj2Str(T src) {
if (src == null) {
return null;
}
try {
return src instanceof String ? (String) src : objectMapper.writeValueAsString(src);
} catch (IOException e) {
log.error("【JSON 转换:对象 --> 字符串】,异常堆栈:{}", e);
return null;
}
}

/**
* json 字符串转化为对象
*
* @param src 源 json 字符串
* @param typeReference 转化后的类型
* @param <T> 类型
* @return 返回转化后的对象
*/
public static <T> T str2Obj(String src, TypeReference<T> typeReference) {
if (src == null || typeReference == null) {
return null;
}
try {
return (T) (typeReference.getType().equals(String.class) ? src : objectMapper.readValue(src, typeReference));
} catch (Exception e) {
log.error("【JSON 转换:字符串 --> 对象】,异常堆栈:{}", e);
return null;
}
}
}
```


+ 41
- 0
spring-boot-demo-aoplog/pom.xml View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-boot-demo-aoplog</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spring-boot-demo-aoplog</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../spring-boot-demo-parent/pom.xml</relativePath>
</parent>

<properties>
<useragent.version>1.20</useragent.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--UserAgent工具类-->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>${useragent.version}</version>
</dependency>
</dependencies>

<build>
<finalName>spring-boot-demo-aoplog</finalName>
</build>

</project>

+ 12
- 0
spring-boot-demo-aoplog/src/main/java/com/xkcoding/springbootdemoaoplog/SpringBootDemoAoplogApplication.java View File

@@ -0,0 +1,12 @@
package com.xkcoding.springbootdemoaoplog;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootDemoAoplogApplication {

public static void main(String[] args) {
SpringApplication.run(SpringBootDemoAoplogApplication.class, args);
}
}

+ 69
- 0
spring-boot-demo-aoplog/src/main/java/com/xkcoding/springbootdemoaoplog/aspectj/AopLog.java View File

@@ -0,0 +1,69 @@
package com.xkcoding.springbootdemoaoplog.aspectj;

import com.xkcoding.springbootdemoaoplog.util.JsonMapper;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
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.util.Map;

/**
* aop 切面记录请求日志
*
* @package: com.xkcoding.springbootdemoaoplog.aspectj
* @description:aop 切面记录请求日志
* @author: yangkai.shen
* @date: Created in 2017/11/24 上午9:43
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
@Aspect
@Component
@Slf4j
public class AopLog {
private static final String START_TIME = "start-request";

@Pointcut("execution(public * com.xkcoding.springbootdemoaoplog.controller.*Controller.*(..))")
public void log() {
}

@Before("log()")
public void beforeLog(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();

log.info("【请求 URL】:{}", request.getRequestURL());
log.info("【请求 IP】:{}", request.getRemoteAddr());
log.info("【请求类名】:{},【请求方法名】:{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
Map parameterMap = request.getParameterMap();
log.info("【请求参数】:{},", JsonMapper.obj2Str(parameterMap));
Long start = System.currentTimeMillis();
request.setAttribute(START_TIME, start);
}

@Around("log()")
public Object arroundLog(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object result = proceedingJoinPoint.proceed();
log.info("【返回值】:{}", JsonMapper.obj2Str(result));
return result;
}

@AfterReturning("log()")
public void afterReturning(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = 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);
}
}

+ 38
- 0
spring-boot-demo-aoplog/src/main/java/com/xkcoding/springbootdemoaoplog/controller/IndexController.java View File

@@ -0,0 +1,38 @@
package com.xkcoding.springbootdemoaoplog.controller;

import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;
import java.util.concurrent.ConcurrentMap;

/**
* IndexController
*
* @package: com.xkcoding.springbootdemoaoplog.controller
* @description:IndexController
* @author: yangkai.shen
* @date: Created in 2017/11/24 上午9:36
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
@Slf4j
@RestController
public class IndexController {

@GetMapping({"", ""})
public String index() {
return "index";
}

@GetMapping({"/test"})
public Map test(@RequestParam String name) {
ConcurrentMap<String, Object> ret = Maps.newConcurrentMap();
ret.put("name", name);
return ret;
}
}

+ 62
- 0
spring-boot-demo-aoplog/src/main/java/com/xkcoding/springbootdemoaoplog/util/JsonMapper.java View File

@@ -0,0 +1,62 @@
package com.xkcoding.springbootdemoaoplog.util;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;

/**
* Json 转化工具类
*
* @package: com.xkcoding.springbootdemoaoplog.util
* @description:Json 转化工具类
* @author: yangkai.shen
* @date: Created in 2017/11/24 上午9:36
* @copyright: Copyright (c) 2017
* @version: 0.0.1
* @modified: yangkai.shen
*/
@Slf4j
public class JsonMapper {
private static ObjectMapper objectMapper = new ObjectMapper();

/**
* 对象转 json 字符串
*
* @param src 元对象
* @param <T> 类型
* @return json 字符串
*/
public static <T> String obj2Str(T src) {
if (src == null) {
return null;
}
try {
return src instanceof String ? (String) src : objectMapper.writeValueAsString(src);
} catch (IOException e) {
log.error("【JSON 转换:对象 --> 字符串】,异常堆栈:{}", e);
return null;
}
}

/**
* json 字符串转化为对象
*
* @param src 源 json 字符串
* @param typeReference 转化后的类型
* @param <T> 类型
* @return 返回转化后的对象
*/
public static <T> T str2Obj(String src, TypeReference<T> typeReference) {
if (src == null || typeReference == null) {
return null;
}
try {
return (T) (typeReference.getType().equals(String.class) ? src : objectMapper.readValue(src, typeReference));
} catch (Exception e) {
log.error("【JSON 转换:字符串 --> 对象】,异常堆栈:{}", e);
return null;
}
}
}

+ 3
- 0
spring-boot-demo-aoplog/src/main/resources/application.yml View File

@@ -0,0 +1,3 @@
server:
port: 8080
context-path: /demo

+ 16
- 0
spring-boot-demo-aoplog/src/test/java/com/xkcoding/springbootdemoaoplog/SpringBootDemoAoplogApplicationTests.java View File

@@ -0,0 +1,16 @@
package com.xkcoding.springbootdemoaoplog;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootDemoAoplogApplicationTests {

@Test
public void contextLoads() {
}

}

+ 1
- 0
spring-boot-demo-parent/pom.xml View File

@@ -17,6 +17,7 @@
<module>../spring-boot-demo-actuator</module>
<module>../spring-boot-demo-admin</module>
<module>../spring-boot-demo-logback</module>
<module>../spring-boot-demo-aoplog</module>
<module>../spring-boot-demo-orm-jpa</module>
<module>../spring-boot-demo-orm-mybatis</module>
<module>../spring-boot-demo-cache-redis</module>


Loading…
Cancel
Save