Add http client/server example (#632)

This commit is contained in:
Lalit Kumar Bhasin 2021-03-30 16:10:47 +05:30 committed by GitHub
parent 63372257f9
commit 179a7f4047
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 354 additions and 12 deletions

View File

@ -6,3 +6,4 @@ add_subdirectory(simple)
add_subdirectory(batch)
add_subdirectory(metrics_simple)
add_subdirectory(multithreaded)
add_subdirectory(http)

View File

@ -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
examples/http/README.md Normal file
View File

@ -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
examples/http/client.cc Normal file
View File

@ -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
examples/http/server.cc Normal file
View File

@ -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
examples/http/server.hpp Normal file
View File

@ -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();
}
};
}

View File

@ -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);
}
}

View File

@ -107,15 +107,12 @@ private:
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;
}
}
};

View File

@ -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";
}
}

View File

@ -145,7 +145,8 @@ TEST(OStreamSpanExporter, PrintChangedSpanCout)
" description : Test Description\n"
" span kind : Client\n"
" status : Ok\n"
" attributes : attr1: string\n"
" attributes : \n"
"\tattr1: string\n"
"}\n";
ASSERT_EQ(stdoutOutput.str(), expectedOutput);
}
@ -208,7 +209,8 @@ 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);
}
@ -270,7 +272,8 @@ 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);
}

View File

@ -14,6 +14,7 @@
#pragma once
#include <algorithm>
#include <atomic>
#include <cassert>
#include <cstddef>
#include <cstring>