]> git.proxmox.com Git - ceph.git/blame - ceph/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_http_exporter_test.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / jaegertracing / opentelemetry-cpp / exporters / otlp / test / otlp_http_exporter_test.cc
CommitLineData
1e59de90
TL
1// Copyright The OpenTelemetry Authors
2// SPDX-License-Identifier: Apache-2.0
3
4#ifndef HAVE_CPP_STDLIB
5
6# include "opentelemetry/exporters/otlp/otlp_http_exporter.h"
7
8# include "opentelemetry/exporters/otlp/protobuf_include_prefix.h"
9
10# include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h"
11
12# include "opentelemetry/exporters/otlp/protobuf_include_suffix.h"
13
14# include "opentelemetry/ext/http/client/http_client_factory.h"
15# include "opentelemetry/ext/http/client/nosend/http_client_nosend.h"
16# include "opentelemetry/ext/http/server/http_server.h"
17# include "opentelemetry/sdk/trace/batch_span_processor.h"
18# include "opentelemetry/sdk/trace/tracer_provider.h"
19# include "opentelemetry/trace/provider.h"
20
21# include <gtest/gtest.h>
22# include "gmock/gmock.h"
23
24# include "nlohmann/json.hpp"
25
26# if defined(_MSC_VER)
27# include "opentelemetry/sdk/common/env_variables.h"
28using opentelemetry::sdk::common::setenv;
29using opentelemetry::sdk::common::unsetenv;
30# endif
31using namespace testing;
32
33OPENTELEMETRY_BEGIN_NAMESPACE
34namespace exporter
35{
36namespace otlp
37{
38
39namespace trace_api = opentelemetry::trace;
40namespace resource = opentelemetry::sdk::resource;
41
42template <class T, size_t N>
43static nostd::span<T, N> MakeSpan(T (&array)[N])
44{
45 return nostd::span<T, N>(array);
46}
47
48OtlpHttpClientOptions MakeOtlpHttpClientOptions(HttpRequestContentType content_type)
49{
50 OtlpHttpExporterOptions options;
51 options.content_type = content_type;
52 options.console_debug = true;
53 options.timeout = std::chrono::system_clock::duration::zero();
54 options.http_headers.insert(
55 std::make_pair<const std::string, std::string>("Custom-Header-Key", "Custom-Header-Value"));
56 OtlpHttpClientOptions otlp_http_client_options(
57 options.url, options.content_type, options.json_bytes_mapping, options.use_json_name,
58 options.console_debug, options.timeout, options.http_headers);
59 return otlp_http_client_options;
60}
61
62namespace http_client = opentelemetry::ext::http::client;
63
64class OtlpHttpExporterTestPeer : public ::testing::Test
65{
66public:
67 std::unique_ptr<sdk::trace::SpanExporter> GetExporter(std::unique_ptr<OtlpHttpClient> http_client)
68 {
69 return std::unique_ptr<sdk::trace::SpanExporter>(new OtlpHttpExporter(std::move(http_client)));
70 }
71
72 // Get the options associated with the given exporter.
73 const OtlpHttpExporterOptions &GetOptions(std::unique_ptr<OtlpHttpExporter> &exporter)
74 {
75 return exporter->options_;
76 }
77
78 static std::pair<OtlpHttpClient *, std::shared_ptr<http_client::HttpClient>>
79 GetMockOtlpHttpClient(HttpRequestContentType content_type)
80 {
81 auto http_client = http_client::HttpClientFactory::CreateNoSend();
82 return {new OtlpHttpClient(MakeOtlpHttpClientOptions(content_type), http_client), http_client};
83 }
84};
85
86// Create spans, let processor call Export()
87TEST_F(OtlpHttpExporterTestPeer, ExportJsonIntegrationTest)
88{
89 auto mock_otlp_client =
90 OtlpHttpExporterTestPeer::GetMockOtlpHttpClient(HttpRequestContentType::kJson);
91 auto mock_otlp_http_client = mock_otlp_client.first;
92 auto client = mock_otlp_client.second;
93 auto exporter = GetExporter(std::unique_ptr<OtlpHttpClient>{mock_otlp_http_client});
94
95 resource::ResourceAttributes resource_attributes = {{"service.name", "unit_test_service"},
96 {"tenant.id", "test_user"}};
97 resource_attributes["bool_value"] = true;
98 resource_attributes["int32_value"] = static_cast<int32_t>(1);
99 resource_attributes["uint32_value"] = static_cast<uint32_t>(2);
100 resource_attributes["int64_value"] = static_cast<int64_t>(0x1100000000LL);
101 resource_attributes["uint64_value"] = static_cast<uint64_t>(0x1200000000ULL);
102 resource_attributes["double_value"] = static_cast<double>(3.1);
103 resource_attributes["vec_bool_value"] = std::vector<bool>{true, false, true};
104 resource_attributes["vec_int32_value"] = std::vector<int32_t>{1, 2};
105 resource_attributes["vec_uint32_value"] = std::vector<uint32_t>{3, 4};
106 resource_attributes["vec_int64_value"] = std::vector<int64_t>{5, 6};
107 resource_attributes["vec_uint64_value"] = std::vector<uint64_t>{7, 8};
108 resource_attributes["vec_double_value"] = std::vector<double>{3.2, 3.3};
109 resource_attributes["vec_string_value"] = std::vector<std::string>{"vector", "string"};
110 auto resource = resource::Resource::Create(resource_attributes);
111
112 auto processor_opts = sdk::trace::BatchSpanProcessorOptions();
113 processor_opts.max_export_batch_size = 5;
114 processor_opts.max_queue_size = 5;
115 processor_opts.schedule_delay_millis = std::chrono::milliseconds(256);
116 auto processor = std::unique_ptr<sdk::trace::SpanProcessor>(
117 new sdk::trace::BatchSpanProcessor(std::move(exporter), processor_opts));
118 auto provider = nostd::shared_ptr<trace::TracerProvider>(
119 new sdk::trace::TracerProvider(std::move(processor), resource));
120
121 std::string report_trace_id;
122
123 char trace_id_hex[2 * trace_api::TraceId::kSize] = {0};
124 auto tracer = provider->GetTracer("test");
125 auto parent_span = tracer->StartSpan("Test parent span");
126
127 trace_api::StartSpanOptions child_span_opts = {};
128 child_span_opts.parent = parent_span->GetContext();
129
130 auto child_span = tracer->StartSpan("Test child span", child_span_opts);
131
132 nostd::get<trace_api::SpanContext>(child_span_opts.parent)
133 .trace_id()
134 .ToLowerBase16(MakeSpan(trace_id_hex));
135 report_trace_id.assign(trace_id_hex, sizeof(trace_id_hex));
136
137 auto no_send_client = std::static_pointer_cast<http_client::nosend::HttpClient>(client);
138 auto mock_session =
139 std::static_pointer_cast<http_client::nosend::Session>(no_send_client->session_);
140 EXPECT_CALL(*mock_session, SendRequest)
141 .WillOnce([&mock_session,
142 report_trace_id](opentelemetry::ext::http::client::EventHandler &callback) {
143 auto check_json = nlohmann::json::parse(mock_session->GetRequest()->body_, nullptr, false);
144 auto resource_span = *check_json["resource_spans"].begin();
145 auto instrumentation_library_span = *resource_span["instrumentation_library_spans"].begin();
146 auto span = *instrumentation_library_span["spans"].begin();
147 auto received_trace_id = span["trace_id"].get<std::string>();
148 EXPECT_EQ(received_trace_id, report_trace_id);
149
150 auto custom_header = mock_session->GetRequest()->headers_.find("Custom-Header-Key");
151 ASSERT_TRUE(custom_header != mock_session->GetRequest()->headers_.end());
152 if (custom_header != mock_session->GetRequest()->headers_.end())
153 {
154 EXPECT_EQ("Custom-Header-Value", custom_header->second);
155 }
156 // let the otlp_http_client to continue
157 http_client::nosend::Response response;
158 callback.OnResponse(response);
159 });
160
161 child_span->End();
162 parent_span->End();
163}
164
165// Create spans, let processor call Export()
166TEST_F(OtlpHttpExporterTestPeer, ExportBinaryIntegrationTest)
167{
168 auto mock_otlp_client =
169 OtlpHttpExporterTestPeer::GetMockOtlpHttpClient(HttpRequestContentType::kBinary);
170 auto mock_otlp_http_client = mock_otlp_client.first;
171 auto client = mock_otlp_client.second;
172 auto exporter = GetExporter(std::unique_ptr<OtlpHttpClient>{mock_otlp_http_client});
173
174 resource::ResourceAttributes resource_attributes = {{"service.name", "unit_test_service"},
175 {"tenant.id", "test_user"}};
176 resource_attributes["bool_value"] = true;
177 resource_attributes["int32_value"] = static_cast<int32_t>(1);
178 resource_attributes["uint32_value"] = static_cast<uint32_t>(2);
179 resource_attributes["int64_value"] = static_cast<int64_t>(0x1100000000LL);
180 resource_attributes["uint64_value"] = static_cast<uint64_t>(0x1200000000ULL);
181 resource_attributes["double_value"] = static_cast<double>(3.1);
182 resource_attributes["vec_bool_value"] = std::vector<bool>{true, false, true};
183 resource_attributes["vec_int32_value"] = std::vector<int32_t>{1, 2};
184 resource_attributes["vec_uint32_value"] = std::vector<uint32_t>{3, 4};
185 resource_attributes["vec_int64_value"] = std::vector<int64_t>{5, 6};
186 resource_attributes["vec_uint64_value"] = std::vector<uint64_t>{7, 8};
187 resource_attributes["vec_double_value"] = std::vector<double>{3.2, 3.3};
188 resource_attributes["vec_string_value"] = std::vector<std::string>{"vector", "string"};
189 auto resource = resource::Resource::Create(resource_attributes);
190
191 auto processor_opts = sdk::trace::BatchSpanProcessorOptions();
192 processor_opts.max_export_batch_size = 5;
193 processor_opts.max_queue_size = 5;
194 processor_opts.schedule_delay_millis = std::chrono::milliseconds(256);
195
196 auto processor = std::unique_ptr<sdk::trace::SpanProcessor>(
197 new sdk::trace::BatchSpanProcessor(std::move(exporter), processor_opts));
198 auto provider = nostd::shared_ptr<trace::TracerProvider>(
199 new sdk::trace::TracerProvider(std::move(processor), resource));
200
201 std::string report_trace_id;
202
203 uint8_t trace_id_binary[trace_api::TraceId::kSize] = {0};
204 auto tracer = provider->GetTracer("test");
205 auto parent_span = tracer->StartSpan("Test parent span");
206
207 trace_api::StartSpanOptions child_span_opts = {};
208 child_span_opts.parent = parent_span->GetContext();
209
210 auto child_span = tracer->StartSpan("Test child span", child_span_opts);
211 nostd::get<trace_api::SpanContext>(child_span_opts.parent)
212 .trace_id()
213 .CopyBytesTo(MakeSpan(trace_id_binary));
214 report_trace_id.assign(reinterpret_cast<char *>(trace_id_binary), sizeof(trace_id_binary));
215
216 auto no_send_client = std::static_pointer_cast<http_client::nosend::HttpClient>(client);
217 auto mock_session =
218 std::static_pointer_cast<http_client::nosend::Session>(no_send_client->session_);
219 EXPECT_CALL(*mock_session, SendRequest)
220 .WillOnce([&mock_session,
221 report_trace_id](opentelemetry::ext::http::client::EventHandler &callback) {
222 opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest request_body;
223 request_body.ParseFromArray(&mock_session->GetRequest()->body_[0],
224 static_cast<int>(mock_session->GetRequest()->body_.size()));
225 auto received_trace_id =
226 request_body.resource_spans(0).instrumentation_library_spans(0).spans(0).trace_id();
227 EXPECT_EQ(received_trace_id, report_trace_id);
228
229 auto custom_header = mock_session->GetRequest()->headers_.find("Custom-Header-Key");
230 ASSERT_TRUE(custom_header != mock_session->GetRequest()->headers_.end());
231 if (custom_header != mock_session->GetRequest()->headers_.end())
232 {
233 EXPECT_EQ("Custom-Header-Value", custom_header->second);
234 }
235 // let the otlp_http_client to continue
236 http_client::nosend::Response response;
237 callback.OnResponse(response);
238 });
239
240 child_span->End();
241 parent_span->End();
242}
243
244// Test exporter configuration options
245TEST_F(OtlpHttpExporterTestPeer, ConfigTest)
246{
247 OtlpHttpExporterOptions opts;
248 opts.url = "http://localhost:45455/v1/traces";
249 std::unique_ptr<OtlpHttpExporter> exporter(new OtlpHttpExporter(opts));
250 EXPECT_EQ(GetOptions(exporter).url, "http://localhost:45455/v1/traces");
251}
252
253// Test exporter configuration options with use_json_name
254TEST_F(OtlpHttpExporterTestPeer, ConfigUseJsonNameTest)
255{
256 OtlpHttpExporterOptions opts;
257 opts.use_json_name = true;
258 std::unique_ptr<OtlpHttpExporter> exporter(new OtlpHttpExporter(opts));
259 EXPECT_EQ(GetOptions(exporter).use_json_name, true);
260}
261
262// Test exporter configuration options with json_bytes_mapping=JsonBytesMappingKind::kHex
263TEST_F(OtlpHttpExporterTestPeer, ConfigJsonBytesMappingTest)
264{
265 OtlpHttpExporterOptions opts;
266 opts.json_bytes_mapping = JsonBytesMappingKind::kHex;
267 std::unique_ptr<OtlpHttpExporter> exporter(new OtlpHttpExporter(opts));
268 EXPECT_EQ(GetOptions(exporter).json_bytes_mapping, JsonBytesMappingKind::kHex);
269}
270
271# ifndef NO_GETENV
272// Test exporter configuration options with use_ssl_credentials
273TEST_F(OtlpHttpExporterTestPeer, ConfigFromEnv)
274{
275 const std::string url = "http://localhost:9999/v1/traces";
276 setenv("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:9999", 1);
277 setenv("OTEL_EXPORTER_OTLP_TIMEOUT", "20s", 1);
278 setenv("OTEL_EXPORTER_OTLP_HEADERS", "k1=v1,k2=v2", 1);
279 setenv("OTEL_EXPORTER_OTLP_TRACES_HEADERS", "k1=v3,k1=v4", 1);
280
281 std::unique_ptr<OtlpHttpExporter> exporter(new OtlpHttpExporter());
282 EXPECT_EQ(GetOptions(exporter).url, url);
283 EXPECT_EQ(
284 GetOptions(exporter).timeout.count(),
285 std::chrono::duration_cast<std::chrono::system_clock::duration>(std::chrono::seconds{20})
286 .count());
287 EXPECT_EQ(GetOptions(exporter).http_headers.size(), 3);
288 {
289 // Test k2
290 auto range = GetOptions(exporter).http_headers.equal_range("k2");
291 EXPECT_TRUE(range.first != range.second);
292 EXPECT_EQ(range.first->second, std::string("v2"));
293 ++range.first;
294 EXPECT_TRUE(range.first == range.second);
295 }
296 {
297 // k1
298 auto range = GetOptions(exporter).http_headers.equal_range("k1");
299 EXPECT_TRUE(range.first != range.second);
300 EXPECT_EQ(range.first->second, std::string("v3"));
301 ++range.first;
302 EXPECT_EQ(range.first->second, std::string("v4"));
303 ++range.first;
304 EXPECT_TRUE(range.first == range.second);
305 }
306
307 unsetenv("OTEL_EXPORTER_OTLP_ENDPOINT");
308 unsetenv("OTEL_EXPORTER_OTLP_TIMEOUT");
309 unsetenv("OTEL_EXPORTER_OTLP_HEADERS");
310 unsetenv("OTEL_EXPORTER_OTLP_TRACES_HEADERS");
311}
312
313TEST_F(OtlpHttpExporterTestPeer, ConfigFromTracesEnv)
314{
315 const std::string url = "http://localhost:9999/v1/traces";
316 setenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", url.c_str(), 1);
317 setenv("OTEL_EXPORTER_OTLP_TIMEOUT", "20s", 1);
318 setenv("OTEL_EXPORTER_OTLP_HEADERS", "k1=v1,k2=v2", 1);
319 setenv("OTEL_EXPORTER_OTLP_TRACES_HEADERS", "k1=v3,k1=v4", 1);
320
321 std::unique_ptr<OtlpHttpExporter> exporter(new OtlpHttpExporter());
322 EXPECT_EQ(GetOptions(exporter).url, url);
323 EXPECT_EQ(
324 GetOptions(exporter).timeout.count(),
325 std::chrono::duration_cast<std::chrono::system_clock::duration>(std::chrono::seconds{20})
326 .count());
327 EXPECT_EQ(GetOptions(exporter).http_headers.size(), 3);
328 {
329 // Test k2
330 auto range = GetOptions(exporter).http_headers.equal_range("k2");
331 EXPECT_TRUE(range.first != range.second);
332 EXPECT_EQ(range.first->second, std::string("v2"));
333 ++range.first;
334 EXPECT_TRUE(range.first == range.second);
335 }
336 {
337 // k1
338 auto range = GetOptions(exporter).http_headers.equal_range("k1");
339 EXPECT_TRUE(range.first != range.second);
340 EXPECT_EQ(range.first->second, std::string("v3"));
341 ++range.first;
342 EXPECT_EQ(range.first->second, std::string("v4"));
343 ++range.first;
344 EXPECT_TRUE(range.first == range.second);
345 }
346
347 unsetenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT");
348 unsetenv("OTEL_EXPORTER_OTLP_TIMEOUT");
349 unsetenv("OTEL_EXPORTER_OTLP_HEADERS");
350 unsetenv("OTEL_EXPORTER_OTLP_TRACES_HEADERS");
351}
352# endif
353
354} // namespace otlp
355} // namespace exporter
356OPENTELEMETRY_END_NAMESPACE
357#endif