Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add http client/server example #632

Merged
merged 17 commits into from
Mar 30, 2021
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(
lalitb marked this conversation as resolved.
Show resolved Hide resolved
http_server ${CMAKE_THREAD_LIBS_INIT} ${CORE_RUNTIME_LIBS}
opentelemetry_trace http_client_curl opentelemetry_exporter_ostream_span)
endif()
86 changes: 86 additions & 0 deletions examples/http/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# 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 : sentRequest
trace_id : a70a75ade26d2c1fed6cfd5116f4a3db
span_id : ed870ae26eb1b78c
parent_span_id: 0000000000000000
start : 1616754223528238500
duration : 1848300
description :
span kind : Client
status : Unset
attributes :
http.session_state: 8
http.header.Date: Fri, 26 Mar 2021 10:23:43 GMT
http.header.Content-Length: 0
http.status_code: 200
http.header.Host: localhost
http.header.Content-Type: text/plain
http.header.Connection: keep-alive
http.url: http://localhost:8800/helloworld
http.method: GET
component: http
}

```

* Server console

```console
{
name : handle_request
trace_id : f92ff8f5f6553ead210f2643779e1587
span_id : 195fae1770f514ce
parent_span_id: 0000000000000000
lalitb marked this conversation as resolved.
Show resolved Hide resolved
start : 1616754460952018400
duration : 20600
description :
span kind : Server
status : Unset
attributes :
http.req.headerHost: localhost:8800
http.req.headerAccept: */*
component: http
req_method: GET
req_url: /helloworld
lalitb marked this conversation as resolved.
Show resolved Hide resolved
}
```
61 changes: 61 additions & 0 deletions examples/http/client.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#include "opentelemetry/ext/http/client/http_client_factory.h"
#include "tracer_common.hpp"

namespace
{

void sendRequest(std::string url)
lalitb marked this conversation as resolved.
Show resolved Hide resolved
{
auto http_client = opentelemetry::ext::http::client::HttpClientFactory::CreateSync();

// start active span
opentelemetry::trace::StartSpanOptions options;
options.kind = opentelemetry::trace::SpanKind::kClient; // server
lalitb marked this conversation as resolved.
Show resolved Hide resolved
auto span =
get_tracer("http-client")
->StartSpan("sentRequest", {{"component", "http"}, {"http.method", "GET"}}, options);
auto scope = get_tracer("http-client")->WithActiveSpan(span);

opentelemetry::ext::http::client::Result result = http_client->Get(url);
span->SetAttribute("http.url", url);
if (result)
{
// set span attributes
span->SetAttribute("http.status_code", result.GetResponse().GetStatusCode());
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;
});
}
span->SetAttribute(
"http.session_state",
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);
}
71 changes: 71 additions & 0 deletions examples/http/server.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include "server.hpp"
#include "tracer_common.hpp"

#include <iostream>
#include <thread>

namespace
{
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

auto span =
get_tracer("http-server")
->StartSpan(
"handle_request",
{{"req_url", request.uri}, {"req_method", request.method}, {"component", "http"}},
options);
auto scope = get_tracer("http_server")->WithActiveSpan(span);
for (auto &kv : request.headers)
{
span->SetAttribute("http.req.header" + std::string(kv.first.data()), kv.second);
}
if (request.uri == "/helloworld")
{
span->AddEvent("Processing request");
response.headers["Content-Type"] = "text/plain";
lalitb marked this conversation as resolved.
Show resolved Hide resolved
span->End();
return 200;
}
span->End();
return 404;
}
};
} // namespace

int main(int argc, char *argv[])
{
initTracer();
constexpr char default_host[] = "localhost";
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;
}

HttpServer http_server(default_host, port);
RequestHandler req_handler;
http_server.AddHandler("/helloworld", &req_handler);
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();
return 0;
}
49 changes: 49 additions & 0 deletions examples/http/server.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#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_) {
server_.addListeningPort(port_);
lalitb marked this conversation as resolved.
Show resolved Hide resolved
server_.start();
is_running_ = true;
lalitb marked this conversation as resolved.
Show resolved Hide resolved
}
}

void Stop() {
if (is_running_){
lalitb marked this conversation as resolved.
Show resolved Hide resolved
server_.stop();
is_running_ = false;
}
}

~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 @@ -110,11 +110,15 @@ class OStreamSpanExporter final : public sdktrace::SpanExporter
size_t i = 1;
for (auto kv : map)
{
sout_ << kv.first << ": ";
if (i == 1)
lalitb marked this conversation as resolved.
Show resolved Hide resolved
{
sout_ << std::endl;
}
lalitb marked this conversation as resolved.
Show resolved Hide resolved
sout_ << "\t" << kv.first << ": ";
print_value(kv.second);

if (i != size)
sout_ << ", ";
sout_ << std::endl;
i++;
}
}
Expand Down
6 changes: 3 additions & 3 deletions exporters/ostream/test/ostream_span_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ TEST(OStreamSpanExporter, PrintChangedSpanCout)
" description : Test Description\n"
" span kind : Client\n"
" status : Ok\n"
" attributes : attr1: string\n"
" attributes : \n\tattr1: string\n"
lalitb marked this conversation as resolved.
Show resolved Hide resolved
"}\n";
ASSERT_EQ(stdoutOutput.str(), expectedOutput);
}
Expand Down Expand Up @@ -208,7 +208,7 @@ TEST(OStreamSpanExporter, PrintChangedSpanCerr)
" description : Test Description\n"
" span kind : Consumer\n"
" status : Ok\n"
" attributes : attr1: [0,1,0]\n"
" attributes : \n\tattr1: [0,1,0]\n"
"}\n";
ASSERT_EQ(stdcerrOutput.str(), expectedOutput);
}
Expand Down Expand Up @@ -270,7 +270,7 @@ TEST(OStreamSpanExporter, PrintChangedSpanClog)
" description : Test Description\n"
" span kind : Internal\n"
" status : Ok\n"
" attributes : attr1: [1,2,3]\n"
" attributes : \n\tattr1: [1,2,3]\n"
"}\n";
ASSERT_EQ(stdclogOutput.str(), expectedOutput);
}
1 change: 1 addition & 0 deletions ext/include/opentelemetry/ext/http/server/socket_tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#pragma once

#include <algorithm>
#include <atomic>
lalitb marked this conversation as resolved.
Show resolved Hide resolved
#include <cassert>
#include <cstddef>
#include <cstring>
Expand Down