zipkin_exporter_test.cc 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // Copyright The OpenTelemetry Authors
  2. // SPDX-License-Identifier: Apache-2.0
  3. #ifndef OPENTELEMETRY_STL_VERSION
  4. # include "opentelemetry/exporters/zipkin/zipkin_exporter.h"
  5. # include "opentelemetry/ext/http/client/curl/http_client_curl.h"
  6. # include "opentelemetry/ext/http/server/http_server.h"
  7. # include "opentelemetry/sdk/trace/batch_span_processor.h"
  8. # include "opentelemetry/sdk/trace/batch_span_processor_options.h"
  9. # include "opentelemetry/sdk/trace/tracer_provider.h"
  10. # include "opentelemetry/trace/provider.h"
  11. # include <gtest/gtest.h>
  12. # include "gmock/gmock.h"
  13. # include "nlohmann/json.hpp"
  14. # include <string>
  15. # include <utility>
  16. # if defined(_MSC_VER)
  17. # include "opentelemetry/sdk/common/env_variables.h"
  18. using opentelemetry::sdk::common::setenv;
  19. using opentelemetry::sdk::common::unsetenv;
  20. # endif
  21. namespace sdk_common = opentelemetry::sdk::common;
  22. using namespace testing;
  23. OPENTELEMETRY_BEGIN_NAMESPACE
  24. namespace exporter
  25. {
  26. namespace zipkin
  27. {
  28. namespace trace_api = opentelemetry::trace;
  29. namespace resource = opentelemetry::sdk::resource;
  30. template <class T, size_t N>
  31. static nostd::span<T, N> MakeSpan(T (&array)[N])
  32. {
  33. return nostd::span<T, N>(array);
  34. }
  35. class ZipkinExporterTestPeer : public ::testing::Test
  36. {
  37. public:
  38. std::unique_ptr<sdk::trace::SpanExporter> GetExporter(
  39. std::shared_ptr<opentelemetry::ext::http::client::HttpClientSync> http_client)
  40. {
  41. return std::unique_ptr<sdk::trace::SpanExporter>(new ZipkinExporter(std::move(http_client)));
  42. }
  43. // Get the options associated with the given exporter.
  44. const ZipkinExporterOptions &GetOptions(std::unique_ptr<ZipkinExporter> &exporter)
  45. {
  46. return exporter->options_;
  47. }
  48. };
  49. class MockHttpClient : public opentelemetry::ext::http::client::HttpClientSync
  50. {
  51. public:
  52. MOCK_METHOD(ext::http::client::Result,
  53. Post,
  54. (const nostd::string_view &,
  55. const ext::http::client::HttpSslOptions &,
  56. const ext::http::client::Body &,
  57. const ext::http::client::Headers &,
  58. const ext::http::client::Compression &),
  59. (noexcept, override));
  60. MOCK_METHOD(ext::http::client::Result,
  61. Get,
  62. (const nostd::string_view &,
  63. const ext::http::client::HttpSslOptions &,
  64. const ext::http::client::Headers &,
  65. const ext::http::client::Compression &),
  66. (noexcept, override));
  67. };
  68. class IsValidMessageMatcher
  69. {
  70. public:
  71. IsValidMessageMatcher(const std::string &trace_id) : trace_id_(trace_id) {}
  72. template <typename T>
  73. bool MatchAndExplain(const T &p, MatchResultListener * /* listener */) const
  74. {
  75. auto body = std::string(p.begin(), p.end());
  76. nlohmann::json check_json = nlohmann::json::parse(body);
  77. auto trace_id_kv = check_json.at(0).find("traceId");
  78. auto received_trace_id = trace_id_kv.value().get<std::string>();
  79. return trace_id_ == received_trace_id;
  80. }
  81. void DescribeTo(std::ostream *os) const { *os << "received trace_id matches"; }
  82. void DescribeNegationTo(std::ostream *os) const { *os << "received trace_id does not matche"; }
  83. private:
  84. std::string trace_id_;
  85. };
  86. PolymorphicMatcher<IsValidMessageMatcher> IsValidMessage(const std::string &trace_id)
  87. {
  88. return MakePolymorphicMatcher(IsValidMessageMatcher(trace_id));
  89. }
  90. // Create spans, let processor call Export()
  91. TEST_F(ZipkinExporterTestPeer, ExportJsonIntegrationTest)
  92. {
  93. auto mock_http_client = new MockHttpClient;
  94. // Leave a comment line here or different version of clang-format has a different result here
  95. auto exporter = GetExporter(
  96. std::shared_ptr<opentelemetry::ext::http::client::HttpClientSync>{mock_http_client});
  97. resource::ResourceAttributes resource_attributes = {{"service.name", "unit_test_service"},
  98. {"tenant.id", "test_user"}};
  99. resource_attributes["bool_value"] = true;
  100. resource_attributes["int32_value"] = static_cast<int32_t>(1);
  101. resource_attributes["uint32_value"] = static_cast<uint32_t>(2);
  102. resource_attributes["int64_value"] = static_cast<int64_t>(0x1100000000LL);
  103. resource_attributes["uint64_value"] = static_cast<uint64_t>(0x1200000000ULL);
  104. resource_attributes["double_value"] = static_cast<double>(3.1);
  105. resource_attributes["vec_bool_value"] = std::vector<bool>{true, false, true};
  106. resource_attributes["vec_int32_value"] = std::vector<int32_t>{1, 2};
  107. resource_attributes["vec_uint32_value"] = std::vector<uint32_t>{3, 4};
  108. resource_attributes["vec_int64_value"] = std::vector<int64_t>{5, 6};
  109. resource_attributes["vec_uint64_value"] = std::vector<uint64_t>{7, 8};
  110. resource_attributes["vec_double_value"] = std::vector<double>{3.2, 3.3};
  111. resource_attributes["vec_string_value"] = std::vector<std::string>{"vector", "string"};
  112. auto resource = resource::Resource::Create(resource_attributes);
  113. auto processor_opts = sdk::trace::BatchSpanProcessorOptions();
  114. processor_opts.max_export_batch_size = 5;
  115. processor_opts.max_queue_size = 5;
  116. processor_opts.schedule_delay_millis = std::chrono::milliseconds(256);
  117. auto processor = std::unique_ptr<sdk::trace::SpanProcessor>(
  118. new sdk::trace::BatchSpanProcessor(std::move(exporter), processor_opts));
  119. auto provider = nostd::shared_ptr<trace::TracerProvider>(
  120. new sdk::trace::TracerProvider(std::move(processor), resource));
  121. std::string report_trace_id;
  122. char trace_id_hex[2 * trace_api::TraceId::kSize] = {0};
  123. auto tracer = provider->GetTracer("test");
  124. auto parent_span = tracer->StartSpan("Test parent span");
  125. trace_api::StartSpanOptions child_span_opts = {};
  126. child_span_opts.parent = parent_span->GetContext();
  127. auto child_span = tracer->StartSpan("Test child span", child_span_opts);
  128. nostd::get<trace_api::SpanContext>(child_span_opts.parent)
  129. .trace_id()
  130. .ToLowerBase16(MakeSpan(trace_id_hex));
  131. report_trace_id.assign(trace_id_hex, sizeof(trace_id_hex));
  132. auto expected_url = nostd::string_view{"http://localhost:9411/api/v2/spans"};
  133. EXPECT_CALL(*mock_http_client, Post(expected_url, _, IsValidMessage(report_trace_id), _, _))
  134. .Times(Exactly(1))
  135. .WillOnce(Return(ByMove(ext::http::client::Result{
  136. std::unique_ptr<ext::http::client::Response>{new ext::http::client::curl::Response()},
  137. ext::http::client::SessionState::Response})));
  138. child_span->End();
  139. parent_span->End();
  140. }
  141. // Create spans, let processor call Export()
  142. TEST_F(ZipkinExporterTestPeer, ShutdownTest)
  143. {
  144. auto mock_http_client = new MockHttpClient;
  145. // Leave a comment line here or different version of clang-format has a different result here
  146. auto exporter = GetExporter(
  147. std::shared_ptr<opentelemetry::ext::http::client::HttpClientSync>{mock_http_client});
  148. auto recordable_1 = exporter->MakeRecordable();
  149. recordable_1->SetName("Test span 1");
  150. auto recordable_2 = exporter->MakeRecordable();
  151. recordable_2->SetName("Test span 2");
  152. // exporter should not be shutdown by default
  153. nostd::span<std::unique_ptr<sdk::trace::Recordable>> batch_1(&recordable_1, 1);
  154. EXPECT_CALL(*mock_http_client, Post(_, _, _, _, _))
  155. .Times(Exactly(1))
  156. .WillOnce(Return(ByMove(ext::http::client::Result{
  157. std::unique_ptr<ext::http::client::Response>{new ext::http::client::curl::Response()},
  158. ext::http::client::SessionState::Response})));
  159. auto result = exporter->Export(batch_1);
  160. EXPECT_EQ(sdk_common::ExportResult::kSuccess, result);
  161. exporter->Shutdown();
  162. nostd::span<std::unique_ptr<sdk::trace::Recordable>> batch_2(&recordable_2, 1);
  163. result = exporter->Export(batch_2);
  164. EXPECT_EQ(sdk_common::ExportResult::kFailure, result);
  165. }
  166. // Test exporter configuration options
  167. TEST_F(ZipkinExporterTestPeer, ConfigTest)
  168. {
  169. ZipkinExporterOptions opts;
  170. opts.endpoint = "http://localhost:45455/v1/traces";
  171. std::unique_ptr<ZipkinExporter> exporter(new ZipkinExporter(opts));
  172. EXPECT_EQ(GetOptions(exporter).endpoint, "http://localhost:45455/v1/traces");
  173. }
  174. # ifndef NO_GETENV
  175. // Test exporter configuration options from env
  176. TEST_F(ZipkinExporterTestPeer, ConfigFromEnv)
  177. {
  178. const std::string endpoint = "http://localhost:9999/v1/traces";
  179. setenv("OTEL_EXPORTER_ZIPKIN_ENDPOINT", endpoint.c_str(), 1);
  180. std::unique_ptr<ZipkinExporter> exporter(new ZipkinExporter());
  181. EXPECT_EQ(GetOptions(exporter).endpoint, endpoint);
  182. unsetenv("OTEL_EXPORTER_ZIPKIN_ENDPOINT");
  183. }
  184. # endif // NO_GETENV
  185. } // namespace zipkin
  186. } // namespace exporter
  187. OPENTELEMETRY_END_NAMESPACE
  188. #endif /* OPENTELEMETRY_STL_VERSION */