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:
export OTEL_EXPORTER_OTLP_ENDPOINT="https://<MW_UID>.middleware.io:443"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
export OTEL_EXPORTER_OTLP_HEADERS="authorization=<MW_API_KEY>"
export OTEL_SERVICE_NAME="<service-name>"
export 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:
sudo apt-get update
sudo 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:
git clone --depth 1 --branch v1.28.0 https://github.com/open-telemetry/opentelemetry-cpp.git
cd opentelemetry-cpp
cmake -B build -DCMAKE_BUILD_TYPE=Release -DWITH_OTLP_HTTP=ON -DBUILD_TESTING=OFF -DWITH_BENCHMARK=OFF
cmake --build build --target all --config Release
sudo 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#
cmake_minimum_required(VERSION 3.16)
project(my-cpp-app CXX)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(opentelemetry-cpp CONFIG REQUIRED COMPONENTS api sdk exporters_otlp_http)
add_executable(my-cpp-app main.cc)
target_link_libraries(my-cpp-app
PRIVATE
opentelemetry-cpp::api
opentelemetry-cpp::trace
opentelemetry-cpp::otlp_http_exporter
)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:
#include <memory>
#include <utility>
#include "opentelemetry/exporters/otlp/otlp_http_exporter_factory.h"
#include "opentelemetry/exporters/otlp/otlp_http_exporter_options.h"
#include "opentelemetry/sdk/resource/resource.h"
#include "opentelemetry/sdk/trace/batch_span_processor_factory.h"
#include "opentelemetry/sdk/trace/batch_span_processor_options.h"
#include "opentelemetry/sdk/trace/provider.h"
#include "opentelemetry/sdk/trace/tracer_provider.h"
#include "opentelemetry/sdk/trace/tracer_provider_factory.h"
#include "opentelemetry/trace/provider.h"
namespace otlp = opentelemetry::exporter::otlp;
namespace resource = opentelemetry::sdk::resource;
namespace trace_api = opentelemetry::trace;
namespace trace_sdk = opentelemetry::sdk::trace;
std::shared_ptr<trace_sdk::TracerProvider> tracer_provider;
void InitTracer()
{
otlp::OtlpHttpExporterOptions exporter_options;
auto exporter = otlp::OtlpHttpExporterFactory::Create(exporter_options);
trace_sdk::BatchSpanProcessorOptions processor_options;
auto processor =
trace_sdk::BatchSpanProcessorFactory::Create(std::move(exporter), processor_options);
tracer_provider =
trace_sdk::TracerProviderFactory::Create(std::move(processor), resource::Resource::Create({}));
std::shared_ptr<trace_api::TracerProvider> api_provider = tracer_provider;
trace_sdk::Provider::SetTracerProvider(api_provider);
}
void CleanupTracer()
{
// Flush pending spans before the application exits
if (tracer_provider)
{
tracer_provider->ForceFlush();
}
tracer_provider.reset();
std::shared_ptr<trace_api::TracerProvider> none;
trace_sdk::Provider::SetTracerProvider(none);
}
int main()
{
InitTracer();
auto tracer = trace_api::Provider::GetTracerProvider()->GetTracer("my-cpp-app");
{
auto span = tracer->StartSpan("main-operation");
auto scope = tracer->WithActiveSpan(span);
// your application logic
span->End();
}
CleanupTracer();
return 0;
}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#
cmake -B build
cmake --build build
./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:
auto span = tracer->StartSpan("handle-order", {{"order.id", order_id}});
span->SetAttribute("order.items", item_count);
// ...
span->End();Other deployment targets#
The OpenTelemetry configuration is the same everywhere; only the way you declare environment variables changes.
docker run \
-e OTEL_EXPORTER_OTLP_ENDPOINT="https://<MW_UID>.middleware.io:443" \
-e OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf" \
-e OTEL_EXPORTER_OTLP_HEADERS="authorization=<MW_API_KEY>" \
-e OTEL_SERVICE_NAME="<service-name>" \
<your-image>env:
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "https://<MW_UID>.middleware.io:443"
- name: OTEL_EXPORTER_OTLP_PROTOCOL
value: "http/protobuf"
- name: OTEL_EXPORTER_OTLP_HEADERS
value: "authorization=<MW_API_KEY>"
- name: OTEL_SERVICE_NAME
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.
What did you think of this content?