全部產品
Search
文件中心

Application Real-Time Monitoring Service:通過OpenTelemetry上報Java應用資料

更新時間:Nov 27, 2024

通過OpenTelemetry為應用埋點並上報鏈路資料至Managed Service for OpenTelemetry後,Managed Service for OpenTelemetry即可開始監控應用,您可以查看應用拓撲、調用鏈路、異常事務、慢事務和SQL分析等一系列監控資料。本文介紹如何使用OpenTelemetry Java Agent/SDK進行自動或手動埋點並上報資料。

前提條件

擷取存取點資訊

  1. 登入ARMS控制台,在左側導覽列單擊接入中心

  2. 服務端應用地區單擊OpenTelemetry卡片。

  3. 在彈出的OpenTelemetry面板中選擇資料需要上報的地區。

    說明

    初次接入的地區將會自動進行資源初始化。

  4. 選擇串連方式上報方式,然後複製存取點資訊。

    • 串連方式:若您的服務部署在阿里雲上,且所屬地區與選擇的接入地區一致,推薦使用阿里雲內網方式,否則選擇公網方式。

    • 上報方式:根據用戶端支援的協議類型選擇HTTP或gRPC協議上報資料。

    75.jpg

背景資訊

OpenTelemetry Java Agent支援自動埋點的Java架構列表如下,完整資訊請參考Supported Libraries and Versions

展開查看支援監控的Java架構

架構

架構版本限制

Akka Actors

2.5+

Akka HTTP

10.0+

Apache Axis2

1.6+

Apache Camel

2.20+(暫不支援3.x)

Apache DBCP

2.0+

Apache CXF JAX-RS

3.2+

Apache CXF JAX-WS

3.0+

Apache Dubbo

2.7+

Apache HttpAsyncClient

4.1+

Apache HttpClient

2.0+

Apache Kafka Producer/Consumer API

0.11+

Apache Kafka Streams API

0.11+

Apache MyFaces

1.2+(暫不支援3.x)

Apache Pulsar

2.8+

Apache RocketMQ gRPC/Protobuf-based Client

5.0+

Apache RocketMQ Remoting-based Client

4.8+

Apache Struts 2

2.3+

Apache Tapestry

5.4+

Apache Wicket

8.0+

Armeria

1.3+

AsyncHttpClient

1.9+

AWS Lambda

1.0+

AWS SDK

1.11.x和2.2+

Azure Core

1.14+

Cassandra Driver

3.0+

Couchbase Client

2.0+和3.1+

c3p0

0.9.2+

Dropwizard Metrics

4.0+(預設禁用)

Dropwizard Views

0.7+

Eclipse Grizzly

2.3+

Eclipse Jersey

2.0+(暫不支援3.x)

Eclipse Jetty HTTP Client

9.2+(暫不支援10+)

Eclipse Metro

2.2+

Eclipse Mojarra

1.2+(暫不支援3.x)

Elasticsearch API Client

7.16+和8.0+

Elasticsearch REST Client

5.0+

Elasticsearch Transport Client

5.0+

Finatra

2.9+

Geode Client

1.4+

Google HTTP Client

1.19+

Grails

3.0+

GraphQL Java

12.0+

gRPC

1.6+

Guava ListenableFuture

10.0+

GWT

2.0+

Hibernate

3.3+

Hibernate Reactive

1.0+

HikariCP

3.0+

HttpURLConnection

Java 8+

Hystrix

1.4+

Java Executors

Java 8+

Java Http Client

Java 11+

java.util.logging

Java 8+

Java Platform

Java 8+

JAX-RS

0.5+

JAX-RS Client

1.1+

JAX-WS

2.0+(暫不包含3.x)

JBoss Log Manager

1.1+

JDBC

Java 8+

Jedis

1.4+

JMS

1.1+

Jodd Http

4.2+

JSP

2.3+

Kotlin Coroutines

1.0+

Ktor

1.0+

Kubernetes Client

7.0+

Lettuce

4.0+

Log4j 1

1.2+

Log4j 2

2.11+

Logback

1.0+

Micrometer

1.5+

MongoDB Driver

3.1+

Netty

3.8+

OkHttp

2.2+

Oracle UCP

11.2+

OSHI

5.3.1+

Play

2.4+

Play WS

1.0+

Quartz

2.0+

