通过 Nginx 的 requestID 在分布式系统中实现调用链跟踪

文章目录
  1. nginx配置 - nginx.conf
  2. hello.groovy
  3. logback.xml
  4. application.properties

参考文档:

  1. Nginx Unique Tracing ID
  2. 使用requestId在分布式系统追踪请求
  3. 在SpringBoot项目中添加logback的MDC

nginx配置 - nginx.conf

1
2
3
4
5
6
7
8
9
log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$request_id" ';
# '"$http_user_agent" "$http_x_forwarded_for"';

location /hello {
proxy_pass http://127.0.0.1:8080;
proxy_set_header X-Request-Id $request_id;
}

hello.groovy

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.*;
@RestController
@Log
class Hello {
@Autowired
private IceService iceService;

@RequestMapping("/hello")
def hello(HttpServletRequest request) {
def requestHeader = request.getHeaderNames()
requestHeader.each{headerKey->
log.info "${headerKey} : ${request.getHeader(headerKey)}"
}
return "Hello World Groovy! at ${iceService.helloService()}"
}

}

@Service
@Log
public class IceService {

def helloService() {
def helloTime=new Date().toString()
log.info "Get current time in service instance."
return helloTime
}
}

/*
@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
@Autowired
private LogInterceptor logInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(logInterceptor);
super.addInterceptors(registry);
}
}

import org.springframework.web.servlet.HandlerInterceptor
@Component
@Log
public class LogInterceptor implements HandlerInterceptor {

private final static String REQUEST_ID = "x-request-id";

@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
String xForwardedForHeader = httpServletRequest.getHeader("X-Forwarded-For");
String remoteIp = httpServletRequest.getRemoteAddr();
//String uuid = UUID.randomUUID().toString();
String uuid = httpServletRequest.getHeader(REQUEST_ID);
log.info("===> prehandle :${uuid}");
log.info("===> request id:${uuid}, client ip:${remoteIp}, X-Forwarded-For:${xForwardedForHeader}");
MDC.put(REQUEST_ID, uuid);
return true;
}

@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
String uuid = MDC.get(REQUEST_ID);
log.info("===>remove requestId (${uuid}) from logger");
MDC.remove(REQUEST_ID);
}

@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

}
}
*/
import javax.servlet.Filter
import javax.servlet.FilterConfig
import org.springframework.boot.web.servlet.ServletComponentScan
import javax.servlet.annotation.WebFilter
import javax.servlet.ServletException
import javax.servlet.*
import org.slf4j.*

@Component
@ServletComponentScan
@Log
@WebFilter(urlPatterns = "/hello/*",filterName = "helloFilter")
public class HelloFilter implements Filter{

public static final String REQUEST_ID_KEY = "x-request-id";
//public static ThreadLocal<String> requestIdThreadLocal = new ThreadLocal<String>();

public static String getRequestId(HttpServletRequest request) {
String requestId = null;
String parameterRequestId = request.getParameter(REQUEST_ID_KEY);
String headerRequestId = request.getHeader(REQUEST_ID_KEY);

if (parameterRequestId == null && headerRequestId == null) {
log.info("request parameter 和header 都没有requestId入参");
requestId = UUID.randomUUID().toString();
} else {
requestId = parameterRequestId != null ? parameterRequestId : headerRequestId;
}
//requestIdThreadLocal.set(requestId);
MDC.put(REQUEST_ID_KEY, requestId)
return requestId;
}

@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("============== filter ==============")
HelloFilter.getRequestId(servletRequest)
filterChain.doFilter(servletRequest,servletResponse)
}

@Override
public void destroy() {
MDC.remove(REQUEST_ID_KEY);
}
}

logback.xml

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
<!-- Logback configuration. See http://logback.qos.ch/manual/index.html -->
<configuration scan="true" scanPeriod="10 seconds">
<include resource="org/springframework/boot/logging/logback/base.xml" />

<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${LOG_PATH}/info.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/info-%d{yyyyMMdd}.log.%i</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>500MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>2</maxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{x-request-id}] [%thread] %-5level %logger{36} -%msg%n
</Pattern>
</layout>
</appender>

<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<File>${LOG_PATH}/error.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/error-%d{yyyyMMdd}.log.%i
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>500MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>2</maxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%msg%n

</Pattern>
</layout>
</appender>

<!-- hibernate日志输入 -->
<!-- <logger name="org.hibernate.type.descriptor.sql.BasicBinder"
level="TRACE" />
<logger name="org.hibernate.type.descriptor.sql.BasicExtractor"
level="TRACE" />
<logger name="org.hibernate.SQL" level="INFO" />
<logger name="org.hibernate.engine.QueryParameters" level="INFO" />
<logger name="org.hibernate.engine.query.HQLQueryPlan" level="INFO" /> -->

<root level="INFO">
<appender-ref ref="INFO_FILE" />
<appender-ref ref="ERROR_FILE" />
</root>

</configuration>

application.properties

1
2
logging.config=classpath:logback.xml
logging.path=G:/workspace/spring-cli/log