更新时间:2026-03-05 GMT+08:00
分享

OpenTelemetry方式接入APM实现链路追踪指标监控

操作步骤

  1. 登录APM控制台
  2. 单击左侧,选择“管理与监管 > 应用性能管理 APM”,进入APM服务页面。
  3. 在左侧导航栏中选择“链路追踪 > 探针接入”,进入接入应用页面。
  4. 选择“区域”和“应用”。
  5. 选择接入类型“OpenTelemetry”。

    如果当前应用支持OpenTelemetry类型,则Skywalking类型置灰,不支持选择。

    图1 接入OpenTelemetry类型

  6. 根据应用类型选择接入对应的接入方式,按照步骤接入。请注意,同一个应用下的组件名称不能重复。

    • JAVA应用接入
      1. 下载 OpenTelemetry Java Agent。前往Agent 下载地址,下载后将 Agent 文件放在 Java 进程有访问权限的目录。
      2. 添加启动命令并重启应用。
        java -javaagent:探针安装路径 \
        -Dotel.exporter.otlp.protocol=grpc \
        -Dotel.exporter.otlp.traces.endpoint=http://***.**.**.***:**** \
        -Dotel.exporter.otlp.headers=Authentication= ****** \
        -Dotel.service.name=应用名称.组件名称.环境名称 \
        -Dotel.metrics.exporter=none \
        -Dotel.logs.exporter=none \
        -jar < 用户应用 >.jar 

        启动命令示例:

        curl http://***.**.**.***:****/rolldice?player=Alex 

        其中,“http://***.**.**.***:****”与endpoint相同。

        表1 参数说明

        参数

        说明

        protocol

        数据的 OTLP 传输协议。

        traces.endpoint

        对接服务上报数据的 ip 以及端口。

        Authentication

        对接服务 token 信息。

        service.name

        应用名称格式:应用.子应用.组件.环境。子应用:选填,在应用下面子文件夹。该参数可以为空,为空代表应用挂载在根应用下面,子应用可以多层次,最多三层。比如 a/b/c,a、b、c各代表一层。

    • PHP应用接入
      • 方式一 非侵入式接入。
        1. 接入环境准备。
          1. PHP 8.0+。
          2. PECL。
          3. composer。

          安装好以后需在 shell 脚本下验证 PHP、composer 版本。

        2. 安装 OpenTelemetry 扩展组件。
          1. 安装必须构建工具。
            sudo apt-get install gcc make autoconf
          2. 使用构建安装扩展组件。
            sudo apt-get install php-dev
            pecl install opentelemetry
            pecl install grpc
            pecl install protobuf-3.21.12
          3. 将扩展名添加到您的文件中:php.ini。
            [opentelemetry]
            extension=opentelemetry.so
            [PHP]
            extension=grpc.so
            extension=protobuf.so
          4. 验证扩展是否已安装并启用。
            php --ri opentelemetry
            php --ri grpc
            php --ri protobuf
        3. 安装服务框架所需 opentelemetry 依赖包。
          1. 安装服务框架所依赖的 opentelemetry 自动插件包,比如 slim 框架需安装 open-telemetry/opentelemetry-auto-slim。
            composer config allow-plugins.php-http/discovery false
            composer require open-telemetry/sdk
            composer require open-telemetry/exporter-otlp
            composer require open-telemetry/transport-grpc
            composer require open-telemetry/opentelemetry-auto-slim
            composer update
          2. 接入 Agent 需要在环境变量或者 php.ini 中添加以下配置项。
            OTEL_EXPORTER_OTLP_ENDPOINT=http://***.**.**.***:*** // 对接服务上报数据的 ip 以及端口
            OTEL_EXPORTER_OTLP_HEADERS=Authentication=**** // 对接服务 token 信息
            OTEL_EXPORTER_OTLP_PROTOCOL=grpc// 数据的 OTLP 传输协议
            OTEL_PHP_AUTOLOAD_ENABLED=true
            OTEL_SERVICE_NAME=应用名称.组件名称.环境名称// 对接服务的服务名
            
      • 侵入式接入:请参考官网
    • GO应用接入

      使用 GRPC 协议上报数据

      1. 添加 OpenTelemetry依赖。
        import (
        	"context"
        	"go.opentelemetry.io/otel"
        	"go.opentelemetry.io/otel/attribute"
        	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
        	"go.opentelemetry.io/otel/sdk/resource"
        	sdktrace "go.opentelemetry.io/otel/sdk/trace"
        	"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
        )
      2. 对接 OpenTelemetry。
        const (
        	SERVICE_NAME = "应用名称.组件名称.环境名称"
        	APM_ENDPOINT="***.**.**.***:****"
        	APM_TOKEN= "****"
        )
        func main() {
        	cleanup := initTracer()
        	defer cleanup(context.Background())
        
        	r := gin.Default()
        	r.Use(otelgin.Middleware(SERVICE_NAME))
        
        	..业务代码
        }
        
        func initTracer() func(context.Context) error {
        	headers := map[string]string{"Authentication": APM_TOKEN}
        	exporter, err := otlptracegrpc.New(
        		context.Background(),
        		otlptracegrpc.WithInsecure(),
        		otlptracegrpc.WithEndpoint(APM_ENDPOINT),
        		otlptracegrpc.WithHeaders(headers),
        	)
        
        	if err != nil {
        		log.Fatalf("Failed to create exporter: %v", err)
        	}
        
        	resources, err := resource.New(
        		context.Background(),
        		resource.WithAttributes(
        			attribute.String("service.name", SERVICE_NAME),
        			attribute.String("library.language", "go"),
        		),
        	)
        
        	if err != nil {
        		log.Fatalf("Could not set resources: %v", err)
        	}
        
        	otel.SetTracerProvider(
        		sdktrace.NewTracerProvider(
        			sdktrace.WithSampler(sdktrace.AlwaysSample()),
        			sdktrace.WithBatcher(exporter),
        			sdktrace.WithResource(resources),
        		),
        	)
        	return exporter.Shutdown
        }	
      表2 参数说明

      参数

      说明

      APM_ENDPOINT

      对接服务上报数据的 ip 以及端口。

      APM_TOKEN

      对接服务 token 信息。

      SERVICE_NAME

      应用名称格式:应用.子应用.组件.环境。子应用:选填,在应用下面子文件夹。该参数可以为空,为空代表应用挂载在根应用下面,子应用可以多层次,最多三层。比如 a/b/c,a、b、c各代表一层。

    • Python应用接入
      • 非侵入式接入
        1. 下载相关包。
          pip install opentelemetry-distro
          pip install opentelemetry-exporter-otlp
          opentelemetry-bootstrap -a install
        2. 在启动 python 应用时,使用下面命令。

          接入 Agent 需要配置 token、endpoint、服务名、主机名四个参数,在您的启动文件中配置如下参数:

          opentelemetry-instrument \
          --traces_exporter otlp \
          --traces_exporter none \
          --service_name 应用名称.组件名称.环境名称 \
          --resource_attributes host.name=host.name \
          --exporter_otlp_endpoint http://***.**.**.***:**** \
          --exporter_otlp_headers Authentication=**** \
          python myapp.py
          表3 参数说明

          参数

          说明

          endpoint

          对接服务上报数据的 ip 以及端口。

          headers Authentication

          对接服务 token 信息。

          service_name

          应用名称格式:应用.子应用.组件.环境。子应用:选填,在应用下面子文件夹。该参数可以为空,为空代表应用挂载在根应用下面,子应用可以多层次,最多三层。比如 a/b/c,a、b、c各代表一层。

    • Nodejs应用接入
      • 侵入式接入
        1. 添加依赖。
          npm install @opentelemetry/sdk-node @opentelemetry/api @opentelemetry/auto-instrumentations-node @opentelemetry/sdk-metrics @opentelemetry/sdk-trace-node
        2. 对接 OpenTelemetry。

          在 express入口文件业务代码前面加上如下内容:

          export OTEL_TRACES_EXPORTER="otlp"
          export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"
          export OTEL_EXPORTER_OTLP_ENDPOINT="http://100.79.29.106:4317"
          export OTEL_NODE_RESOURCE_DETECTORS="env,host,os"
          export OTEL_SERVICE_NAME="应用名称.组件名称.环境名称"
          export OTEL_EXPORTER_OTLP_HEADERS="Authentication=*****"
          export NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"
          表4 参数说明

          参数

          说明

          PROTOCO

          数据的 OTLP 传输协议.

          ENDPOINT

          对接服务上报数据的 ip 以及端口。

          EXPORTER_OTLP_HEADERS

          对接服务 token 信息。

          SERVICE_NAME

          应用名称格式:应用.子应用.组件.环境。子应用:选填,在应用下面子文件夹。该参数可以为空,为空代表应用挂载在根应用下面,子应用可以多层次,最多三层。比如 a/b/c,a、b、c各代表一层。

      • 其他框架接入

        请参考官方仓库实例

      • 非侵入式接入

        请参考官方网站

    • C++应用接入
      1. cmake 构建接入 opentelemetry 流程
      2. 环境要求。
        1. C++ 版本 ≥ 14(低版本 opentelemetry-cpp 需要≥ C++11)。
        2. 请在 Supported C++ Versions and Development Platforms 中查看支持的 C++ 版本和开发平台。
      3. 环境准备。
        1. 安装构建工具 bazel,安装步骤可以参考 bazel 安装指导
        2. 通过构建 OpenTelemetry C++ Library 检查环境已经搭建好。
          git clone https://github.com/open-telemetry/opentelemetry-cpp.git
              cd opentelemetry-cpp
              bazel build //...
      4. 在项目中使用 OpenTelemetry C++ Library。
        • WORKSPACE 文件,内容如下:
          load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
          load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
          load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
          
          http_archive(
              name = "io_opentelemetry_cpp",
              sha256 = "<sha256>", # specify sha256 based on the URL specified in the urls parameter.
              strip_prefix = "opentelemetry-cpp-1.10.0",
              urls = [
                  "https://github.com/open-telemetry/opentelemetry-cpp/archive/refs/tags/v1.10.0.tar.gz"
              ],
          )
          
          # Load OpenTelemetry dependencies after load.
          load("@io_opentelemetry_cpp//bazel:repository.bzl", "opentelemetry_cpp_deps")
          
          opentelemetry_cpp_deps()
          
          # (required after v1.8.0) Load extra dependencies required for OpenTelemetry
          load("@io_opentelemetry_cpp//bazel:extra_deps.bzl", "opentelemetry_extra_deps")
          
          opentelemetry_extra_deps()
          
          # Load gRPC dependencies after load.
          load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps")
          
          grpc_deps()
          
          # Load extra gRPC dependencies due to https://github.com/grpc/grpc/issues/20511
          load("@com_github_grpc_grpc//bazel:grpc_extra_deps.bzl", "grpc_extra_deps")
          
          grpc_extra_deps()
        • BUILD 文件,内容如下:
          # Copyright The OpenTelemetry Authors
          # SPDX-License-Identifier: Apache-2.0
          
          cc_binary(
              name = "example_http_server",
              srcs = [
                  "server.cc",
                  "server.h",
              ],
              tags = ["ostream"],
              deps = [
                  "@io_opentelemetry_cpp//api",
                  "@io_opentelemetry_cpp//exporters/ostream:ostream_span_exporter",
                  "@io_opentelemetry_cpp//ext:headers",
                  "@io_opentelemetry_cpp//sdk/src/trace",
                  "@io_opentelemetry_cpp//exporters/otlp:otlp_grpc_exporter",
              ],
          )
        • 测试代码包含 server.cc、server.h 两个文件。

          server.cc 文件,内容如下:

          #include "server.h"
            #include "opentelemetry/trace/context.h"
            #include "opentelemetry/trace/semantic_conventions.h"
          
            #include "opentelemetry/exporters/ostream/span_exporter_factory.h"
            #include "opentelemetry/sdk/trace/exporter.h"
            #include "opentelemetry/sdk/trace/processor.h"
            #include "opentelemetry/sdk/trace/simple_processor_factory.h"
            #include "opentelemetry/sdk/trace/tracer_context.h"
            #include "opentelemetry/sdk/trace/tracer_context_factory.h"
            #include "opentelemetry/sdk/trace/tracer_provider_factory.h"
            #include "opentelemetry/trace/provider.h"
          
            #include "opentelemetry/context/propagation/global_propagator.h"
            #include "opentelemetry/context/propagation/text_map_propagator.h"
            #include "opentelemetry/trace/propagation/http_trace_context.h"
          
            #include <cstring>
            #include <string>
            #include <unistd.h>
            #include <iostream>
            #include <vector>
            #include "opentelemetry/ext/http/client/http_client.h"
            #include "opentelemetry/nostd/shared_ptr.h"
          
            #include "opentelemetry/sdk/trace/tracer_provider.h"
            #include "opentelemetry/exporters/otlp/otlp_grpc_exporter_factory.h"
          
            #include <iostream>
            #include <thread>
          
            namespace trace = opentelemetry::trace;
            namespace trace_sdk = opentelemetry::sdk::trace;
            namespace otlp = opentelemetry::exporter::otlp;
          
            namespace {
          
              using namespace opentelemetry::trace;
              namespace context = opentelemetry::context;
              opentelemetry::exporter::otlp::OtlpGrpcExporterOptions opts;
          
              uint16_t server_port = 8800;
              constexpr const char *server_name = "localhost";
          
              std::string getHostName() {
                  char hostname[256];
                  if (gethostname(hostname, sizeof(hostname)) == 0) {
                      return hostname;
                  } else {
                      return "default_hostname";
                  }
              }
          
              template<typename T>
              class HttpTextMapCarrier : public opentelemetry::context::propagation::TextMapCarrier {
              public:
                  HttpTextMapCarrier(T &headers) : headers_(headers) {}
          
                  HttpTextMapCarrier() = default;
          
                  virtual opentelemetry::nostd::string_view Get(opentelemetry::nostd::string_view key) const noexcept override {
                      std::string key_to_compare = key.data();
                      // Header's first letter seems to be  automatically capitaliazed by our test http-server, so
                      // compare accordingly.
                      if (key == opentelemetry::trace::propagation::kTraceParent) {
                          key_to_compare = "Traceparent";
                      } else if (key == opentelemetry::trace::propagation::kTraceState) {
                          key_to_compare = "Tracestate";
                      }
                      auto it = headers_.find(key_to_compare);
                      if (it != headers_.end()) {
                          return it->second;
                      }
                      return "";
                  }
          
                  virtual void Set(opentelemetry::nostd::string_view key, opentelemetry::nostd::string_view value) noexcept override {
                      headers_.insert(std::pair<std::string, std::string>(std::string(key), std::string(value)));
                  }
          
                  T headers_;
              };
          
              void InitTracer() {
                  // Change this parameter based on the configuration of the access site.
          
          
                std::string endpoint = "http://***.**.**.***:****";
                std::string token = "*****";
                std::string serviceName = "应用名称.组件名称.环境名称";
          
          
                opts.endpoint = endpoint;
                opts.metadata.insert(std::pair<std::string, std::string>("authentication", token));
                // Create OTLP exporter instance
                auto exporter = otlp::OtlpGrpcExporterFactory::Create(opts);
                auto processor = trace_sdk::SimpleSpanProcessorFactory::Create(std::move(exporter));
          
                opentelemetry::sdk::resource::ResourceAttributes attributes = {
                    {"service.name", serviceName},
                    {"host.name",    getHostName()}
                };
                auto resource = opentelemetry::sdk::resource::Resource::Create(attributes);
          
                std::shared_ptr <opentelemetry::trace::TracerProvider> provider = trace_sdk::TracerProviderFactory::Create(std::move(processor), std::move(resource));
                // Set the global trace provider
                trace::Provider::SetTracerProvider(provider);
              }
          
              void CleanupTracer() {
                // We call ForceFlush to prevent to cancel running exportings, It's optional.
                opentelemetry::nostd::shared_ptr <opentelemetry::trace::TracerProvider> provider = trace::Provider::GetTracerProvider();
                if (provider) {
                    static_cast<trace_sdk::TracerProvider *>(provider.get())->ForceFlush();
                }
          
                std::shared_ptr <opentelemetry::trace::TracerProvider> none;
                trace::Provider::SetTracerProvider(none);
              }
          
              opentelemetry::nostd::shared_ptr <opentelemetry::trace::Tracer> get_tracer(std::string tracer_name) {
                auto provider = opentelemetry::trace::Provider::GetTracerProvider();
                return provider->GetTracer(tracer_name);
              }
          
              class RequestHandler : public HTTP_SERVER_NS::HttpRequestCallback {
              public:
                virtual int onHttpRequest(HTTP_SERVER_NS::HttpRequest const &request, HTTP_SERVER_NS::HttpResponse &response) override {
                    StartSpanOptions options;
                    options.kind = SpanKind::kServer;  // server
                    std::string span_name = request.uri;
          
                    // extract context from http header
                    std::map <std::string, std::string> &request_headers =
                            const_cast<std::map <std::string, std::string> &>(request.headers);
                    const HttpTextMapCarrier<std::map < std::string, std::string>>
                    carrier(request_headers);
                    auto prop = context::propagation::GlobalTextMapPropagator::GetGlobalPropagator();
                    auto current_ctx = context::RuntimeContext::GetCurrent();
                    auto new_context = prop->Extract(carrier, current_ctx);
                    options.parent = GetSpan(new_context)->GetContext();
          
                    // start span with parent context extracted from http header
                    auto span = get_tracer("http-server")
                            ->StartSpan(span_name,
                            {{"server.address", server_name},
                            {"server.port",    server_port},
                            {"http.method",    request.method},
                            {"url.scheme",     "http"},
                            {"http.request.body.size",
                            static_cast<uint64_t>(request.content?.length())},
                            {"client.address", request.client}},
                            options);
          
                    auto scope = get_tracer("http_server")->WithActiveSpan(span);
          
                    for (auto &kv : request.headers) {
                        span->SetAttribute("http.header." + std::string(kv.first.data()), kv.second);
                    }
                    if (request.uri == "/helloworld") {
                        span->AddEvent("Processing request");
                        response.headers[HTTP_SERVER_NS::CONTENT_TYPE] = HTTP_SERVER_NS::CONTENT_TYPE_TEXT;
                        response.body = "Welcome to the APM demo!";
                        span->End();
                        return 200;
                    }
                    span->End();
                    return 404;
                }
              };
            }  // namespace
          
            int main(int argc, char *argv[]) {
                InitTracer();
          
                // The port the validation service listens to can be specified via the command line.
                if (argc > 1) {
                  server_port = (uint16_t) atoi(argv[1]);
                }
          
                HttpServer http_server(server_name, server_port);
                RequestHandler req_handler;
                http_server.AddHandler("/helloworld", &req_handler);
                auto root_span = get_tracer("http_server")->StartSpan(__func__);
                Scope scope(root_span);
                http_server.Start();
                std::cout << "Server is running..Press ctrl-c to exit...
          ";
                while (1) {
                  std::this_thread::sleep_for(std::chrono::seconds(100));
                }
                http_server.Stop();
                root_span->End();
                CleanupTracer();
                return 0;
            }
          
          表5 参数说明

          参数

          说明

          endpoint

          对接服务上报数据的 ip 以及端口。

          token

          对接服务 token 信息。

          serviceName

          应用名称格式:应用.子应用.组件.环境。子应用:选填,在应用下面子文件夹。该参数可以为空,为空代表应用挂载在根应用下面,子应用可以多层次,最多三层。比如 a/b/c,a、b、c各代表一层。

          server.h 文件,内容如下:

          // Copyright The OpenTelemetry Authors
          // SPDX-License-Identifier: Apache-2.0
          
          #pragma once
          
          #include <atomic>
          #include <string>
          #include "opentelemetry/ext/http/server/http_server.h"
          
          namespace {
          
              class HttpServer : public HTTP_SERVER_NS::HttpRequestCallback {
          
              protected:
                  HTTP_SERVER_NS::HttpServer server_;
                  std::string server_url_;
                  uint16_t port_;
                  std::atomic<bool> is_running_{false};
          
              public:
                  HttpServer(std::string server_name = "test_server", uint16_t port = 8800) : port_(port) {
                      server_.setServerName(server_name);
                      server_.setKeepalive(false);
                  }
          
                  void AddHandler(std::string path, HTTP_SERVER_NS::HttpRequestCallback *request_handler) {
                      server_.addHandler(path, *request_handler);
                  }
          
                  void Start() {
                      if (!is_running_.exchange(true)) {
                          server_.addListeningPort(port_);
                          server_.start();
                      }
                  }
          
                  void Stop() {
                      if (is_running_.exchange(false)) {
                          server_.stop();
                      }
                  }
          
                  ~HttpServer() { Stop(); }
              };
          
          }  // namespace
    • .Net应用接入
      1. 添加 OpenTelemetry依赖。
        curl -L -O https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/latest/download/otel-dotnet-auto-install.sh 
        chmod +x otel-dotnet-auto-install.sh 
        ./otel-dotnet-auto-install.sh
      2. 对接 OpenTelemetry。
        export OTEL_TRACES_EXPORTER=otlp \
        OTEL_EXPORTER_OTLP_PROTOCOL=grpc \
        OTEL_METRICS_EXPORTER=none OTEL_LOGS_EXPORTER=none \
        OTEL_SERVICE_NAME=应用名称.组件名称.环境名称 \
        OTEL_EXPORTER_OTLP_ENDPOINT=***.**.**.***:**** \
        OTEL_EXPORTER_OTLP_HEADERS="Authentication=****
        . $HOME/.otel-dotnet-auto/instrument.sh
        表6 参数说明

        参数

        说明

        PROTOCOL

        数据的 OTLP 传输协议。

        ENDPOINT

        对接服务上报数据的 ip 以及端口。

        Authentication

        对接服务 token 信息。

        SERVICE_NAME

        应用名称格式:应用.子应用.组件.环境。子应用:选填,在应用下面子文件夹。该参数可以为空,为空代表应用挂载在根应用下面,子应用可以多层次,最多三层。比如 a/b/c,a、b、c各代表一层。

相关文档