OpenTelemetry系列 - 第2篇 Java端接入OpenTelemetry

news2025/1/17 3:01:11

目录

    • 一、架构说明
    • 二、方式1 - 自动化
      • 2.1 opentelemetry-javaagent.jar(Java8+ )
      • 2.2 使用opentelemetry-javaagent.jar完成自动注入
      • 2.3 配置opentelemetry-javaagent.jar
      • 2.4 使用注解(@WithSpan, @SpanAttribute)
        • 2.5.1 代码集成@WithSpan, @SpanAttribute
        • 2.5.2 禁用已标注@WithSpan的方法的自动注入:
        • 2.5.3 不修改代码的情况下启用@WithSpan
        • 设置Agent日志级别
    • 三、方式2 -【推荐】自动化 & 手动API
      • 3.1 maven依赖
      • 3.2 Java启动命令注入OTel Agent
      • 3.3 Exporter导出器
      • 3.4 代码端集成自定义Traces和Metrics
      • 3.5 Logback集成OTel
    • 四、方式3 - 手动 & SpringBoot集成
      • 4.1 maven依赖
      • 4.2 应用配置
    • 五、OpenTelemetry SDK手动编码
      • 5.1 Traces
      • 5.2 Metrics

一、架构说明

在这里插入图片描述

  • 支持集成Agent 自动 插桩(Instrumentation) - 无需改变代码

  • 手动编码进行插桩 - 侵入代码

    • API - OTel接口定义,具体实现可通过依赖SDK或者Agent注入
      • annotations - OTel通用注解(如@WithSpan, @SpanAttr等)
      • semconv - OTel通用语义约定(如常用属性名称)
    • SDK - 具体OTel实现
    • Instumentation Libraries - 被注入OTel插桩的代码库
    • Resource Detector - 识别服务自身信息
  • Exporter - 导出器,将OTel相关数据导出到OTel Collector 或者 具体的监控后端

    • OTLP Exporter - OTel官方协议OTLP导出器,可导出数据到OTel Collector 或者其他接收OTLP协议的监控后端
    • vender exporters - 不同监控后端厂商的导出器,负责将OTel数据导出到不同的监控后端
      • Jaeger, Ziplin
      • Prometheus
      • Skywalking
  • OTel Collector - OTel官方的收集器(支持OTLP gRpc/http协议)

二、方式1 - 自动化

支持的编程语言:

  • .NET
  • Java
  • JavaScript
  • PHP
  • Python

2.1 opentelemetry-javaagent.jar(Java8+ )

Java自动插桩使用的Java agent JAR可以附加到任何Java 8+应用程序。它动态注入字节码来捕获来自许多流行库和框架的遥测。它可以用于在应用程序或服务的“边缘”捕获遥测数据,例如入站请求、出站HTTP调用、数据库调用等。
支持的库、框架等包括:

  • Spring: Boot, Web, MVC, WebFlux, Cloud Gateway, Batch, Scheduling, Data, MQ(JMS, Kafka, RabbitMq), Micrometer, RestTemplate
  • HTTP客户端工具: HttpClient 2.0+, OkHttp 2.2+, HttpURLConnection, Java Http Client
  • Web容器: Servlet 2.2+, Tomcat 7~10, Jetty 9 ~ 11, Undertow 1.4+, Netty 3.8+
  • 数据库连接: Hibernate 3.3+, JDBC, HIkariCP 3.0+, c3p0 0.9.2+, DBCP 2.0+, MongoDB Driver 3.1+, R2DBC 1.0+
  • RPC: Dubbo 2.7+, gRPC 1.6+
  • 消息队列: RabbitMQ 2.7+, Kafka 0.11+, RocketMq, Pulsar 2.8+, JMS
  • 缓存: Jedis 1.4+, Lettuce 4.0+, Redisson 3.0+
  • 日志: Log4j2 2.11+, Logback 1.0+
  • 搜索引擎: ES 5.0+
  • 云原生: AWS Lambda 1.0+, AWS SDK, Azure Core 1.14+, Kubernetes Client 7.0+
  • 其他: RxJava 1.0+, Reactor 3.1+, Guava, Quartz 2.0+, GraphQL 12.0+, Hystrix, …

具体支持情况可参见:
https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md

下载:opentelemetry-javaagent.jar

