Instrument C++ applications with OpenTelemetry Metrics
This guide shows how to instrument your C++ application with OpenTelemetry and export the metrics to Dynatrace.
- To learn more about how Dynatrace works with OpenTelemetry, see Send data to Dynatrace with OpenTelemetry.
OpenTelemetry-cpp prerequisites
- A supported development platform.
- A compatible C++ compiler.
- Git for fetching opentelemetry-cpp source code from repository. For details, see how to set up git.
- CMake for building opentelemetry-cpp API and SDK. For details, see how to install CMake.
- GoogleTest framework to build and run the OpenTelemetry unit tests. For details, see how to install GoogleTest.
- Google benchmark, a library to benchmark code snippets. For details, see how to install benchmark.
OpenTelemetry-cpp OTLP exporter prerequisites
- Protobuf library to serialize structured data. For details, see how to install Protobuf.
- libcurl the multiprotocol file transfer library. For details, see (libcurl documentation).
- nlohmann/json the JSON for Modern C++ (you can use cmake to build and install nlohmann/json from its github repository).
Build and install the OpenTelemetry-cpp
Now that you have all the dependencies, let's build the opentelemetry-cpp using CMake.
We will follow the steps from OpenTelemetry, with some adjustments to also build the OTLP_HTTP exporter.
- Get the opentelemetry-cpp source in the directory where you want to create the code repository:
git clone --recursive https://github.com/open-telemetry/opentelemetry-cpp
- Navigate to the cloned repository and create the CMake build configuration:
cd opentelemetry-cpp
mkdir build && cd build
cmake -DBUILD_TESTING=OFF -DWITH_OTLP=ON -DWITH_OTLP_HTTP=ON ..
- Once the build configuration is created, build the CMake targets. This step includes building SDKs and the unit tests for API and SDK. Note that, since the API is a header-only library, no separate build is triggered for it.
cmake --build . --target all
- To validate your build, run the tests:
ctest
- Install the header files for the API, and the generated targets and header files for the SDK. You can also define the install location by adding
--prefix /<install_root>/
to the command below, or execute it as is to use the default one.
cmake --install .
Overview
To monitor your C++ application with OpenTelemetry
Instrument your application
Send the data to Dynatrace
Restart your application and verify that the metrics are ingested into Dynatrace
Instrument your application
C++ instrumentation requires some manual steps. Add the following to your CMakeLists.txt
file and add the code snippet below to any C++ method you want to monitor.
- Set names for the meter and the instrument.
- Add a description and attributes to the instrument as you see fit.
find_package(CURL REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(opentelemetry-cpp CONFIG REQUIRED)
find_package(Protobuf REQUIRED)
find_package(gRPC CONFIG REQUIRED)
target_include_directories(app PRIVATE ${OPENTELEMETRY_CPP_INCLUDE_DIRS})
target_link_libraries(app ${OPENTELEMETRY_CPP_LIBRARIES} opentelemetry_trace opentelemetry_http_client_curl opentelemetry_metrics
opentelemetry_exporter_otlp_http_client opentelemetry_exporter_otlp_http opentelemetry_exporter_otlp_http_metric opentelemetry_resources gRPC::grpc++)
Send data to Dynatrace
Dynatrace version 1.254+
You can create a new header file to take care of all metrics-related configurations.
Simply include it in on the top of your application's files and call its method.
#include "meter_common.h"
initMeter();
This is the code for the header file meter_common.h
:
//meter_common.h
//Add dependencies
#include "opentelemetry/exporters/otlp/otlp_grpc_metric_exporter_factory.h"
#include "opentelemetry/exporters/otlp/otlp_http_metric_exporter_factory.h"
#include "opentelemetry/exporters/otlp/otlp_http_metric_exporter.h"
#include "opentelemetry/ext/http/client/http_client_factory.h"
#include "opentelemetry/metrics/provider.h"
#include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h"
#include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.h"
#include "opentelemetry/sdk/metrics/meter.h"
#include "opentelemetry/sdk/metrics/meter_provider.h"
#include <memory>
#include <thread>
//namespaces
namespace metric_sdk = opentelemetry::sdk::metrics;
namespace metrics_api = opentelemetry::metrics;
namespace otlp_exporter = opentelemetry::exporter::otlp;
namespace
{
otlp_exporter::OtlpHttpMetricExporterOptions options;
std::string version{ "1.0.1" }; //TODO Replace with the version of your application
std::string name{ "cpp-quickstart" }; //TODO Replace with the name of your application
std::string schema{ "https://opentelemetry.io/schemas/1.2.0" };
void initMeter() {
//These are the options that are passed to the exporter, like authorization, endpoint, etc.
otlp_exporter::OtlpHttpMetricExporterOptions otlpOptions;
otlpOptions.url = "<URL>"; //TODO Replace <URL> to your SaaS/Managed-URL as mentioned in the next step
otlpOptions.aggregation_temporality = opentelemetry::sdk::metrics::AggregationTemporality::kDelta;
otlpOptions.content_type = otlp_exporter::HttpRequestContentType::kBinary;
otlpOptions.http_headers.insert(
std::make_pair<const std::string, std::string>("Authorization", "Api-Token <TOKEN>")); //TODO Replace <TOKEN> with your API Token as mentioned in the next step
//This creates the exporter with the options we have defined above.
auto exporter = otlp_exporter::OtlpHttpMetricExporterFactory::Create(otlpOptions);
//This builds a MeterProvider and a Reader
metric_sdk::PeriodicExportingMetricReaderOptions options;
options.export_interval_millis = std::chrono::milliseconds(1000);
options.export_timeout_millis = std::chrono::milliseconds(500);
std::unique_ptr<metric_sdk::MetricReader> reader{
new metric_sdk::PeriodicExportingMetricReader(std::move(exporter), options) };
auto provider = std::shared_ptr<metrics_api::MeterProvider>(new metric_sdk::MeterProvider());
auto p = std::static_pointer_cast<metric_sdk::MeterProvider>(provider);
p->AddMetricReader(std::move(reader));
//This builds a Meter for your instruments
std::unique_ptr<metric_sdk::InstrumentSelector> instrument_selector{
new metric_sdk::InstrumentSelector(metric_sdk::InstrumentType::kCounter, name) };
std::unique_ptr<metric_sdk::MeterSelector> meter_selector{
new metric_sdk::MeterSelector(name, version, schema) };
std::unique_ptr<metric_sdk::View> sum_view{
new metric_sdk::View{name, "description", metric_sdk::AggregationType::kSum} };
p->AddView(std::move(instrument_selector), std::move(meter_selector), std::move(sum_view));
//This builds your instrument, in this case: a counter
auto meter = provider->GetMeter(name, "0.0.0"); //TODO Replace with the version of your meter
auto double_counter = meter->CreateDoubleCounter("cpp-double-counter"); //TODO Replace with the name of your instrument
}
}
In this case, we used a Counter instrument. To find the instrument that fits your needs, see the official OpenTelemetry documentation.
The code below creates the instrument with attributes and calls the Add
method on it.
//This creates a label set which annotates metric values
std::map<std::string, std::string> labels = { {"key", "value"} }; //TODO Add attributes
auto labelkv = opentelemetry::common::KeyValueIterableView<decltype(labels)>{ labels };
//This will add to the counter
double_counter->Add(1.0, labelkv);
If no OneAgent is available locally, you need to define the correct endpoint and token to make sure your data arrives where it should be.
- To set the endpoint
- Use your Environment ID to set the endpoint to which your app will send traces as follows:
- Dynatrace SaaS
https://{your-environment-id}.live.dynatrace.com/api/v2/metrics/ingest
- Dynatrace Managed
https://{your-domain}/e/{your-environment-id}/api/v2/metrics/ingest
- Dynatrace ActiveGate
https://{your-activegate-endpoint}/e/{your-environment-id}/api/v2/metrics/ingest
- You may need to include the port to your ActiveGate endpoint. For example:
https://{your-activegate-endpoint}:9999/e/{your-environment-id}/api/v2/metrics/ingest
- If you are running a containerized ActiveGate, you need to use the FQDN of it. For example:
https://{your-activegate-service-name}.dynatrace.svc.cluster.local/e/{your-environment-id}/api/v2/metrics/ingest
- You may need to include the port to your ActiveGate endpoint. For example:
- Dynatrace SaaS
- Replace
<URL>
in the code snippet above with your endpoint.
- Use your Environment ID to set the endpoint to which your app will send traces as follows:
- To create an authentication token
- In the Dynatrace menu, go to Access tokens and select Generate new token.
- Provide a Token name.
- In the Search scopes box, search for
Ingest metrics
(underAPI v2 scopes
), and select the checkbox. - Select Generate token.
- Select Copy to copy the token to your clipboard.
- Save the token in a safe place; you can't display it again.
- Replace
<TOKEN>
in the code snippet above with your token.
Verify that the metrics are ingested into Dynatrace
A few minutes after restarting your app, look for your metrics:
- In the Dynatrace menu, go to Metrics and filter for your metrics (by the names you have given them).
- To get more detailed information, or to filter for different criteria, expand Details for a metric and select Create chart.
If your application doesn't receive any traffic, no metrics are generated, consequently Dynatrace won't ingest any metrics.