基于日志MDC请求链路跟踪实现

实现思路

       采用filebeat、logstash、elasticsearch、kibana完成整体的日志收集与分析过程。其中filebeat和logstash用于日志文件的采集,elasticsearch用于日志的存储、kibana负责最终日志数据的查询分析。

       有了底层的基础支撑,那么服务之间的链路调用跟踪可简单采用Log日志追踪MDC来实现,实际上就是基于TheadLocal的原理在当前线程能传递一个traceId来进行调用的关联。

日志收集基础环境

        具体的filebeat、logstash、elasticsearch、kibana搭建不在这里说明,可参考:手把手教你Windows下搭建Filebeat+Logstash+ElasticSearch+Kibana系统_filebeat windows-CSDN博客

代码实现

(1)application.yml修改

        配置文件中需要添加日志路径与日志输出格式的设置,日志路径只是为了让filebeat能够扫描所进行的设置,其他位置都是可以的。日志输出格式的内容中增加了应用名称、通过mdc获取的traceId和时间戳ts

logging:
  file:
    path: 'E:\applicationFile\log\${spring.application.name}'
  pattern:
    level: '%5p [${spring.application.name},%mdc{traceId:-},%mdc{ts:-}]'

(2)增加自定义的过滤器

        其中最重要的就是在通过MDC.put方法设置参数,这里我们设置traceId和ts两个参数就可以。

import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Date;
import java.util.UUID;

@Slf4j
public class MdcFilter implements Filter {


    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        try{
            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
            String traceId = httpServletRequest.getHeader("traceId");
            if(traceId == null){
                traceId = UUID.randomUUID().toString();
            }
            MDC.put("traceId", traceId);
            MDC.put("ts", String.valueOf(new Date().getTime()));
            log.info(httpServletRequest.getRequestURI()+"call received");
            filterChain.doFilter(servletRequest, servletResponse);
        }finally {
            MDC.clear();
        }
    }

}

(3)注册过滤器Bean

        由于采用springboot进行的业务开发,为了让自定义的过滤器能使用,就需要注入的spring容器中。

import com.example.order.filter.MdcFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<MdcFilter> loggingFilter(){
        FilterRegistrationBean<MdcFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new MdcFilter());
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }
}

(4)简单写个Controller

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class TestController {

    @GetMapping("/test")
    public String test(String id){
        log.info("调用test方法,入参:{}",id);
        return "调用成功,id:"+id;
    }
}

(5)进行接口调用

可以看到打印的日志中已经有了traceId和ts的数据信息了。

(6)后续操作思考

        那么基于上面的配置过程,只需要在服务接收到请求时查看请求头中是否存在traceId,如果存在就放入本地的日志MDC中,就可以将traceId进行传递。

        当然这里通过MDC获取traceId只是在同一线程内的起效,那如果跨线程或非http请求的方式,那么是获取不到的。也就是在调用传递的过程需要额外处理。

        例如mq传递时就将traceId放到传递参数中,http不用请求头用请求参数等都是可以的,具体情况具体分析。

日志分析

        例如有两个服务,一个订单服务、一个是消费订单的下游服务,它们之间通过mq进行消息的传递和消费。订单服务接收到请求后就可以看到traceId,当然这个traceId可以在订单创建时就生成并持久化到数据库中,后续进行订单查询时就可以根据具体的链路id获取到整个链路的运行情况 。      ​​​​​

        在kibana中通过查询traceId就可以看到调用过程的日志链路

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值