C++语言接入
限制条件
- 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 //...
操作步骤
- 登录APM控制台。
- 单击左侧
,选择“管理与监管 > 应用性能管理 APM”,进入APM服务页面。 - 在左侧导航栏中选择“应用监控 > 应用列表”。
- 单击“接入应用”,进入接入应用页面。
- 选择“区域”和“应用”。单击“创建应用”,弹出“创建应用”弹窗,可以具体操作参见创建应用。
- “接入方式”选择OpenTelemetry。
- “服务端语言”选择C++。
- 数据接入,相关参数与操作步骤如下。
表1 参数说明 参数
说明
是否必填
应用名称
应用显示的名称。一个应用代表一个逻辑单元,是一个全局概念,各个region都可以看到相同的应用信息,比如一个租户下面比较独立的功能模块可以定义为一个应用。
必填
组件名称
组件名称,代表一个组件,需要使用英文字符开头。同一个应用下,组件名称不能重复。一个组件可以包含多个环境。不能重复,如果要重复,使用instanceName区分。
必填
环境名称
环境名称,代表一个应用在一个地方的部署。一个应用程序根据配置不同可以部署多个环境,比如测试环境,现网环境。每个环境都在一个region部署,具有唯一的region属性。该参数可以为空,代表默认环境。
选填
在项目中使用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 = "8ecF***x"; 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; } - 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文件,内容如下: