更新时间:2025-10-27 GMT+08:00
分享

C++语言接入

限制条件

  1. C++版本 ≥ 14(低版本opentelemetry-cpp需要≥ C++11)。
  2. 请在 Supported C++ Versions and Development Platforms 中查看支持的C++版本和开发平台。

环境准备

  1. 安装构建工具bazel,安装步骤可以参考bazel安装指导
  2. 通过构建OpenTelemetry C++ Library检查环境已经搭建好。
    git clone https://github.com/open-telemetry/opentelemetry-cpp.git
        cd opentelemetry-cpp
        bazel build //...

操作步骤

  1. 登录APM控制台
  2. 单击左侧,选择“管理与监管 > 应用性能管理 APM”,进入APM服务页面。
  3. 在左侧导航栏中选择“应用监控 > 应用列表”。
  4. 单击“接入应用”,进入接入应用页面。
  5. 选择“区域”和“应用”。单击“创建应用”,弹出“创建应用”弹窗,可以具体操作参见创建应用
  6. “接入方式”选择OpenTelemetry。
  7. “服务端语言”选择C++。
  8. 数据接入,相关参数与操作步骤如下。

    表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

相关文档