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

目录

    • 一、架构说明
    • 二、方式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"));
  });

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

智能诊疗体验:整合AI技术的互联网医院小程序开发

在科技化的趋势下&#xff0c;互联网医院小程序的开发变得愈发重要&#xff0c;尤其是通过整合人工智能&#xff08;AI&#xff09;技术&#xff0c;进一步提升了就医的效率。 一、引言 互联网医院小程序其开发目标是提高医疗服务的效率&#xff0c;同时也也提升了用户的就医…

【读书笔记】微习惯

周日晚上尝试速读一本书《微习惯》&#xff0c;共七章看了下目录结构并不复杂&#xff0c;计划每章7-8分钟读完&#xff0c; 从20:15-21:00。读的时候&#xff0c;订下闹钟&#xff0c;催促着自己的进度。边读边记了一些要点和微信读书里面的划线。 第六章实践内容最为丰富&…

Ubuntu中安装IDEA,并配置桌面快捷方式

1、首先自己下载linux版本的idea 这一步省略不说了 2、在/usr/local/路径下新建安装目录IDEA&#xff1a; mkdir -p /usr/local/IDEA3、执行如下命令&#xff0c;解压下载的压缩包到指定目录&#xff1a; tar -zxvf ideaIU-2022.3.3.tar.gz -C /usr/local/IDEA 注意&#x…

蓝桥杯每日一题2023.12.3

题目描述 1.移动距离 - 蓝桥云课 (lanqiao.cn) 题目分析 对于此题需要对行列的关系进行一定的探究&#xff0c;所求实际上为曼哈顿距离&#xff0c;只需要两个行列的绝对值想加即可&#xff0c;预处理使下标从0开始可以更加明确之间的关系&#xff0c;奇数行时这一行的数字需…

Proteus仿真--基于ADC0832设计的两路电压表

本文介绍基于ADC0832实现的双路电压表采集设计&#xff08;完整仿真源文件及代码见文末链接&#xff09; 仿真图如下 采集芯片选用ADC0832&#xff0c;电压显示在LCD1602液晶显示屏上 仿真运行视频 Proteus仿真--基于ADC0832设计的两路电压表 附完整Proteus仿真资料代码资料…

代理模式介绍(静态代理、jdk动态代理、cglib代理)

一、静态代理 &#xff08;一&#xff09;定义 1、定义 为其他对象提供一种代理以控制对这个对象的访问&#xff1b; 2、涉及到的角色 &#xff08;1&#xff09;抽象主题角色&#xff1a;真实主题和代理主题的共同接口&#xff0c;便于在使用真实主题的地方都可以使用代理…

p标签在div中居中

新建一个html文件&#xff0c;命名为test.html&#xff0c;用于讲解如何在css中让div中的p标签居中。 在test.html文件内&#xff0c;在div内&#xff0c;使用p标签创建一行文字&#xff0c;用于测试。 在test.html文件内&#xff0c;设置div标签的class属性为mydiv。 在…

HttpRunner自动化工具之实现参数化传递

参数化实现及重复执行 参数化测试&#xff1a;在接口测试中&#xff0c;为了实现不同组数据对同一个功能模块进行测试&#xff0c;需要准备多组测试数据对模块进行测试的过程。 在httprunner中可以通过如下方式实现参数化&#xff1a; 1、在YAML/JSON 中直接指定参数列表 2、…

Qt OpenCV 学习(一):环境搭建

对应版本 Qt 5.15.2OpenCV 3.4.9MinGW 8.1.0 32-bit 1. OpenCV 下载 确保安装 Qt 时勾选了 MinGW 编译器 本文使用 MinGW 编译好的 OpenCV 库&#xff0c;无需自行编译 确保下载的 MinGW 和上述安装 Qt 时勾选的 MinGW 编译器位数一致&#xff0c;此处均为 x86/32-bit下载地址…

【java+vue+微信小程序项目】从零开始搭建——健身房管理平台(3)路由导航卫士、主页实现

项目笔记为项目总结笔记,若有错误欢迎指出哟~ 【项目专栏】 【java+vue+微信小程序项目】从零开始搭建——健身房管理平台(1)spring boot项目搭建、vue项目搭建、微信小程序项目搭建 【java+vue+微信小程序项目】从零开始搭建——健身房管理平台(2)后端跨域、登录模块、sp…

