SpringCloud:日志traceId错乱

文章讲述了在SpringCloud项目中,由于日志中traceId不一致的问题,作者通过排查发现是由于请求头中的hc-traceid属性影响。最终提供了一种通过重写HttpServletRequestWrapper的getHeader方法来解决此问题的方案。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

一、问题背景

二、排查过程

三、解决方案

总结


提示:以下是本篇文章正文内容,下面案例可供参考

一、问题背景

springcloud项目,对外提供的都是restful协议,为了方便日志的查询与管理,在filter中设置了traceId属性,并在logback日志配置文件中进行输出。

近期总是碰到生产环境有好多请求的日志,日志中的traceId不一致,例如一次完整的请求,在filter中输出的日志和正式的业务日志里的traceId就不同。

二、排查过程

1、排查方向:有另外一个地方设置了新的traceId

排查结果:没有找到其他设置traceId的地方,将Filter中唯一的设置位置屏蔽之后,日志中不再输出traceId。

2、怀疑是MDC的问题

排查结果:更换为ThreadLocal、查看MDC的源码,均没有找到问题所在

而且生产环境中,这种情况也不是必现,一度陷入了僵局......

3、偶然发现有个奇怪的现象

①Filter日志输出请求参数

②业务日志

③Filter日志输出响应结果

发现出现不一致的情况时,该请求的完整日志输出中,①和③的traceId一致,②的traceId居然和1中的请求头里的某个属性 hc-traceid 一致。

有了这个发现,我本机做了一个测试。

当请求头里包含hc-traceid属性时,对应的请求的业务日志中,traceId与请求头里的一致。

当请求头里不包含hc-traceid属性时,①②③中的traceId一致。

hc-traceid,从命名上来看,我猜是公司里某个公共jar包里引用的,将其屏蔽掉之后,请求里就没有这个属性了。正常情况下,就顺着情况一或者是多线程方向去找,一般都没有问题。

三、解决方案

有两个思路

1、修改请求头里的属性,将该属性移除。

2、不改变原有请求头里的属性,改变获取方法的实现

这里我们给出第二个方案的代码示例:

public class ContentCacheRequestWrapper extends HttpServletRequestWrapper {

    private byte[] body;

    private BufferedReader reader;

    private ServletInputStream inputStream;

    private Map<String, String> headerMap = new HashMap<>();
    private Map<String , String[]> params = new HashMap<>();

    public ContentCacheRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        //读一次 然后缓存起来
        body = IOUtils.toByteArray(request.getInputStream());
        inputStream = new RequestCachingInputStream(body);
        this.params.putAll(request.getParameterMap());
    }


    public byte[] getBody() {
        return body;
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (inputStream != null) {
            return inputStream;
        }
        return super.getInputStream();
    }

    @Override
    public BufferedReader getReader() throws IOException {
        if (reader == null) {
            reader = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncoding()));
        }
        return reader;
    }

    //代理一下ServletInputStream 里面真是内容为当前缓存的bytes
    private static class RequestCachingInputStream extends ServletInputStream {

        private final ByteArrayInputStream inputStream;

        public RequestCachingInputStream(byte[] bytes) {
            inputStream = new ByteArrayInputStream(bytes);
        }

        @Override
        public int read() throws IOException {
            return inputStream.read();
        }

        @Override
        public boolean isFinished() {
            return inputStream.available() == 0;
        }

        @Override
        public boolean isReady() {
            return true;
        }

        @Override
        public void setReadListener(ReadListener readlistener) {
        }

    }

    @Override
    public String getHeader(String name) {
        if (Objects.equals(name, "hc-traceid")) {
            return MDC.get("traceId");
        }
        String headerValue = super.getHeader(name);
        if (headerMap.containsKey(name)) {
            headerValue = headerMap.get(name);
        }
        return headerValue;
    }

}

这里说明一下,我们在原来的基础上,修改getHeader的实现,覆盖原有实现,当要获取hc-traceid属性的值时,返回的是我们自己设置的traceId值,至此,问题得以解决。


总结

这个问题困扰挺久了,终于解决了!

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值