Skip to content

Commit

Permalink
Add http client/server example (#632)
Browse files Browse the repository at this point in the history
  • Loading branch information
lalitb authored Mar 30, 2021
1 parent 6337225 commit 179a7f4
Show file tree
Hide file tree
Showing 11 changed files with 354 additions and 12 deletions.
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ add_subdirectory(simple)
add_subdirectory(batch)
add_subdirectory(metrics_simple)
add_subdirectory(multithreaded)
add_subdirectory(http)
24 changes: 24 additions & 0 deletions examples/http/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
find_package(CURL)

if(NOT CURL_FOUND)
message(WARNING "Skipping http client/server example build: CURL not found")
else()
include_directories(${CMAKE_SOURCE_DIR}/exporters/ostream/include
${CMAKE_SOURCE_DIR}/ext/include ${CMAKE_SOURCE_DIR/})

add_executable(http_client client.cc)
add_executable(http_server server.cc)

target_link_libraries(
http_client
${CMAKE_THREAD_LIBS_INIT}
${CORE_RUNTIME_LIBS}
opentelemetry_trace
http_client_curl
opentelemetry_exporter_ostream_span
${CURL_LIBRARIES})

target_link_libraries(
http_server ${CMAKE_THREAD_LIBS_INIT} ${CORE_RUNTIME_LIBS}
opentelemetry_trace http_client_curl opentelemetry_exporter_ostream_span)
endif()
87 changes: 87 additions & 0 deletions examples/http/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# OpenTelemetry C++ Example

## HTTP

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`
* Span Attributes
* Span Events
* Using the ostream exporter
* Nested spans (TBD)
* W3c Trace Context Propagation (TBD)

### Running the example

1. The example uses HTTP server and client provided as part of this repo:
* [HTTP Client](https://github.com/open-telemetry/opentelemetry-cpp/tree/main/ext/include/opentelemetry/ext/http/client)
* [HTTP Server](https://github.com/open-telemetry/opentelemetry-cpp/tree/main/ext/include/opentelemetry/ext/http/server)

2. Build and Deploy the opentelementry-cpp as described in [INSTALL.md](../../INSTALL.md)

3. Start the server from the `examples/http` directory

```console
$ http_server 8800
Server is running..Press ctrl-c to exit...
```

4. In a separate terminal window, run the client to make a single request:

```console
$ ./http_client 8800
...
...
```

5. You should see console exporter output for both the client and server sessions.
* Client console

```console
{
name : /helloworld
trace_id : 15c7ca1993b536085f4097f2818a7be4
span_id : 7d9136e4eb4cb59d
parent_span_id: 0000000000000000
start : 1617075613395810300
duration : 1901100
description :
span kind : Client
status : Unset
attributes :
http.header.Date: Tue, 30 Mar 2021 03:40:13 GMT
http.header.Content-Length: 0
http.status_code: 200
http.method: GET
http.header.Host: localhost
http.header.Content-Type: text/plain
http.header.Connection: keep-alive
http.scheme: http
http.url: h**p://localhost:8800/helloworld
}
```

* Server console

```console
{
name : /helloworld
trace_id : bfa611a4bbb8b1871ef6a222d6a0f4dd
span_id : 19e3cda7df63c9b9
parent_span_id: 0000000000000000
start : 1617075522491536300
duration : 50700
description :
span kind : Server
status : Unset
attributes :
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.method: GET
net.host.port: 8800
http.server_name: localhost
}
```
78 changes: 78 additions & 0 deletions examples/http/client.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#include "opentelemetry/ext/http/client/http_client_factory.h"
#include "opentelemetry/ext/http/common/url_parser.h"
#include "tracer_common.hpp"

namespace
{

void sendRequest(const std::string &url)
{
auto http_client = opentelemetry::ext::http::client::HttpClientFactory::CreateSync();

// start active span
opentelemetry::trace::StartSpanOptions options;
options.kind = opentelemetry::trace::SpanKind::kClient; // client
opentelemetry::ext::http::common::UrlParser url_parser(url);

std::string span_name = url_parser.path_;
auto span = get_tracer("http-client")
->StartSpan(span_name,
{{"http.url", url_parser.url_},
{"http.scheme", url_parser.scheme_},
{"http.method", "GET"}},
options);
auto scope = get_tracer("http-client")->WithActiveSpan(span);

opentelemetry::ext::http::client::Result result = http_client->Get(url);
if (result)
{
// set span attributes
auto status_code = result.GetResponse().GetStatusCode();
span->SetAttribute("http.status_code", status_code);
result.GetResponse().ForEachHeader([&span](opentelemetry::nostd::string_view header_name,
opentelemetry::nostd::string_view header_value) {
span->SetAttribute("http.header." + std::string(header_name.data()), header_value);
return true;
});

if (status_code >= 400)
{
span->SetStatus(opentelemetry::trace::StatusCode::kError);
}
}
else
{
span->SetStatus(opentelemetry::trace::StatusCode::kError,
"Response Status :" +
std::to_string(static_cast<typename std::underlying_type<
opentelemetry::ext::http::client::SessionState>::type>(
result.GetSessionState())));
}
// end span and export data
span->End();
}

} // namespace

int main(int argc, char *argv[])
{
initTracer();
constexpr char default_host[] = "localhost";
constexpr char default_path[] = "/helloworld";
constexpr uint16_t default_port = 8800;
uint16_t port;

// The port the validation service listens to can be specified via the command line.
if (argc > 1)
{
port = atoi(argv[1]);
}
else
{
port = default_port;
}

std::string url = "http://" + std::string(default_host) + ":" + std::to_string(port) +
std::string(default_path);
sendRequest(url);
}
75 changes: 75 additions & 0 deletions examples/http/server.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#include "server.hpp"
#include "tracer_common.hpp"

#include <iostream>
#include <thread>

namespace
{
uint16_t server_port = 8800;
constexpr char server_name[] = "localhost";

class RequestHandler : public HTTP_SERVER_NS::HttpRequestCallback
{
public:
virtual int onHttpRequest(HTTP_SERVER_NS::HttpRequest const &request,
HTTP_SERVER_NS::HttpResponse &response) override
{
opentelemetry::trace::StartSpanOptions options;
options.kind = opentelemetry::trace::SpanKind::kServer; // server
std::string span_name = request.uri;

auto span = get_tracer("http-server")
->StartSpan(span_name,
{{"http.server_name", server_name},
{"net.host.port", server_port},
{"http.method", request.method},
{"http.scheme", "http"},
{"http.request_content_length", request.content.length()},
{"http.client_ip", request.client}},
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);
}
if (request.uri == "/helloworld")
{
span->AddEvent("Processing request");
response.headers[HTTP_SERVER_NS::CONTENT_TYPE] = HTTP_SERVER_NS::CONTENT_TYPE_TEXT;
span->End();
return 200;
}
span->End();
return 404;
}
};
} // namespace

int main(int argc, char *argv[])
{
initTracer();
uint16_t port;

// The port the validation service listens to can be specified via the command line.
if (argc > 1)
{
server_port = atoi(argv[1]);
}

HttpServer http_server(server_name, server_port);
RequestHandler req_handler;
http_server.AddHandler("/helloworld", &req_handler);
auto root_span = get_tracer("http_server")->StartSpan(__func__);
opentelemetry::trace::Scope scope(root_span);
http_server.Start();
std::cout << "Server is running..Press ctrl-c to exit...\n";
while (1)
{
std::this_thread::sleep_for(std::chrono::seconds(100));
}
http_server.Stop();
root_span->End();
return 0;
}
47 changes: 47 additions & 0 deletions examples/http/server.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#pragma once
#include "opentelemetry/ext/http/server/http_server.h"
#include<string>
#include<atomic>


namespace {

class HttpServer : public HTTP_SERVER_NS::HttpRequestCallback
{

protected:
HTTP_SERVER_NS::HttpServer server_;
std::string server_url_ ;
uint16_t port_ ;
std::atomic<bool> 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();
}
};

}
29 changes: 29 additions & 0 deletions examples/http/tracer_common.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#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 <iostream>

namespace {

void initTracer() {
auto exporter = std::unique_ptr<sdktrace::SpanExporter>(
new opentelemetry::exporter::trace::OStreamSpanExporter);
auto processor = std::shared_ptr<sdktrace::SpanProcessor>(
new sdktrace::SimpleSpanProcessor(std::move(exporter)));
auto provider = nostd::shared_ptr<opentelemetry::trace::TracerProvider>(
new sdktrace::TracerProvider(processor, opentelemetry::sdk::resource::Resource::Create({}),
std::make_shared<opentelemetry::sdk::trace::AlwaysOnSampler>()));
// Set the global trace provider
opentelemetry::trace::Provider::SetTracerProvider(provider);
}

nostd::shared_ptr<opentelemetry::trace::Tracer> get_tracer(std::string tracer_name)
{
auto provider = opentelemetry::trace::Provider::GetTracerProvider();
return provider->GetTracer(tracer_name);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,12 @@ class OStreamSpanExporter final : public sdktrace::SpanExporter
void printAttributes(std::unordered_map<std::string, sdkcommon::OwnedAttributeValue> map)
{
size_t size = map.size();
size_t i = 1;
// size_t i = 1;
for (auto kv : map)
{
sout_ << kv.first << ": ";
sout_ << "\t" << kv.first << ": ";
print_value(kv.second);

if (i != size)
sout_ << ", ";
i++;
sout_ << std::endl;
}
}
};
Expand Down
6 changes: 3 additions & 3 deletions exporters/ostream/src/span_exporter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ sdktrace::ExportResult OStreamSpanExporter::Export(
<< "\n duration : " << span->GetDuration().count()
<< "\n description : " << span->GetDescription()
<< "\n span kind : " << span->GetSpanKind()
<< "\n status : " << statusMap[int(span->GetStatus())]
<< "\n attributes : ";
<< "\n status : " << statusMap[int(span->GetStatus())] << "\n attributes : "
<< "\n";
printAttributes(span->GetAttributes());
sout_ << "\n}\n";
sout_ << "}\n";
}
}

Expand Down
Loading

0 comments on commit 179a7f4

Please sign in to comment.