From d8467a78b3cf3accfa3a0a2569bfd3b90822ad02 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Sat, 8 May 2021 13:40:47 +0530 Subject: [PATCH] Enhance existing http example to support w3c trace context propagation (#727) --- examples/http/BUILD | 6 +-- examples/http/README.md | 41 ++++++++++---- examples/http/client.cc | 11 +++- examples/http/server.cc | 14 ++++- examples/http/server.h | 50 +++++++++++++++++ examples/http/server.hpp | 47 ---------------- examples/http/tracer_common.h | 95 +++++++++++++++++++++++++++++++++ examples/http/tracer_common.hpp | 32 ----------- 8 files changed, 199 insertions(+), 97 deletions(-) create mode 100644 examples/http/server.h delete mode 100644 examples/http/server.hpp create mode 100644 examples/http/tracer_common.h delete mode 100644 examples/http/tracer_common.hpp diff --git a/examples/http/BUILD b/examples/http/BUILD index 4fce0a2e27..6f0618fd85 100644 --- a/examples/http/BUILD +++ b/examples/http/BUILD @@ -2,7 +2,7 @@ cc_binary( name = "example_http_client", srcs = [ "client.cc", - "tracer_common.hpp", + "tracer_common.h", ], # TODO: Move copts/linkopts for static CURL usage into shared bzl file. copts = [ @@ -30,8 +30,8 @@ cc_binary( name = "example_http_server", srcs = [ "server.cc", - "server.hpp", - "tracer_common.hpp", + "server.h", + "tracer_common.h", ], deps = [ "//api", diff --git a/examples/http/README.md b/examples/http/README.md index 5d24e482a8..58ca97ca02 100644 --- a/examples/http/README.md +++ b/examples/http/README.md @@ -5,11 +5,12 @@ This is a simple example that demonstrates tracing an HTTP request from client to server. The example shows several aspects of tracing such as: * Using the `TracerProvider` +* Using the `GlobalPropagator` * Span Attributes * Span Events * Using the ostream exporter * Nested spans (TBD) -* W3c Trace Context Propagation (TBD) +* W3C Trace Context Propagation ### Running the example @@ -38,18 +39,20 @@ This is a simple example that demonstrates tracing an HTTP request from client t * Client console ```console + { name : /helloworld - trace_id : 15c7ca1993b536085f4097f2818a7be4 - span_id : 7d9136e4eb4cb59d + trace_id : baa922bc0da6f79d46373371f4416463 + span_id : 83bed4da7a01267d + tracestate : parent_span_id: 0000000000000000 - start : 1617075613395810300 - duration : 1901100 + start : 1620287026111457000 + duration : 5164400 description : span kind : Client status : Unset attributes : - http.header.Date: Tue, 30 Mar 2021 03:40:13 GMT + http.header.Date: Thu, 06 May 2021 07:43:46 GMT http.header.Content-Length: 0 http.status_code: 200 http.method: GET @@ -58,30 +61,46 @@ This is a simple example that demonstrates tracing an HTTP request from client t http.header.Connection: keep-alive http.scheme: http http.url: h**p://localhost:8800/helloworld + events : + links : + } ``` * Server console ```console + { name : /helloworld - trace_id : bfa611a4bbb8b1871ef6a222d6a0f4dd - span_id : 19e3cda7df63c9b9 - parent_span_id: 0000000000000000 - start : 1617075522491536300 + trace_id : baa922bc0da6f79d46373371f4416463 + span_id : c3e7e23042eb670e + tracestate : + parent_span_id: 83bed4da7a01267d + start : 1620287026115443300 duration : 50700 description : span kind : Server status : Unset attributes : + http.header.Traceparent: 00-baa922bc0da6f79d46373371f4416463-83bed4da7a01267d-01 http.header.Accept: */* http.request_content_length: 0 http.header.Host: localhost:8800 http.scheme: http - http.client_ip: 127.0.0.1:44616 + http.client_ip: 127.0.0.1:50792 http.method: GET net.host.port: 8800 http.server_name: localhost + events : + { + name : Processing request + timestamp : 1620287026115464000 + attributes : + } + links : + } ``` + + As seen from example above, `trace_id` and `parent_span_id` is propagated from client to server. diff --git a/examples/http/client.cc b/examples/http/client.cc index 64e9722ed3..9b24825dde 100644 --- a/examples/http/client.cc +++ b/examples/http/client.cc @@ -1,6 +1,6 @@ #include "opentelemetry/ext/http/client/http_client_factory.h" #include "opentelemetry/ext/http/common/url_parser.h" -#include "tracer_common.hpp" +#include "tracer_common.h" namespace { @@ -23,7 +23,14 @@ void sendRequest(const std::string &url) options); auto scope = get_tracer("http-client")->WithActiveSpan(span); - opentelemetry::ext::http::client::Result result = http_client->Get(url); + // inject current context into http header + auto current_ctx = opentelemetry::context::RuntimeContext::GetCurrent(); + HttpTextMapCarrier carrier; + auto prop = opentelemetry::context::propagation::GlobalTextMapPropagator::GetGlobalPropagator(); + prop->Inject(carrier, current_ctx); + + // send http request + opentelemetry::ext::http::client::Result result = http_client->Get(url, carrier.headers_); if (result) { // set span attributes diff --git a/examples/http/server.cc b/examples/http/server.cc index d1aa4c2d99..a9f1a81e08 100644 --- a/examples/http/server.cc +++ b/examples/http/server.cc @@ -1,5 +1,5 @@ -#include "server.hpp" -#include "tracer_common.hpp" +#include "server.h" +#include "tracer_common.h" #include #include @@ -19,6 +19,15 @@ class RequestHandler : public HTTP_SERVER_NS::HttpRequestCallback options.kind = opentelemetry::trace::SpanKind::kServer; // server std::string span_name = request.uri; + // extract context from http header + const HttpTextMapCarrier> carrier( + (std::map &)request.headers); + auto prop = opentelemetry::context::propagation::GlobalTextMapPropagator::GetGlobalPropagator(); + auto current_ctx = opentelemetry::context::RuntimeContext::GetCurrent(); + auto new_context = prop->Extract(carrier, current_ctx); + options.parent = GetSpanFromContext(new_context)->GetContext(); + + // start span with parent context extracted from http header auto span = get_tracer("http-server") ->StartSpan(span_name, {{"http.server_name", server_name}, @@ -30,6 +39,7 @@ class RequestHandler : public HTTP_SERVER_NS::HttpRequestCallback options); auto scope = get_tracer("http_server")->WithActiveSpan(span); + for (auto &kv : request.headers) { span->SetAttribute("http.header." + std::string(kv.first.data()), kv.second); diff --git a/examples/http/server.h b/examples/http/server.h new file mode 100644 index 0000000000..f37b60a302 --- /dev/null +++ b/examples/http/server.h @@ -0,0 +1,50 @@ +#pragma once +#include +#include +#include "opentelemetry/ext/http/server/http_server.h" + +namespace +{ + +class HttpServer : public HTTP_SERVER_NS::HttpRequestCallback +{ + +protected: + HTTP_SERVER_NS::HttpServer server_; + std::string server_url_; + uint16_t port_; + std::atomic is_running_{false}; + +public: + HttpServer(std::string server_name = "test_server", uint16_t port = 8800) : port_(port) + { + server_.setServerName(server_name); + server_.setKeepalive(false); + } + + void AddHandler(std::string path, HTTP_SERVER_NS::HttpRequestCallback *request_handler) + { + server_.addHandler(path, *request_handler); + } + + void Start() + { + if (!is_running_.exchange(true)) + { + server_.addListeningPort(port_); + server_.start(); + } + } + + void Stop() + { + if (is_running_.exchange(false)) + { + server_.stop(); + } + } + + ~HttpServer() { Stop(); } +}; + +} // namespace \ No newline at end of file diff --git a/examples/http/server.hpp b/examples/http/server.hpp deleted file mode 100644 index 1dc83788e2..0000000000 --- a/examples/http/server.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -#include "opentelemetry/ext/http/server/http_server.h" -#include -#include - - -namespace { - -class HttpServer : public HTTP_SERVER_NS::HttpRequestCallback -{ - -protected: - HTTP_SERVER_NS::HttpServer server_; - std::string server_url_ ; - uint16_t port_ ; - std::atomic is_running_{false}; - -public: - - HttpServer(std::string server_name = "test_server",uint16_t port = 8800): port_(port){ - server_.setServerName(server_name); - server_.setKeepalive(false); - } - - void AddHandler(std::string path, HTTP_SERVER_NS::HttpRequestCallback *request_handler){ - server_.addHandler(path, *request_handler); - } - - void Start() { - if (!is_running_.exchange(true)) { - server_.addListeningPort(port_); - server_.start(); - } - } - - void Stop() { - if (is_running_.exchange(false)){ - server_.stop(); - } - } - - ~HttpServer(){ - Stop(); - } -}; - -} \ No newline at end of file diff --git a/examples/http/tracer_common.h b/examples/http/tracer_common.h new file mode 100644 index 0000000000..a618175b1d --- /dev/null +++ b/examples/http/tracer_common.h @@ -0,0 +1,95 @@ +#pragma once +#include "opentelemetry/exporters/ostream/span_exporter.h" +#include "opentelemetry/sdk/trace/simple_processor.h" +#include "opentelemetry/sdk/trace/tracer_provider.h" +#include "opentelemetry/trace/provider.h" + +#include "opentelemetry/context/propagation/global_propagator.h" +#include "opentelemetry/context/propagation/text_map_propagator.h" +#include "opentelemetry/trace/propagation/http_trace_context.h" + +#include +#include +#include +#include "opentelemetry/ext/http/client/http_client.h" +#include "opentelemetry/nostd/shared_ptr.h" + +namespace +{ +// TBD - This function be removed once #723 is merged +inline nostd::shared_ptr GetSpanFromContext( + const opentelemetry::context::Context &context) +{ + opentelemetry::context::ContextValue span = context.GetValue(opentelemetry::trace::kSpanKey); + if (nostd::holds_alternative>(span)) + { + return nostd::get>(span); + } + static nostd::shared_ptr invalid_span{ + new opentelemetry::trace::DefaultSpan(opentelemetry::trace::SpanContext::GetInvalid())}; + return invalid_span; +} + +template +class HttpTextMapCarrier : public opentelemetry::context::propagation::TextMapCarrier +{ +public: + HttpTextMapCarrier(T &headers) : headers_(headers) {} + HttpTextMapCarrier() = default; + virtual nostd::string_view Get(nostd::string_view key) const noexcept override + { + std::string key_to_compare = key.data(); + // Header's first letter seems to be automatically capitaliazed by our test http-server, so + // compare accordingly. + if (key == opentelemetry::trace::propagation::kTraceParent) + { + key_to_compare = "Traceparent"; + } + else if (key == opentelemetry::trace::propagation::kTraceState) + { + key_to_compare == "Tracestate"; + } + auto it = headers_.find(key_to_compare); + if (it != headers_.end()) + { + return it->second; + } + return ""; + } + + virtual void Set(nostd::string_view key, nostd::string_view value) noexcept override + { + headers_.insert(std::pair(std::string(key), std::string(value))); + } + + T headers_; +}; + +void initTracer() +{ + auto exporter = std::unique_ptr( + new opentelemetry::exporter::trace::OStreamSpanExporter); + auto processor = std::unique_ptr( + new sdktrace::SimpleSpanProcessor(std::move(exporter))); + std::vector> processors; + processors.push_back(std::move(processor)); + // Default is an always-on sampler. + auto context = std::make_shared(std::move(processors)); + auto provider = nostd::shared_ptr( + new sdktrace::TracerProvider(context)); + // Set the global trace provider + opentelemetry::trace::Provider::SetTracerProvider(provider); + + // set global propagator + opentelemetry::context::propagation::GlobalTextMapPropagator::SetGlobalPropagator( + nostd::shared_ptr( + new opentelemetry::trace::propagation::HttpTraceContext())); +} + +nostd::shared_ptr get_tracer(std::string tracer_name) +{ + auto provider = opentelemetry::trace::Provider::GetTracerProvider(); + return provider->GetTracer(tracer_name); +} + +} // namespace diff --git a/examples/http/tracer_common.hpp b/examples/http/tracer_common.hpp deleted file mode 100644 index 0b0723a61a..0000000000 --- a/examples/http/tracer_common.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -#include "opentelemetry/exporters/ostream/span_exporter.h" -#include "opentelemetry/sdk/trace/simple_processor.h" -#include "opentelemetry/sdk/trace/tracer_provider.h" -#include "opentelemetry/trace/provider.h" - -#include - -namespace { - -void initTracer() { - auto exporter = std::unique_ptr( - new opentelemetry::exporter::trace::OStreamSpanExporter); - auto processor = std::unique_ptr( - new sdktrace::SimpleSpanProcessor(std::move(exporter))); - std::vector> processors; - processors.push_back(std::move(processor)); - // Default is an always-on sampler. - auto context = std::make_shared(std::move(processors)); - auto provider = nostd::shared_ptr( - new sdktrace::TracerProvider(context)); - // Set the global trace provider - opentelemetry::trace::Provider::SetTracerProvider(provider); -} - -nostd::shared_ptr get_tracer(std::string tracer_name) -{ - auto provider = opentelemetry::trace::Provider::GetTracerProvider(); - return provider->GetTracer(tracer_name); -} - -}