SpringCloud项目接入Jaeger(下)

1、上篇回顾

之前一篇文章中说到当我们放弃spring-cloud-sleuth这个组件时,会面临两个问题。首先是日志中无法显示traceId和spanId这些链路信息,其次是不能在用spring-cloud-sleuth所提供的方式进行链路传值。现在就让我们来解决这两个问题。上篇回顾

2、日志显示traceId

spring-cloud-sleuth是将traceId等链路信息保存在slf4j的MDC(Mapped Diagnostic Contexts)中,然后通过%X{traceId}这种方式将traceId提取出来,比如打印到控制台的默认格式是:

%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(%5p [${spring.application.name:-},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}
复制代码

opentracing中提供了ThreadLocalScopeManager这个类来管理调用上下文中的Span,我们可以继承该类将traceId设置到MDC中。

public class MDCScopeManager extends ThreadLocalScopeManager {

    @Override
    public Scope activate(Span span, boolean finishOnClose) {
        return new ScopeWrapper(super.activate(span, finishOnClose));
    }

    @Override
    public Scope activate(Span span) {
        return new ScopeWrapper(super.activate(span));
    }

    private static class ScopeWrapper implements Scope {

        private final Scope scope;
        private final String previousTraceId;
        private final String previousSpanId;
        private final String previousParentSpanId;
        private final String previousSampled;

        ScopeWrapper(Scope scope) {
            this.scope = scope;
            this.previousTraceId = lookup("traceId");
            this.previousSpanId = lookup("spanId");
            this.previousParentSpanId = lookup("parentSpanId");
            this.previousSampled = lookup("traceSampled");

            JaegerSpanContext ctx = (JaegerSpanContext) scope.span().context();
            String traceId = ctx.getTraceId();
            String spanId = Long.toHexString(ctx.getSpanId());
            String sampled = String.valueOf(ctx.isSampled());
            String parentSpanId = Long.toHexString(ctx.getParentId());

            replace("traceId", traceId);
            replace("spanId", spanId);
            replace("parentSpanId", parentSpanId);
            replace("traceSampled", sampled);
        }

        @Override
        public void close() {
            this.scope.close();
            replace("traceId", previousTraceId);
            replace("spanId", previousSpanId);
            replace("parentSpanId", previousParentSpanId);
            replace("traceSampled", previousSampled);
        }

        @Override
        public Span span() {
            return this.scope.span();
        }
    }

    private static String lookup(String key) {
        return MDC.get(key);
    }

    private static void replace(String key, String value) {
        if (value == null) {
            MDC.remove(key);
        } else {
            MDC.put(key, value);
        }
    }
}
复制代码

然后把这个类定义成Bean,这样就能把它绑定到当前的tracer中去:

@Bean
public TracerBuilderCustomizer mdcBuilderCustomizer() {
		return builder -> builder.withScopeManager(new MDCScopeManager());
}
复制代码

然后利用%X{traceId}这种方式设置打印格式,启动程序后就能在控制台中看到输出了:

是不是和spring-cloud-sleuth提供的方式一样~~~

3、跨服务传值

opentracing中提供了baggage元素来做跨进程的kv传递,我们可以利用baggage来传递我们需要传递的值。(注意:同时他也会产生巨大的开销,请小心使用此特性)

public class TraceContext {

    public static void setField(String key, String value) {
        if (GlobalTracer.isRegistered() && StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) {
            Tracer tracer = GlobalTracer.get();
            tracer.activeSpan().setBaggageItem(key, value);
        }
    }

    public static String getFiled(String key, String defaultValue) {
        if (GlobalTracer.isRegistered() && StringUtils.isNotBlank(key)) {
            Tracer tracer = GlobalTracer.get();
            return tracer.activeSpan().getBaggageItem(key);
        }
        return defaultValue;
    }

}
复制代码

4、多线程中的trace

项目中需要依赖opentracing-concurrent

<dependency>
    <groupId>io.opentracing.contrib</groupId>
    <artifactId>opentracing-concurrent</artifactId>
    <version>0.4.0</version>
</dependency>
复制代码

然后可以通过TraceRunnable来创建带有trace的线程

new Thread(new TracedRunnable(() -> {
	//线程中干活....
	
}, GlobalTracer.get()));
复制代码

Spring环境中也可以用@Autowired来获取tracer

@Autowired
private Tracer tracer;
复制代码

转载于:https://juejin.im/post/5d0afbaa51882533e13376ac

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当您在Spring Boot应用程序中集成Jaeger时,您需要进行以下步骤: 1. 添加依赖:在您的Spring Boot项目的`pom.xml`文件中添加Jaeger客户端库的依赖项。例如: ```xml <dependency> <groupId>io.jaegertracing</groupId> <artifactId>jaeger-client</artifactId> <version>1.6.0</version> </dependency> ``` 2. 配置Jaeger:在您的应用程序的配置文件(如`application.properties`或`application.yml`)中添加Jaeger的配置。例如: ```yaml # Jaeger配置 jaeger: service-name: your-service-name udp-sender: host: localhost port: 6831 ``` 这里,您需要指定`service-name`作为您的应用程序的名称,并配置Jaeger的发送器(可以是UDP、HTTP等)的主机和端口。 3. 创建Jaeger Tracer Bean:在您的Spring Boot应用程序的配置类中创建一个Jaeger Tracer Bean。例如: ```java import io.jaegertracing.Configuration; import io.jaegertracing.internal.samplers.ConstSampler; import io.opentracing.Tracer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class JaegerConfig { @Bean public Tracer jaegerTracer() { Configuration.SamplerConfiguration samplerConfig = Configuration.SamplerConfiguration.fromEnv() .withType(ConstSampler.TYPE) .withParam(1); Configuration.ReporterConfiguration reporterConfig = Configuration.ReporterConfiguration.fromEnv() .withLogSpans(true); Configuration config = new Configuration("your-service-name") .withSampler(samplerConfig) .withReporter(reporterConfig); return config.getTracer(); } } ``` 在上面的代码中,我们创建了一个`jaegerTracer()`方法,该方法使用了Jaeger的配置和参数,并返回一个Jaeger Tracer实例。 4. 使用Jaeger Tracer:在您的应用程序中使用注入的Jaeger Tracer实例进行跟踪。例如,在您的控制器类中: ```java import io.opentracing.Span; import io.opentracing.Tracer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class MyController { @Autowired private Tracer tracer; @GetMapping("/hello") public String hello() { Span span = tracer.buildSpan("helloSpan").start(); // 执行您的业务逻辑 span.finish(); return "Hello, World!"; } } ``` 在上面的例子中,我们使用了注入的`tracer`实例创建了一个Span,用于跟踪`hello()`方法的执行。您可以根据需要在其他地方创建更多的Span。 5. 运行Jaeger Agent:在集成Jaeger之前,请确保Jaeger Agent正在运行并监听所配置的主机和端口。Jaeger Agent负责接收来自应用程序的跟踪数据并将其发送到Jaeger Collector。 这样,您就完成了Spring Boot应用程序与Jaeger的集成。 请注意,这只是一个简单的示例,您可以根据您的需求进行更复杂的集成。更多关于Jaeger的配置和使用方法,请参考Jaeger的官方文档。 希望这个示例对您有所帮助!如有任何其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值