C++

This guide shows you how to instrument a C++ application with the upstream opentelemetry-cpp SDK and send traces directly to Middleware. No Middleware Host Agent is required: the OTLP HTTP exporter in your application sends data straight to your Middleware account.

Prerequisites#

  • A C++14 (or newer) compiler and CMake 3.16+
  • Linux build dependencies for the OTLP HTTP exporter: Protobuf, Abseil and libcurl. The commands below use Debian/Ubuntu package names.
  • Middleware account & project: your Middleware UID (https://<uid>.middleware.io) and API key (MW_API_KEY)
  • Network access from your application to https://<uid>.middleware.io:443

For a deeper overview of Middleware's OTLP endpoints and headers, see the OpenTelemetry getting started guide.

Setup#

1 Set environment variables#

The OTLP HTTP exporter and the SDK resource read their configuration from standard environment variables. Set them wherever your application runs:

1export OTEL_EXPORTER_OTLP_ENDPOINT="https://<MW_UID>.middleware.io:443"
2export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
3export OTEL_EXPORTER_OTLP_HEADERS="authorization=<MW_API_KEY>"
4export OTEL_SERVICE_NAME="<service-name>"
5export OTEL_RESOURCE_ATTRIBUTES="service.version=<service-version>,deployment.environment=<environment>"

Replace:

  • <uid> with your Middleware project UID.
  • <MW_API_KEY> with your Middleware API key.
  • <service-name> with a meaningful name for this service (for example, payments-api).

Set OTEL_EXPORTER_OTLP_ENDPOINT to the root URL only. The OTLP HTTP exporter appends /v1/traces for you.

2 Build and install opentelemetry-cpp#

Install the build dependencies:

1sudo apt-get update
2sudo apt-get install -y build-essential cmake git libprotobuf-dev protobuf-compiler libabsl-dev libcurl4-openssl-dev

Build and install the SDK with the OTLP HTTP exporter enabled:

1git clone --depth 1 --branch v1.28.0 https://github.com/open-telemetry/opentelemetry-cpp.git
2cd opentelemetry-cpp
3cmake -B build -DCMAKE_BUILD_TYPE=Release -DWITH_OTLP_HTTP=ON -DBUILD_TESTING=OFF -DWITH_BENCHMARK=OFF
4cmake --build build --target all --config Release
5sudo cmake --install build --config Release --prefix /usr/local

If you use a package manager such as vcpkg or Conan, install opentelemetry-cpp with the OTLP HTTP exporter feature instead of building from source. The CMake targets in the next step are the same.

1cmake_minimum_required(VERSION 3.16)
2project(my-cpp-app CXX)
3
4set(CMAKE_CXX_STANDARD 14)
5set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
7find_package(opentelemetry-cpp CONFIG REQUIRED COMPONENTS api sdk exporters_otlp_http)
8
9add_executable(my-cpp-app main.cc)
10target_link_libraries(my-cpp-app
11  PRIVATE
12    opentelemetry-cpp::api
13    opentelemetry-cpp::trace
14    opentelemetry-cpp::otlp_http_exporter
15)

4 Initialize the tracer#

Set up a tracer provider with a batch span processor and the OTLP HTTP exporter in main.cc. Call CleanupTracer() before the process exits so buffered spans are flushed:

1#include <memory>
2#include <utility>
3
4#include "opentelemetry/exporters/otlp/otlp_http_exporter_factory.h"
5#include "opentelemetry/exporters/otlp/otlp_http_exporter_options.h"
6#include "opentelemetry/sdk/resource/resource.h"
7#include "opentelemetry/sdk/trace/batch_span_processor_factory.h"
8#include "opentelemetry/sdk/trace/batch_span_processor_options.h"
9#include "opentelemetry/sdk/trace/provider.h"
10#include "opentelemetry/sdk/trace/tracer_provider.h"
11#include "opentelemetry/sdk/trace/tracer_provider_factory.h"
12#include "opentelemetry/trace/provider.h"
13
14namespace otlp      = opentelemetry::exporter::otlp;
15namespace resource  = opentelemetry::sdk::resource;
16namespace trace_api = opentelemetry::trace;
17namespace trace_sdk = opentelemetry::sdk::trace;
18
19std::shared_ptr<trace_sdk::TracerProvider> tracer_provider;
20
21void InitTracer()
22{
23  otlp::OtlpHttpExporterOptions exporter_options;
24  auto exporter = otlp::OtlpHttpExporterFactory::Create(exporter_options);
25
26  trace_sdk::BatchSpanProcessorOptions processor_options;
27  auto processor =
28      trace_sdk::BatchSpanProcessorFactory::Create(std::move(exporter), processor_options);
29
30  tracer_provider =
31      trace_sdk::TracerProviderFactory::Create(std::move(processor), resource::Resource::Create({}));
32
33  std::shared_ptr<trace_api::TracerProvider> api_provider = tracer_provider;
34  trace_sdk::Provider::SetTracerProvider(api_provider);
35}
36
37void CleanupTracer()
38{
39  // Flush pending spans before the application exits
40  if (tracer_provider)
41  {
42    tracer_provider->ForceFlush();
43  }
44  tracer_provider.reset();
45  std::shared_ptr<trace_api::TracerProvider> none;
46  trace_sdk::Provider::SetTracerProvider(none);
47}
48
49int main()
50{
51  InitTracer();
52
53  auto tracer = trace_api::Provider::GetTracerProvider()->GetTracer("my-cpp-app");
54  {
55    auto span  = tracer->StartSpan("main-operation");
56    auto scope = tracer->WithActiveSpan(span);
57    // your application logic
58    span->End();
59  }
60
61  CleanupTracer();
62  return 0;
63}

A default-constructed OtlpHttpExporterOptions reads the endpoint, protocol and headers from the OTEL_EXPORTER_OTLP_* variables, and Resource::Create({}) merges OTEL_SERVICE_NAME and OTEL_RESOURCE_ATTRIBUTES into the resource.

5 Build and run your application#

1cmake -B build
2cmake --build build
3./build/my-cpp-app

Add attributes to spans#

Attach attributes when you start a span, or later on the span object, so you can filter on them in Middleware:

1auto span = tracer->StartSpan("handle-order", {{"order.id", order_id}});
2span->SetAttribute("order.items", item_count);
3// ...
4span->End();

Other deployment targets#

The OpenTelemetry configuration is the same everywhere; only the way you declare environment variables changes.

1docker run \
2  -e OTEL_EXPORTER_OTLP_ENDPOINT="https://<MW_UID>.middleware.io:443" \
3  -e OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf" \
4  -e OTEL_EXPORTER_OTLP_HEADERS="authorization=<MW_API_KEY>" \
5  -e OTEL_SERVICE_NAME="<service-name>" \
6  <your-image>
1env:
2  - name: OTEL_EXPORTER_OTLP_ENDPOINT
3    value: "https://<MW_UID>.middleware.io:443"
4  - name: OTEL_EXPORTER_OTLP_PROTOCOL
5    value: "http/protobuf"
6  - name: OTEL_EXPORTER_OTLP_HEADERS
7    value: "authorization=<MW_API_KEY>"
8  - name: OTEL_SERVICE_NAME
9    value: "<service-name>"

Store the API key in a Kubernetes Secret and reference it with valueFrom.secretKeyRef in production.

View your data#

After you start the application and send some traffic, wait 3–5 minutes, then open APM → Services or the Traces explorer in Middleware and filter by your service name.

Environment variables#

VariablePurposeExample
OTEL_EXPORTER_OTLP_ENDPOINTMiddleware OTLP endpoint for your project.https://<uid>.middleware.io:443
OTEL_EXPORTER_OTLP_PROTOCOLOTLP transport used by the exporter.http/protobuf
OTEL_EXPORTER_OTLP_HEADERSAuthenticates requests with your Middleware API key.authorization=<MW_API_KEY>
OTEL_SERVICE_NAMEService name shown in Middleware APM.payments-api
OTEL_RESOURCE_ATTRIBUTESExtra resource attributes attached to every span.service.version=1.4.2,deployment.environment=prod

Troubleshooting#

  • find_package(opentelemetry-cpp) fails: pass the install prefix to CMake, for example cmake -B build -DCMAKE_PREFIX_PATH=/usr/local.
  • No data in Middleware: confirm the environment variables are set in the process that runs your app, and that the endpoint does not already include /v1/traces.
  • Spans missing for short-lived programs: call CleanupTracer() before exiting. The batch processor exports in the background, and anything still buffered is lost if the process exits first.
  • Debugging the exporter: set exporter_options.console_debug = true; before creating the exporter, and raise the SDK log level with opentelemetry::sdk::common::internal_log::GlobalLogHandler::SetLogLevel(opentelemetry::sdk::common::internal_log::LogLevel::Debug); (from opentelemetry/sdk/common/global_log_handler.h) to log each export request and response.

Need assistance or want to learn more about Middleware? Get in touch with us via our Contact Us or join our Slack channel.