日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

實戰:如何編寫一個 OpenTelemetry Extensions

來源: 責編: 時間:2024-04-16 08:32:28 176觀看
導讀前言前段時間我們從 SkyWalking 切換到了 OpenTelemetry ,與此同時之前使用 SkyWalking 編寫的插件也得轉移到 OpenTelemetry 體系下。好在 OpenTelemetry 社區也提供了 Extensions 的擴展開發,我們可以不用去修改社區

1dl28資訊網——每日最新資訊28at.com

前言

前段時間我們從 SkyWalking 切換到了 OpenTelemetry ,與此同時之前使用 SkyWalking 編寫的插件也得轉移到 OpenTelemetry 體系下。1dl28資訊網——每日最新資訊28at.com

好在 OpenTelemetry 社區也提供了 Extensions 的擴展開發,我們可以不用去修改社區發行版:opentelemetry-javaagent.jar 的源碼也可以擴展其中的能力。1dl28資訊網——每日最新資訊28at.com

比如可以:1dl28資訊網——每日最新資訊28at.com

  • 修改一些 trace,某些 span 不想記錄等。
  • 新增 metrics

這次我準備編寫的插件也是和 metrics 有關的,因為 pulsar 的 Java sdk 中并沒有暴露客戶端的一些監控指標,所以我需要在插件中攔截到一些關鍵函數,然后執行暴露出指標。1dl28資訊網——每日最新資訊28at.com

截止到本文編寫的時候, Pulsar 社區也已經將 Java-client 集成了 OpenTelemetry,后續正式發版后我這個插件也可以光榮退休了。1dl28資訊網——每日最新資訊28at.com

由于 OpenTelemetry 社區還處于高速發展階段,我在中文社區沒有找到類似的參考文章(甚至英文社區也沒有,只有一些 example 代碼,或者是只有去社區成熟插件里去參考代碼)1dl28資訊網——每日最新資訊28at.com

其中也踩了不少坑,所以覺得非常有必要分享出來幫助大家減少遇到同類問題的機會。1dl28資訊網——每日最新資訊28at.com

開發流程

OpenTelemetry extension 的寫法其實和 skywalking 相似,都是用的 bytebuddy這個字節碼增強庫,只是在一些 API 上有一些區別。1dl28資訊網——每日最新資訊28at.com

創建項目

首先需要創建一個 Java 項目,這里我直接參考了官方的示例,使用了 gradle 進行管理(理論上 maven 也是可以的,只是要找到在 gradle 使用的 maven 插件)。1dl28資訊網——每日最新資訊28at.com

這里貼一下簡化版的 build.gradle 文件:1dl28資訊網——每日最新資訊28at.com

