首先:修改NGINX的配置文件

1.11以上
proxy_set_header X-Request-Id $request_id;
#后端的Web服务器可以通过X-Forwarded-For获取用户真实IP
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

其次:添加spring boot aop的依赖包

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

第三步:编写AOP切面代码,对关键信息进行拦截

import com.alibaba.fastjson.JSON;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.MDC;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Component
@Configuration
@Aspect
public class LogMDCAspectConfig {


@Pointcut("execution(public * com.neusoft.www.imagerecognition.controller..*.*(..))")
public void webLog(){}

@Before("webLog()")
public void before(JoinPoint joinPoint){
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
assert sra != null;
HttpServletRequest request = sra.getRequest();
String url = request.getRequestURL().toString();
String method = request.getMethod();
String queryName=getFieldsName(joinPoint,request);
String x_request_id = request.getHeader("X-Request-Id");
String x_real_ip = getCliectIp(request);
//nginx返回的唯一请求Id
MDC.put("X_REQUEST_ID", x_request_id);
//本项目自动生成的唯一请求Id
MDC.put("TRACE_ID",UUID.randomUUID().toString());
//请求的服务器的真实的IP地址
MDC.put("X_REAL_IP",x_real_ip);
//服务请求路径
MDC.put("REQUEST_URI",url);
//服务请求的方法,post或者get
MDC.put("REMOTE_ADDR_METHOD",method);
//服务的请求的参数
MDC.put("QUERY_NAME",queryName);
}

/**
* 对于涉及到ThreadLocal相关使用的接口,都需要去考虑在使用完上下文对象时,
* 清除掉对应的数据,以避免内存泄露问题
* @param ret
*/
@AfterReturning(pointcut = "webLog()", returning = "ret")
public void afterReturning(Object ret){
MDC.remove("X_REQUEST_ID");
MDC.remove("TRACE_ID");
MDC.remove("X_REAL_IP");
MDC.remove("REQUEST_URI");
MDC.remove("REMOTE_ADDR_METHOD");
MDC.remove("QUERY_NAME");
}

/**
* 获取客户段的IP
* @param request
* @return
*/
private static String getCliectIp(HttpServletRequest request)
{
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.trim() == "" || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.trim() == "" || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.trim() == "" || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// 多个路由时,取第一个非unknown的ip
final String[] arr = ip.split(",");
for (final String str : arr) {
if (!"unknown".equalsIgnoreCase(str)) {
ip = str;
break;
}
}
return ip;
}

/**
* 获取请求的参数
* @param joinPoint
* @return
*/
private static String getFieldsName(JoinPoint joinPoint,HttpServletRequest request) {
String method = request.getMethod();
String params = "";
Object[] args = joinPoint.getArgs();
String queryString = request.getQueryString();
if (args.length > 0) {
if ("POST".equals(method)) {
Object object = args[0];
Map map = getKeyAndValue(object);
params = JSON.toJSONString(map);
;
} else if ("GET".equals(method)) {
params = queryString;
}
}
return params;
}

/**
* 获取post对象的参数参数值
* @param obj
* @return
*/
private static Map<String,Object> getKeyAndValue(Object obj){
Map<String, Object> map = new HashMap<>();
// 得到类对象
Class userCla = (Class)obj.getClass();
/* 得到类中的所有属性集合 */
Field[] fs = userCla.getDeclaredFields();
for (int i = 0; i < fs.length; i++) {
Field f = fs[i];
f.setAccessible(true); // 设置属性是可以访问的
Object val = new Object();
try {
val = f.get(obj);
// 得到此属性的值
map.put(f.getName(), val);// 设置键值
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return map;
}
}

第四步:编辑logback的MDC,规范日志格式

<?xml version="1.0" encoding="UTF-8"?>
<!-- 每隔一分钟扫描配置文件 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 设置上下文名称为 demo -->
<contextName>imagerecognition</contextName>
<!--设置系统日志目录-->
<property name="APPDIR" value="imagerecognition"/>
<!--9444_imagerecognition:端口号和对应的服务名称-->
<property name="SPRING_PROFILES_ACTIVE" value="9444_imagerecognition"/>
<!-- 定义日志输出格式变量:%d表示时间 花括号内为时间格式 %level表示日志级别 %thread表示线程名 %logger{0}表示输出日志的类名 [%line]表示行号用方括号包裹 %msg表示日志消息 %n换行 -->
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS,+08:00} [%t] ${SPRING_PROFILES_ACTIVE} %p %logger [%mdc{X_REQUEST_ID},%mdc{TRACE_ID},%mdc{X_REAL_IP},%mdc{REQUEST_URI},%mdc{REMOTE_ADDR_METHOD},%mdc{QUERY_NAME}] ${CONTEXT_NAME} - %m%n"/>
<!-- 定义日志字符集 -->
<property name="log.charset" value="UTF-8"/>
<!-- 定义日志级别 -->
<property name="log.level" value="INFO"/>
<!-- 定义日志存放路径 -->
<property name="log.path" value="logs"/>
<!-- 输出到控制台 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- 日志输出格式 -->
<encoder>
<!-- 日志字符集 -->
<charset>${log.charset}</charset>
<!-- 日志输出格式 -->
<pattern>${log.pattern}</pattern>
</encoder>
</appender>

<!-- 时间滚动输出日志 -->
<appender name="COMMON" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 写入的文件名 -->
<file>${log.path}/imagerecognition.log</file>
<!-- 追加到文件结尾 -->
<append>true</append>
<!-- 滚动策略:按照每天生成日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天日志归档路径及文件名格式 -->
<fileNamePattern>${log.path}/imagerecognition.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志文件保留天数 -->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<charset>${log.charset}</charset>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 日志记录器,日期滚动记录,即系统产生的错误日志信息 -->
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--正在记录的日志文件的路径及文件名-->
<file>${log.path}/error.log</file>
<append>true</append>
<!--日志记录器的滚动策略,按照日期,按大小记录-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--归档的日志文件的路径,%d{yyyy-MM-dd} 指定日期格式 %i指定索引-->
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- 单日志文件最大限制100兆 超过则将文件内容归档到按照 fileNamePattern 命名的文件中 源文件则清空 -->
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 级别过滤器匹配 ERROR 级别日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<charset>${log.charset}</charset>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 指定 com.neusoft.www.imagerecognition.service 包要使用的 appender 且不向上级传递 -->
<logger name="com.neusoft.www.imagerecognition.service" level="DEBUG" additivity="false">
<appender-ref ref="ERROR"/>
</logger>

<!-- logger -->
<root level="${log.level}">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="COMMON"/>
<appender-ref ref="ERROR"/>
</root>

</configuration>