Skip to content

Commit 179a7f4

Browse files
authored
Add http client/server example (#632)
1 parent 6337225 commit 179a7f4

File tree

11 files changed

+354
-12
lines changed

11 files changed

+354
-12
lines changed

examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ add_subdirectory(simple)
66
add_subdirectory(batch)
77
add_subdirectory(metrics_simple)
88
add_subdirectory(multithreaded)
9+
add_subdirectory(http)

examples/http/CMakeLists.txt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
find_package(CURL)
2+
3+
if(NOT CURL_FOUND)
4+
message(WARNING "Skipping http client/server example build: CURL not found")
5+
else()
6+
include_directories(${CMAKE_SOURCE_DIR}/exporters/ostream/include
7+
${CMAKE_SOURCE_DIR}/ext/include ${CMAKE_SOURCE_DIR/})
8+
9+
add_executable(http_client client.cc)
10+
add_executable(http_server server.cc)
11+
12+
target_link_libraries(
13+
http_client
14+
${CMAKE_THREAD_LIBS_INIT}
15+
${CORE_RUNTIME_LIBS}
16+
opentelemetry_trace
17+
http_client_curl
18+
opentelemetry_exporter_ostream_span
19+
${CURL_LIBRARIES})
20+
21+
target_link_libraries(
22+
http_server ${CMAKE_THREAD_LIBS_INIT} ${CORE_RUNTIME_LIBS}
23+
opentelemetry_trace http_client_curl opentelemetry_exporter_ostream_span)
24+
endif()

examples/http/README.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# OpenTelemetry C++ Example
2+
3+
## HTTP
4+
5+
This is a simple example that demonstrates tracing an HTTP request from client to server. The example shows several aspects of tracing such as:
6+
7+
* Using the `TracerProvider`
8+
* Span Attributes
9+
* Span Events
10+
* Using the ostream exporter
11+
* Nested spans (TBD)
12+
* W3c Trace Context Propagation (TBD)
13+
14+
### Running the example
15+
16+
1. The example uses HTTP server and client provided as part of this repo:
17+
* [HTTP Client](https://github.com/open-telemetry/opentelemetry-cpp/tree/main/ext/include/opentelemetry/ext/http/client)
18+
* [HTTP Server](https://github.com/open-telemetry/opentelemetry-cpp/tree/main/ext/include/opentelemetry/ext/http/server)
19+
20+
2. Build and Deploy the opentelementry-cpp as described in [INSTALL.md](../../INSTALL.md)
21+
22+
3. Start the server from the `examples/http` directory
23+
24+
```console
25+
$ http_server 8800
26+
Server is running..Press ctrl-c to exit...
27+
```
28+
29+
4. In a separate terminal window, run the client to make a single request:
30+
31+
```console
32+
$ ./http_client 8800
33+
...
34+
...
35+
```
36+
37+
5. You should see console exporter output for both the client and server sessions.
38+
* Client console
39+
40+
```console
41+
{
42+
name : /helloworld
43+
trace_id : 15c7ca1993b536085f4097f2818a7be4
44+
span_id : 7d9136e4eb4cb59d
45+
parent_span_id: 0000000000000000
46+
start : 1617075613395810300
47+
duration : 1901100
48+
description :
49+
span kind : Client
50+
status : Unset
51+
attributes :
52+
http.header.Date: Tue, 30 Mar 2021 03:40:13 GMT
53+
http.header.Content-Length: 0
54+
http.status_code: 200
55+
http.method: GET
56+
http.header.Host: localhost
57+
http.header.Content-Type: text/plain
58+
http.header.Connection: keep-alive
59+
http.scheme: http
60+
http.url: h**p://localhost:8800/helloworld
61+
}
62+
```
63+
64+
* Server console
65+
66+
```console
67+
{
68+
name : /helloworld
69+
trace_id : bfa611a4bbb8b1871ef6a222d6a0f4dd
70+
span_id : 19e3cda7df63c9b9
71+
parent_span_id: 0000000000000000
72+
start : 1617075522491536300
73+
duration : 50700
74+
description :
75+
span kind : Server
76+
status : Unset
77+
attributes :
78+
http.header.Accept: */*
79+
http.request_content_length: 0
80+
http.header.Host: localhost:8800
81+
http.scheme: http
82+
http.client_ip: 127.0.0.1:44616
83+
http.method: GET
84+
net.host.port: 8800
85+
http.server_name: localhost
86+
}
87+
```

examples/http/client.cc

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#include "opentelemetry/ext/http/client/http_client_factory.h"
2+
#include "opentelemetry/ext/http/common/url_parser.h"
3+
#include "tracer_common.hpp"
4+
5+
namespace
6+
{
7+
8+
void sendRequest(const std::string &url)
9+
{
10+
auto http_client = opentelemetry::ext::http::client::HttpClientFactory::CreateSync();
11+
12+
// start active span
13+
opentelemetry::trace::StartSpanOptions options;
14+
options.kind = opentelemetry::trace::SpanKind::kClient; // client
15+
opentelemetry::ext::http::common::UrlParser url_parser(url);
16+
17+
std::string span_name = url_parser.path_;
18+
auto span = get_tracer("http-client")
19+
->StartSpan(span_name,
20+
{{"http.url", url_parser.url_},
21+
{"http.scheme", url_parser.scheme_},
22+
{"http.method", "GET"}},
23+
options);
24+
auto scope = get_tracer("http-client")->WithActiveSpan(span);
25+
26+
opentelemetry::ext::http::client::Result result = http_client->Get(url);
27+
if (result)
28+
{
29+
// set span attributes
30+
auto status_code = result.GetResponse().GetStatusCode();
31+
span->SetAttribute("http.status_code", status_code);
32+
result.GetResponse().ForEachHeader([&span](opentelemetry::nostd::string_view header_name,
33+
opentelemetry::nostd::string_view header_value) {
34+
span->SetAttribute("http.header." + std::string(header_name.data()), header_value);
35+
return true;
36+
});
37+
38+
if (status_code >= 400)
39+
{
40+
span->SetStatus(opentelemetry::trace::StatusCode::kError);
41+
}
42+
}
43+
else
44+
{
45+
span->SetStatus(opentelemetry::trace::StatusCode::kError,
46+
"Response Status :" +
47+
std::to_string(static_cast<typename std::underlying_type<
48+
opentelemetry::ext::http::client::SessionState>::type>(
49+
result.GetSessionState())));
50+
}
51+
// end span and export data
52+
span->End();
53+
}
54+
55+
} // namespace
56+
57+
int main(int argc, char *argv[])
58+
{
59+
initTracer();
60+
constexpr char default_host[] = "localhost";
61+
constexpr char default_path[] = "/helloworld";
62+
constexpr uint16_t default_port = 8800;
63+
uint16_t port;
64+
65+
// The port the validation service listens to can be specified via the command line.
66+
if (argc > 1)
67+
{
68+
port = atoi(argv[1]);
69+
}
70+
else
71+
{
72+
port = default_port;
73+
}
74+
75+
std::string url = "http://" + std::string(default_host) + ":" + std::to_string(port) +
76+
std::string(default_path);
77+
sendRequest(url);
78+
}

examples/http/server.cc

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#include "server.hpp"
2+
#include "tracer_common.hpp"
3+
4+
#include <iostream>
5+
#include <thread>
6+
7+
namespace
8+
{
9+
uint16_t server_port = 8800;
10+
constexpr char server_name[] = "localhost";
11+
12+
class RequestHandler : public HTTP_SERVER_NS::HttpRequestCallback
13+
{
14+
public:
15+
virtual int onHttpRequest(HTTP_SERVER_NS::HttpRequest const &request,
16+
HTTP_SERVER_NS::HttpResponse &response) override
17+
{
18+
opentelemetry::trace::StartSpanOptions options;
19+
options.kind = opentelemetry::trace::SpanKind::kServer; // server
20+
std::string span_name = request.uri;
21+
22+
auto span = get_tracer("http-server")
23+
->StartSpan(span_name,
24+
{{"http.server_name", server_name},
25+
{"net.host.port", server_port},
26+
{"http.method", request.method},
27+
{"http.scheme", "http"},
28+
{"http.request_content_length", request.content.length()},
29+
{"http.client_ip", request.client}},
30+
options);
31+
32+
auto scope = get_tracer("http_server")->WithActiveSpan(span);
33+
for (auto &kv : request.headers)
34+
{
35+
span->SetAttribute("http.header." + std::string(kv.first.data()), kv.second);
36+
}
37+
if (request.uri == "/helloworld")
38+
{
39+
span->AddEvent("Processing request");
40+
response.headers[HTTP_SERVER_NS::CONTENT_TYPE] = HTTP_SERVER_NS::CONTENT_TYPE_TEXT;
41+
span->End();
42+
return 200;
43+
}
44+
span->End();
45+
return 404;
46+
}
47+
};
48+
} // namespace
49+
50+
int main(int argc, char *argv[])
51+
{
52+
initTracer();
53+
uint16_t port;
54+
55+
// The port the validation service listens to can be specified via the command line.
56+
if (argc > 1)
57+
{
58+
server_port = atoi(argv[1]);
59+
}
60+
61+
HttpServer http_server(server_name, server_port);
62+
RequestHandler req_handler;
63+
http_server.AddHandler("/helloworld", &req_handler);
64+
auto root_span = get_tracer("http_server")->StartSpan(__func__);
65+
opentelemetry::trace::Scope scope(root_span);
66+
http_server.Start();
67+
std::cout << "Server is running..Press ctrl-c to exit...\n";
68+
while (1)
69+
{
70+
std::this_thread::sleep_for(std::chrono::seconds(100));
71+
}
72+
http_server.Stop();
73+
root_span->End();
74+
return 0;
75+
}

examples/http/server.hpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#pragma once
2+
#include "opentelemetry/ext/http/server/http_server.h"
3+
#include<string>
4+
#include<atomic>
5+
6+
7+
namespace {
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+
20+
HttpServer(std::string server_name = "test_server",uint16_t port = 8800): port_(port){
21+
server_.setServerName(server_name);
22+
server_.setKeepalive(false);
23+
}
24+
25+
void AddHandler(std::string path, HTTP_SERVER_NS::HttpRequestCallback *request_handler){
26+
server_.addHandler(path, *request_handler);
27+
}
28+
29+
void Start() {
30+
if (!is_running_.exchange(true)) {
31+
server_.addListeningPort(port_);
32+
server_.start();
33+
}
34+
}
35+
36+
void Stop() {
37+
if (is_running_.exchange(false)){
38+
server_.stop();
39+
}
40+
}
41+
42+
~HttpServer(){
43+
Stop();
44+
}
45+
};
46+
47+
}

examples/http/tracer_common.hpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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 <iostream>
8+
9+
namespace {
10+
11+
void initTracer() {
12+
auto exporter = std::unique_ptr<sdktrace::SpanExporter>(
13+
new opentelemetry::exporter::trace::OStreamSpanExporter);
14+
auto processor = std::shared_ptr<sdktrace::SpanProcessor>(
15+
new sdktrace::SimpleSpanProcessor(std::move(exporter)));
16+
auto provider = nostd::shared_ptr<opentelemetry::trace::TracerProvider>(
17+
new sdktrace::TracerProvider(processor, opentelemetry::sdk::resource::Resource::Create({}),
18+
std::make_shared<opentelemetry::sdk::trace::AlwaysOnSampler>()));
19+
// Set the global trace provider
20+
opentelemetry::trace::Provider::SetTracerProvider(provider);
21+
}
22+
23+
nostd::shared_ptr<opentelemetry::trace::Tracer> get_tracer(std::string tracer_name)
24+
{
25+
auto provider = opentelemetry::trace::Provider::GetTracerProvider();
26+
return provider->GetTracer(tracer_name);
27+
}
28+
29+
}

exporters/ostream/include/opentelemetry/exporters/ostream/span_exporter.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,12 @@ class OStreamSpanExporter final : public sdktrace::SpanExporter
107107
void printAttributes(std::unordered_map<std::string, sdkcommon::OwnedAttributeValue> map)
108108
{
109109
size_t size = map.size();
110-
size_t i = 1;
110+
// size_t i = 1;
111111
for (auto kv : map)
112112
{
113-
sout_ << kv.first << ": ";
113+
sout_ << "\t" << kv.first << ": ";
114114
print_value(kv.second);
115-
116-
if (i != size)
117-
sout_ << ", ";
118-
i++;
115+
sout_ << std::endl;
119116
}
120117
}
121118
};

exporters/ostream/src/span_exporter.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ sdktrace::ExportResult OStreamSpanExporter::Export(
6969
<< "\n duration : " << span->GetDuration().count()
7070
<< "\n description : " << span->GetDescription()
7171
<< "\n span kind : " << span->GetSpanKind()
72-
<< "\n status : " << statusMap[int(span->GetStatus())]
73-
<< "\n attributes : ";
72+
<< "\n status : " << statusMap[int(span->GetStatus())] << "\n attributes : "
73+
<< "\n";
7474
printAttributes(span->GetAttributes());
75-
sout_ << "\n}\n";
75+
sout_ << "}\n";
7676
}
7777
}
7878

0 commit comments

Comments
 (0)