| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557 |
- // Copyright The OpenTelemetry Authors
- // SPDX-License-Identifier: Apache-2.0
- #include <gtest/gtest.h>
- #include <chrono>
- #include <cstdint>
- #include <ctime>
- #include <fstream>
- #include <functional>
- #include <iostream>
- #include <nlohmann/json.hpp>
- #include <sstream>
- #include <string>
- #include <thread>
- #include <unordered_map>
- #include <utility>
- #include <vector>
- // IWYU pragma: no_include <features.h>
- #include "opentelemetry/common/timestamp.h"
- #include "opentelemetry/exporters/otlp/otlp_file_client.h"
- #include "opentelemetry/exporters/otlp/otlp_file_client_options.h"
- #include "opentelemetry/exporters/otlp/otlp_file_client_runtime_options.h"
- #include "opentelemetry/exporters/otlp/otlp_recordable.h"
- #include "opentelemetry/exporters/otlp/otlp_recordable_utils.h"
- #include "opentelemetry/nostd/shared_ptr.h"
- #include "opentelemetry/nostd/span.h"
- #include "opentelemetry/nostd/unique_ptr.h"
- #include "opentelemetry/nostd/variant.h"
- #include "opentelemetry/sdk/common/exporter_utils.h"
- #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h"
- #include "opentelemetry/sdk/resource/resource.h"
- #include "opentelemetry/sdk/trace/recordable.h"
- #include "opentelemetry/trace/span_context.h"
- #include "opentelemetry/trace/span_id.h"
- #include "opentelemetry/trace/span_metadata.h"
- #include "opentelemetry/trace/trace_flags.h"
- #include "opentelemetry/trace/trace_id.h"
- #include "opentelemetry/trace/trace_state.h"
- #include "opentelemetry/version.h"
- // clang-format off
- #include "opentelemetry/exporters/otlp/protobuf_include_prefix.h" // IWYU pragma: keep
- #include <google/protobuf/message_lite.h>
- #include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h"
- #include "opentelemetry/proto/trace/v1/trace.pb.h"
- #include "opentelemetry/exporters/otlp/protobuf_include_suffix.h" // IWYU pragma: keep
- // clang-format on
- using namespace testing;
- OPENTELEMETRY_BEGIN_NAMESPACE
- namespace exporter
- {
- namespace otlp
- {
- namespace resource = opentelemetry::sdk::resource;
- class ProtobufGlobalSymbolGuard
- {
- public:
- ProtobufGlobalSymbolGuard() {}
- ~ProtobufGlobalSymbolGuard() { google::protobuf::ShutdownProtobufLibrary(); }
- };
- static std::tm GetLocalTime(std::chrono::system_clock::time_point tp)
- {
- std::time_t now = std::chrono::system_clock::to_time_t(tp);
- #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || defined(__STDC_LIB_EXT1__)
- std::tm ret;
- localtime_s(&now, &ret);
- #elif defined(_MSC_VER) && _MSC_VER >= 1300
- std::tm ret;
- localtime_s(&ret, &now);
- #elif defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || \
- defined(_POSIX_SOURCE)
- std::tm ret;
- localtime_r(&now, &ret);
- #else
- std::tm ret = *localtime(&now);
- #endif
- return ret;
- }
- template <class T, size_t N>
- static nostd::span<T, N> MakeSpan(T (&array)[N])
- {
- return nostd::span<T, N>(array);
- }
- static resource::Resource MakeResource()
- {
- static ProtobufGlobalSymbolGuard global_symbol_guard;
- resource::ResourceAttributes resource_attributes = {{"service.name", "unit_test_service"},
- {"tenant.id", "test_user"}};
- resource_attributes["bool_value"] = true;
- resource_attributes["int32_value"] = static_cast<int32_t>(1);
- resource_attributes["uint32_value"] = static_cast<uint32_t>(2);
- resource_attributes["int64_value"] = static_cast<int64_t>(0x1100000000LL);
- resource_attributes["uint64_value"] = static_cast<uint64_t>(0x1200000000ULL);
- resource_attributes["double_value"] = static_cast<double>(3.1);
- resource_attributes["vec_bool_value"] = std::vector<bool>{true, false, true};
- resource_attributes["vec_int32_value"] = std::vector<int32_t>{1, 2};
- resource_attributes["vec_uint32_value"] = std::vector<uint32_t>{3, 4};
- resource_attributes["vec_int64_value"] = std::vector<int64_t>{5, 6};
- resource_attributes["vec_uint64_value"] = std::vector<uint64_t>{7, 8};
- resource_attributes["vec_double_value"] = std::vector<double>{3.2, 3.3};
- resource_attributes["vec_string_value"] = std::vector<std::string>{"vector", "string"};
- return resource::Resource::Create(resource_attributes);
- }
- static opentelemetry::nostd::unique_ptr<
- opentelemetry::sdk::instrumentationscope::InstrumentationScope>
- MakeInstrumentationScope()
- {
- return opentelemetry::sdk::instrumentationscope::InstrumentationScope::Create(
- "otlp_file_client_test", "1.11.0", "https://opentelemetry.io/schemas/1.11.0");
- }
- static std::unique_ptr<opentelemetry::sdk::trace::Recordable> MakeRecordable(
- const resource::Resource &resource,
- const opentelemetry::sdk::instrumentationscope::InstrumentationScope &instrumentation_scope)
- {
- OtlpRecordable *recordable = new OtlpRecordable();
- recordable->SetResource(resource);
- recordable->SetInstrumentationScope(instrumentation_scope);
- recordable->SetName("otlp_file_client_test_span");
- recordable->SetSpanKind(opentelemetry::trace::SpanKind::kInternal);
- recordable->SetAttribute("test-attribute-key", "test-attribute-value");
- recordable->SetDuration(std::chrono::nanoseconds(1234567890));
- recordable->SetStartTime(
- opentelemetry::common::SystemTimestamp(std::chrono::system_clock::now()));
- recordable->SetStatus(opentelemetry::trace::StatusCode::kOk, "success");
- {
- constexpr uint8_t trace_id_buf[] = {1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8};
- constexpr uint8_t span_id_buf[] = {1, 2, 3, 4, 5, 6, 7, 8};
- constexpr uint8_t parent_span_id_buf[] = {8, 7, 6, 5, 4, 3, 2, 1};
- opentelemetry::trace::TraceId trace_id{trace_id_buf};
- opentelemetry::trace::SpanId span_id{span_id_buf};
- opentelemetry::trace::SpanId parent_span_id{parent_span_id_buf};
- const auto trace_state = opentelemetry::trace::TraceState::GetDefault()->Set("key1", "value");
- const opentelemetry::trace::SpanContext span_context{
- trace_id, span_id,
- opentelemetry::trace::TraceFlags{opentelemetry::trace::TraceFlags::kIsSampled}, true,
- trace_state};
- recordable->SetIdentity(span_context, parent_span_id);
- }
- return std::unique_ptr<opentelemetry::sdk::trace::Recordable>(recordable);
- }
- TEST(OtlpFileClientTest, Shutdown)
- {
- opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest request;
- auto client = std::unique_ptr<opentelemetry::exporter::otlp::OtlpFileClient>(
- new opentelemetry::exporter::otlp::OtlpFileClient(
- opentelemetry::exporter::otlp::OtlpFileClientOptions(),
- opentelemetry::exporter::otlp::OtlpFileClientRuntimeOptions()));
- ASSERT_FALSE(client->IsShutdown());
- ASSERT_TRUE(client->Shutdown());
- ASSERT_TRUE(client->IsShutdown());
- auto result = client->Export(request, 1);
- EXPECT_EQ(result, opentelemetry::sdk::common::ExportResult::kFailure);
- }
- TEST(OtlpFileClientTest, ExportToOstreamTest)
- {
- auto resource = MakeResource();
- auto instrumentation_scope = MakeInstrumentationScope();
- std::unique_ptr<opentelemetry::sdk::trace::Recordable> recordable[] = {
- MakeRecordable(resource, *instrumentation_scope)};
- opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest request;
- OtlpRecordableUtils::PopulateRequest(MakeSpan(recordable), &request);
- std::stringstream output_stream;
- opentelemetry::exporter::otlp::OtlpFileClientOptions opts;
- opentelemetry::exporter::otlp::OtlpFileClientRuntimeOptions rt_opts;
- opts.backend_options = std::ref(output_stream);
- auto client = std::unique_ptr<opentelemetry::exporter::otlp::OtlpFileClient>(
- new opentelemetry::exporter::otlp::OtlpFileClient(std::move(opts), std::move(rt_opts)));
- client->Export(request, 1);
- {
- auto check_json = nlohmann::json::parse(output_stream.str(), nullptr, false);
- auto resource_span = *check_json["resourceSpans"].begin();
- auto scope_span = *resource_span["scopeSpans"].begin();
- auto span = *scope_span["spans"].begin();
- auto received_trace_id = span["traceId"].get<std::string>();
- EXPECT_EQ(received_trace_id, "01020304050607080102030405060708");
- auto received_name = span["name"].get<std::string>();
- EXPECT_EQ(received_name, request.resource_spans(0).scope_spans(0).spans(0).name());
- auto receivec_attributes = span["attributes"];
- int attribute_found = 0;
- for (auto iter = receivec_attributes.begin(); iter != receivec_attributes.end(); ++iter)
- {
- auto key = (*iter)["key"].get<std::string>();
- if (key == "test-attribute-key")
- {
- auto value = (*iter)["value"]["stringValue"];
- ++attribute_found;
- EXPECT_EQ(value.get<std::string>(), "test-attribute-value");
- }
- }
- EXPECT_EQ(attribute_found, 1);
- auto receivec_resource_attributes = resource_span["resource"]["attributes"];
- for (auto iter = receivec_resource_attributes.begin();
- iter != receivec_resource_attributes.end(); ++iter)
- {
- auto key = (*iter)["key"].get<std::string>();
- if (key == "service.name")
- {
- auto value = (*iter)["value"]["stringValue"];
- ++attribute_found;
- EXPECT_EQ(value.get<std::string>(), "unit_test_service");
- }
- else if (key == "tenant.id")
- {
- auto value = (*iter)["value"]["stringValue"];
- ++attribute_found;
- EXPECT_EQ(value.get<std::string>(), "test_user");
- }
- else if (key == "int32_value")
- {
- auto value = (*iter)["value"]["intValue"];
- ++attribute_found;
- if (value.is_number())
- {
- EXPECT_EQ(value.get<int32_t>(), 1);
- }
- else
- {
- EXPECT_EQ(value.get<std::string>(), "1");
- }
- }
- }
- EXPECT_EQ(attribute_found, 4);
- }
- }
- TEST(OtlpFileClientTest, ExportToFileSystemRotateIndexTest)
- {
- auto resource = MakeResource();
- auto instrumentation_scope = MakeInstrumentationScope();
- std::unique_ptr<opentelemetry::sdk::trace::Recordable> recordable[] = {
- MakeRecordable(resource, *instrumentation_scope)};
- opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest request;
- OtlpRecordableUtils::PopulateRequest(MakeSpan(recordable), &request);
- std::stringstream output_stream;
- // Clear old files
- {
- std::fstream clear_file1("otlp_file_client_test_dir/trace-1.jsonl",
- std::ios::out | std::ios::trunc);
- std::fstream clear_file2("otlp_file_client_test_dir/trace-2.jsonl",
- std::ios::out | std::ios::trunc);
- std::fstream clear_file3("otlp_file_client_test_dir/trace-3.jsonl",
- std::ios::out | std::ios::trunc);
- std::fstream clear_file4("otlp_file_client_test_dir/trace-latest.jsonl",
- std::ios::out | std::ios::trunc);
- }
- opentelemetry::exporter::otlp::OtlpFileClientFileSystemOptions backend_opts;
- backend_opts.file_pattern = "otlp_file_client_test_dir/trace-%n.jsonl";
- backend_opts.alias_pattern = "otlp_file_client_test_dir/trace-latest.jsonl";
- // Smaller than the size of one record, so it will rotate after each record.
- backend_opts.file_size = 1500;
- backend_opts.rotate_size = 3;
- opentelemetry::exporter::otlp::OtlpFileClientOptions opts;
- opentelemetry::exporter::otlp::OtlpFileClientRuntimeOptions rt_opts;
- opts.backend_options = backend_opts;
- auto client = std::unique_ptr<opentelemetry::exporter::otlp::OtlpFileClient>(
- new opentelemetry::exporter::otlp::OtlpFileClient(std::move(opts), std::move(rt_opts)));
- // Write 5 records with rotatation index 1,2,3,1,2
- for (int i = 0; i < 4; ++i)
- {
- client->Export(request, 1);
- }
- request.mutable_resource_spans(0)->set_schema_url("https://opentelemetry.io/schemas/1.12.0");
- client->Export(request, 1);
- client->ForceFlush();
- std::unique_ptr<std::ifstream> input_file[5] = {
- std::unique_ptr<std::ifstream>(
- new std::ifstream("otlp_file_client_test_dir/trace-1.jsonl", std::ios::in)),
- std::unique_ptr<std::ifstream>(
- new std::ifstream("otlp_file_client_test_dir/trace-2.jsonl", std::ios::in)),
- std::unique_ptr<std::ifstream>(
- new std::ifstream("otlp_file_client_test_dir/trace-3.jsonl", std::ios::in)),
- std::unique_ptr<std::ifstream>(
- new std::ifstream("otlp_file_client_test_dir/trace-4.jsonl", std::ios::in)),
- std::unique_ptr<std::ifstream>(
- new std::ifstream("otlp_file_client_test_dir/trace-latest.jsonl", std::ios::in))};
- EXPECT_TRUE(input_file[0]->is_open());
- EXPECT_TRUE(input_file[1]->is_open());
- EXPECT_TRUE(input_file[2]->is_open());
- EXPECT_FALSE(input_file[3]->is_open());
- EXPECT_TRUE(input_file[4]->is_open());
- std::string jsonl[4];
- std::getline(*input_file[0], jsonl[0]);
- std::getline(*input_file[1], jsonl[1]);
- std::getline(*input_file[2], jsonl[2]);
- std::getline(*input_file[4], jsonl[3]);
- EXPECT_EQ(jsonl[0], jsonl[2]);
- EXPECT_EQ(jsonl[1], jsonl[3]);
- {
- auto check_json = nlohmann::json::parse(jsonl[0], nullptr, false);
- auto resource_span = *check_json["resourceSpans"].begin();
- auto scope_span = *resource_span["scopeSpans"].begin();
- auto span = *scope_span["spans"].begin();
- auto received_trace_id = span["traceId"].get<std::string>();
- EXPECT_EQ(received_trace_id, "01020304050607080102030405060708");
- auto received_name = span["name"].get<std::string>();
- EXPECT_EQ(received_name, request.resource_spans(0).scope_spans(0).spans(0).name());
- auto receivec_attributes = span["attributes"];
- int attribute_found = 0;
- for (auto iter = receivec_attributes.begin(); iter != receivec_attributes.end(); ++iter)
- {
- auto key = (*iter)["key"].get<std::string>();
- if (key == "test-attribute-key")
- {
- auto value = (*iter)["value"]["stringValue"];
- ++attribute_found;
- EXPECT_EQ(value.get<std::string>(), "test-attribute-value");
- }
- }
- EXPECT_EQ(attribute_found, 1);
- auto receivec_resource_attributes = resource_span["resource"]["attributes"];
- for (auto iter = receivec_resource_attributes.begin();
- iter != receivec_resource_attributes.end(); ++iter)
- {
- auto key = (*iter)["key"].get<std::string>();
- if (key == "service.name")
- {
- auto value = (*iter)["value"]["stringValue"];
- ++attribute_found;
- EXPECT_EQ(value.get<std::string>(), "unit_test_service");
- }
- else if (key == "tenant.id")
- {
- auto value = (*iter)["value"]["stringValue"];
- ++attribute_found;
- EXPECT_EQ(value.get<std::string>(), "test_user");
- }
- else if (key == "int32_value")
- {
- auto value = (*iter)["value"]["intValue"];
- ++attribute_found;
- if (value.is_number())
- {
- EXPECT_EQ(value.get<int32_t>(), 1);
- }
- else
- {
- EXPECT_EQ(value.get<std::string>(), "1");
- }
- }
- }
- EXPECT_EQ(attribute_found, 4);
- }
- }
- TEST(OtlpFileClientTest, ExportToFileSystemRotateByTimeTest)
- {
- auto resource = MakeResource();
- auto instrumentation_scope = MakeInstrumentationScope();
- std::unique_ptr<opentelemetry::sdk::trace::Recordable> recordable[] = {
- MakeRecordable(resource, *instrumentation_scope)};
- opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest request;
- OtlpRecordableUtils::PopulateRequest(MakeSpan(recordable), &request);
- std::stringstream output_stream;
- opentelemetry::exporter::otlp::OtlpFileClientFileSystemOptions backend_opts;
- backend_opts.file_pattern = "otlp_file_client_test_dir/trace-%Y-%m-%d-%H-%M-%S.jsonl";
- backend_opts.alias_pattern = "";
- // Smaller than the size of one record, so it will rotate after each record.
- backend_opts.file_size = 1500;
- opentelemetry::exporter::otlp::OtlpFileClientOptions opts;
- opentelemetry::exporter::otlp::OtlpFileClientRuntimeOptions rt_opts;
- opts.backend_options = backend_opts;
- auto client = std::unique_ptr<opentelemetry::exporter::otlp::OtlpFileClient>(
- new opentelemetry::exporter::otlp::OtlpFileClient(std::move(opts), std::move(rt_opts)));
- auto start_time = std::chrono::system_clock::now();
- client->Export(request, 1);
- std::this_thread::sleep_for(std::chrono::seconds{1});
- client->Export(request, 1);
- client->ForceFlush();
- std::unique_ptr<std::ifstream> input_file[2];
- std::size_t found_file_index = 0;
- // Try to load the file in 5s, it should finished.
- for (int i = 0; i < 5; ++i)
- {
- char file_path_buf[256] = {0};
- std::tm local_tm = GetLocalTime(start_time);
- std::strftime(file_path_buf, sizeof(file_path_buf) - 1,
- "otlp_file_client_test_dir/trace-%Y-%m-%d-%H-%M-%S.jsonl", &local_tm);
- start_time += std::chrono::seconds{1};
- input_file[found_file_index] =
- std::unique_ptr<std::ifstream>(new std::ifstream(file_path_buf, std::ios::in));
- if (input_file[found_file_index]->is_open())
- {
- ++found_file_index;
- }
- if (found_file_index >= 2)
- {
- break;
- }
- }
- ASSERT_EQ(found_file_index, 2);
- std::string jsonl[2];
- std::getline(*input_file[0], jsonl[0]);
- std::getline(*input_file[1], jsonl[1]);
- EXPECT_EQ(jsonl[0], jsonl[1]);
- {
- auto check_json = nlohmann::json::parse(jsonl[0], nullptr, false);
- auto resource_span = *check_json["resourceSpans"].begin();
- auto scope_span = *resource_span["scopeSpans"].begin();
- auto span = *scope_span["spans"].begin();
- auto received_trace_id = span["traceId"].get<std::string>();
- EXPECT_EQ(received_trace_id, "01020304050607080102030405060708");
- auto received_name = span["name"].get<std::string>();
- EXPECT_EQ(received_name, request.resource_spans(0).scope_spans(0).spans(0).name());
- auto receivec_attributes = span["attributes"];
- int attribute_found = 0;
- for (auto iter = receivec_attributes.begin(); iter != receivec_attributes.end(); ++iter)
- {
- auto key = (*iter)["key"].get<std::string>();
- if (key == "test-attribute-key")
- {
- auto value = (*iter)["value"]["stringValue"];
- ++attribute_found;
- EXPECT_EQ(value.get<std::string>(), "test-attribute-value");
- }
- }
- EXPECT_EQ(attribute_found, 1);
- auto receivec_resource_attributes = resource_span["resource"]["attributes"];
- for (auto iter = receivec_resource_attributes.begin();
- iter != receivec_resource_attributes.end(); ++iter)
- {
- auto key = (*iter)["key"].get<std::string>();
- if (key == "service.name")
- {
- auto value = (*iter)["value"]["stringValue"];
- ++attribute_found;
- EXPECT_EQ(value.get<std::string>(), "unit_test_service");
- }
- else if (key == "tenant.id")
- {
- auto value = (*iter)["value"]["stringValue"];
- ++attribute_found;
- EXPECT_EQ(value.get<std::string>(), "test_user");
- }
- else if (key == "int32_value")
- {
- auto value = (*iter)["value"]["intValue"];
- ++attribute_found;
- if (value.is_number())
- {
- EXPECT_EQ(value.get<int32_t>(), 1);
- }
- else
- {
- EXPECT_EQ(value.get<std::string>(), "1");
- }
- }
- }
- EXPECT_EQ(attribute_found, 4);
- }
- }
- // Test client configuration options
- TEST(OtlpFileClientTest, ConfigTest)
- {
- {
- opentelemetry::exporter::otlp::OtlpFileClientOptions opts;
- opentelemetry::exporter::otlp::OtlpFileClientRuntimeOptions rt_opts;
- opts.console_debug = true;
- opts.backend_options = std::ref(std::cout);
- auto client = std::unique_ptr<opentelemetry::exporter::otlp::OtlpFileClient>(
- new opentelemetry::exporter::otlp::OtlpFileClient(std::move(opts), std::move(rt_opts)));
- ASSERT_TRUE(client->GetOptions().console_debug);
- ASSERT_TRUE(opentelemetry::nostd::holds_alternative<std::reference_wrapper<std::ostream>>(
- client->GetOptions().backend_options));
- }
- {
- opentelemetry::exporter::otlp::OtlpFileClientFileSystemOptions backend_opts;
- backend_opts.file_pattern = "test_file_pattern.jsonl";
- opentelemetry::exporter::otlp::OtlpFileClientOptions opts;
- opentelemetry::exporter::otlp::OtlpFileClientRuntimeOptions rt_opts;
- opts.console_debug = false;
- opts.backend_options = backend_opts;
- auto client = std::unique_ptr<opentelemetry::exporter::otlp::OtlpFileClient>(
- new opentelemetry::exporter::otlp::OtlpFileClient(std::move(opts), std::move(rt_opts)));
- ASSERT_FALSE(client->GetOptions().console_debug);
- ASSERT_TRUE(opentelemetry::nostd::holds_alternative<
- opentelemetry::exporter::otlp::OtlpFileClientFileSystemOptions>(
- client->GetOptions().backend_options));
- EXPECT_EQ(
- opentelemetry::nostd::get<opentelemetry::exporter::otlp::OtlpFileClientFileSystemOptions>(
- client->GetOptions().backend_options)
- .file_pattern,
- "test_file_pattern.jsonl");
- }
- }
- } // namespace otlp
- } // namespace exporter
- OPENTELEMETRY_END_NAMESPACE
|