Add http client/server example (#632)
This commit is contained in:
parent
63372257f9
commit
179a7f4047
|
@ -6,3 +6,4 @@ add_subdirectory(simple)
|
||||||
add_subdirectory(batch)
|
add_subdirectory(batch)
|
||||||
add_subdirectory(metrics_simple)
|
add_subdirectory(metrics_simple)
|
||||||
add_subdirectory(multithreaded)
|
add_subdirectory(multithreaded)
|
||||||
|
add_subdirectory(http)
|
||||||
|
|
|
@ -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()
|
|
@ -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
|
||||||
|
}
|
||||||
|
```
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -107,15 +107,12 @@ private:
|
||||||
void printAttributes(std::unordered_map<std::string, sdkcommon::OwnedAttributeValue> map)
|
void printAttributes(std::unordered_map<std::string, sdkcommon::OwnedAttributeValue> map)
|
||||||
{
|
{
|
||||||
size_t size = map.size();
|
size_t size = map.size();
|
||||||
size_t i = 1;
|
// size_t i = 1;
|
||||||
for (auto kv : map)
|
for (auto kv : map)
|
||||||
{
|
{
|
||||||
sout_ << kv.first << ": ";
|
sout_ << "\t" << kv.first << ": ";
|
||||||
print_value(kv.second);
|
print_value(kv.second);
|
||||||
|
sout_ << std::endl;
|
||||||
if (i != size)
|
|
||||||
sout_ << ", ";
|
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -69,10 +69,10 @@ sdktrace::ExportResult OStreamSpanExporter::Export(
|
||||||
<< "\n duration : " << span->GetDuration().count()
|
<< "\n duration : " << span->GetDuration().count()
|
||||||
<< "\n description : " << span->GetDescription()
|
<< "\n description : " << span->GetDescription()
|
||||||
<< "\n span kind : " << span->GetSpanKind()
|
<< "\n span kind : " << span->GetSpanKind()
|
||||||
<< "\n status : " << statusMap[int(span->GetStatus())]
|
<< "\n status : " << statusMap[int(span->GetStatus())] << "\n attributes : "
|
||||||
<< "\n attributes : ";
|
<< "\n";
|
||||||
printAttributes(span->GetAttributes());
|
printAttributes(span->GetAttributes());
|
||||||
sout_ << "\n}\n";
|
sout_ << "}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -145,7 +145,8 @@ TEST(OStreamSpanExporter, PrintChangedSpanCout)
|
||||||
" description : Test Description\n"
|
" description : Test Description\n"
|
||||||
" span kind : Client\n"
|
" span kind : Client\n"
|
||||||
" status : Ok\n"
|
" status : Ok\n"
|
||||||
" attributes : attr1: string\n"
|
" attributes : \n"
|
||||||
|
"\tattr1: string\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
ASSERT_EQ(stdoutOutput.str(), expectedOutput);
|
ASSERT_EQ(stdoutOutput.str(), expectedOutput);
|
||||||
}
|
}
|
||||||
|
@ -208,7 +209,8 @@ TEST(OStreamSpanExporter, PrintChangedSpanCerr)
|
||||||
" description : Test Description\n"
|
" description : Test Description\n"
|
||||||
" span kind : Consumer\n"
|
" span kind : Consumer\n"
|
||||||
" status : Ok\n"
|
" status : Ok\n"
|
||||||
" attributes : attr1: [0,1,0]\n"
|
" attributes : \n"
|
||||||
|
"\tattr1: [0,1,0]\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
ASSERT_EQ(stdcerrOutput.str(), expectedOutput);
|
ASSERT_EQ(stdcerrOutput.str(), expectedOutput);
|
||||||
}
|
}
|
||||||
|
@ -270,7 +272,8 @@ TEST(OStreamSpanExporter, PrintChangedSpanClog)
|
||||||
" description : Test Description\n"
|
" description : Test Description\n"
|
||||||
" span kind : Internal\n"
|
" span kind : Internal\n"
|
||||||
" status : Ok\n"
|
" status : Ok\n"
|
||||||
" attributes : attr1: [1,2,3]\n"
|
" attributes : \n"
|
||||||
|
"\tattr1: [1,2,3]\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
ASSERT_EQ(stdclogOutput.str(), expectedOutput);
|
ASSERT_EQ(stdclogOutput.str(), expectedOutput);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
Loading…
Reference in New Issue