The background,
Development problem with the most popular means of screening system is to check the system log, ELK are generally used in the distributed environment to collect the logs, but use the log when complicated with large positioning problem is more troublesome, due to a large number of other users/other threads log output through together also lead to difficult to filter out all of the specified request related log, And logs corresponding to downstream threads/services.
Second, the solution
- Use one for each request
A unique identifier
To track all links displayed in the log, and do not modify the original printing mode (code intrusion free) - The use of Logback
MDC
Added to the mechanism log templatetraceId
Indicates that the value is%X{traceId}
MDC (Mapped Diagnostic Context) is a feature provided by LOG4J and LogBack to facilitate logging in multithreaded conditions. MDC can be thought of as a Map bound to the current thread to which you can add key-value pairs. The content contained in MDC can be accessed by code executing in the same thread. Children of the current thread inherit MDC content from their parent thread. To record logs, you only need to obtain the required information from the MDC. The MDC contents are saved by the program at the appropriate time. For a Web application, this data is usually saved at the very beginning of the request being processed.
Iii. Implementation of the scheme
Since MDC uses ThreadLocal internally, only the local thread is valid, and values in child threads and downstream service MDC are lost. Therefore, the main difficulty of the scheme is to solve the problem of value transfer.
3.1. Modify log templates
Logback Configuration file template format Add id %X{traceId}
3.2. Add a filter to the gateway
Generate a traceId and pass the header to the downstream service
@Component
public class TraceFilter extends ZuulFilter {
@Autowired
private TraceProperties traceProperties;
@Override
public String filterType(a) {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder(a) {
return FORM_BODY_WRAPPER_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter(a) {
// Controls whether to enable the filter according to the configuration
return traceProperties.getEnable();
}
@Override
public Object run(a) {
// Link tracing ID
String traceId = IdUtil.fastSimpleUUID();
MDC.put(CommonConstant.LOG_TRACE_ID, traceId);
RequestContext ctx = RequestContext.getCurrentContext();
ctx.addZuulRequestHeader(CommonConstant.TRACE_ID_HEADER, traceId);
return null; }}Copy the code
3.3. Add spring interceptors for downstream services
Receives and saves the traceId value interceptor
public class TraceInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String traceId = request.getHeader(CommonConstant.TRACE_ID_HEADER);
if (StrUtil.isNotEmpty(traceId)) {
MDC.put(CommonConstant.LOG_TRACE_ID, traceId);
}
return true; }}Copy the code
Register interceptors
public class DefaultWebMvcConfig extends WebMvcConfigurationSupport {
@Override
protected void addInterceptors(InterceptorRegistry registry) {
// Log link tracing interceptor
registry.addInterceptor(new TraceInterceptor()).addPathPatterns("/ * *");
super.addInterceptors(registry); }}Copy the code
3.4. Added feIGN interceptor for downstream services
Continue passing the traceId value of the current service to the downstream service
public class FeignInterceptorConfig {
@Bean
public RequestInterceptor requestInterceptor(a) {
RequestInterceptor requestInterceptor = template -> {
// Pass the log traceId
String traceId = MDC.get(CommonConstant.LOG_TRACE_ID);
if(StrUtil.isNotEmpty(traceId)) { template.header(CommonConstant.TRACE_ID_HEADER, traceId); }};returnrequestInterceptor; }}Copy the code
3.5. Extend the thread pool
Thread pools are used for business purposes (asynchronous, parallel processing), and Spring has its own @async annotation to use thread pools, so you need to extend the ThreadPoolTaskExecutor thread pool implementation to copy the PARENT thread’s MDC content to child threads
public class CustomThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
/** * assigns the parent thread's MDC content to the child thread *@param runnable
*/
@Override
public void execute(Runnable runnable) {
Map<String, String> mdcContext = MDC.getCopyOfContextMap();
super.execute(() -> run(runnable, mdcContext));
}
@Override
public <T> Future<T> submit(Callable<T> task) {
Map<String, String> mdcContext = MDC.getCopyOfContextMap();
return super.submit(() -> call(task, mdcContext));
}
/** * Child delegate execution method *@param runnable {@link Runnable}
* @paramMdcContext Parent thread MDC content */
private void run(Runnable runnable, String tenantId, Map<String, String> mdcContext) {
// Pass the parent thread's MDC content to the child thread
if(mdcContext ! =null) {
MDC.setContextMap(mdcContext);
}
try {
// Perform asynchronous operations
runnable.run();
} finally {
// Clear MDC contentMDC.clear(); }}/** * Child delegate execution method *@param task {@link Callable}
* @paramMdcContext Parent thread MDC content */
private <T> T call(Callable<T> task, Map<String, String> mdcContext) throws Exception {
// Pass the parent thread's MDC content to the child thread
if(mdcContext ! =null) {
MDC.setContextMap(mdcContext);
}
try {
// Perform asynchronous operations
return task.call();
} finally {
// Clear MDC contentMDC.clear(); }}}Copy the code
4. Scene test
4.1. The test code is as follows
4.2. Logs generated by the API gateway
The gateway generated traceId value of 13 d9800c8c7944c78a06ce28c36de670
4.3. Logs generated when a request is made to jump to the file service
The traceId displayed is the same as that displayed on the gateway. The exception scenario is simulated
4.4. ELK aggregation logs passtraceId
Example Query the logs of the entire link
When the system is abnormal, you can query all the requested logs in the log center based on the traceId value of the exception log
Download the source code
Attached is my open source microservices framework (including the code in this article), welcome to Star
Gitee.com/zlt2000/mic…