
# C++应用接入APM
#### 限制条件
- C++版本 ≥ 14（低版本opentelemetry-cpp需要≥ C++11）。
- 请在 [Supported C++ Versions and Development Platforms](https://github.com/open-telemetry/opentelemetry-cpp#supported-c-versions) 中查看支持的C++版本和开发平台。
 
#### 环境准备
1. 安装构建工具bazel，安装步骤可以参考[bazel安装指导](https://docs.bazel.build/versions/3.7.0/install.html)。
2. 通过构建OpenTelemetry C++ Library检查环境已经搭建好。
   ```
   git clone https://github.com/open-telemetry/opentelemetry-cpp.git 
   cd opentelemetry-cpp 
   bazel build //...
   ```
   
3. 熟悉[cmake构建接入opentelemetry流程](https://github.com/open-telemetry/opentelemetry-cpp/blob/v1.10.0/INSTALL.md)。
 
#### C++应用接入APM
1. 登录[APM控制台](https://console.huaweicloud.com/apm2/)。
2. 在左侧导航栏中选择"应用监控 \> 应用列表"。
3. 进入"接入应用"页面。 
   - 首次接入应用时，单击"点击接入应用"，进入"接入应用"页面。
   
   - 已有探针接入时，在页面右上角，单击"接入应用"，进入"接入应用"页面。
   
   
   
   
4. 选择"区域"和"应用"。若未有应用，可单击"创建应用"，弹出"创建应用"弹窗，具体操作请参见[创建应用](https://support.huaweicloud.com/usermanual-apm2/apm_07_0050.html#apm_07_0050__section1684713176449)。
5. "接入方式"选择OpenTelemetry。
6. "服务端语言"选择C++。
7. 数据接入。 
   在项目中使用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;
     }
     ```
     
   
   - 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
     ```
     
     
    
   
   
 