LeetCode | 101. 对称二叉树

LeetCode | 101. 对称二叉树 OJ链接 在本函数里不好进行判断&#xff0c;我们另外定义一个函数来如果两个都相等为空&#xff0c;就返回true一个为空&#xff0c;一个不为空都不为空,就比较值然后递归1的左&#xff0c;2的右&#xff0c;1的右&#xff0c;2的左 bool _isSymm…

Gateway网关--java

网关是建立于请求到服务之前的,可以用网关限制访问量,添加过滤等 创建网关模块,引入相关pome依赖 配置yml 具体相关的作用可以参考 Spring Cloud Gateway 这样就可以了 基础的网关配置,我们的实现效果 我们可以通过10010端口访问,通过转发到nacos,再找到相应的模块,实现…

54.多级缓存

目录 一、传统缓存的问题、多级缓存方案。 二、JVM进程缓存。 1&#xff09;进程缓存和缓存。 2&#xff09;导入商品案例。 1.安装MySQL 2.导入SQL 3.导入Demo工程 4.导入商品查询页面 3&#xff09;初识Caffeine&#xff08;就是在springboot学过的注解方式的cache&…

掌握视频剪辑技巧:批量置入视频封面,提升视频品质

在当今数字化时代&#xff0c;视频已成为生活的重要组成部分。无论是观看电影、电视剧、综艺节目&#xff0c;还是分享个人生活、工作成果&#xff0c;视频都以其独特的魅力吸引着大众的视线。视频封面是视频内容的缩影&#xff0c;是观众对视频的第一印象。一个好的封面能吸引…

【计算机组成原理】存储器知识

目录 1、存储器分类 1.1、按存储介质分类 1.2、按存取方式分类 1.3、按信息的可改写性分类 1.4、按信息的可保存性分类 1.5、按功能和存取速度分类 2、存储器技术指标 2.1、存储容量 2.2、存取速度 3、存储系统层次结构 4、主存的基本结构 5、主存中数据的存放 5.…

LabVIEW远程监控

LabVIEW远程监控 远程监控的应用场景 从办公室远程监控工厂车间的测试设备。 在世界另一端的偏远地区监控客户现场的发电设备。 从公司远程监控外场的产品。 技术更新与方法 自2018年以来&#xff0c;NI对基于Web的应用程序支持大幅增长。一些最初的方法&#xff08;如Lab…

搭建CIG容器重量级监控平台

CIG简介 CIG监控平台是基于CAdvisor、InfluxDB和Granfana构建的一个容器重量级监控系统&#xff0c;用于监控容器的各项性能指标&#xff0c;通过三者的结合&#xff0c;CIG监控平台可以实现对容器性能的全面监控和可视化展示&#xff0c;为容器的性能和运行状态提供了一个全面…

React如何检查组件性能

可以使用Profiler来查看组件的渲染速度 Profiler的基本使用 需要将<Profiler>标签包裹在需要检查渲染速度的组件外部需要绑定id属性&#xff0c;该属性是唯一标识&#xff0c;用于区分其他Profiler需要onRender函数&#xff0c;该函数一共有六个参数&#xff0c;分别为…

数据结构入门————树(C语言/零基础/小白/新手+模拟实现+例题讲解)

目录 1. 树的概念及其结构 1.1 树的概念&#xff1a; 1.2 树的相关概念&#xff1a; 1.3 树的表示方法&#xff1a; ​编辑 1.4 树的应用&#xff1a; 2. 二叉树的概念及其结构 2.1 概念: 2.2 特点&#xff1a; 2.3 特殊二叉树&#xff1a; 2.4 二叉树的性质&#xf…

【文献阅读笔记】基于自监督的异常检测和定位:SSM

2022 IEEE TRANSACTIONS ON MULTIMEDIA 领域&#xff1a;异常检测 目标&#xff1a;图像输入数据 文章目录 1、模型2、方法2.1、random masking2.2、restoration network2.3、损失函数2.4、推理时的渐进细化 3、实验4、引用5、想法 1、模型 训练&#xff1a; 每个图像实时生成随…