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-devBuild 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/localIf 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.
3 Link opentelemetry-cpp in CMakeLists.txt#
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-appAdd 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#
| Variable | Purpose | Example |
|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT | Middleware OTLP endpoint for your project. | https://<uid>.middleware.io:443 |
OTEL_EXPORTER_OTLP_PROTOCOL | OTLP transport used by the exporter. | http/protobuf |
OTEL_EXPORTER_OTLP_HEADERS | Authenticates requests with your Middleware API key. | authorization=<MW_API_KEY> |
OTEL_SERVICE_NAME | Service name shown in Middleware APM. | payments-api |
OTEL_RESOURCE_ATTRIBUTES | Extra 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 examplecmake -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 withopentelemetry::sdk::common::internal_log::GlobalLogHandler::SetLogLevel(opentelemetry::sdk::common::internal_log::LogLevel::Debug);(fromopentelemetry/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.