diff --git a/spring-boot-demo-log-aop/.gitignore b/spring-boot-demo-log-aop/.gitignore
new file mode 100644
index 0000000..82eca33
--- /dev/null
+++ b/spring-boot-demo-log-aop/.gitignore
@@ -0,0 +1,25 @@
+/target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/build/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
\ No newline at end of file
diff --git a/spring-boot-demo-log-aop/README.md b/spring-boot-demo-log-aop/README.md
new file mode 100644
index 0000000..8908633
--- /dev/null
+++ b/spring-boot-demo-log-aop/README.md
@@ -0,0 +1,201 @@
+# spring-boot-demo-log-aop
+
+> 此 demo 主要是演示如何使用 aop 切面对请求进行日志记录,并且记录 UserAgent 信息。
+
+## pom.xml
+
+```xml
+
+
+ 4.0.0
+
+ com.xkcoding
+ spring-boot-demo-log-aop
+ 0.0.1-SNAPSHOT
+ jar
+
+ spring-boot-demo-log-aop
+ Demo project for Spring Boot
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.0.5.RELEASE
+
+
+
+
+ UTF-8
+ UTF-8
+ 1.8
+ 4.1.14
+ 1.20
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+ cn.hutool
+ hutool-all
+ ${hutool.version}
+
+
+
+
+ eu.bitwalker
+ UserAgentUtils
+ ${user.agent.version}
+
+
+
+
+ spring-boot-demo-log-aop
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
+```
+
+## AopLog.java
+
+```java
+/**
+ *
+ * 使用 aop 切面记录请求日志信息
+ *
+ *
+ * @package: com.xkcoding.log.aop.aspectj
+ * @description: 使用 aop 切面记录请求日志信息
+ * @author: yangkai.shen
+ * @date: Created in 2018/10/1 10:05 PM
+ * @copyright: Copyright (c) 2018
+ * @version: V1.0
+ * @modified: yangkai.shen
+ */
+@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);
+ }
+}
+```
+
+## TestController.java
+
+```java
+/**
+ *
+ * 测试 Controller
+ *
+ *
+ * @package: com.xkcoding.log.aop.controller
+ * @description: 测试 Controller
+ * @author: yangkai.shen
+ * @date: Created in 2018/10/1 10:10 PM
+ * @copyright: Copyright (c) 2018
+ * @version: V1.0
+ * @modified: yangkai.shen
+ */
+@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);
+ }
+
+}
+```
+
diff --git a/spring-boot-demo-log-aop/pom.xml b/spring-boot-demo-log-aop/pom.xml
new file mode 100644
index 0000000..d2b07a3
--- /dev/null
+++ b/spring-boot-demo-log-aop/pom.xml
@@ -0,0 +1,75 @@
+
+
+ 4.0.0
+
+ com.xkcoding
+ spring-boot-demo-log-aop
+ 0.0.1-SNAPSHOT
+ jar
+
+ spring-boot-demo-log-aop
+ Demo project for Spring Boot
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.0.5.RELEASE
+
+
+
+
+ UTF-8
+ UTF-8
+ 1.8
+ 4.1.14
+ 1.20
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+ cn.hutool
+ hutool-all
+ ${hutool.version}
+
+
+
+
+ eu.bitwalker
+ UserAgentUtils
+ ${user.agent.version}
+
+
+
+
+ spring-boot-demo-log-aop
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/spring-boot-demo-log-aop/src/main/java/com/xkcoding/log/aop/SpringBootDemoLogAopApplication.java b/spring-boot-demo-log-aop/src/main/java/com/xkcoding/log/aop/SpringBootDemoLogAopApplication.java
new file mode 100644
index 0000000..8240bcc
--- /dev/null
+++ b/spring-boot-demo-log-aop/src/main/java/com/xkcoding/log/aop/SpringBootDemoLogAopApplication.java
@@ -0,0 +1,25 @@
+package com.xkcoding.log.aop;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ *
+ * 启动类
+ *
+ *
+ * @package: com.xkcoding.log.aop
+ * @description: 启动类
+ * @author: yangkai.shen
+ * @date: Created in 2018/10/1 10:05 PM
+ * @copyright: Copyright (c) 2018
+ * @version: V1.0
+ * @modified: yangkai.shen
+ */
+@SpringBootApplication
+public class SpringBootDemoLogAopApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringBootDemoLogAopApplication.class, args);
+ }
+}
diff --git a/spring-boot-demo-log-aop/src/main/java/com/xkcoding/log/aop/aspectj/AopLog.java b/spring-boot-demo-log-aop/src/main/java/com/xkcoding/log/aop/aspectj/AopLog.java
new file mode 100644
index 0000000..9caa858
--- /dev/null
+++ b/spring-boot-demo-log-aop/src/main/java/com/xkcoding/log/aop/aspectj/AopLog.java
@@ -0,0 +1,95 @@
+package com.xkcoding.log.aop.aspectj;
+
+import cn.hutool.json.JSONUtil;
+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;
+import java.util.Objects;
+
+/**
+ *
+ * 使用 aop 切面记录请求日志信息
+ *
+ *
+ * @package: com.xkcoding.log.aop.aspectj
+ * @description: 使用 aop 切面记录请求日志信息
+ * @author: yangkai.shen
+ * @date: Created in 2018/10/1 10:05 PM
+ * @copyright: Copyright (c) 2018
+ * @version: V1.0
+ * @modified: yangkai.shen
+ */
+@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);
+ }
+}
diff --git a/spring-boot-demo-log-aop/src/main/java/com/xkcoding/log/aop/controller/TestController.java b/spring-boot-demo-log-aop/src/main/java/com/xkcoding/log/aop/controller/TestController.java
new file mode 100644
index 0000000..8cff8c4
--- /dev/null
+++ b/spring-boot-demo-log-aop/src/main/java/com/xkcoding/log/aop/controller/TestController.java
@@ -0,0 +1,35 @@
+package com.xkcoding.log.aop.controller;
+
+import cn.hutool.core.lang.Dict;
+import cn.hutool.core.util.StrUtil;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ *
+ * 测试 Controller
+ *
+ *
+ * @package: com.xkcoding.log.aop.controller
+ * @description: 测试 Controller
+ * @author: yangkai.shen
+ * @date: Created in 2018/10/1 10:10 PM
+ * @copyright: Copyright (c) 2018
+ * @version: V1.0
+ * @modified: yangkai.shen
+ */
+@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);
+ }
+
+}
diff --git a/spring-boot-demo-log-aop/src/main/resources/application.yml b/spring-boot-demo-log-aop/src/main/resources/application.yml
new file mode 100644
index 0000000..af5002c
--- /dev/null
+++ b/spring-boot-demo-log-aop/src/main/resources/application.yml
@@ -0,0 +1,4 @@
+server:
+ port: 8080
+ servlet:
+ context-path: /demo
diff --git a/spring-boot-demo-log-aop/src/main/resources/logback-spring.xml b/spring-boot-demo-log-aop/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000..284bb16
--- /dev/null
+++ b/spring-boot-demo-log-aop/src/main/resources/logback-spring.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+ INFO
+
+
+ %date [%thread] %-5level [%logger{50}] %file:%line - %msg%n
+ UTF-8
+
+
+
+
+
+
+
+ ERROR
+
+ DENY
+
+ ACCEPT
+
+
+
+
+
+
+ logs/spring-boot-demo-log-aop/info.created_on_%d{yyyy-MM-dd}.part_%i.log
+
+ 90
+
+
+
+
+ 2MB
+
+
+
+
+
+
+ %date [%thread] %-5level [%logger{50}] %file:%line - %msg%n
+ UTF-8
+
+
+
+
+
+
+ Error
+
+
+
+
+
+
+ logs/spring-boot-demo-log-aop/error.created_on_%d{yyyy-MM-dd}.part_%i.log
+
+ 90
+
+
+ 2MB
+
+
+
+ %date [%thread] %-5level [%logger{50}] %file:%line - %msg%n
+ UTF-8
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-boot-demo-log-aop/src/test/java/com/xkcoding/log/aop/SpringBootDemoLogAopApplicationTests.java b/spring-boot-demo-log-aop/src/test/java/com/xkcoding/log/aop/SpringBootDemoLogAopApplicationTests.java
new file mode 100644
index 0000000..f2af7df
--- /dev/null
+++ b/spring-boot-demo-log-aop/src/test/java/com/xkcoding/log/aop/SpringBootDemoLogAopApplicationTests.java
@@ -0,0 +1,16 @@
+package com.xkcoding.log.aop;
+
+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 SpringBootDemoLogAopApplicationTests {
+
+ @Test
+ public void contextLoads() {
+ }
+
+}