如何使用gRPC拦截器在Spring-Boot应用程序中附加/更新日志记录MDC

时间:2019-04-09 14:36:25

标签: spring-boot slf4j grpc grpc-java mdc

问题

我有一个Spring-Boot应用程序,其中也正在启动gRPC服务器/服务。 servlet和gRPC代码都将请求发送到公共对象以处理该请求。当请求到达时,我想更新日志以显示唯一的“ ID”,以便我可以通过系统跟踪请求。

在Spring端,我设置了一个“过滤器”,用于更新日志记录MDC,以将一些数据添加到日志请求(see this example)中。 这很好

在gRPC端,我创建了一个'ServerInterceptor'并将其添加到服务中,而拦截器被调用以更新MDC的代码不坚持,因此当请求通过gRPC服务我没有在日志中打印ID。 我意识到这与以下事实有关:我在一个线程中拦截了该调用,而gRPC在另一个线程中将其调度了,我似乎无法弄清楚的是,如何截获该调用调用执行工作的线程或添加MDC信息,以便将其正确传播到执行工作的线程。

我尝试过的

我进行了很多搜索,很惊讶没有找到这个问题/答案,我只能假设我的查询技能缺乏:(

我刚接触gRPC,这是我正在编写的第一个拦截器。我尝试了几种不同的方式(通过ServerInterceptors.intercept,BindableService instance.intercept)添加拦截器。

我看过LogNet's Spring Boot gRPC Starter,但是我不确定这是否可以解决问题。

这是我在拦截器类中添加的代码

@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(final ServerCall<ReqT, RespT> call, final Metadata headers, final ServerCallHandler<ReqT, RespT> next) {
    try {
        final String mdcData = String.format("[requestID=%s]",
            UUID.randomUUID().toString());
    MDC.put(MDC_DATA_KEY, mdcData);
    return next.startCall(call, headers);
    } finally {
        MDC.clear();
    }
}

预期结果

当通过RESTful API发出请求时,我会看到这样的日志输出

2019-04-09 10:19:16.331 [requestID=380e28db-c8da-4e35-a097-4b8c90c006f4] INFO 87100 --- [nio-8080-exec-1] c.c.es.xxx: processing request step 1
2019-04-09 10:19:16.800 [requestID=380e28db-c8da-4e35-a097-4b8c90c006f4] INFO 87100 --- [nio-8080-exec-1] c.c.es.xxx: processing request step 2
2019-04-09 10:19:16.803 [requestID=380e28db-c8da-4e35-a097-4b8c90c006f4] INFO 87100 --- [nio-8080-exec-1] c.c.es.xxx: Processing request step 3
...

当请求通过gRPC服务时,我希望得到类似的输出。

谢谢

1 个答案:

答案 0 :(得分:1)

由于没有人回答,因此我不断尝试并为我的 interceptCall 函数提出以下解决方案。我不是100%知道为什么会这样,但是对于我的用例却可以。

    private class LogInterceptor implements ServerInterceptor {
        @Override
        public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(final ServerCall<ReqT, RespT> call,
                                                                     final Metadata headers,
                                                                     final ServerCallHandler<ReqT, RespT> next) {
            Context context = Context.current();
            final String requestId = UUID.randomUUID().toString();
            return Contexts.interceptCall(context, call, headers, new ServerCallHandler<ReqT, RespT>() {
                @Override
                public ServerCall.Listener<ReqT> startCall(ServerCall<ReqT, RespT> call, Metadata headers) {

                    return new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(next.startCall(call, headers)) {
                        /**
                         * The actual service call happens during onHalfClose().
                         */
                        @Override
                        public void onHalfClose() {
                            try (final CloseableThreadContext.Instance ctc = CloseableThreadContext.put("requestID",
                                    UUID.randomUUID().toString())) {
                                super.onHalfClose();
                            }
                        }
                    };
                }
            });
        }
    }

在我的application.properties中,添加了以下内容(我已经拥有了)

  

logging.pattern.level = [%X]%-5level

'%X'告诉日志记录系统打印所有 CloseableThreadContext 键/值。

希望这可以帮助其他人。