plugins {    id 'java'    id "com.github.johnrengelman.shadow" version "8.1.1"    id "com.diffplug.spotless" version "6.24.0"}group = 'com.xx.otel.extensions'version = '1.0.0'ext {    versions = [            // this line is managed by .github/scripts/update-sdk-version.sh            opentelemetrySdk           : "1.34.1",            // these lines are managed by .github/scripts/update-version.sh            opentelemetryJavaagent     : "2.1.0-SNAPSHOT",            opentelemetryJavaagentAlpha: "2.1.0-alpha-SNAPSHOT",            junit                      : "5.10.1"    ]    deps = [    // 自動生成服務發現 service 文件            autoservice: dependencies.create(group: 'com.google.auto.service', name: 'auto-service', version: '1.1.1')    ]}repositories {    mavenLocal()    maven { url "https://maven.aliyun.com/repository/public" }    mavenCentral()}configurations {    otel}dependencies {    implementation(platform("io.opentelemetry:opentelemetry-bom:${versions.opentelemetrySdk}"))    /*    Interfaces and SPIs that we implement. We use `compileOnly` dependency because during    runtime all necessary classes are provided by javaagent itself.     */    compileOnly 'io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.34.1'    compileOnly 'io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:1.32.0'    compileOnly 'io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api:1.32.0-alpha'    //Provides @AutoService annotation that makes registration of our SPI implementations much easier    compileOnly deps.autoservice    annotationProcessor deps.autoservice    // https://mvnrepository.com/artifact/org.apache.pulsar/pulsar-client    compileOnly 'org.apache.pulsar:pulsar-client:2.8.0'}test {    useJUnitPlatform()}

然后便是要創建  javaagent 的一個核心類:1dl28資訊網——每日最新資訊28at.com

@AutoService(InstrumentationModule.class)  public class PulsarInstrumentationModule extends InstrumentationModule {    public PulsarInstrumentationModule() {        super("pulsar-client-metrics", "pulsar-client-metrics-2.8.0");    } }

在這個類中定義我們插件的名稱,同時使用 @AutoService 注解可以在打包的時候幫我們在 META-INF/services/目錄下生成 SPI 服務發現的文件:1dl28資訊網——每日最新資訊28at.com

1dl28資訊網——每日最新資訊28at.com

這是一個 Google 的插件,本質是插件是使用 SPI 的方式進行開發的。1dl28資訊網——每日最新資訊28at.com

關于 SPI 以前也寫過一篇文章,不熟的朋友可以用作參考:1dl28資訊網——每日最新資訊28at.com

  • Java SPI 的原理與應用

創建 Instrumentation

之后就需要創建自己的 Instrumentation,這里可以把它理解為自己的攔截器,需要配置對哪個類的哪個函數進行攔截:1dl28資訊網——每日最新資訊28at.com

public class ProducerCreateImplInstrumentation implements TypeInstrumentation {    @Override    public ElementMatcher<TypeDescription> typeMatcher() {        return named("org.apache.pulsar.client.impl.ProducerBuilderImpl");    }    @Override    public void transform(TypeTransformer transformer) {        transformer.applyAdviceToMethod(                isMethod()                        .and(named("createAsync")),                ProducerCreateImplInstrumentation.class.getName() + "$ProducerCreateImplConstructorAdvice");    }

比如這就是對 ProducerBuilderImpl 類的 createAsync 創建函數進行攔截,攔截之后的邏輯寫在了 ProducerCreateImplConstructorAdvice 類中。1dl28資訊網——每日最新資訊28at.com

值得注意的是對一些繼承和實現類的攔截方式是不相同的:1dl28資訊網——每日最新資訊28at.com

@Override  public ElementMatcher<TypeDescription> typeMatcher() {      return extendsClass(named(ENHANCE_CLASS));      // return implementsInterface(named(ENHANCE_CLASS));}

從這兩個函數名稱就能看出,分別是針對繼承和實現類進行攔截的。1dl28資訊網——每日最新資訊28at.com

這里的 API 比 SkyWalking 的更易讀一些。1dl28資訊網——每日最新資訊28at.com

之后需要把我們自定義的 Instrumentation 注冊到剛才的 PulsarInstrumentationModule 類中:1dl28資訊網——每日最新資訊28at.com

@Override    public List<TypeInstrumentation> typeInstrumentations() {        return Arrays.asList(                new ProducerCreateImplInstrumentation(),                new ProducerCloseImplInstrumentation(),                );    }

有多個的話也都得進行注冊。1dl28資訊網——每日最新資訊28at.com

編寫切面代碼

之后便是編寫我們自定義的切面邏輯了,也就是剛才自定義的 ProducerCreateImplConstructorAdvice 類:1dl28資訊網——每日最新資訊28at.com

public static class ProducerCreateImplConstructorAdvice {        @Advice.OnMethodEnter(suppress = Throwable.class)        public static void onEnter() {            // inert your code            MetricsRegistration.registerProducer();        }        @Advice.OnMethodExit(suppress = Throwable.class)        public static void after(                @Advice.Return CompletableFuture<Producer> completableFuture) {            try {                Producer producer = completableFuture.get();                CollectionHelper.PRODUCER_COLLECTION.addObject(producer);            } catch (Throwable e) {                System.err.println(e.getMessage());            }        }    }

可以看得出來其實就是兩個核心的注解:1dl28資訊網——每日最新資訊28at.com

  • @Advice.OnMethodEnter 切面函數調用之前
  • @Advice.OnMethodExit 切面函數調用之后

還可以在 @Advice.OnMethodExit的函數中使用 @Advice.Return獲得函數調用的返回值。1dl28資訊網——每日最新資訊28at.com

當然也可以使用 @Advice.This 來獲取切面的調用對象。1dl28資訊網——每日最新資訊28at.com

編寫自定義 metrics

因為我這個插件的主要目的是暴露一些自定義的 metrics,所以需要使用到 io.opentelemetry.api.metrics 這個包:1dl28資訊網——每日最新資訊28at.com

這里以 Producer 生產者為例,整體流程如下:1dl28資訊網——每日最新資訊28at.com

  • 創建生產者的時候將生產者對象存儲起來
  • OpenTelemetry 框架會每隔一段時間回調一個自定義的函數
  • 在這個函數中遍歷所有的 producer 獲取它的監控指標,然后暴露出去。

注冊函數:1dl28資訊網——每日最新資訊28at.com

public static void registerObservers() {      Meter meter = MetricsRegistration.getMeter();        meter.gaugeBuilder("pulsar_producer_num_msg_send")              .setDescription("The number of messages published in the last interval")              .ofLongs()              .buildWithCallback(                      r -> recordProducerMetrics(r, ProducerStats::getNumMsgsSent));

private static void recordProducerMetrics(ObservableLongMeasurement observableLongMeasurement, Function<ProducerStats, Long> getter) {      for (Producer producer : CollectionHelper.PRODUCER_COLLECTION.list()) {          ProducerStats stats = producer.getStats();          String topic = producer.getTopic();          if (topic.endsWith(RetryMessageUtil.RETRY_GROUP_TOPIC_SUFFIX)) {              continue;          }        observableLongMeasurement.record(getter.apply(stats),                  Attributes.of(PRODUCER_NAME, producer.getProducerName(), TOPIC, topic));      }}

回調函數,在這個函數中遍歷所有的生產者,然后讀取它的監控指標。1dl28資訊網——每日最新資訊28at.com

這樣就完成了一個自定義指標的暴露,使用的時候只需要加載這個插件即可:1dl28資訊網——每日最新資訊28at.com

java -javaagent:opentelemetry-javaagent.jar /     -Dotel.javaagent.extensinotallow=ext.jar     -jar myapp.jar

-Dotel.javaagent.extensinotallow=/extensions當然也可以指定一個目錄,該目錄下所有的 jar 都會被作為 extensions 被加入進來。1dl28資訊網——每日最新資訊28at.com

打包

使用 ./gradlew build 打包,之后可以在build/libs/目錄下找到生成物。1dl28資訊網——每日最新資訊28at.com

當然也可以將 extension 直接打包到 opentelemetry-javaagent.jar中,這樣就可以不用指定 -Dotel.javaagent.extensions參數了。1dl28資訊網——每日最新資訊28at.com

具體可以在 gradle 中加入以下 task:1dl28資訊網——每日最新資訊28at.com

task extendedAgent(type: Jar) {  dependsOn(configurations.otel)  archiveFileName = "opentelemetry-javaagent.jar"  from zipTree(configurations.otel.singleFile)  from(tasks.shadowJar.archiveFile) {    into "extensions"  }  //Preserve MANIFEST.MF file from the upstream javaagent  doFirst {    manifest.from(      zipTree(configurations.otel.singleFile).matching {        include 'META-INF/MANIFEST.MF'      }.singleFile    )  }}

具體可以參考這里的配置:https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/examples/extension/build.gradle#L1251dl28資訊網——每日最新資訊28at.com

踩坑

看起來這個開發過程挺簡單的,但其中的坑還是不少。1dl28資訊網——每日最新資訊28at.com

NoClassDefFoundError

首先第一個就是我在調試過程中出現 NoClassDefFoundError 的異常。1dl28資訊網——每日最新資訊28at.com

但我把打包好的 extension 解壓后明明是可以看到這個類的。1dl28資訊網——每日最新資訊28at.com

1dl28資訊網——每日最新資訊28at.com

排查一段時間后沒啥頭緒,我就從頭仔細閱讀了開發文檔:1dl28資訊網——每日最新資訊28at.com

1dl28資訊網——每日最新資訊28at.com

發現我們需要重寫 getAdditionalHelperClassNames函數,用于將我們外部的一些工具類加入到應用的 class loader 中,不然在應用在運行的時候就會報 NoClassDefFoundError 的錯誤。1dl28資訊網——每日最新資訊28at.com

因為是字節碼增強的關系,所以很多日常開發覺得很常見的地方都不行了,比如:1dl28資訊網——每日最新資訊28at.com

  • 如果切面類是一個內部類的時候,必須使用靜態函數。
  • 只能包含靜態函數。
  • 不能包含任何字段,常量。
  • 不能使用任何外部類,如果要使用就得使用 getAdditionalHelperClassNames 額外加入到 class loader 中(這一條就是我遇到的問題)。
  • 所有的函數必須使用 @Advice 注解。

以上的內容其實在文檔中都有寫:1dl28資訊網——每日最新資訊28at.com

1dl28資訊網——每日最新資訊28at.com

所以還是得仔細閱讀文檔。1dl28資訊網——每日最新資訊28at.com

缺少異常日志

其實上述的異常剛開始都沒有打印出來,只有一個現象就是程序沒有正常運行。1dl28資訊網——每日最新資訊28at.com

因為沒有日志也不知道如何排查,也懷疑是不是運行過程中報錯了,所以就嘗試把@Advice 注解的函數全部 try catch ,果然打印了上述的異常日志。1dl28資訊網——每日最新資訊28at.com

1dl28資訊網——每日最新資訊28at.com

之后我注意到了注解的這個參數,原來在默認情況下是不會打印任何日志的,需要手動打開。1dl28資訊網——每日最新資訊28at.com

比如這樣:@Advice.OnMethodExit(suppress = Throwable.class)1dl28資訊網——每日最新資訊28at.com

調試日志

最后就是調試功能了,因為我這個插件的是把指標發送到 OpenTelemetry-collector ,再由它發往 VictoriaMetrics/Prometheus;由于整個鏈路比較長,我想看到最終生成的指標是否正常的干擾條件太多了。1dl28資訊網——每日最新資訊28at.com

好在 OpenTelemetry 提供了多種 metrics.exporter 的輸出方式:1dl28資訊網——每日最新資訊28at.com

  • -Dotel.metrics.exporter=otlp (default),默認通過 otlp 協議輸出到 collector 中。
  • -Dotel.metrics.exporter=logging,以 stdout 的方式輸出到控制臺,主要用于調試
  • -Dotel.metrics.exporter=logging-otlp
  • -Dotel.metrics.exporter=prometheus,以 Prometheus 的方式輸出,還可以配置端口,這樣也可以讓 Prometheus 進行遠程采集,同樣的也可以在本地調試。

采用哪種方式可以根據環境情況自行選擇。1dl28資訊網——每日最新資訊28at.com

Opentelemetry-operator 配置 extension

最近在使用 opentelemetry-operator注入 agent 的時候發現 operator 目前并不支持配置 extension,所以在社區也提交了一個草案,下周會嘗試提交一個 PR 來新增這個特性。1dl28資訊網——每日最新資訊28at.com

這個需求我在 issue 列表中找到了好幾個,時間也挺久遠了,不太確定為什么社區還為實現。1dl28資訊網——每日最新資訊28at.com

目前 operator 只支持在自定義鏡像中配置 javaagent.jar,無法配置 extension:1dl28資訊網——每日最新資訊28at.com

這個原理在之前的文章中有提到。1dl28資訊網——每日最新資訊28at.com

apiVersion: opentelemetry.io/v1alpha1kind: Instrumentationmetadata:  name: my-instrumentationspec:  java:    image: your-customized-auto-instrumentation-image:java

我的目的是可以在自定義鏡像中把 extension 也復制進去,類似于這樣:1dl28資訊網——每日最新資訊28at.com

FROM busyboxADD open-telemetry/opentelemetry-javaagent.jar /javaagent.jar# Copy extensions to specify a path.ADD open-telemetry/ext-1.0.0.jar /ext-1.0.0.jarRUN chmod -R go+r /javaagent.jarRUN chmod -R go+r /ext-1.0.0.jar

然后在 CRD 中配置這個 extension 的路徑:1dl28資訊網——每日最新資訊28at.com

apiVersion: opentelemetry.io/v1alpha1kind: Instrumentationmetadata:  name: my-instrumentationspec:  java:    image: custom-image:1.0.0    extensions: /ext-1.0.0.jar    env:    # If extension.jar already exists in the container, you can only specify a specific path with this environment variable.      - name: OTEL_EXTENSIONS_DIR        value: /custom-dir

這樣 operator 在拿到 extension 的路徑時,就可以在環境變量中加入 -Dotel.javaagent.extensinotallow=${java.extensions} 參數,從而實現自定義 extension 的目的。1dl28資訊網——每日最新資訊28at.com

總結

整個過程其實并不復雜,只是由于目前用的人還不算多,所以也很少有人寫教程或者文章,相信用不了多久就會慢慢普及。1dl28資訊網——每日最新資訊28at.com

這里有一些官方的 example可以參考。1dl28資訊網——每日最新資訊28at.com

參考鏈接:1dl28資訊網——每日最新資訊28at.com

  • https://github.com/apache/pulsar/pull/22178。
  • https://opentelemetry.io/docs/languages/java/automatic/extensions/。
  • https://github.com/open-telemetry/opentelemetry-java-。instrumentation/tree/main/examples/extension#extensions-examples。
  • https://github.com/open-telemetry/opentelemetry-operator/issues/1758#issuecomment-1982159356。

本文鏈接:http://www.www897cc.com/showinfo-26-83782-0.html實戰:如何編寫一個 OpenTelemetry Extensions

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 對線面試官-CHAR和VARCHAR了解嘛,區別是什么

下一篇: 詳解@Stomp/Stompjs在Vue3中的應用與實踐

標簽:
  • 熱門焦點
Top 主站蜘蛛池模板: 德安县| 南安市| 通辽市| 饶平县| 中江县| 樟树市| 图片| 富川| 夹江县| 浙江省| 阆中市| 商河县| 密云县| 澳门| 安岳县| 利川市| 保德县| 天镇县| 枝江市| 文昌市| 重庆市| 垣曲县| 卢龙县| 宁强县| 清远市| 平山县| 连平县| 贵德县| 安吉县| 肥城市| 麦盖提县| 广元市| 离岛区| 平山县| 广宁县| 昭平县| 双桥区| 盈江县| 沛县| 翁牛特旗| 寿光市|