Skip to content

Commit d8467a7

Browse files
authored
Enhance existing http example to support w3c trace context propagation (#727)
1 parent 01b7a84 commit d8467a7

File tree

8 files changed

+199
-97
lines changed

8 files changed

+199
-97
lines changed

examples/http/BUILD

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ cc_binary(
22
name = "example_http_client",
33
srcs = [
44
"client.cc",
5-
"tracer_common.hpp",
5+
"tracer_common.h",
66
],
77
# TODO: Move copts/linkopts for static CURL usage into shared bzl file.
88
copts = [
@@ -30,8 +30,8 @@ cc_binary(
3030
name = "example_http_server",
3131
srcs = [
3232
"server.cc",
33-
"server.hpp",
34-
"tracer_common.hpp",
33+
"server.h",
34+
"tracer_common.h",
3535
],
3636
deps = [
3737
"//api",

examples/http/README.md

+30-11
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
This is a simple example that demonstrates tracing an HTTP request from client to server. The example shows several aspects of tracing such as:
66

77
* Using the `TracerProvider`
8+
* Using the `GlobalPropagator`
89
* Span Attributes
910
* Span Events
1011
* Using the ostream exporter
1112
* Nested spans (TBD)
12-
* W3c Trace Context Propagation (TBD)
13+
* W3C Trace Context Propagation
1314

1415
### Running the example
1516

@@ -38,18 +39,20 @@ This is a simple example that demonstrates tracing an HTTP request from client t
3839
* Client console
3940

4041
```console
42+
4143
{
4244
name : /helloworld
43-
trace_id : 15c7ca1993b536085f4097f2818a7be4
44-
span_id : 7d9136e4eb4cb59d
45+
trace_id : baa922bc0da6f79d46373371f4416463
46+
span_id : 83bed4da7a01267d
47+
tracestate :
4548
parent_span_id: 0000000000000000
46-
start : 1617075613395810300
47-
duration : 1901100
49+
start : 1620287026111457000
50+
duration : 5164400
4851
description :
4952
span kind : Client
5053
status : Unset
5154
attributes :
52-
http.header.Date: Tue, 30 Mar 2021 03:40:13 GMT
55+
http.header.Date: Thu, 06 May 2021 07:43:46 GMT
5356
http.header.Content-Length: 0
5457
http.status_code: 200
5558
http.method: GET
@@ -58,30 +61,46 @@ This is a simple example that demonstrates tracing an HTTP request from client t
5861
http.header.Connection: keep-alive
5962
http.scheme: http
6063
http.url: h**p://localhost:8800/helloworld
64+
events :
65+
links :
66+
6167
}
6268
```
6369

6470
* Server console
6571

6672
```console
73+
6774
{
6875
name : /helloworld
69-
trace_id : bfa611a4bbb8b1871ef6a222d6a0f4dd
70-
span_id : 19e3cda7df63c9b9
71-
parent_span_id: 0000000000000000
72-
start : 1617075522491536300
76+
trace_id : baa922bc0da6f79d46373371f4416463
77+
span_id : c3e7e23042eb670e
78+
tracestate :
79+
parent_span_id: 83bed4da7a01267d
80+
start : 1620287026115443300
7381
duration : 50700
7482
description :
7583
span kind : Server
7684
status : Unset
7785
attributes :
86+
http.header.Traceparent: 00-baa922bc0da6f79d46373371f4416463-83bed4da7a01267d-01
7887
http.header.Accept: */*
7988
http.request_content_length: 0
8089
http.header.Host: localhost:8800
8190
http.scheme: http
82-
http.client_ip: 127.0.0.1:44616
91+
http.client_ip: 127.0.0.1:50792
8392
http.method: GET
8493
net.host.port: 8800
8594
http.server_name: localhost
95+
events :
96+
{
97+
name : Processing request
98+
timestamp : 1620287026115464000
99+
attributes :
100+
}
101+
links :
102+
86103
}
87104
```
105+
106+
As seen from example above, `trace_id` and `parent_span_id` is propagated from client to server.

examples/http/client.cc

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include "opentelemetry/ext/http/client/http_client_factory.h"
22
#include "opentelemetry/ext/http/common/url_parser.h"
3-
#include "tracer_common.hpp"
3+
#include "tracer_common.h"
44

55
namespace
66
{
@@ -23,7 +23,14 @@ void sendRequest(const std::string &url)
2323
options);
2424
auto scope = get_tracer("http-client")->WithActiveSpan(span);
2525

26-
opentelemetry::ext::http::client::Result result = http_client->Get(url);
26+
// inject current context into http header
27+
auto current_ctx = opentelemetry::context::RuntimeContext::GetCurrent();
28+
HttpTextMapCarrier<opentelemetry::ext::http::client::Headers> carrier;
29+
auto prop = opentelemetry::context::propagation::GlobalTextMapPropagator::GetGlobalPropagator();
30+
prop->Inject(carrier, current_ctx);
31+
32+
// send http request
33+
opentelemetry::ext::http::client::Result result = http_client->Get(url, carrier.headers_);
2734
if (result)
2835
{
2936
// set span attributes

examples/http/server.cc

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
#include "server.hpp"
2-
#include "tracer_common.hpp"
1+
#include "server.h"
2+
#include "tracer_common.h"
33

44
#include <iostream>
55
#include <thread>
@@ -19,6 +19,15 @@ class RequestHandler : public HTTP_SERVER_NS::HttpRequestCallback
1919
options.kind = opentelemetry::trace::SpanKind::kServer; // server
2020
std::string span_name = request.uri;
2121

22+
// extract context from http header
23+
const HttpTextMapCarrier<std::map<std::string, std::string>> carrier(
24+
(std::map<std::string, std::string> &)request.headers);
25+
auto prop = opentelemetry::context::propagation::GlobalTextMapPropagator::GetGlobalPropagator();
26+
auto current_ctx = opentelemetry::context::RuntimeContext::GetCurrent();
27+
auto new_context = prop->Extract(carrier, current_ctx);
28+
options.parent = GetSpanFromContext(new_context)->GetContext();
29+
30+
// start span with parent context extracted from http header
2231
auto span = get_tracer("http-server")
2332
->StartSpan(span_name,
2433
{{"http.server_name", server_name},
@@ -30,6 +39,7 @@ class RequestHandler : public HTTP_SERVER_NS::HttpRequestCallback
3039
options);
3140

3241
auto scope = get_tracer("http_server")->WithActiveSpan(span);
42+
3343
for (auto &kv : request.headers)
3444
{
3545
span->SetAttribute("http.header." + std::string(kv.first.data()), kv.second);

examples/http/server.h

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#pragma once
2+
#include <atomic>
3+
#include <string>
4+
#include "opentelemetry/ext/http/server/http_server.h"
5+
6+
namespace
7+
{
8+
9+
class HttpServer : public HTTP_SERVER_NS::HttpRequestCallback
10+
{
11+
12+
protected:
13+
HTTP_SERVER_NS::HttpServer server_;
14+
std::string server_url_;
15+
uint16_t port_;
16+
std::atomic<bool> is_running_{false};
17+
18+
public:
19+
HttpServer(std::string server_name = "test_server", uint16_t port = 8800) : port_(port)
20+
{
21+
server_.setServerName(server_name);
22+
server_.setKeepalive(false);
23+
}
24+
25+
void AddHandler(std::string path, HTTP_SERVER_NS::HttpRequestCallback *request_handler)
26+
{
27+
server_.addHandler(path, *request_handler);
28+
}
29+
30+
void Start()
31+
{
32+
if (!is_running_.exchange(true))
33+
{
34+
server_.addListeningPort(port_);
35+
server_.start();
36+
}
37+
}
38+
39+
void Stop()
40+
{
41+
if (is_running_.exchange(false))
42+
{
43+
server_.stop();
44+
}
45+
}
46+
47+
~HttpServer() { Stop(); }
48+
};
49+
50+
} // namespace

examples/http/server.hpp

-47
This file was deleted.

examples/http/tracer_common.h

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#pragma once
2+
#include "opentelemetry/exporters/ostream/span_exporter.h"
3+
#include "opentelemetry/sdk/trace/simple_processor.h"
4+
#include "opentelemetry/sdk/trace/tracer_provider.h"
5+
#include "opentelemetry/trace/provider.h"
6+
7+
#include "opentelemetry/context/propagation/global_propagator.h"
8+
#include "opentelemetry/context/propagation/text_map_propagator.h"
9+
#include "opentelemetry/trace/propagation/http_trace_context.h"
10+
11+
#include <cstring>
12+
#include <iostream>
13+
#include <vector>
14+
#include "opentelemetry/ext/http/client/http_client.h"
15+
#include "opentelemetry/nostd/shared_ptr.h"
16+
17+
namespace
18+
{
19+
// TBD - This function be removed once #723 is merged
20+
inline nostd::shared_ptr<opentelemetry::trace::Span> GetSpanFromContext(
21+
const opentelemetry::context::Context &context)
22+
{
23+
opentelemetry::context::ContextValue span = context.GetValue(opentelemetry::trace::kSpanKey);
24+
if (nostd::holds_alternative<nostd::shared_ptr<opentelemetry::trace::Span>>(span))
25+
{
26+
return nostd::get<nostd::shared_ptr<opentelemetry::trace::Span>>(span);
27+
}
28+
static nostd::shared_ptr<opentelemetry::trace::Span> invalid_span{
29+
new opentelemetry::trace::DefaultSpan(opentelemetry::trace::SpanContext::GetInvalid())};
30+
return invalid_span;
31+
}
32+
33+
template <typename T>
34+
class HttpTextMapCarrier : public opentelemetry::context::propagation::TextMapCarrier
35+
{
36+
public:
37+
HttpTextMapCarrier<T>(T &headers) : headers_(headers) {}
38+
HttpTextMapCarrier() = default;
39+
virtual nostd::string_view Get(nostd::string_view key) const noexcept override
40+
{
41+
std::string key_to_compare = key.data();
42+
// Header's first letter seems to be automatically capitaliazed by our test http-server, so
43+
// compare accordingly.
44+
if (key == opentelemetry::trace::propagation::kTraceParent)
45+
{
46+
key_to_compare = "Traceparent";
47+
}
48+
else if (key == opentelemetry::trace::propagation::kTraceState)
49+
{
50+
key_to_compare == "Tracestate";
51+
}
52+
auto it = headers_.find(key_to_compare);
53+
if (it != headers_.end())
54+
{
55+
return it->second;
56+
}
57+
return "";
58+
}
59+
60+
virtual void Set(nostd::string_view key, nostd::string_view value) noexcept override
61+
{
62+
headers_.insert(std::pair<std::string, std::string>(std::string(key), std::string(value)));
63+
}
64+
65+
T headers_;
66+
};
67+
68+
void initTracer()
69+
{
70+
auto exporter = std::unique_ptr<sdktrace::SpanExporter>(
71+
new opentelemetry::exporter::trace::OStreamSpanExporter);
72+
auto processor = std::unique_ptr<sdktrace::SpanProcessor>(
73+
new sdktrace::SimpleSpanProcessor(std::move(exporter)));
74+
std::vector<std::unique_ptr<sdktrace::SpanProcessor>> processors;
75+
processors.push_back(std::move(processor));
76+
// Default is an always-on sampler.
77+
auto context = std::make_shared<sdktrace::TracerContext>(std::move(processors));
78+
auto provider = nostd::shared_ptr<opentelemetry::trace::TracerProvider>(
79+
new sdktrace::TracerProvider(context));
80+
// Set the global trace provider
81+
opentelemetry::trace::Provider::SetTracerProvider(provider);
82+
83+
// set global propagator
84+
opentelemetry::context::propagation::GlobalTextMapPropagator::SetGlobalPropagator(
85+
nostd::shared_ptr<opentelemetry::context::propagation::TextMapPropagator>(
86+
new opentelemetry::trace::propagation::HttpTraceContext()));
87+
}
88+
89+
nostd::shared_ptr<opentelemetry::trace::Tracer> get_tracer(std::string tracer_name)
90+
{
91+
auto provider = opentelemetry::trace::Provider::GetTracerProvider();
92+
return provider->GetTracer(tracer_name);
93+
}
94+
95+
} // namespace

examples/http/tracer_common.hpp

-32
This file was deleted.

0 commit comments

Comments
 (0)