1 // Copyright The OpenTelemetry Authors
2 // SPDX-License-Identifier: Apache-2.0
4 #include "opentelemetry/sdk/trace/recordable.h"
5 #include "opentelemetry/sdk/trace/simple_processor.h"
6 #include "opentelemetry/sdk/trace/span_data.h"
7 #include "opentelemetry/sdk/trace/tracer_provider.h"
8 #include "opentelemetry/trace/provider.h"
10 #include "opentelemetry/sdk/trace/exporter.h"
12 #include "opentelemetry/common/timestamp.h"
13 #include "opentelemetry/exporters/zipkin/recordable.h"
15 #include <gtest/gtest.h>
17 namespace trace
= opentelemetry::trace
;
18 namespace nostd
= opentelemetry::nostd
;
19 namespace sdktrace
= opentelemetry::sdk::trace
;
20 namespace common
= opentelemetry::common
;
21 namespace zipkin
= opentelemetry::exporter::zipkin
;
22 using json
= nlohmann::json
;
24 // Testing Shutdown functionality of OStreamSpanExporter, should expect no data to be sent to Stream
25 TEST(ZipkinSpanRecordable
, SetIdentity
)
27 json j_span
= {{"id", "0000000000000002"},
28 {"parentId", "0000000000000003"},
29 {"traceId", "00000000000000000000000000000001"}};
30 zipkin::Recordable rec
;
31 const trace::TraceId
trace_id(std::array
<const uint8_t, trace::TraceId::kSize
>(
32 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}));
34 const trace::SpanId
span_id(
35 std::array
<const uint8_t, trace::SpanId::kSize
>({0, 0, 0, 0, 0, 0, 0, 2}));
37 const trace::SpanId
parent_span_id(
38 std::array
<const uint8_t, trace::SpanId::kSize
>({0, 0, 0, 0, 0, 0, 0, 3}));
40 const trace::SpanContext span_context
{trace_id
, span_id
,
41 trace::TraceFlags
{trace::TraceFlags::kIsSampled
}, true};
43 rec
.SetIdentity(span_context
, parent_span_id
);
44 EXPECT_EQ(rec
.span(), j_span
);
47 // according to https://zipkin.io/zipkin-api/#/ in case root span is created
48 // the parentId filed should be absent.
49 TEST(ZipkinSpanRecordable
, SetIdentityEmptyParent
)
51 json j_span
= {{"id", "0000000000000002"}, {"traceId", "00000000000000000000000000000001"}};
52 zipkin::Recordable rec
;
53 const trace::TraceId
trace_id(std::array
<const uint8_t, trace::TraceId::kSize
>(
54 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}));
56 const trace::SpanId
span_id(
57 std::array
<const uint8_t, trace::SpanId::kSize
>({0, 0, 0, 0, 0, 0, 0, 2}));
59 const trace::SpanId
parent_span_id(
60 std::array
<const uint8_t, trace::SpanId::kSize
>({0, 0, 0, 0, 0, 0, 0, 0}));
62 const trace::SpanContext span_context
{trace_id
, span_id
,
63 trace::TraceFlags
{trace::TraceFlags::kIsSampled
}, true};
65 rec
.SetIdentity(span_context
, parent_span_id
);
66 EXPECT_EQ(rec
.span(), j_span
);
69 TEST(ZipkinSpanRecordable
, SetName
)
71 nostd::string_view name
= "Test Span";
72 json j_span
= {{"name", name
}};
73 zipkin::Recordable rec
;
75 EXPECT_EQ(rec
.span(), j_span
);
78 TEST(ZipkinSpanRecordable
, SetStartTime
)
80 zipkin::Recordable rec
;
81 std::chrono::system_clock::time_point start_time
= std::chrono::system_clock::now();
82 common::SystemTimestamp
start_timestamp(start_time
);
85 std::chrono::duration_cast
<std::chrono::microseconds
>(start_time
.time_since_epoch()).count();
86 json j_span
= {{"timestamp", unix_start
}};
87 rec
.SetStartTime(start_timestamp
);
88 EXPECT_EQ(rec
.span(), j_span
);
91 TEST(ZipkinSpanRecordable
, SetDuration
)
93 std::chrono::nanoseconds
durationNS(1000000000); // in ns
94 std::chrono::microseconds durationMS
=
95 std::chrono::duration_cast
<std::chrono::microseconds
>(durationNS
); // in ms
96 json j_span
= {{"duration", durationMS
.count()}, {"timestamp", 0}};
97 zipkin::Recordable rec
;
99 common::SystemTimestamp start_timestamp
;
101 rec
.SetStartTime(start_timestamp
);
102 rec
.SetDuration(durationNS
);
103 EXPECT_EQ(rec
.span(), j_span
);
106 TEST(ZipkinSpanRecordable
, SetInstrumentationLibrary
)
108 using InstrumentationLibrary
= opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary
;
110 const char *library_name
= "otel-cpp";
111 const char *library_version
= "0.5.0";
113 {"tags", {{"otel.library.name", library_name
}, {"otel.library.version", library_version
}}}};
114 zipkin::Recordable rec
;
116 rec
.SetInstrumentationLibrary(*InstrumentationLibrary::Create(library_name
, library_version
));
118 EXPECT_EQ(rec
.span(), j_span
);
121 TEST(ZipkinSpanRecordable
, SetStatus
)
123 std::string description
= "Error description";
124 std::vector
<trace::StatusCode
> status_codes
= {trace::StatusCode::kError
, trace::StatusCode::kOk
};
125 for (auto &status_code
: status_codes
)
127 zipkin::Recordable rec
;
128 trace::StatusCode
code(status_code
);
130 if (status_code
== trace::StatusCode::kError
)
131 j_span
= {{"tags", {{"otel.status_code", status_code
}, {"error", description
}}}};
133 j_span
= {{"tags", {{"otel.status_code", status_code
}}}};
135 rec
.SetStatus(code
, description
);
136 EXPECT_EQ(rec
.span(), j_span
);
140 TEST(ZipkinSpanRecordable
, SetSpanKind
)
142 json j_json_client
= {{"kind", "CLIENT"}};
143 zipkin::Recordable rec
;
144 rec
.SetSpanKind(trace::SpanKind::kClient
);
145 EXPECT_EQ(rec
.span(), j_json_client
);
148 TEST(ZipkinSpanRecordable
, AddEventDefault
)
150 zipkin::Recordable rec
;
151 nostd::string_view name
= "Test Event";
153 std::chrono::system_clock::time_point event_time
= std::chrono::system_clock::now();
154 common::SystemTimestamp
event_timestamp(event_time
);
156 rec
.sdktrace::Recordable::AddEvent(name
, event_timestamp
);
158 uint64_t unix_event_time
=
159 std::chrono::duration_cast
<std::chrono::microseconds
>(event_time
.time_since_epoch()).count();
163 {{{"value", json({{name
, json::object()}}).dump()}, {"timestamp", unix_event_time
}}}}};
164 EXPECT_EQ(rec
.span(), j_span
);
167 TEST(ZipkinSpanRecordable
, AddEventWithAttributes
)
169 zipkin::Recordable rec
;
171 std::chrono::system_clock::time_point event_time
= std::chrono::system_clock::now();
172 common::SystemTimestamp
event_timestamp(event_time
);
173 uint64_t unix_event_time
=
174 std::chrono::duration_cast
<std::chrono::microseconds
>(event_time
.time_since_epoch()).count();
176 const int kNumAttributes
= 3;
177 std::string keys
[kNumAttributes
] = {"attr1", "attr2", "attr3"};
178 int values
[kNumAttributes
] = {4, 7, 23};
179 std::map
<std::string
, int> attributes
= {
180 {keys
[0], values
[0]}, {keys
[1], values
[1]}, {keys
[2], values
[2]}};
182 rec
.AddEvent("Test Event", event_timestamp
,
183 common::KeyValueIterableView
<std::map
<std::string
, int>>(attributes
));
185 nlohmann::json j_span
= {
187 {{{"value", json({{"Test Event", {{"attr1", 4}, {"attr2", 7}, {"attr3", 23}}}}).dump()},
188 {"timestamp", unix_event_time
}}}}};
189 EXPECT_EQ(rec
.span(), j_span
);
192 // Test non-int single types. Int single types are tested using templates (see IntAttributeTest)
193 TEST(ZipkinSpanRecordable
, SetSingleAtrribute
)
195 zipkin::Recordable rec
;
196 nostd::string_view bool_key
= "bool_attr";
197 common::AttributeValue
bool_val(true);
198 rec
.SetAttribute(bool_key
, bool_val
);
200 nostd::string_view double_key
= "double_attr";
201 common::AttributeValue
double_val(3.3);
202 rec
.SetAttribute(double_key
, double_val
);
204 nostd::string_view str_key
= "str_attr";
205 common::AttributeValue
str_val(nostd::string_view("Test"));
206 rec
.SetAttribute(str_key
, str_val
);
207 nlohmann::json j_span
= {
208 {"tags", {{"bool_attr", true}, {"double_attr", 3.3}, {"str_attr", "Test"}}}};
210 EXPECT_EQ(rec
.span(), j_span
);
213 // Test non-int array types. Int array types are tested using templates (see IntAttributeTest)
214 TEST(ZipkinSpanRecordable
, SetArrayAtrribute
)
216 zipkin::Recordable rec
;
217 nlohmann::json j_span
= {{"tags",
218 {{"bool_arr_attr", {true, false, true}},
219 {"double_arr_attr", {22.3, 33.4, 44.5}},
220 {"str_arr_attr", {"Hello", "World", "Test"}}}}};
221 const int kArraySize
= 3;
223 bool bool_arr
[kArraySize
] = {true, false, true};
224 nostd::span
<const bool> bool_span(bool_arr
);
225 rec
.SetAttribute("bool_arr_attr", bool_span
);
227 double double_arr
[kArraySize
] = {22.3, 33.4, 44.5};
228 nostd::span
<const double> double_span(double_arr
);
229 rec
.SetAttribute("double_arr_attr", double_span
);
231 nostd::string_view str_arr
[kArraySize
] = {"Hello", "World", "Test"};
232 nostd::span
<const nostd::string_view
> str_span(str_arr
);
233 rec
.SetAttribute("str_arr_attr", str_span
);
235 EXPECT_EQ(rec
.span(), j_span
);
238 TEST(ZipkinSpanRecordable
, SetResource
)
240 zipkin::Recordable rec
;
241 std::string service_name
= "test";
242 auto resource
= opentelemetry::sdk::resource::Resource::Create({{"service.name", service_name
}});
243 rec
.SetResource(resource
);
244 EXPECT_EQ(rec
.GetServiceName(), service_name
);
248 * AttributeValue can contain different int types, such as int, int64_t,
249 * unsigned int, and uint64_t. To avoid writing test cases for each, we can
250 * use a template approach to test all int types.
252 template <typename T
>
253 struct ZipkinIntAttributeTest
: public testing::Test
255 using IntParamType
= T
;
258 using IntTypes
= testing::Types
<int, int64_t, unsigned int, uint64_t>;
259 TYPED_TEST_SUITE(ZipkinIntAttributeTest
, IntTypes
);
261 TYPED_TEST(ZipkinIntAttributeTest
, SetIntSingleAttribute
)
263 using IntType
= typename
TestFixture::IntParamType
;
265 common::AttributeValue
int_val(i
);
267 zipkin::Recordable rec
;
268 rec
.SetAttribute("int_attr", int_val
);
269 nlohmann::json j_span
= {{"tags", {{"int_attr", 2}}}};
270 EXPECT_EQ(rec
.span(), j_span
);
273 TYPED_TEST(ZipkinIntAttributeTest
, SetIntArrayAttribute
)
275 using IntType
= typename
TestFixture::IntParamType
;
277 const int kArraySize
= 3;
278 IntType int_arr
[kArraySize
] = {4, 5, 6};
279 nostd::span
<const IntType
> int_span(int_arr
);
281 zipkin::Recordable rec
;
282 rec
.SetAttribute("int_arr_attr", int_span
);
283 nlohmann::json j_span
= {{"tags", {{"int_arr_attr", {4, 5, 6}}}}};
284 EXPECT_EQ(rec
.span(), j_span
);