R2DBC

1.0+

RabbitMQ Client

2.7+

Ratpack

1.4+

Reactor

3.1+

Reactor Netty

0.9+

Rediscala

1.8+

Redisson

3.0+

RESTEasy

3.0+

Restlet

1.0+

RMI

Java 8+

RxJava

1.0+

Scala ForkJoinPool

2.8+

Servlet

2.2+

Spark Web Framework

2.3+

Spring Boot

-

Spring Batch

3.0+(暫不支援5.0+)

Spring Cloud Gateway

2.0+

Spring Data

1.8+

Spring Integration

4.1+(暫不支援6.0+)

Spring JMS

2.0+

Spring Kafka

2.7+

Spring RabbitMQ

1.0+

Spring Scheduling

3.1+

Spring RestTemplate

3.1+

Spring Web MVC

3.1+

Spring Web Services

2.0+

Spring WebFlux

5.3+

Spymemcached

2.12+

Tomcat JDBC Pool

8.5+

Twilio

6.6+(暫不支援8.x)

Undertow

1.4+

Vaadin

14.2+

Vert.x Web

3.0+

Vert.x HttpClient

3.0+

Vert.x Kafka Client

3.6+

Vert.x RxJava2

3.5+

Vert.x SQL Client

4.0+

Vibur DBCP

11.0+

ZIO

2.0+

樣本Demo

範例程式碼倉庫地址:java-opentelemetry-demo

方法一:使用OpenTelemetry Java Agent自動埋點

OpenTelemetry Java Agent提供了無侵入的接入方式,支援上百種Java架構自動上傳Trace資料,詳細的Java架構列表,請參見Supported Libraries and Versions

  1. 下載Java Agent

  2. 通過修改Java啟動的VM參數上報鏈路資料。

    如果您選擇直接上報資料,請將<token><endpoint>替換為前提條件中擷取的資訊。

    說明

    Http方式不需要設定鑒權Token,僅需設定存取點(endpoint)。

    Http方式

    java -javaagent:/path/to/opentelemetry-javaagent.jar   //請將路徑修改為您檔案下載的實際地址。
    -Dotel.exporter.otlp.protocol=http/protobuf \
    -Dotel.exporter.otlp.traces.endpoint=<traces.endpoint> \   //替換為前提條件中擷取到的trace存取點。
    -Dotel.exporter.otlp.metrics.endpoint=<metrics.endpoint> \   //替換為前提條件中擷取到的metric存取點。
    -Dotel.logs.exporter=none \
    -jar /path/to/your/app.jar

    例如:

    java -javaagent:/path/to/opentelemetry-javaagent.jar \
    -Dotel.exporter.otlp.protocol=http/protobuf \
    -Dotel.exporter.otlp.traces.endpoint=http://tracing-analysis-dc-hz-internal.aliyuncs.com/adapt_ggxw4l****@7323a5caae3****_ggxw4l****@53df7ad2afe****/api/otlp/traces \
    -Dotel.exporter.otlp.metrics.endpoint=http://tracing-analysis-dc-hz-internal.aliyuncs.com/adapt_ggxw4l****@7323a5caae3****_ggxw4l****@53df7ad2afe****/api/otlp/metrics \
    -Dotel.logs.exporter=none \
    -jar /path/to/your/app.jar

    gRPC方式

    java -javaagent:/path/to/opentelemetry-javaagent.jar \   //請將路徑修改為您檔案下載的實際地址。
    -Dotel.exporter.otlp.protocol=grpc \
    -Dotel.exporter.otlp.headers=Authentication=<token> \   //替換為前提條件中擷取到的鑒權Token。
    -Dotel.exporter.otlp.endpoint=<endpoint> \   //替換為前提條件中擷取到的存取點。
    -Dotel.logs.exporter=none \
    -jar /path/to/your/app.jar

    例如:

    java -javaagent:/path/to/opentelemetry-javaagent.jar \
    -Dotel.exporter.otlp.protocol=grpc \
    -Dotel.exporter.otlp.headers=Authentication=ggxw4l****@7323a5caae3****_ggxw4l****@53df7ad2afe**** \
    -Dotel.exporter.otlp.endpoint=http://tracing-analysis-dc-hz-internal.aliyuncs.com:8090 \
    -Dotel.logs.exporter=none \
    -jar /path/to/your/app.jar
    說明

    如果您選擇使用OpenTelemetry Collector轉寄,則需刪除-Dotel.exporter.otlp.headers=Authentication=<token>並修改<endpoint>為您本地部署的服務地址。

