OpenTelemetry方式接入APM实现链路追踪指标监控
操作步骤
- 登录APM控制台。
- 单击左侧
,选择“管理与监管 > 应用性能管理 APM”,进入APM服务页面。 - 在左侧导航栏中选择“链路追踪 > 探针接入”,进入接入应用页面。
- 选择“区域”和“应用”。
- 选择接入类型“OpenTelemetry”。
如果当前应用支持OpenTelemetry类型,则Skywalking类型置灰,不支持选择。
图1 接入OpenTelemetry类型
- 根据应用类型选择接入对应的接入方式,按照步骤接入。请注意,同一个应用下的组件名称不能重复。
- JAVA应用接入
- 下载 OpenTelemetry Java Agent。前往Agent 下载地址,下载后将 Agent 文件放在 Java 进程有访问权限的目录。
- 添加启动命令并重启应用。
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应用接入
- 方式一 非侵入式接入。
- 接入环境准备。
- PHP 8.0+。
- PECL。
- composer。
安装好以后需在 shell 脚本下验证 PHP、composer 版本。
- 安装 OpenTelemetry 扩展组件。
- 安装必须构建工具。
sudo apt-get install gcc make autoconf
- 使用构建安装扩展组件。
sudo apt-get install php-dev pecl install opentelemetry pecl install grpc pecl install protobuf-3.21.12
- 将扩展名添加到您的文件中:php.ini。
[opentelemetry] extension=opentelemetry.so [PHP] extension=grpc.so extension=protobuf.so
- 验证扩展是否已安装并启用。
php --ri opentelemetry php --ri grpc php --ri protobuf
- 安装必须构建工具。
- 安装服务框架所需 opentelemetry 依赖包。
- 安装服务框架所依赖的 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
- 接入 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=应用名称.组件名称.环境名称// 对接服务的服务名
- 安装服务框架所依赖的 opentelemetry 自动插件包,比如 slim 框架需安装 open-telemetry/opentelemetry-auto-slim。
- 接入环境准备。
- 侵入式接入:请参考官网。
- 方式一 非侵入式接入。
- GO应用接入
- 添加 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" )
- 对接 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各代表一层。
- 添加 OpenTelemetry依赖。
- Python应用接入
- 非侵入式接入
- 下载相关包。
pip install opentelemetry-distro pip install opentelemetry-exporter-otlp opentelemetry-bootstrap -a install
- 在启动 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应用接入
- 侵入式接入
- 添加依赖。
npm install @opentelemetry/sdk-node @opentelemetry/api @opentelemetry/auto-instrumentations-node @opentelemetry/sdk-metrics @opentelemetry/sdk-trace-node
- 对接 OpenTelemetry。
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++应用接入
- cmake 构建接入 opentelemetry 流程。
- 环境要求。
- C++ 版本 ≥ 14(低版本 opentelemetry-cpp 需要≥ C++11)。
- 请在 Supported C++ Versions and Development Platforms 中查看支持的 C++ 版本和开发平台。
- 环境准备。
- 安装构建工具 bazel,安装步骤可以参考 bazel 安装指导。
- 通过构建 OpenTelemetry C++ Library 检查环境已经搭建好。
git clone https://github.com/open-telemetry/opentelemetry-cpp.git cd opentelemetry-cpp bazel build //...
- 在项目中使用 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 两个文件。
#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
- WORKSPACE 文件,内容如下:
- .Net应用接入
- 添加 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
- 对接 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各代表一层。
- 添加 OpenTelemetry依赖。
- JAVA应用接入