OpenTelemetry Metrics for C++

Prerequisites

Make sure your exporter is configured before you start instrumenting code. Follow Getting started with OpenTelemetry C++ or set up OTLP Configuration first.

If you are not familiar with metrics terminology, read the introduction to OpenTelemetry Metrics first.

Initialize meter provider

cpp
#include "opentelemetry/metrics/provider.h"
#include "opentelemetry/sdk/metrics/meter_provider.h"

namespace metrics_api = opentelemetry::metrics;

auto provider = std::shared_ptr<metrics_api::MeterProvider>(
    new opentelemetry::sdk::metrics::MeterProvider());
auto p = std::static_pointer_cast<opentelemetry::sdk::metrics::MeterProvider>(provider);
// Add metric reader...

Create a counter

cpp
auto meter = provider->GetMeter("app_name", "1.2.0");
auto double_counter = meter->CreateDoubleCounter("counter_name");

std::map<std::string, std::string> labels = {{"key", "value"}};
auto labelkv = opentelemetry::common::KeyValueIterableView<decltype(labels)>{labels};
double_counter->Add(1.0, labelkv);

Create a histogram

cpp
auto meter = provider->GetMeter("app_name", "1.2.0");
auto histogram = meter->CreateDoubleHistogram("histogram_name");

std::map<std::string, std::string> labels = {{"method", "GET"}};
auto labelkv = opentelemetry::common::KeyValueIterableView<decltype(labels)>{labels};
histogram->Record(150.5, labelkv);

Create an observable counter

cpp
auto meter = provider->GetMeter("app_name", "1.2.0");
auto counter = meter->CreateDoubleObservableCounter("observable_counter");

counter->AddCallback([](opentelemetry::metrics::ObserverResult result, void*) {
    result.Observe(get_current_value(), {});
}, nullptr);

Complete example

cpp
#include <chrono>
#include <cstdlib>
#include "opentelemetry/exporters/otlp/otlp_http_metric_exporter_factory.h"
#include "opentelemetry/exporters/otlp/otlp_http_metric_exporter_options.h"
#include "opentelemetry/sdk/metrics/meter_provider_factory.h"
#include "opentelemetry/sdk/metrics/meter_provider.h"
#include "opentelemetry/sdk/metrics/meter_context_factory.h"
#include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader_factory.h"
#include "opentelemetry/metrics/provider.h"

namespace otlp = opentelemetry::exporter::otlp;
namespace metrics_sdk = opentelemetry::sdk::metrics;
namespace metrics_api = opentelemetry::metrics;

void InitMetrics() {
    otlp::OtlpHttpMetricExporterOptions opts;
    opts.url = "https://api.uptrace.dev/v1/metrics";

    const char* dsn = std::getenv("UPTRACE_DSN");
    opts.http_headers = {{"uptrace-dsn", dsn ? dsn : ""}};

    auto exporter = otlp::OtlpHttpMetricExporterFactory::Create(opts);

    metrics_sdk::PeriodicExportingMetricReaderOptions reader_opts;
    reader_opts.export_interval_millis = std::chrono::milliseconds(15000);

    auto reader = metrics_sdk::PeriodicExportingMetricReaderFactory::Create(
        std::move(exporter), reader_opts);

    auto context = metrics_sdk::MeterContextFactory::Create();
    context->AddMetricReader(std::move(reader));

    auto u_provider = metrics_sdk::MeterProviderFactory::Create(std::move(context));
    std::shared_ptr<metrics_api::MeterProvider> provider(std::move(u_provider));
    metrics_api::Provider::SetMeterProvider(provider);
}

void CleanupMetrics() {
    auto provider = metrics_api::Provider::GetMeterProvider();
    if (provider) {
        static_cast<metrics_sdk::MeterProvider*>(provider.get())->ForceFlush();
    }
    std::shared_ptr<metrics_api::MeterProvider> none;
    metrics_api::Provider::SetMeterProvider(none);
}

int main() {
    InitMetrics();

    auto meter = metrics_api::Provider::GetMeterProvider()
        ->GetMeter("myservice", "1.0.0");

    auto counter = meter->CreateUInt64Counter("requests_total");
    counter->Add(1, {{"method", "GET"}, {"status", "200"}});

    CleanupMetrics();
    return 0;
}

What's next?