Micro service architecture project, a request may be called multiple services, this will result in multiple micro service request log, when we want to see the entire request link log, it becomes difficult, fortunately we have some focused log collection tools, such as popular ELK, we need to get these logs together, this is a critical problem, If not together, the query is very difficult, our approach is generated at the beginning of the request system is a globally unique id, this id to accompany the whole request call cycle, when a service call another service, will be passed down, forming a link, when we look at the journal, only need to search this id, The logs of the entire link can now be traced.

Now, in the context of the Dubbo microservices architecture, for example:

A -> B -> CCopy the code

We need to print the logs between A/B/C/ three microservices in chain format. As we all know, Dubbo’s RpcContext can only be shared by consumers and providers. For example, A->B, then both A and B can obtain the RpcContext of the same content. A and C will not be able to share the same RpcContext, that is, will not be able to chain-print the log.

So how do we do that?

We can do this by swapping left hand for right hand, assuming the left hand is the thread’s ThreadLocal and the right hand is the RpcContext, then we first save the necessary log information to the ThreadLocal before swapping.

Microservices in our project can be roughly divided into two container types. One is Dubbo container, which is characterized by only using Spring container to start, then using Dubbo to expose the service, and then registering the service with ZooKeeper to provide services to consumers. The other is the SpringMVC container, which is our common WEB container. It is the only container that our project can open its interface to the outside world, and also acts as a gateway function for the project.

Now that we know that the first layer of the call chain must be in the SpringMVC container layer, we can write a custom interceptor in this layer.

As an example of the Demo code, the public interceptor’s preintercept looks like this:

public class CommonInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception { // ... // Initialize the global Context container Request Request = initRequest(httpServletRequest); // Create a globally unique request traceId and set it to request.setTraceid (jrnGenerator.gentraceid ()); Context.initiallocal (request); context.initialLocal (request); / /... return true; } / /... }Copy the code

System internal context objects:

public class Context {
    
    // ...
    
    private static final ThreadLocal<Request> REQUEST_LOCAL = new ThreadLocal<>();
    
    public final static void initialLocal(Request request) {
        if (null == request) {
            return;
        }
        REQUEST_LOCAL.set(request);
    }
    
    public static Request getCurrentRequest() {
        return REQUEST_LOCAL.get();
    }
    
    // ...
}Copy the code

Interceptor achieved org. Springframework. Web. Servlet. HandlerInterceptor interface, its main function is used to deal with the intercept request, can do some logging in MVC layer with permission check operation, such as the equivalent of MVC layer of AOP, That is, all functionality that meets crosscutting concerns can be implemented in interceptors.

Here the initRequest (it); It encapsulates the Request information into the system content Request object, initializes a globally unique traceId into the Request, and then puts it into the system internal context ThreadLocal field.

We’ll talk about how to put ThreadLocal into RpcContext. Before we get there, we’ll talk about Dubbo’s spi extension mechanism.

The service provider and service consumer invoke procedural interception. Most of the functionality of Dubbo itself is implemented based on this extension point. This interception is executed each time a remote method is executed.

This means that the interceptor will intercept the call before we make a remote call to the service, and then we will be fine. We can put the contents of ThreadLocal into the RpcContext container before the consumer calls the remote service. We can extend the two interceptors based on dubbo’s SPI mechanism. One on the consumer side and the other on the provider side:

Add com in meta-inf. Alibaba. Dubbo. RPC. The Filter files, content is as follows:

provider=com.objcoding.dubbo.filter.ProviderFilter
consumer=com.objcoding.dubbo.filter.ConsumerFilterCopy the code

Consumer-side interception handling:

public class ConsumerFilter implements Filter { @Override public Result invoke(Invoker<? > invoker, Invocation invocation) throws RpcException { //1. Request information was obtained from the ThreadLocal Request Request = Context. GetCurrentRequest (); RpcContext rpcCTX = rpcContext.getContext (); Context.initiallocal (request); context.initialLocal (request); / /... }}Copy the code

Context.getCurrentRequest(); It’s getting the Request from a ThreadLocal, contextToDubboContext(Request); Put the Request contents into the RpcContext container of the current thread.

It’s easy to think of a provider as taking the contents of an RpcContext and putting them into a ThreadLocal:

public class ProviderFilter extends AbstractDubboFilter implements Filter{ @Override public Result invoke(Invoker<? > invoker, Invocation invocation) throws RpcException { // 1. RpcContext rpcCTX = rpcContext.getContext (); Request Request = dubboContextToContext(rpcCTX); // 2. Context.initiallocal (request); context.initialLocal (request); / /... }}Copy the code

Next, we need to configure log4j2 so that the messages printed by each container associated with the same request have a common traceId. Then, when ELK wants to query a request, it only needs to search the traceId and can see the log of the whole request link.

We add traceId information to the Context of log4j2 in the initialLocal(Request Request) method of the Context Context object:

public class Context { // ... final public static String TRACEID = "_traceid"; public final static void initialLocal(Request request) { if (null == request) { return; } // Add traceId threadContext.put (traceId, request.getTraceid ()) to the log4j2 context; REQUEST_LOCAL.set(request); } / /... }Copy the code

Next implement org. Apache. Logging. Log4j. Core. The appender. Rewrite. RewritePolicy:

@Plugin(name = "Rewrite", category = "Core", elementType = "rewritePolicy", printObject = true)
public final class MyRewritePolicy implements RewritePolicy {

    // ...
    
    @Override
    public LogEvent rewrite(final LogEvent source) {
        HashMap<String, String> contextMap = Maps.newHashMap(source.getContextMap());
        contextMap.put(Context.TRACEID, contextMap.containsKey(Context.TRACEID) ? contextMap.get(Context.TRACEID) : NULL);
        return new Log4jLogEvent.Builder(source).setContextMap(contextMap).build();
    }
    
    // ...
}Copy the code

The purpose of RewritePolicy is that every time we output a log, Log4j calls this class to do some processing.

Configuration log4j2. XML:

<Configuration status="warn"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="[%d{yyyy/MM/dd HH:mm:ss,SSS}][${ctx:_traceid}]%m%n" /> </Console> <! Rewrite--> <Rewrite name="Rewrite"> <MyRewritePolicy/> <! > <AppenderRef ref="Console"/> </Rewrite> </Appenders> <Loggers> <! Logger name="com.objcoding.MyLogger" level="debug" additivity="false"> <! Rewrite--> <AppenderRef ref="Rewrite"/> </Logger> </Loggers> </Configuration>Copy the code

Custom logging classes:

public class MyLogger { private static final Logger logger = LoggerFactory.getLogger(MyLogger.class); public static void info(String msg, Object... args) { if (canLog() == 1 && logger.isInfoEnabled()) { logger.info(msg, args); } } public static void debug(String message, Object... args) { if (canLog() == 1 && logger.isDebugEnabled()) { logger.debug(message, args); }} / /.. }Copy the code

More exciting articles please pay attention to the author of the maintenance of the public number “back-end advanced”, this is a focus on back-end related technology of the public number.

Pay attention to the public number and reply “back end” to get free back end related electronic books.

Welcome to share, reprint please reserve source.