方法二:使用OpenTelemetry Java SDK手動埋點

OpenTelemetry Java SDK是OpenTelemetry Java Agent實現的基礎,同時提供了豐富的自訂能力。當OpenTelemetry Java Agent的埋點不滿足您的情境或者需要增加一些自訂業務埋點時,可以使用以下方式接入。

  1. 引入Maven POM依賴。

    <dependencies>
      <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-api</artifactId>
      </dependency>
      <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-sdk-trace</artifactId>
      </dependency>
      <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-exporter-otlp</artifactId>
      </dependency>
      <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-sdk</artifactId>
      </dependency>
      <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-semconv</artifactId>
        <version>1.23.0-alpha</version>
      </dependency>
    </dependencies>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.opentelemetry</groupId>
                <artifactId>opentelemetry-bom</artifactId>
                <version>1.23.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
  2. 擷取OpenTelemetry Tracer。

    • <logical-service-name>為服務名,<host-name>為主機名稱,請根據您的實際情境配置。

    • 如果您選擇直接上報資料,請將以下代碼中的<token>替換成前提條件中擷取的Token,將<endpoint>替換成對應地區的Endpoint。

    package com.alibaba.arms.brightroar.console.util;
    
    import io.opentelemetry.api.OpenTelemetry;
    import io.opentelemetry.api.common.Attributes;
    import io.opentelemetry.api.trace.Tracer;
    import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
    import io.opentelemetry.context.propagation.ContextPropagators;
    import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
    import io.opentelemetry.sdk.OpenTelemetrySdk;
    import io.opentelemetry.sdk.resources.Resource;
    import io.opentelemetry.sdk.trace.SdkTracerProvider;
    import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
    import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
    
    public class OpenTelemetrySupport {
    
        static {
            // 擷取OpenTelemetry Tracer
            Resource resource = Resource.getDefault()
                    .merge(Resource.create(Attributes.of(
                            ResourceAttributes.SERVICE_NAME, "<logical-service-name>",
                            ResourceAttributes.HOST_NAME, "<host-name>"
                    )));
    
            SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
                    .addSpanProcessor(BatchSpanProcessor.builder(OtlpGrpcSpanExporter.builder()
                            .setEndpoint("<endpoint>")
                            .addHeader("Authentication", "<token>")
                            .build()).build())
                    .setResource(resource)
                    .build();
    
            OpenTelemetry openTelemetry = OpenTelemetrySdk.builder()
                    .setTracerProvider(sdkTracerProvider)
                    .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance()))
                    .buildAndRegisterGlobal();
    
            tracer = openTelemetry.getTracer("<your_tracer_name>", "1.0.0");
        }
    
        private static Tracer tracer;
    
        public static Tracer getTracer() {
            return tracer;
        }
    
    }
  3. 參考以下內容修改Controller代碼和Service代碼。

    • Controller代碼:

      package com.alibaba.arms.brightroar.console.controller;
      
      import com.alibaba.arms.brightroar.console.service.UserService;
      import com.alibaba.arms.brightroar.console.util.OpenTelemetrySupport;
      import io.opentelemetry.api.GlobalOpenTelemetry;
      import io.opentelemetry.api.OpenTelemetry;
      import io.opentelemetry.api.trace.Span;
      import io.opentelemetry.api.trace.StatusCode;
      import io.opentelemetry.api.trace.Tracer;
      import io.opentelemetry.context.Context;
      import io.opentelemetry.context.Scope;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      
      /**
       * 參考文檔:
       * 1. https://opentelemetry.io/docs/java/manual_instrumentation/
       */
      @RestController
      @RequestMapping("/user")
      public class UserController {
      
          @Autowired
          private UserService userService;
      
          private ExecutorService es = Executors.newFixedThreadPool(5);
      
          private void biz() {
              Tracer tracer = OpenTelemetrySupport.getTracer();
              Span span = tracer.spanBuilder("biz (manual)")
                      .setParent(Context.current().with(Span.current())) // 可選,自動化佈建
                      .startSpan();
      
              try (Scope scope = span.makeCurrent()) {
                  span.setAttribute("biz-id", "111");
      
                  es.submit(new Runnable() {
                      @Override
                      public void run() {
                          Span asyncSpan = tracer.spanBuilder("async")
                                  .setParent(Context.current().with(span))
                                  .startSpan();
                          try {
                              Thread.sleep(1000L); // some async jobs
                          } catch (Throwable e) {
                          }
                          asyncSpan.end();
                      }
                  });
      
                  Thread.sleep(1000); // fake biz logic
                  System.out.println("biz done");
                  OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
                  openTelemetry.getPropagators();
              } catch (Throwable t) {
                  span.setStatus(StatusCode.ERROR, "handle biz error");
              } finally {
                  span.end();
              }
          }
      
          private void child(String userType) {
              Span span = OpenTelemetrySupport.getTracer().spanBuilder("child span").startSpan();
              try (Scope scope = span.makeCurrent()) {
                  span.setAttribute("user.type", userType);
                  System.out.println(userType);
                  biz();
              } catch (Throwable t) {
                  span.setStatus(StatusCode.ERROR, "handle child span error");
              } finally {
                  span.end();
              }
          }
      
          @RequestMapping("/async")
          public String async() {
              System.out.println("UserController.async -- " + Thread.currentThread().getId());
              Span span = OpenTelemetrySupport.getTracer().spanBuilder("parent span").startSpan();
              span.setAttribute("user.id", "123456");
              try (Scope scope = span.makeCurrent()) {
                  userService.async();
                  child("vip");
              } catch (Throwable t) {
                  span.setStatus(StatusCode.ERROR, "handle parent span error");
              } finally {
                  span.end();
              }
              return "async";
          }
      
      }
         
    • Service代碼:

      package com.alibaba.arms.brightroar.console.service;
      
      import org.springframework.scheduling.annotation.Async;
      import org.springframework.stereotype.Service;
      
      @Service
      public class UserService {
      
          @Async
          public void async() {
              System.out.println("UserService.async -- " + Thread.currentThread().getId());
              System.out.println("my name is async");
              System.out.println("UserService.async -- ");
          }
      }
  4. 啟動應用。

    登入ARMS控制台後,在應用監控 > 應用列表頁面選擇目標應用,查看鏈路資料。

    說明

    語言列顯示image表徵圖的應用為接入應用監控的應用,顯示-表徵圖的應用為接入可觀測鏈路 OpenTelemetry 版的應用。