2.2 使用opentelemetry-javaagent.jar完成自动注入

方式1 - 使用java启动命令指定javaagment:

java -javaagent:path/to/opentelemetry-javaagent.jar 
	 -Dotel.service.name=your-service-name 
	 -jar myapp.jar

方式2 - 通过环境变量全局配置javaagent:

export JAVA_TOOL_OPTIONS="-javaagent:path/to/opentelemetry-javaagent.jar"
export OTEL_SERVICE_NAME="your-service-name"
java -jar myapp.jar

2.3 配置opentelemetry-javaagent.jar

方式1 - 通过Java系统属性:

java -javaagent:path/to/opentelemetry-javaagent.jar \
     -Dotel.service.name=your-service-name \
     -Dotel.traces.exporter=zipkin \
     -jar myapp.jar

方式2 - 通过环境变量:

OTEL_SERVICE_NAME=your-service-name \
OTEL_TRACES_EXPORTER=zipkin \
java -javaagent:path/to/opentelemetry-javaagent.jar \
     -jar myapp.jar

方式3 - 通过属性文件:

OTEL_JAVAAGENT_CONFIGURATION_FILE=path/to/properties/file.properties \
java -javaagent:path/to/opentelemetry-javaagent.jar \
     -jar myapp.jar

开启agent debug日志:

-Dotel.javaagent.debug=true

关于opentelemetry-javaagent.jar的更多配置可参见:
https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md
https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/

2.4 使用注解(@WithSpan, @SpanAttribute)

添加maven依赖:

<dependencies>
  <dependency>
    <groupId>io.opentelemetry.instrumentation</groupId>
    <artifactId>opentelemetry-instrumentation-annotations</artifactId>
    <version>1.31.0</version>
  </dependency>
</dependencies>

2.5.1 代码集成@WithSpan, @SpanAttribute
import io.opentelemetry.instrumentation.annotations.WithSpan;

public class MyClass {
  @WithSpan
  public void myMethod() {
      <...>
  }

   @WithSpan
   public void myMethod(@SpanAttribute("parameter1") String parameter1,
       @SpanAttribute("parameter2") long parameter2) {
       <...>
   }
}
2.5.2 禁用已标注@WithSpan的方法的自动注入:
-Dotel.instrumentation.opentelemetry-instrumentation-annotations.exclude-methods=
my.package.MyClass1[method1,method2];my.package.MyClass2[method3]
2.5.3 不修改代码的情况下启用@WithSpan

适用于无法修改源码的情况:

-Dotel.instrumentation.methods.include=
my.package.MyClass1[method1,method2];my.package.MyClass2[method3]
设置Agent日志级别

https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/#java-agent-logging-output

The agent’s logging output can be configured by setting the following property:

System property: otel.javaagent.logging
Description: The Java agent logging mode. The following 3 modes are supported:

  • simple: The agent will print out its logs using the standard error stream. Only INFO or higher logs will be printed. This is the default Java agent logging mode.
  • none: The agent will not log anything - not even its own version.
  • application: The agent will attempt to redirect its own logs to the instrumented application’s slf4j logger. This works the best for simple one-jar applications that do not use multiple classloaders; Spring Boot apps are supported as well. The Java agent output logs can be further configured using the instrumented application’s logging configuration (e.g. logback.xml or log4j2.xml). Make sure to test that this mode works for your application before running it in a production environment

三、方式2 -【推荐】自动化 & 手动API

应用端通过Agent注入opentelemetry-javaagent.jar,
应用端代码仅需集成OTel API接口依赖(无需集成SDK实现依赖),
额外需集成Logback OTel Appender相关依赖(若不需要可移除)。

注:
最推荐此种集成方式,
保留了Agent注入,减少了代码侵入,
仅当有自定义Traces/Metrics等需求时,依赖OTel Api完成指标等埋点,
即便不注入Agent,也不影响本地应用开发与运行,
在线上环境通过Docker镜像、K8S挂载等完成Agent注入。

3.1 maven依赖

<properties>
    <otel.version>1.32.0</otel.version>
    <otel.springboot.version>1.32.0-alpha</otel.springboot.version>
    <otel.logback.version>1.32.0-alpha</otel.logback.version>
</properties>

