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 {@WithSpanpublic void myMethod() {<...>}@WithSpanpublic 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: falseendpoint: http://localhost:4317timeout: 10s# 导出到Zipkinzipkin:enabled: falseendpoint: http://localhost:9411/api/v2/spans# 导出到Jaegerjaeger:enabled: trueendpoint: http://10.170.xx.xxx:xxxxtimeout: 10s# 导出到日志logging:enabled: truetraces: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;//获取Tracerthis.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创建SpanSpan 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 {//结束SpanparentSpan.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 {//结束SpanchildSpan.end();}}  
}

Context传递示例:

//Context读取(从请求头中获取context信息,如请求头traceparent)
TextMapGetter<HttpHeaders> getter =new TextMapGetter<HttpHeaders>() {@Overridepublic String get(HttpHeaders headers, String s) {assert headers != null;return headers.getHeaderString(s);}@Overridepublic 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>() {@Overridepublic void set(HttpURLConnection carrier, String key, String value) {// Insert the context as Headercarrier.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 ConventionsserverSpan.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.mzph.cn/news/194095.shtml

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

相关文章

【栈和队列(2)】

文章目录 前言队列队列方法队列模拟实现循环队列练习1 队列实现栈 前言 队列和栈是相反的&#xff0c;栈是先进后出&#xff0c;队列是先进先出&#xff0c;相当于排队打饭&#xff0c;排第一的是最先打到饭出去的。 队列 队列&#xff1a;只允许在一端进行插入数据操作&…

20、Resnet 为什么这么重要

&#xff08;本文已加入“计算机视觉入门与调优”专栏&#xff0c;点击专栏查看更多文章信息&#xff09; resnet 这一网络的重要性&#xff0c;上一节大概介绍了一下&#xff0c;可以从以下两个方面来有所体现&#xff1a;第一是 resnet 广泛的作为其他神经网络的 back bone&…

Redis集合对象

一. 编码 集合对象的编码可以是intset或者hashtable。 intset编码的集合对象使用整数集合作为底层实现&#xff0c;集合对象包含的所有元素都保存在整数集合里面。 127.0.0.1:6379> sadd numbers 1 3 5 (integer) 3 127.0.0.1:6379> object encoding numbers "ints…

使用凌鲨进行内网穿透

为了方便在本地进行开发和调试工作&#xff0c;有时候需要安全地连接内网或Kubernetes集群中的服务。 在net proxy server中可以限制访问用户&#xff0c;也可以设置端口转发的密码。 使用 连接端口转发服务 列出可转发端口 可转发端口是服务端设置的&#xff0c;不会暴露真…

锁表的原因及解决办法

引言 作为开发人员&#xff0c;我们经常会和数据库打交道。 当我们对数据库进行修改操作的时候&#xff0c;例如添加字段&#xff0c;更新记录等&#xff0c;没有正确评估该表在这一时刻的使用频率&#xff0c;直接进行修改&#xff0c;致使修改操作长时间无法响应&#xff0…

2023年【起重机司机(限桥式起重机)】报名考试及起重机司机(限桥式起重机)考试资料

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年【起重机司机(限桥式起重机)】报名考试及起重机司机(限桥式起重机)考试资料&#xff0c;包含起重机司机(限桥式起重机)报名考试答案和解析及起重机司机(限桥式起重机)考试资料练习。安全生产模拟考试一点通结合…

正是阶段高等数学复习--函数极限的计算

之前在预备阶段中函数极限的解决方式分三步&#xff0c;第一步观察形式并确定用什么方式来解决&#xff0c;第二步化简&#xff0c;化简方式一共有7种&#xff0c;分别是最重要的三种&#xff08;等价替换、拆分极限存在的项、计算非零因子&#xff09;以及次重要的4种&#xf…

BurpSuite 请求/响应解密插件开发

BurpSuite 请求/响应解密插件开发 本文主要记录如何利用burp官方的新版API即MontoyaApi 写一个请求/响应的解密插件。背景下面是主要的操作步骤&#xff1a;根据上述操作做完之后&#xff0c;生成&#xff0c;然后在burp中加载插件&#xff0c;然后通关抓包看效果&#xff0c;具…

DevEco Studio 调整开发工具中的字体大小与行高

我们打开编辑器 选择 左上角 File 下的 Settings 将左侧菜单栏 编辑 展开 我们在编辑下面 选择 Font 然后 如下图指向的两个位置 我们可以调整它的字体大小和行高 设置好之后 右下角 点击 Apply 应用 然后点击 OK即可 当然 你按着 Ctrl 然后鼠标滚动 也可以像浏览器那样 拉…

维基百科文章爬虫和聚类:高级聚类和可视化

一、说明 维基百科是丰富的信息和知识来源。它可以方便地构建为带有类别和其他文章链接的文章&#xff0c;还形成了相关文档的网络。我的 NLP 项目下载、处理和应用维基百科文章上的机器学习算法。 在我的上一篇文章中&#xff0c;KMeans 聚类应用于一组大约 300 篇维基百科文…

Spring IOC—基于XML配置和管理Bean 万字详解(通俗易懂)

目录 一、前言 二、通过类型来获取Bean 0.总述&#xff08;重要&#xff09; : 1.基本介绍 : 2.应用实例 : 三、通过指定构造器为Bean注入属性 1.基本介绍 : 2.应用实例 : 四、通过p命名空间为Bean注入属性 1.基本介绍 : 2.应用实例 : 五、通过ref引用实现Bean的相…

吸烟(抽烟)检测和识别2:Pytorch实现吸烟(抽烟)检测和识别(含吸烟(抽烟)数据集和训练代码)

吸烟(抽烟)检测和识别2&#xff1a;Pytorch实现吸烟(抽烟)检测和识别(含吸烟(抽烟)数据集和训练代码) 目录 吸烟(抽烟)检测和识别2&#xff1a;Pytorch实现吸烟(抽烟)检测和识别(含吸烟(抽烟)数据集和训练代码) 1.吸烟(抽烟)检测和识别 2.吸烟(抽烟)数据集 &#xff08;1&am…

OpenGL ES入门教程(三)之为平面桌子添加混合色

OpenGL ES入门教程&#xff08;三&#xff09;之为平面桌子添加渐变色 前言零、OpenGL ES实现混合色的原理一、修改绘制的桌子结构1. 三角形扇介绍2. 基于三角形扇结构绘制平面桌子 二、为每个顶点添加颜色属性三、修改着色器1. 顶点着色器2. 片段这色器 四、绘制具有混合颜色的…

7.24 SpringBoot项目实战【审核评论】

文章目录 前言一、编写控制器二、编写服务层三、Postman测试前言 我们在 上文 7.23 已经实现了 评论 功能,本文我们继续SpringBoot项目实战 审核评论 功能。逻辑如下: 一是判断管理员权限,关于角色权限校验 在 7.5 和 7.6 分别基于 拦截器Interceptor 和 切面AOP 都实现过…

Qt OpenCV 学习(二):两个简单图片识别案例

1. 寻找匹配物体 1.1 mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <opencv2/opencv.hpp>#include <QImage> #include <QString> #include <QPixmap>QT_BEGIN_NAMESPACE namespace Ui { class Main…

TimeGPT:时序预测领域终于迎来了第一个大模型

时间序列预测领域在最近的几年有着快速的发展&#xff0c;比如N-BEATS、N-HiTS、PatchTST和TimesNet。 大型语言模型(llm)最近在ChatGPT等应用程序中变得非常流行&#xff0c;因为它们可以适应各种各样的任务&#xff0c;而无需进一步的训练。 这就引出了一个问题:时间序列的…

速达软件全系产品存在任意文件上传漏洞 附POC

@[toc] 速达软件全系产品存在任意文件上传漏洞 附POC 免责声明:请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。…

基于Java SSM框架+Vue实现药品保健品购物网站项目【项目源码+论文说明】

基于java的SSM框架Vue实现药品保健品购物网站演示 摘要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 ssm药源购物网站&#xff0c;主要的模块包括两个用户&#xff0c;管理员权限&#xff1a;用…

【数据库】基于封锁的数据库调度器,以及等待锁处理的优先级策略

封锁调度器的体系结构 ​专栏内容&#xff1a; 手写数据库toadb 本专栏主要介绍如何从零开发&#xff0c;开发的步骤&#xff0c;以及开发过程中的涉及的原理&#xff0c;遇到的问题等&#xff0c;让大家能跟上并且可以一起开发&#xff0c;让每个需要的人成为参与者。 本专栏会…

分享一个国内可用的免费AI-GPT网站

背景 ChatGPT作为一种基于人工智能技术的自然语言处理工具&#xff0c;近期的热度直接沸腾&#x1f30b;。 我们也忍不住做了一个基于ChatGPT的网站&#xff0c;可以免登陆&#xff01;&#xff01;国内可直接对话AI&#xff0c;也有各种提供工作效率的工具供大家使用。 可以这…