OpenTelemetry Context Propagation [C++]

This guide covers C++ implementation of context propagation. For concepts, see OpenTelemetry Context Propagation.

What is traceparent header?

The traceparent HTTP header contains trace context information:

text
traceparent: 00-80e1afed08e019fc1110464cfa66635c-7a085853722dc6d2-01

Format: version-trace_id-span_id-trace_flags

Setting up propagators

cpp
#include "opentelemetry/context/propagation/global_propagator.h"
#include "opentelemetry/trace/propagation/http_trace_context.h"

namespace propagation = opentelemetry::context::propagation;
namespace trace_prop = opentelemetry::trace::propagation;

void InitPropagator() {
    propagation::GlobalTextMapPropagator::SetGlobalPropagator(
        opentelemetry::nostd::shared_ptr<propagation::TextMapPropagator>(
            new trace_prop::HttpTraceContext()));
}

HTTP Header Carrier

cpp
#include "opentelemetry/context/propagation/text_map_propagator.h"
#include <map>

namespace propagation = opentelemetry::context::propagation;

class HttpHeaderCarrier : public propagation::TextMapCarrier {
public:
    HttpHeaderCarrier(std::map<std::string, std::string>& headers)
        : headers_(headers) {}

    opentelemetry::nostd::string_view Get(
        opentelemetry::nostd::string_view key) const noexcept override {
        auto it = headers_.find(std::string(key));
        if (it != headers_.end()) return it->second;
        return "";
    }

    void Set(opentelemetry::nostd::string_view key,
             opentelemetry::nostd::string_view value) noexcept override {
        headers_[std::string(key)] = std::string(value);
    }

private:
    std::map<std::string, std::string>& headers_;
};

Extracting context

Extract trace context from incoming HTTP requests:

cpp
#include "opentelemetry/context/propagation/global_propagator.h"
#include "opentelemetry/context/runtime_context.h"
#include "opentelemetry/trace/provider.h"

namespace propagation = opentelemetry::context::propagation;
namespace trace_api = opentelemetry::trace;

void handleRequest(std::map<std::string, std::string>& headers) {
    HttpHeaderCarrier carrier(headers);
    auto propagator = propagation::GlobalTextMapPropagator::GetGlobalPropagator();

    auto current_ctx = opentelemetry::context::RuntimeContext::GetCurrent();
    auto new_ctx = propagator->Extract(carrier, current_ctx);

    auto tracer = trace_api::Provider::GetTracerProvider()->GetTracer("myservice");

    trace_api::StartSpanOptions opts;
    opts.kind = trace_api::SpanKind::kServer;
    opts.parent = trace_api::GetSpan(new_ctx)->GetContext();

    auto span = tracer->StartSpan("handle-request", opts);
    auto scope = tracer->WithActiveSpan(span);

    // Process request...
    span->End();
}

Injecting context

Inject trace context into outgoing HTTP requests:

cpp
void makeHttpRequest(const std::string& url) {
    auto tracer = trace_api::Provider::GetTracerProvider()->GetTracer("myservice");

    trace_api::StartSpanOptions opts;
    opts.kind = trace_api::SpanKind::kClient;

    auto span = tracer->StartSpan("http-request", opts);
    auto scope = tracer->WithActiveSpan(span);

    std::map<std::string, std::string> headers;
    HttpHeaderCarrier carrier(headers);

    auto propagator = propagation::GlobalTextMapPropagator::GetGlobalPropagator();
    auto current_ctx = opentelemetry::context::RuntimeContext::GetCurrent();
    propagator->Inject(carrier, current_ctx);

    // Headers now contain traceparent - use in HTTP client
    span->End();
}

Complete example

cpp
#include <map>
#include <string>
#include "opentelemetry/context/propagation/global_propagator.h"
#include "opentelemetry/context/propagation/text_map_propagator.h"
#include "opentelemetry/context/runtime_context.h"
#include "opentelemetry/trace/propagation/http_trace_context.h"
#include "opentelemetry/trace/provider.h"

namespace propagation = opentelemetry::context::propagation;
namespace trace_prop = opentelemetry::trace::propagation;
namespace trace_api = opentelemetry::trace;

// Carrier for HTTP headers
class HttpHeaderCarrier : public propagation::TextMapCarrier {
public:
    HttpHeaderCarrier(std::map<std::string, std::string>& headers)
        : headers_(headers) {}

    opentelemetry::nostd::string_view Get(
        opentelemetry::nostd::string_view key) const noexcept override {
        auto it = headers_.find(std::string(key));
        return it != headers_.end() ? it->second : "";
    }

    void Set(opentelemetry::nostd::string_view key,
             opentelemetry::nostd::string_view value) noexcept override {
        headers_[std::string(key)] = std::string(value);
    }

private:
    std::map<std::string, std::string>& headers_;
};

void InitPropagator() {
    propagation::GlobalTextMapPropagator::SetGlobalPropagator(
        opentelemetry::nostd::shared_ptr<propagation::TextMapPropagator>(
            new trace_prop::HttpTraceContext()));
}

void clientRequest() {
    auto tracer = trace_api::Provider::GetTracerProvider()->GetTracer("client");

    auto span = tracer->StartSpan("client-request");
    auto scope = tracer->WithActiveSpan(span);

    std::map<std::string, std::string> headers;
    HttpHeaderCarrier carrier(headers);

    auto propagator = propagation::GlobalTextMapPropagator::GetGlobalPropagator();
    propagator->Inject(carrier, opentelemetry::context::RuntimeContext::GetCurrent());

    // Use headers in HTTP request
    span->End();
}

What's next?