<dependencyManagement>
    <dependencies>
        <!-- OTel基础依赖管理 -->
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-bom</artifactId>
            <version>${otel.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
		<!-- OTel注解 -->
        <dependency>
            <groupId>io.opentelemetry.instrumentation</groupId>
            <artifactId>opentelemetry-instrumentation-annotations</artifactId>
            <version>${otel.version}</version>
        </dependency>


        <!-- OTel Logback -->
        <dependency>
            <groupId>io.opentelemetry.instrumentation</groupId>
            <artifactId>opentelemetry-logback-appender-1.0</artifactId>
            <version>${otel.logback.version}</version>
        </dependency>
        <dependency>
            <groupId>io.opentelemetry.instrumentation</groupId>
            <artifactId>opentelemetry-logback-mdc-1.0</artifactId>
            <version>${otel.logback.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
   <!-- OTel Api -->
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-api</artifactId>
    </dependency>

    <!-- OTel注解 -->
    <dependency>
        <groupId>io.opentelemetry.instrumentation</groupId>
        <artifactId>opentelemetry-instrumentation-annotations</artifactId>
    </dependency>

    <!-- OTel Logback -->
    <dependency>
        <groupId>io.opentelemetry.instrumentation</groupId>
        <artifactId>opentelemetry-logback-appender-1.0</artifactId>
    </dependency>
    <dependency>
        <groupId>io.opentelemetry.instrumentation</groupId>
        <artifactId>opentelemetry-logback-mdc-1.0</artifactId>
    </dependency>
</dependencies>

3.2 Java启动命令注入OTel Agent

启动命令注入及配置opentelemetry-javaagent.jar:

java 
-javaagent:D:/programs/Java/OTel/opentelemetry-javaagent.jar
# 导出traces为控制台日志打印
-Dotel.traces.exporter=logging
# 导出metrics为控制台日志打印
-Dotel.metrics.exporter=logging
# 禁用log日志导出(若使用logging则控制台会出现日志框架如logback打印一次,OTel logging再打印一次,比较混乱,故暂且禁用)
-Dotel.logs.exporter=none
-jar myapp.jar

如下为导出traces、metrics、logs均到OTLP Collector的相关配置:

java 
-javaagent:D:/programs/Java/OTel/opentelemetry-javaagent.jar
-Dotel.traces.exporter=otlp
-Dotel.metrics.exporter=otlp
-Dotel.logs.exporter=otlp
-Dotel.exporter.otlp.endpoint=http://localhost:4317
-jar myapp.jar

如下为导出traces到Jaeger的相关配置:

注:
此种方式已被弃用,目前最新版版本的Jaeger已经内嵌OTel Collector,
可直接通过OTLP协议接收数据。

java 
-javaagent:D:/programs/Java/OTel/opentelemetry-javaagent.jar
# 导出traces到Jaeger端(Jaeger后端需根据实际环境进行调整)
-Dotel.traces.exporter=jaeger
-Dotel.exporter.jaeger.endpoint=http://10.170.xx.xxx:xxx
-Dotel.exporter.jaeger.timeout=10000
# 导出metrics为控制台日志打印
-Dotel.metrics.exporter=logging
# 禁用log日志导出
-Dotel.logs.exporter=none
-jar myapp.jar

导出到Jaeger中的traces展示:
在这里插入图片描述

在这里插入图片描述

3.3 Exporter导出器

导出方式:

  • OTLP exporter
  • Logging exporter
  • Logging OTLP JSON exporter
  • Jaeger exporter
  • Zipkin exporter
  • Prometheus exporter

关于导出器的更多配置可参见:
https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md#exporters

3.4 代码端集成自定义Traces和Metrics

OTel工具类:

/**
 * OpenTelemetry工具类
 *
 * @author luohq
 * @date 2023-11-20 10:01
 */
@Component
public class OTelUtils {

    private static final Logger log = LoggerFactory.getLogger(OTelUtils.class);

    /**
     * OTel实例
     */
    private static OpenTelemetry OTEL_INSTANCE;

    /**
     * 兼容OTel SpringBoot Starter自动注入
     *
     * @param openTelemetry SpringBoot OTel实例
     */
    @Autowired(required = false)
    public void setOpenTelemetry(OpenTelemetry openTelemetry) {
        OTelUtils.OTEL_INSTANCE = openTelemetry;
    }

    /**
     * 获取OTel实例
     *
     * @return OTel实例
     */
    public static OpenTelemetry openTelemetry() {
        //获取自动注入的OTel实例
        if (Objects.nonNull(OTEL_INSTANCE)) {
            return OTEL_INSTANCE;
        }
        //获取全局配置中的OTel实例(使用Agent注入)
        return GlobalOpenTelemetry.get();
    }
}

业务代码集成OTel:

//使用@WithSpan和@SpanAttribute生成Span
@WithSpan(value = "Manual::GoodsService::findGoodsPage")
@Override
public RespResult<GoodsVo> findGoodsPage(@SpanAttribute GoodsPageQueryDto goodsPageQueryDto) {
    /** 获取当前Span */
    Span curSpan = Span.current();
    curSpan.setAttribute("attr.custom", "luohq-test-svc");

    /** 自定义指标 */
    Meter meter = OTelUtils.openTelemetry().meterBuilder("GoodsService::findGoodsPage")
            .setInstrumentationVersion("v1.0")
            .build();
    //构建计数器
    LongCounter findCounter = meter.counterBuilder("findGoodsPage.count")
            .setDescription("FindGoodsPage Sum Count")
            .setUnit("1")
            .build();
    findCounter.add(1);


    /** 自定义Span */
    Tracer tracer = OTelUtils.openTelemetry().getTracer(GoodsMapper.class.getSimpleName());
    Span daoSpan = tracer.spanBuilder("Manual::GoodsMapper::findGoodsWithCNamePage").startSpan();
    daoSpan.setAttribute("attr.custom", "luohq-test-dao");
    try (Scope scope = daoSpan.makeCurrent()) {
        //原处理逻辑
        log.info("findGoodsPage param: {}", goodsPageQueryDto);
        IPage<GoodsVo> goodsPage = this.goodsMapper.findGoodsWithCNamePage(this.toPage(goodsPageQueryDto), goodsPageQueryDto);
        log.info("findGoodsPage result: {}", goodsPage);
        return RespResult.successRows(goodsPage.getTotal(), goodsPage.getRecords());
    } catch (Throwable throwable) {
        //设置Span状态
        daoSpan.setStatus(StatusCode.ERROR, "Something bad happened!");
        //记录异常堆栈
        daoSpan.recordException(throwable);
        return RespResult.failed();
    }finally {
        daoSpan.end();
    }
}

3.5 Logback集成OTel

如下日志配置需使用spring.profiles.active来激活对应的otel或者otel-mdc配置,
如果不需要可移除springProfile段落,直接在configuration下配置相应的日志配置即可,
其中otel、otel-mdc均会自动将日志框架Logback集成OTel并导出日志到相应后端(如默认导出到OTel Collector),
相较于otel,otel-mdc通过 MDC(Mapped Diagnostic Context, 映射调试上下文机制) 将trace_id、span_id、trace_flags添加到日志中。

具体logback-spring.xml配置:

<configuration scan="true" scanPeriod=" 5 seconds">
    <!-- 默认日志配置 -->
    <springProfile name="default">
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>
                    %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
                </pattern>
            </encoder>
        </appender>
        <root level="INFO">
            <appender-ref ref="console"/>
        </root>
    </springProfile>

    <!-- OTel日志配置 -->
    <springProfile name="otel">
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>
                    %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
                </pattern>
            </encoder>
        </appender>
        <appender name="OpenTelemetry"
                  class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender">
        </appender>

        <root level="INFO">
            <appender-ref ref="console"/>
            <appender-ref ref="OpenTelemetry"/>
        </root>
    </springProfile>

    <!-- OTel MDC日志配置 -->
    <springProfile name="otel-mdc">
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <!--
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} trace_id=%X{trace_id} span_id=%X{span_id} trace_flags=%X{trace_flags} [%thread] %-5level %logger{36} - %msg%n</pattern>
                -->
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{trace_id}:%X{span_id}:%X{trace_flags}] [%thread] %-5level %logger{36} - %msg%n</pattern>
            </encoder>
        </appender>

        <!-- Just wrap your logging appender, for example ConsoleAppender, with OpenTelemetryAppender -->
        <appender name="otel" class="io.opentelemetry.instrumentation.logback.mdc.v1_0.OpenTelemetryAppender">
            <appender-ref ref="console"/>
        </appender>

        <!-- Use the wrapped "otel" appender instead of the original "console" one -->
        <root level="INFO">
            <appender-ref ref="otel"/>
        </root>
    </springProfile>