方法三:同時使用Java Agent和Java SDK埋點

您可以在使用Java Agent獲得自動埋點能力的同時,使用Java SDK添加自訂業務埋點。

  1. 下載Java Agent

  2. 方法二的Maven依賴基礎上新增以下依賴。

    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-extension-annotations</artifactId>
    </dependency>
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
        <version>1.23.0-alpha</version>
    </dependency>
    說明

    其中opentelemetry-sdk-extension-autoconfigure完成了SDK的自動設定,將Java Agent的配置傳遞到Java SDK中。

    展開查看完整的Maven POM依賴

    <dependencies>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-api</artifactId>
        </dependency>
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-sdk-trace</artifactId>
        </dependency>
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-extension-annotations</artifactId>
        </dependency>
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-exporter-otlp</artifactId>
        </dependency>
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-sdk</artifactId>
        </dependency>
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-semconv</artifactId>
            <version>1.23.0-alpha</version>
        </dependency>
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
            <version>1.23.0-alpha</version>
        </dependency>
    </dependencies>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.opentelemetry</groupId>
                <artifactId>opentelemetry-bom</artifactId>
                <version>1.23.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
  3. 擷取OpenTelemetry Tracer。

    同時使用Java Agent和Java SDK埋點時,無需再使用方法二中的OpenTelemetrySupport類擷取Tracer。

    OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
    Tracer tracer = openTelemetry.getTracer("instrumentation-library-name", "1.0.0");
  4. 參考以下內容修改Controller代碼和Service代碼。

    • Controller代碼如下,建議使用代碼中的第一種和第二種方式。

      package com.alibaba.arms.brightroar.console.controller;
      
      import com.alibaba.arms.brightroar.console.service.UserService;
      import io.opentelemetry.api.GlobalOpenTelemetry;
      import io.opentelemetry.api.OpenTelemetry;
      import io.opentelemetry.api.trace.Span;
      import io.opentelemetry.api.trace.StatusCode;
      import io.opentelemetry.api.trace.Tracer;
      import io.opentelemetry.context.Context;
      import io.opentelemetry.context.Scope;
      import io.opentelemetry.extension.annotations.SpanAttribute;
      import io.opentelemetry.extension.annotations.WithSpan;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      
      /**
      * 參考文檔:
      * 1. https://opentelemetry.io/docs/java/manual_instrumentation/
      */
      @RestController
          @RequestMapping("/user")
          public class UserController {
      
              @Autowired
              private UserService userService;
      
              private ExecutorService es = Executors.newFixedThreadPool(5);
      
              // 第一種:自動埋點,基於 API 手工添加資訊
              @RequestMapping("/async")
              public String async() {
                  System.out.println("UserController.async -- " + Thread.currentThread().getId());
                  Span span = Span.current();
                  span.setAttribute("user.id", "123456");
                  userService.async();
                  child("vip");
                  return "async";
              }
      
              // 第二種:通過註解建立埋點
              @WithSpan
              private void child(@SpanAttribute("user.type") String userType) {
                  System.out.println(userType);
                  biz();
              }
      
              // 第三種:獲得 Tracer 純手工埋點
              private void biz() {
                  Tracer tracer = GlobalOpenTelemetry.get().getTracer("tracer");
                  Span span = tracer.spanBuilder("biz (manual)")
                      .setParent(Context.current().with(Span.current())) // 可選,自動化佈建
                      .startSpan();
      
                  try (Scope scope = span.makeCurrent()) {
                      span.setAttribute("biz-id", "111");
      
                      es.submit(new Runnable() {
                          @Override
                          public void run() {
                              Span asyncSpan = tracer.spanBuilder("async")
                                  .setParent(Context.current().with(span))
                                  .startSpan();
                              try {
                                  Thread.sleep(1000L); // some async jobs
                              } catch (Throwable e) {
                              }
                              asyncSpan.end();
                          }
                      });
      
                      Thread.sleep(1000); // fake biz logic
                      System.out.println("biz done");
                      OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
                      openTelemetry.getPropagators();
                  } catch (Throwable t) {
                      span.setStatus(StatusCode.ERROR, "handle biz error");
                  } finally {
                      span.end();
                  }
              }
      
          }
      
                                      
    • Service代碼:

      package com.alibaba.arms.brightroar.console.service;
      
      import org.springframework.scheduling.annotation.Async;
      import org.springframework.stereotype.Service;
      
      @Service
      public class UserService {
      
          @Async
          public void async() {
              System.out.println("UserService.async -- " + Thread.currentThread().getId());
              System.out.println("my name is async");
              System.out.println("UserService.async -- ");
          }
      }
  5. 通過修改Java啟動的VM參數上報鏈路資料。

    -javaagent:/path/to/opentelemetry-javaagent.jar    //請將路徑修改為您檔案下載的實際地址。
    -Dotel.resource.attributes=service.name=<appName>     //<appName> 為應用程式名稱。
    -Dotel.exporter.otlp.headers=Authentication=<token>
    -Dotel.exporter.otlp.endpoint=<endpoint>
    • 如果您選擇直接上報資料,請將<token>替換成從前提條件中擷取的Token,將<endpoint>替換成對應地區的Endpoint。

      例如:

      -javaagent:/Users/carpela/Downloads/opentelemetry-javaagent.jar
      -Dotel.resource.attributes=service.name=ot-java-agent-sample
      -Dotel.exporter.otlp.headers=Authentication=b590xxxxuqs@3a75d95xxxxx9b_b59xxxxguqs@53dxxxx2afe8301
      -Dotel.exporter.otlp.endpoint=http://tracing-analysis-dc-bj:8090
    • 如果您選擇使用OpenTelemetry Collector轉寄,則需刪除-Dotel.exporter.otlp.headers=Authentication=<token>並修改<endpoint>為您本地部署的服務地址。

  6. 啟動應用。

    登入ARMS控制台後,在應用監控 > 應用列表頁面選擇目標應用,查看鏈路資料。

    說明

    語言列顯示image表徵圖的應用為接入應用監控的應用,顯示-表徵圖的應用為接入可觀測鏈路 OpenTelemetry 版的應用。