diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_exporter.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_exporter.h index 55322589f9..7aa73f9290 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_exporter.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_exporter.h @@ -52,7 +52,6 @@ struct OtlpHttpExporterOptions // By default, post json data HttpRequestContentType content_type = HttpRequestContentType::kJson; - // TODO: By default when false, set CURLOPT_SSL_VERIFYPEER to false // If convert bytes into hex. By default, we will convert all bytes but id into base64 // This option is ignored if content_type is not kJson JsonBytesMappingKind json_bytes_mapping = JsonBytesMappingKind::kHexId; @@ -64,8 +63,8 @@ struct OtlpHttpExporterOptions // Whether to print the status of the exporter in the console bool console_debug = false; - // Maximum time to wait for response after sending http request(milliseconds) - int response_timeout = 30000; + // TODO: Enable/disable to verify SSL certificate + // TODO: Reuqest timeout }; /** diff --git a/exporters/otlp/test/otlp_http_exporter_test.cc b/exporters/otlp/test/otlp_http_exporter_test.cc index 375abf7794..fe74829a81 100644 --- a/exporters/otlp/test/otlp_http_exporter_test.cc +++ b/exporters/otlp/test/otlp_http_exporter_test.cc @@ -12,7 +12,7 @@ # include "opentelemetry/exporters/otlp/protobuf_include_suffix.h" # include "opentelemetry/ext/http/server/http_server.h" -# include "opentelemetry/sdk/trace/simple_processor.h" +# include "opentelemetry/sdk/trace/batch_span_processor.h" # include "opentelemetry/sdk/trace/tracer_provider.h" # include "opentelemetry/trace/provider.h" @@ -22,14 +22,18 @@ using namespace testing; -namespace http_client = opentelemetry::ext::http::client; - OPENTELEMETRY_BEGIN_NAMESPACE namespace exporter { namespace otlp { +template +static nostd::span MakeSpan(T (&array)[N]) +{ + return nostd::span(array); +} + class OtlpHttpExporterTestPeer : public ::testing::Test, public HTTP_SERVER_NS::HttpRequestCallback { protected: @@ -74,14 +78,24 @@ class OtlpHttpExporterTestPeer : public ::testing::Test, public HTTP_SERVER_NS:: virtual int onHttpRequest(HTTP_SERVER_NS::HttpRequest const &request, HTTP_SERVER_NS::HttpResponse &response) override { + const std::string *request_content_type = nullptr; + { + auto it = request.headers.find("Content-Type"); + if (it != request.headers.end()) + { + request_content_type = &it->second; + } + } + if (request.uri == kDefaultTracePath) { response.headers["Content-Type"] = "application/json"; std::unique_lock lk(mtx_requests); - if (request.headers["Content-Type"] == kHttpBinaryContentType) + if (nullptr != request_content_type && *request_content_type == kHttpBinaryContentType) { opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest request_body; - if (request_body.ParseFromArray(&request.content[0], request.content.size())) + if (request_body.ParseFromArray(&request.content[0], + static_cast(request.content.size()))) { received_requests_binary_.push_back(request_body); response.body = "{\"code\": 0, \"message\": \"success\"}"; @@ -92,7 +106,7 @@ class OtlpHttpExporterTestPeer : public ::testing::Test, public HTTP_SERVER_NS:: return 400; } } - else if (request.headers["Content-Type"] == kHttpJsonContentType) + else if (nullptr != request_content_type && *request_content_type == kHttpJsonContentType) { auto json = nlohmann::json::parse(request.content, nullptr, false); response.headers["Content-Type"] = "application/json"; @@ -124,25 +138,29 @@ class OtlpHttpExporterTestPeer : public ::testing::Test, public HTTP_SERVER_NS:: } } - bool waitForRequests(unsigned timeOutSec, unsigned expected_count = 1) + bool waitForRequests(unsigned timeOutSec, size_t expected_count = 1) { std::unique_lock lk(mtx_requests); - if (cv_got_events.wait_for(lk, std::chrono::milliseconds(1000 * timeOutSec), [&] { - return received_requests_json_.size() + received_requests_binary_.size() >= - expected_count; - })) + if (cv_got_events.wait_for(lk, std::chrono::milliseconds(1000 * timeOutSec), + [&] { return getCurrentRequestCount() >= expected_count; })) { return true; } return false; } + size_t getCurrentRequestCount() const + { + return received_requests_json_.size() + received_requests_binary_.size(); + } + public: std::unique_ptr GetExporter(HttpRequestContentType content_type) { OtlpHttpExporterOptions opts; - opts.url = server_address_; - opts.content_type = content_type; + opts.url = server_address_; + opts.content_type = content_type; + opts.console_debug = true; return std::unique_ptr(new OtlpHttpExporter(opts)); } @@ -153,38 +171,14 @@ class OtlpHttpExporterTestPeer : public ::testing::Test, public HTTP_SERVER_NS:: } }; -// Call Export() directly -TEST_F(OtlpHttpExporterTestPeer, ExportUnitTest) -{ - auto exporter = GetExporter(HttpRequestContentType::kJson); - - auto recordable_1 = exporter->MakeRecordable(); - recordable_1->SetName("Test span 1"); - auto recordable_2 = exporter->MakeRecordable(); - recordable_2->SetName("Test span 2"); - - // Test successful RPC - nostd::span> batch_1(&recordable_1, 1); - EXPECT_CALL(*mock_stub, Export(_, _, _)).Times(Exactly(1)).WillOnce(Return(grpc::Status::OK)); - auto result = exporter->Export(batch_1); - EXPECT_EQ(sdk::common::ExportResult::kSuccess, result); - - // Test failed RPC - nostd::span> batch_2(&recordable_2, 1); - EXPECT_CALL(*mock_stub, Export(_, _, _)) - .Times(Exactly(1)) - .WillOnce(Return(grpc::Status::CANCELLED)); - result = exporter->Export(batch_2); - EXPECT_EQ(sdk::common::ExportResult::kFailure, result); -} - // Create spans, let processor call Export() TEST_F(OtlpHttpExporterTestPeer, ExportJsonIntegrationTest) { - auto exporter = GetExporter(HttpRequestContentType::kJson); + size_t old_count = getCurrentRequestCount(); + auto exporter = GetExporter(HttpRequestContentType::kJson); opentelemetry::sdk::resource::ResourceAttributes resource_attributes = { - {"service.name", 'unit_test_service'}, {"tenant.id", 'test_user'}}; + {"service.name", "unit_test_service"}, {"tenant.id", "test_user"}}; resource_attributes["bool_value"] = true; resource_attributes["int32_value"] = static_cast(1); resource_attributes["uint32_value"] = static_cast(2); @@ -200,29 +194,47 @@ TEST_F(OtlpHttpExporterTestPeer, ExportJsonIntegrationTest) resource_attributes["vec_string_value"] = std::vector{"vector", "string"}; auto resource = opentelemetry::sdk::resource::Resource::Create(resource_attributes); - auto processor = std::unique_ptr( - new sdk::trace::SimpleSpanProcessor(std::move(exporter))); - auto provider = nostd::shared_ptr( + auto processor = std::unique_ptr(new sdk::trace::BatchSpanProcessor( + std::move(exporter), + sdk::trace::BatchSpanProcessorOptions{5, std::chrono::milliseconds(256), 5})); + auto provider = nostd::shared_ptr( new sdk::trace::TracerProvider(std::move(processor), resource)); - auto tracer = provider->GetTracer("test"); - EXPECT_CALL(*mock_stub, Export(_, _, _)) - .Times(AtLeast(1)) - .WillRepeatedly(Return(grpc::Status::OK)); + std::string report_trace_id; + { + char trace_id_hex[2 * opentelemetry::trace::TraceId::kSize] = {0}; + auto tracer = provider->GetTracer("test"); + auto parent_span = tracer->StartSpan("Test parent span"); + + opentelemetry::trace::StartSpanOptions child_span_opts = {}; + child_span_opts.parent = parent_span->GetContext(); + + auto child_span = tracer->StartSpan("Test child span", child_span_opts); + child_span->End(); + parent_span->End(); - auto parent_span = tracer->StartSpan("Test parent span"); - auto child_span = tracer->StartSpan("Test child span"); - child_span->End(); - parent_span->End(); + child_span_opts.parent.trace_id().ToLowerBase16(MakeSpan(trace_id_hex)); + report_trace_id.assign(trace_id_hex, sizeof(trace_id_hex)); + } + + ASSERT_TRUE(waitForRequests(2, old_count + 1)); + auto check_json = received_requests_json_.back(); + auto resource_span = *check_json["resource_spans"].begin(); + auto instrumentation_library_span = *resource_span["instrumentation_library_spans"].begin(); + auto span = *instrumentation_library_span["spans"].begin(); + auto received_trace_id = span["trace_id"].get(); + EXPECT_EQ(received_trace_id, report_trace_id); } // Create spans, let processor call Export() TEST_F(OtlpHttpExporterTestPeer, ExportBinaryIntegrationTest) { + size_t old_count = getCurrentRequestCount(); + auto exporter = GetExporter(HttpRequestContentType::kBinary); opentelemetry::sdk::resource::ResourceAttributes resource_attributes = { - {"service.name", 'unit_test_service'}, {"tenant.id", 'test_user'}}; + {"service.name", "unit_test_service"}, {"tenant.id", "test_user"}}; resource_attributes["bool_value"] = true; resource_attributes["int32_value"] = static_cast(1); resource_attributes["uint32_value"] = static_cast(2); @@ -238,20 +250,37 @@ TEST_F(OtlpHttpExporterTestPeer, ExportBinaryIntegrationTest) resource_attributes["vec_string_value"] = std::vector{"vector", "string"}; auto resource = opentelemetry::sdk::resource::Resource::Create(resource_attributes); - auto processor = std::unique_ptr( - new sdk::trace::SimpleSpanProcessor(std::move(exporter))); - auto provider = nostd::shared_ptr( + auto processor = std::unique_ptr(new sdk::trace::BatchSpanProcessor( + std::move(exporter), + sdk::trace::BatchSpanProcessorOptions{5, std::chrono::milliseconds(256), 5})); + auto provider = nostd::shared_ptr( new sdk::trace::TracerProvider(std::move(processor), resource)); - auto tracer = provider->GetTracer("test"); - EXPECT_CALL(*mock_stub, Export(_, _, _)) - .Times(AtLeast(1)) - .WillRepeatedly(Return(grpc::Status::OK)); + std::string report_trace_id; + { + uint8_t trace_id_binary[opentelemetry::trace::TraceId::kSize] = {0}; + auto tracer = provider->GetTracer("test"); + auto parent_span = tracer->StartSpan("Test parent span"); + + opentelemetry::trace::StartSpanOptions child_span_opts = {}; + child_span_opts.parent = parent_span->GetContext(); - auto parent_span = tracer->StartSpan("Test parent span"); - auto child_span = tracer->StartSpan("Test child span"); - child_span->End(); - parent_span->End(); + auto child_span = tracer->StartSpan("Test child span", child_span_opts); + child_span->End(); + parent_span->End(); + + child_span_opts.parent.trace_id().CopyBytesTo(MakeSpan(trace_id_binary)); + report_trace_id.assign(reinterpret_cast(trace_id_binary), sizeof(trace_id_binary)); + } + + ASSERT_TRUE(waitForRequests(2, old_count + 1)); + + auto received_trace_id = received_requests_binary_.back() + .resource_spans(0) + .instrumentation_library_spans(0) + .spans(0) + .trace_id(); + EXPECT_EQ(received_trace_id, report_trace_id); } // Test exporter configuration options @@ -273,7 +302,7 @@ TEST_F(OtlpHttpExporterTestPeer, ConfigUseJsonNameTest) } // Test exporter configuration options with json_bytes_mapping=JsonBytesMappingKind::kHex -TEST_F(OtlpHttpExporterTestPeer, ConfigUseJsonNameTest) +TEST_F(OtlpHttpExporterTestPeer, ConfigJsonBytesMappingTest) { OtlpHttpExporterOptions opts; opts.json_bytes_mapping = JsonBytesMappingKind::kHex;