</configuration>

关于trace_flags定义参见:
https://www.w3.org/TR/trace-context/#trace-flags

四、方式3 - 手动 & SpringBoot集成

SpringBoot应用需依赖opentelemetry-spring-boot-starter
此种方式下由于starter端依赖了OTel SDK,所以无需Agent注入
该starter集成了OTel API/SDK,并对OpenTelemetry进行了自动配置,

该starter引入的依赖如下:

  • opentelemetry-api
  • opentelemetry-instrumentation-api-semconv
  • opentelemetry-sdk
  • opentelemetry-exporter-otlp
  • opentelemetry-exporter-logging
  • opentelemetry-logback-appender-1.0
  • other instrumentation libs
    • web
    • webmvc
    • webflux
    • jdbc
    • kafka
    • logback / log4j appender
    • micrometer

4.1 maven依赖

<properties>
    <otel.version>1.32.0</otel.version>
    <otel.springboot.version>1.32.0-alpha</otel.springboot.version>
    <otel.logback.version>1.32.0-alpha</otel.logback.version>
</properties>

<dependencyManagement>
    <dependencies>
       <!-- OTel基础依赖管理 -->
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-bom</artifactId>
            <version>${otel.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!-- OTel注解 -->
        <dependency>
            <groupId>io.opentelemetry.instrumentation</groupId>
            <artifactId>opentelemetry-instrumentation-annotations</artifactId>
            <version>${otel.version}</version>
        </dependency>


        <!-- OTel Logback -->
        <dependency>
            <groupId>io.opentelemetry.instrumentation</groupId>
            <artifactId>opentelemetry-logback-appender-1.0</artifactId>
            <version>${otel.logback.version}</version>
        </dependency>
        <dependency>
            <groupId>io.opentelemetry.instrumentation</groupId>
            <artifactId>opentelemetry-logback-mdc-1.0</artifactId>
            <version>${otel.logback.version}</version>
        </dependency>

        <!-- OTel SpringBoot -->
        <dependency>
            <groupId>io.opentelemetry.instrumentation</groupId>
            <artifactId>opentelemetry-spring-boot-starter</artifactId>
            <version>${otel.springboot.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <!-- OTel SpringBoot -->
    <dependency>
        <groupId>io.opentelemetry.instrumentation</groupId>
        <artifactId>opentelemetry-spring-boot-starter</artifactId>
    </dependency>

    <!-- OTel Logback -->
    <dependency>
        <groupId>io.opentelemetry.instrumentation</groupId>
        <artifactId>opentelemetry-logback-appender-1.0</artifactId>
    </dependency>
    <dependency>
        <groupId>io.opentelemetry.instrumentation</groupId>
        <artifactId>opentelemetry-logback-mdc-1.0</artifactId>
    </dependency>

    <!-- Jaeger导出器 -->
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-exporter-jaeger</artifactId>
    </dependency>
</dependencies>

4.2 应用配置

application.yaml:

otel:
  # 导出器配置
  exporter:
    # OTLP导出
    otlp:
      enabled: false
      endpoint: http://localhost:4317
      timeout: 10s
    # 导出到Zipkin
    zipkin:
      enabled: false
      endpoint: http://localhost:9411/api/v2/spans
    # 导出到Jaeger
    jaeger:
      enabled: true
      endpoint: http://10.170.xx.xxx:xxxx
      timeout: 10s
    # 导出到日志
    logging:
      enabled: true
  traces:
    sampler:
      # 采样频率
      probability: 1.0

更多配置说明可参见:
https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/spring/spring-boot-autoconfigure/README.md#features

关于自定义Traces / Metrics、Logback集成OTel可参见 方式2

五、OpenTelemetry SDK手动编码

5.1 Traces

// ...
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Tracer;

public class Dice {

  private int min;
  private int max;
  private Tracer tracer;
  
  //自动注入OpenTelemetry对象
  public Dice(int min, int max, OpenTelemetry openTelemetry) {
    this.min = min;
    this.max = max;
    //获取Tracer
    this.tracer = openTelemetry.getTracer(Dice.class.getName(), "0.1.0");
  }

  public Dice(int min, int max) {
    this(min, max, OpenTelemetry.noop())
  }

  public List<Integer> rollTheDice(int rolls) {
    //通过Tracer创建Span
    Span parentSpan = tracer.spanBuilder("parent")
        //添加关联的Context
  		//.addLink(parentSpan1.getSpanContext())
        //.addLink(parentSpan2.getSpanContext())
        //.addLink(remoteSpanContext)
    	.startSpan();
    
    //为Span添加属性
	parentSpan .setAttribute(SemanticAttributes.HTTP_METHOD, "GET");
	parentSpan .setAttribute(SemanticAttributes.HTTP_URL, "/rolldice");
	
    //为Span添加事件
	Attributes eventAttributes = Attributes.of(
	    AttributeKey.stringKey("key"), "value",
	    AttributeKey.longKey("result"), 0L
	);
	parentSpan .addEvent("End Computation", eventAttributes);

	//开始Span处理
    try (Scope scope = parentSpan.makeCurrent()) {
      List<Integer> results = new ArrayList<Integer>();
      for (int i = 0; i < rolls; i++) {
        //span嵌套
        results.add(this.rollOnce());
      }
      return results;
    } catch (Throwable throwable) {
      //设置Span状态
	  span.setStatus(StatusCode.ERROR, "Something bad happened!");
	  //记录异常堆栈
	  span.recordException(throwable);
	}finally {
      //结束Span
      parentSpan.end();
    }
  }

  private int rollOnce() {
    //子Span处理
    Span childSpan = tracer.spanBuilder("child")
    // NOTE: setParent(...) is not required;
    // `Span.current()` is automatically added as the parent
    .startSpan();
    try(Scope scope = childSpan.makeCurrent()) {
      return ThreadLocalRandom.current().nextInt(this.min, this.max + 1);
    } finally {
      //结束Span
      childSpan.end();
    }
  }  
}

Context传递示例:

//Context读取(从请求头中获取context信息,如请求头traceparent)
TextMapGetter<HttpHeaders> getter =
  new TextMapGetter<HttpHeaders>() {
    @Override
    public String get(HttpHeaders headers, String s) {
      assert headers != null;
      return headers.getHeaderString(s);
    }

    @Override
    public Iterable<String> keys(HttpHeaders headers) {
      List<String> keys = new ArrayList<>();
      MultivaluedMap<String, String> requestHeaders = headers.getRequestHeaders();
      requestHeaders.forEach((k, v) ->{
        keys.add(k);
      });
      return keys.
    }
};

//Context设置(向请求中写入context信息,如请求头traceparent)
TextMapSetter<HttpURLConnection> setter =
  new TextMapSetter<HttpURLConnection>() {
    @Override
    public void set(HttpURLConnection carrier, String key, String value) {
        // Insert the context as Header
        carrier.setRequestProperty(key, value);
    }
};

//...
public void handle(<Library Specific Annotation> HttpHeaders headers){
        //从当前请求中解析上下文
        Context extractedContext = opentelemetry.getPropagators().getTextMapPropagator()
                .extract(Context.current(), headers, getter);
        //使用解析出的上下文
        try (Scope scope = extractedContext.makeCurrent()) {
            // Automatically use the extracted SpanContext as parent.
            Span serverSpan = tracer.spanBuilder("GET /resource")
                .setSpanKind(SpanKind.SERVER)
                .startSpan();

            try(Scope ignored = serverSpan.makeCurrent()) {
                // Add the attributes defined in the Semantic Conventions
                serverSpan.setAttribute(SemanticAttributes.HTTP_METHOD, "GET");
                serverSpan.setAttribute(SemanticAttributes.HTTP_SCHEME, "http");
                serverSpan.setAttribute(SemanticAttributes.HTTP_HOST, "localhost:8080");
                serverSpan.setAttribute(SemanticAttributes.HTTP_TARGET, "/resource");

                HttpURLConnection transportLayer = (HttpURLConnection) url.openConnection();
                //设置新请求的上下文
                // Inject the request with the *current*  Context, which contains our current Span.
                openTelemetry.getPropagators().getTextMapPropagator().inject(Context.current(), transportLayer, setter);
                // Make outgoing call
            }finally {
                serverSpan.end();
            }
      }
}

5.2 Metrics

种类:

  • LongCounter / DoubleCounter - Sync/Async
  • LongUpDownCounter / DoubleUpDownCounter - Sync/Async
  • LongGauge / DoubleGauge - Async
  • LongHistogram / DoubleHistogram - Sync
OpenTelemetry openTelemetry = // obtain instance of OpenTelemetry

// Gets or creates a named meter instance
Meter meter = openTelemetry.meterBuilder("instrumentation-library-name")
        .setInstrumentationVersion("1.0.0")
        .build();

// Build counter e.g. LongCounter
LongCounter counter = meter
      .counterBuilder("processed_jobs")
      .setDescription("Processed jobs")
      .setUnit("1")
      .build();

// It is recommended that the API user keep a reference to Attributes they will record against
Attributes attributes = Attributes.of(AttributeKey.stringKey("Key"), "SomeWork");

// Record data
counter.add(123, attributes);

------------------------------

// Build an asynchronous instrument, e.g. Gauge
meter
  .gaugeBuilder("cpu_usage")
  .setDescription("CPU Usage")
  .setUnit("ms")
  .buildWithCallback(measurement -> {
    measurement.record(getCpuUsage(), Attributes.of(AttributeKey.stringKey("Key"), "SomeWork"));
  });

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1280900.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在Excel中,只需点击几下,就能只复制和粘贴可见单元格

你可以在Excel中隐藏列、行或单元格&#xff0c;以使数据输入或分析更容易。但是&#xff0c;当你复制并粘贴一个包含隐藏单元格的单元格区域时&#xff0c;它们会突然重新出现。 你可能没有意识到&#xff0c;但有一种方法可以只复制和粘贴Microsoft Excel中的可见单元格。只…

SpringMVC常用注解和用法总结

目标&#xff1a; 1. 熟悉使用SpringMVC中的常用注解 目录 前言 1. Controller 2. RestController 3. RequestMapping 4. RequestParam 5. PathVariable 6. SessionAttributes 7. CookieValue 前言 SpringMVC是一款用于构建基于Java的Web应用程序的框架&#xff0c;它通…

FPC和PCB有哪些区别?

现在电子技术越来越先进&#xff0c;CPU可以做到5nm工艺&#xff0c;电路板可以做到几十层&#xff0c;可折叠屏应用多款手机中。 什么是FPC&#xff1f; FPC&#xff1a;Flexible Printed Circuit&#xff0c;柔性电路板&#xff0c;又被称为“软板” FPC 以聚酰亚胺或聚酯薄…

HBASE命令行查看中文字符

问题记录 中文显示的是编码字符不方便查看value\xE5\xB8\xB8\xE5\xAE\x89\xE5\xAE\x891修改前中文显示&#xff1a; 解决方法 1、列族 : 列名 : toString ’2、列族 : 列名 : c(org.apache.hadoop.hbase.util.Bytes).toString ’ scan karry:student,{COLUMNS > [info:…

团队怎么高效制作问卷?

制作调查问卷时并不是一个人就能单独完成&#xff0c;通常情况下&#xff0c;完成一份调查问卷往往需要一个团队的成员参与&#xff0c;相互协作&#xff0c;共同完成。不过&#xff0c;多人协作经常会遇到协作壁垒&#xff0c;导致效率低下&#xff0c;那团队怎么才能高效协作…

K210开发板之VSCode开发环境使用中添加或删除文件(编译失败时)需要注意事项

在最初开始接触&#xff0c;将VScode和编译环境搭载好后&#xff0c;就开始运行第一个程序了&#xff0c;为了后续方便开发测试&#xff0c;这里我自己对照官方提供的例子&#xff0c;自己调试&#xff0c;写了一个简单的文件系统 后续&#xff0c;所有关于开发的源文件都在...…

【无标题】我们只能用成功来摧毁我们,原来的自己只会破败自己的事情。

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

从“AI证件照”到“AI译制片”,爆款AIGC应用的商业化迷思

让郭德纲飙英文、让霉霉说中文的翻译视频生成工具HeyGen和掀起AI证件照热潮的“妙鸭相机”一样&#xff0c;在一阵疯狂刷屏之后&#xff0c;又迅速在各大群里销声匿迹了。 十月份&#xff0c;由HeyGen制作的各种明星跨语言翻译视频&#xff0c;在全网疯传&#xff0c;大家震撼于…

DLL缺失

DLL缺失 参考链接&#xff1a; 方法五&#xff0c;亲测有用

Android 源码编译

一&#xff0c;虚拟机安装 ​ 1.1 进入https://cn.ubuntu.com/download中文官网下载iso镜像 1.2 这里我们下载Ubuntu 18.04 LTS 1.3虚拟VM机安装ubuntu系统&#xff0c;注意编译源码需要至少16G运行内存和400G磁盘空间&#xff0c;尽量设大点 二 配置编译环境 2.1 下载andr…

使用VC++设计程序实现K近邻中值滤波器(KNNMF)、最小均方差滤波器、矢量中值滤波算法进行滤波

VC实现若干种图像滤波技术2 获取源工程可访问gitee可在此工程的基础上进行学习。 该工程的其他文章&#xff1a; 01- 一元熵值、二维熵值 02- 图像平移变换&#xff0c;图像缩放、图像裁剪、图像对角线镜像以及图像的旋转 03-邻域平均平滑算法、中值滤波算法、K近邻均值滤波器 …

页面表格高度自适应

前言 现在后端管理系统主页面基本都是由三部分组成 查询条件&#xff0c;高度不固定&#xff0c;可能有的页面查询条件多&#xff0c;有的少表格&#xff0c;高度不固定&#xff0c;占据页面剩余高度分页&#xff0c;高度固定 这三部分加起来肯定是占满全屏的&#xff0c;那么我…

openEuler学习05-ssh升级到openssh-9.5p1

openEuler的版本是openEuler 20.03&#xff0c;ssh的版本是OpenSSH_8.2p1 [roottest ~]# more /etc/os-release NAME"openEuler" VERSION"20.03 (LTS-SP3)" ID"openEuler" VERSION_ID"20.03" PRETTY_NAME"openEuler 20.03 (LTS-…

Python安装步骤介绍

本文将介绍Python安装的详细步骤如下&#xff1a; 下载 python安装 python配置环境变量&#xff08;安装时勾选配置环境变量的则无需此步骤&#xff09; 一、python下载 官网&#xff1a;Download Python | Python.org 根据电脑位数下载所需的版本 二、Python安装 1.打开安…

基于深度学习面向中医诊断的舌象图像分割系统

1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 研究背景与意义 中医舌诊是通过观察舌的各种特征来了解人体的健康状况&#xff0c;从而对各种疾病做出诊断及病情评估&#xff0c;是传统中国医学应用最广、最有价值的诊法之一。…

[C/C++]数据结构 关于二叉树的OJ题(利用分治思想解决难题)

题目一: 单值二叉树 &#x1f6a9;⛲&#x1f31f;⚡&#x1f966;&#x1f4ac; &#x1f6a9;题目链接:力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 ⛲题目描述: 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。…

每日一题:LeetCode-209. 长度最小的子数组(滑动窗口)

每日一题系列&#xff08;day 11&#xff09; 前言&#xff1a; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f50e…

一文弄懂BFS【广度优先搜索(Breadth-First Search)】

BFS&#xff0c;全名为广度优先搜索(Breadth-First Search)&#xff0c;是一种用于图或树的遍历或搜索的算法。它的主要思想是由节点自身开始向它的邻居节点新进展开搜索&#xff0c;因此也常被形象地称为“层序遍历”。 BFS 基本思想 BFS 工作原理是&#xff0c;从开始节点开…

shell命令编写

1. 1 #!/bin/bash 2 3 directory_path"/txh"4 5 # 使用 find 命令查找指定路径下的文件&#xff0c;并使用 wc 命令统计行数&#xff08;即文件个数&#xff09;6 7 file_count$(find "directory_path" -type f | wc -l)8 9 10 echo "在路径$director…

Wireshark 协议插件Lua开发 -数据包内嵌协议的解释

概述 因为公司项目涉及的协议打包&#xff0c;协议包内又嵌了一层IP包的奇葩套娃结构&#xff0c;为了方便抓包调试&#xff0c;利用Wireshark的协议插件开发功能&#xff0c;写了一个插件&#xff0c;博文记录以备忘。 环境信息 Wireshark 4.0.3 协议结构体套娃图 插件安装…