diff options
Diffstat (limited to 'src/jaegertracing/opentelemetry-cpp/sdk/test/logs')
7 files changed, 802 insertions, 0 deletions
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/BUILD new file mode 100644 index 000000000..c8f051070 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/BUILD @@ -0,0 +1,75 @@ +cc_test( + name = "logger_provider_sdk_test", + srcs = [ + "logger_provider_sdk_test.cc", + ], + tags = [ + "logs", + "test", + ], + deps = [ + "//api", + "//sdk/src/logs", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "logger_sdk_test", + srcs = [ + "logger_sdk_test.cc", + ], + tags = [ + "logs", + "test", + ], + deps = [ + "//sdk/src/logs", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "simple_log_processor_test", + srcs = [ + "simple_log_processor_test.cc", + ], + tags = [ + "logs", + "test", + ], + deps = [ + "//sdk/src/logs", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "log_record_test", + srcs = [ + "log_record_test.cc", + ], + tags = [ + "logs", + "test", + ], + deps = [ + "//sdk/src/logs", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "batch_log_processor_test", + srcs = [ + "batch_log_processor_test.cc", + ], + tags = [ + "logs", + "test", + ], + deps = [ + "//sdk/src/logs", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/CMakeLists.txt new file mode 100644 index 000000000..84b865d22 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/CMakeLists.txt @@ -0,0 +1,10 @@ +foreach(testname logger_provider_sdk_test logger_sdk_test log_record_test + simple_log_processor_test batch_log_processor_test) + add_executable(${testname} "${testname}.cc") + target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_logs) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX logs. + TEST_LIST ${testname}) +endforeach() diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/batch_log_processor_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/batch_log_processor_test.cc new file mode 100644 index 000000000..63e44676c --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/batch_log_processor_test.cc @@ -0,0 +1,269 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/logs/batch_log_processor.h" +# include "opentelemetry/sdk/logs/exporter.h" +# include "opentelemetry/sdk/logs/log_record.h" + +# include <gtest/gtest.h> +# include <chrono> +# include <thread> + +using namespace opentelemetry::sdk::logs; +using namespace opentelemetry::sdk::common; + +/** + * A sample log exporter + * for testing the batch log processor + */ +class MockLogExporter final : public LogExporter +{ +public: + MockLogExporter(std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received, + std::shared_ptr<std::atomic<bool>> is_shutdown, + std::shared_ptr<std::atomic<bool>> is_export_completed, + const std::chrono::milliseconds export_delay = std::chrono::milliseconds(0)) + : logs_received_(logs_received), + is_shutdown_(is_shutdown), + is_export_completed_(is_export_completed), + export_delay_(export_delay) + {} + + std::unique_ptr<Recordable> MakeRecordable() noexcept + { + return std::unique_ptr<Recordable>(new LogRecord()); + } + + // Export method stores the logs received into a shared list of record names + ExportResult Export( + const opentelemetry::nostd::span<std::unique_ptr<Recordable>> &records) noexcept override + { + *is_export_completed_ = false; // Meant exclusively to test scheduled_delay_millis + + for (auto &record : records) + { + auto log = std::unique_ptr<LogRecord>(static_cast<LogRecord *>(record.release())); + if (log != nullptr) + { + logs_received_->push_back(std::move(log)); + } + } + + *is_export_completed_ = true; + return ExportResult::kSuccess; + } + + // toggles the boolean flag marking this exporter as shut down + bool Shutdown( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override + { + *is_shutdown_ = true; + return true; + } + +private: + std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received_; + std::shared_ptr<std::atomic<bool>> is_shutdown_; + std::shared_ptr<std::atomic<bool>> is_export_completed_; + const std::chrono::milliseconds export_delay_; +}; + +/** + * A fixture class for testing the BatchLogProcessor class that uses the TestExporter defined above. + */ +class BatchLogProcessorTest : public testing::Test // ::testing::Test +{ +public: + // returns a batch log processor that received a batch of log records, a shared pointer to a + // is_shutdown flag, and the processor configuration options (default if unspecified) + std::shared_ptr<LogProcessor> GetMockProcessor( + std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received, + std::shared_ptr<std::atomic<bool>> is_shutdown, + std::shared_ptr<std::atomic<bool>> is_export_completed = + std::shared_ptr<std::atomic<bool>>(new std::atomic<bool>(false)), + const std::chrono::milliseconds export_delay = std::chrono::milliseconds(0), + const std::chrono::milliseconds scheduled_delay_millis = std::chrono::milliseconds(5000), + const size_t max_queue_size = 2048, + const size_t max_export_batch_size = 512) + { + return std::shared_ptr<LogProcessor>( + new BatchLogProcessor(std::unique_ptr<LogExporter>(new MockLogExporter( + logs_received, is_shutdown, is_export_completed, export_delay)), + max_queue_size, scheduled_delay_millis, max_export_batch_size)); + } +}; + +TEST_F(BatchLogProcessorTest, TestShutdown) +{ + // initialize a batch log processor with the test exporter + std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received( + new std::vector<std::unique_ptr<LogRecord>>); + std::shared_ptr<std::atomic<bool>> is_shutdown(new std::atomic<bool>(false)); + + auto batch_processor = GetMockProcessor(logs_received, is_shutdown); + + // Create a few test log records and send them to the processor + const int num_logs = 3; + + for (int i = 0; i < num_logs; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetBody("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + + // Test that shutting down the processor will first wait for the + // current batch of logs to be sent to the log exporter + // by checking the number of logs sent and the names of the logs sent + EXPECT_EQ(true, batch_processor->Shutdown()); + // It's safe to shutdown again + EXPECT_TRUE(batch_processor->Shutdown()); + + EXPECT_EQ(num_logs, logs_received->size()); + + // Assume logs are received by exporter in same order as sent by processor + for (int i = 0; i < num_logs; ++i) + { + EXPECT_EQ("Log" + std::to_string(i), logs_received->at(i)->GetBody()); + } + + // Also check that the processor is shut down at the end + EXPECT_TRUE(is_shutdown->load()); +} + +TEST_F(BatchLogProcessorTest, TestForceFlush) +{ + std::shared_ptr<std::atomic<bool>> is_shutdown(new std::atomic<bool>(false)); + std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received( + new std::vector<std::unique_ptr<LogRecord>>); + + auto batch_processor = GetMockProcessor(logs_received, is_shutdown); + const int num_logs = 2048; + + for (int i = 0; i < num_logs; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetBody("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + + EXPECT_TRUE(batch_processor->ForceFlush()); + + EXPECT_EQ(num_logs, logs_received->size()); + for (int i = 0; i < num_logs; ++i) + { + EXPECT_EQ("Log" + std::to_string(i), logs_received->at(i)->GetBody()); + } + + // Create some more logs to make sure that the processor still works + for (int i = 0; i < num_logs; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetBody("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + + EXPECT_TRUE(batch_processor->ForceFlush()); + + EXPECT_EQ(num_logs * 2, logs_received->size()); + for (int i = 0; i < num_logs * 2; ++i) + { + EXPECT_EQ("Log" + std::to_string(i % num_logs), logs_received->at(i)->GetBody()); + } +} + +TEST_F(BatchLogProcessorTest, TestManyLogsLoss) +{ + /* Test that when exporting more than max_queue_size logs, some are most likely lost*/ + + std::shared_ptr<std::atomic<bool>> is_shutdown(new std::atomic<bool>(false)); + std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received( + new std::vector<std::unique_ptr<LogRecord>>); + + const int max_queue_size = 4096; + + auto batch_processor = GetMockProcessor(logs_received, is_shutdown); + + // Create max_queue_size log records + for (int i = 0; i < max_queue_size; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetBody("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + + EXPECT_TRUE(batch_processor->ForceFlush()); + + // Log should be exported by now + EXPECT_GE(max_queue_size, logs_received->size()); +} + +TEST_F(BatchLogProcessorTest, TestManyLogsLossLess) +{ + /* Test that no logs are lost when sending max_queue_size logs */ + + std::shared_ptr<std::atomic<bool>> is_shutdown(new std::atomic<bool>(false)); + std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received( + new std::vector<std::unique_ptr<LogRecord>>); + auto batch_processor = GetMockProcessor(logs_received, is_shutdown); + + const int num_logs = 2048; + + for (int i = 0; i < num_logs; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetBody("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + + EXPECT_TRUE(batch_processor->ForceFlush()); + + EXPECT_EQ(num_logs, logs_received->size()); + for (int i = 0; i < num_logs; ++i) + { + EXPECT_EQ("Log" + std::to_string(i), logs_received->at(i)->GetBody()); + } +} + +TEST_F(BatchLogProcessorTest, TestScheduledDelayMillis) +{ + /* Test that max_export_batch_size logs are exported every scheduled_delay_millis + seconds */ + + std::shared_ptr<std::atomic<bool>> is_shutdown(new std::atomic<bool>(false)); + std::shared_ptr<std::atomic<bool>> is_export_completed(new std::atomic<bool>(false)); + std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received( + new std::vector<std::unique_ptr<LogRecord>>); + + const std::chrono::milliseconds export_delay(0); + const std::chrono::milliseconds scheduled_delay_millis(2000); + const size_t max_export_batch_size = 512; + + auto batch_processor = GetMockProcessor(logs_received, is_shutdown, is_export_completed, + export_delay, scheduled_delay_millis); + + for (std::size_t i = 0; i < max_export_batch_size; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetBody("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + // Sleep for scheduled_delay_millis milliseconds + std::this_thread::sleep_for(scheduled_delay_millis); + + // small delay to give time to export, which is being performed + // asynchronously by the worker thread (this thread will not + // forcibly join() the main thread unless processor's shutdown() is called). + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + // Logs should be exported by now + EXPECT_TRUE(is_export_completed->load()); + EXPECT_EQ(max_export_batch_size, logs_received->size()); + for (size_t i = 0; i < max_export_batch_size; ++i) + { + EXPECT_EQ("Log" + std::to_string(i), logs_received->at(i)->GetBody()); + } +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/log_record_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/log_record_test.cc new file mode 100644 index 000000000..89b07473a --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/log_record_test.cc @@ -0,0 +1,66 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/logs/log_record.h" +# include "opentelemetry/nostd/variant.h" +# include "opentelemetry/trace/span_id.h" +# include "opentelemetry/trace/trace_id.h" + +# include <gtest/gtest.h> + +using opentelemetry::sdk::logs::LogRecord; +namespace trace_api = opentelemetry::trace; +namespace logs_api = opentelemetry::logs; +namespace nostd = opentelemetry::nostd; + +// Test what a default LogRecord with no fields set holds +TEST(LogRecord, GetDefaultValues) +{ + trace_api::TraceId zero_trace_id; + trace_api::SpanId zero_span_id; + trace_api::TraceFlags zero_trace_flags; + LogRecord record; + + ASSERT_EQ(record.GetSeverity(), logs_api::Severity::kInvalid); + ASSERT_EQ(record.GetBody(), ""); + ASSERT_NE(record.GetResource().GetAttributes().size(), 0); + ASSERT_EQ(record.GetAttributes().size(), 0); + ASSERT_EQ(record.GetTraceId(), zero_trace_id); + ASSERT_EQ(record.GetSpanId(), zero_span_id); + ASSERT_EQ(record.GetTraceFlags(), zero_trace_flags); + ASSERT_EQ(record.GetTimestamp().time_since_epoch(), std::chrono::nanoseconds(0)); +} + +// Test LogRecord fields are properly set and get +TEST(LogRecord, SetAndGet) +{ + trace_api::TraceId trace_id; + trace_api::SpanId span_id; + trace_api::TraceFlags trace_flags; + opentelemetry::common::SystemTimestamp now(std::chrono::system_clock::now()); + + // Set all fields of the LogRecord + LogRecord record; + auto resource = opentelemetry::sdk::resource::Resource::Create({{"res1", true}}); + record.SetSeverity(logs_api::Severity::kInvalid); + record.SetBody("Message"); + record.SetResource(resource); + record.SetAttribute("attr1", (int64_t)314159); + record.SetTraceId(trace_id); + record.SetSpanId(span_id); + record.SetTraceFlags(trace_flags); + record.SetTimestamp(now); + + // Test that all fields match what was set + ASSERT_EQ(record.GetSeverity(), logs_api::Severity::kInvalid); + ASSERT_EQ(record.GetBody(), "Message"); + ASSERT_TRUE(nostd::get<bool>(record.GetResource().GetAttributes().at("res1"))); + ASSERT_EQ(nostd::get<int64_t>(record.GetAttributes().at("attr1")), 314159); + ASSERT_EQ(record.GetTraceId(), trace_id); + ASSERT_EQ(record.GetSpanId(), span_id); + ASSERT_EQ(record.GetTraceFlags(), trace_flags); + ASSERT_EQ(record.GetTimestamp().time_since_epoch(), now.time_since_epoch()); +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_provider_sdk_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_provider_sdk_test.cc new file mode 100644 index 000000000..3e5e8dfb2 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_provider_sdk_test.cc @@ -0,0 +1,133 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW + +# include <array> +# include "opentelemetry/logs/provider.h" +# include "opentelemetry/nostd/shared_ptr.h" +# include "opentelemetry/nostd/string_view.h" +# include "opentelemetry/sdk/logs/log_record.h" +# include "opentelemetry/sdk/logs/logger.h" +# include "opentelemetry/sdk/logs/logger_provider.h" +# include "opentelemetry/sdk/logs/simple_log_processor.h" + +# include <gtest/gtest.h> + +using namespace opentelemetry::sdk::logs; +namespace logs_api = opentelemetry::logs; +namespace nostd = opentelemetry::nostd; + +TEST(LoggerProviderSDK, PushToAPI) +{ + auto lp = + nostd::shared_ptr<logs_api::LoggerProvider>(new opentelemetry::sdk::logs::LoggerProvider()); + logs_api::Provider::SetLoggerProvider(lp); + + // Check that the loggerprovider was correctly pushed into the API + ASSERT_EQ(lp, logs_api::Provider::GetLoggerProvider()); +} + +TEST(LoggerProviderSDK, LoggerProviderGetLoggerSimple) +{ + auto lp = std::shared_ptr<logs_api::LoggerProvider>(new LoggerProvider()); + + nostd::string_view schema_url{"https://opentelemetry.io/schemas/1.11.0"}; + auto logger1 = lp->GetLogger("logger1", "", "opentelelemtry_library", "", schema_url); + auto logger2 = lp->GetLogger("logger2", "", "", "", schema_url); + + // Check that the logger is not nullptr + ASSERT_NE(logger1, nullptr); + ASSERT_NE(logger2, nullptr); + + auto sdk_logger1 = static_cast<opentelemetry::sdk::logs::Logger *>(logger1.get()); + auto sdk_logger2 = static_cast<opentelemetry::sdk::logs::Logger *>(logger2.get()); + ASSERT_EQ(sdk_logger1->GetInstrumentationLibrary().GetName(), "opentelelemtry_library"); + ASSERT_EQ(sdk_logger1->GetInstrumentationLibrary().GetVersion(), ""); + ASSERT_EQ(sdk_logger1->GetInstrumentationLibrary().GetSchemaURL(), schema_url); + + ASSERT_EQ(sdk_logger2->GetInstrumentationLibrary().GetName(), "logger2"); + ASSERT_EQ(sdk_logger2->GetInstrumentationLibrary().GetVersion(), ""); + ASSERT_EQ(sdk_logger2->GetInstrumentationLibrary().GetSchemaURL(), schema_url); + + // Check that two loggers with different names aren't the same instance + ASSERT_NE(logger1, logger2); + + // Check that two loggers with the same name are the same instance + auto logger3 = lp->GetLogger("logger1", "", "opentelelemtry_library", "", schema_url); + ASSERT_EQ(logger1, logger3); + auto sdk_logger3 = static_cast<opentelemetry::sdk::logs::Logger *>(logger3.get()); + ASSERT_EQ(sdk_logger3->GetInstrumentationLibrary(), sdk_logger1->GetInstrumentationLibrary()); +} + +TEST(LoggerProviderSDK, LoggerProviderLoggerArguments) +{ + // Currently, arguments are not supported by the loggers. + // TODO: Once the logging spec defines what arguments are allowed, add more + // detail to this test + auto lp = std::shared_ptr<logs_api::LoggerProvider>(new LoggerProvider()); + + nostd::string_view schema_url{"https://opentelemetry.io/schemas/1.11.0"}; + auto logger1 = lp->GetLogger("logger1", "", "opentelelemtry_library", "", schema_url); + + // Check GetLogger(logger_name, args) + std::array<nostd::string_view, 1> sv{"string"}; + nostd::span<nostd::string_view> args{sv}; + auto logger2 = lp->GetLogger("logger2", args, "opentelelemtry_library", "", schema_url); + auto sdk_logger1 = static_cast<opentelemetry::sdk::logs::Logger *>(logger1.get()); + auto sdk_logger2 = static_cast<opentelemetry::sdk::logs::Logger *>(logger2.get()); + ASSERT_EQ(sdk_logger2->GetInstrumentationLibrary(), sdk_logger1->GetInstrumentationLibrary()); +} + +class DummyProcessor : public LogProcessor +{ + std::unique_ptr<Recordable> MakeRecordable() noexcept + { + return std::unique_ptr<Recordable>(new LogRecord); + } + + void OnReceive(std::unique_ptr<Recordable> &&record) noexcept {} + bool ForceFlush(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept + { + return true; + } + bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept + { + return true; + } +}; + +TEST(LoggerProviderSDK, GetResource) +{ + // Create a LoggerProvider without a processor + auto resource = opentelemetry::sdk::resource::Resource::Create({{"key", "value"}}); + LoggerProvider lp{nullptr, resource}; + ASSERT_EQ(nostd::get<std::string>(lp.GetResource().GetAttributes().at("key")), "value"); +} + +TEST(LoggerProviderSDK, Shutdown) +{ + std::unique_ptr<SimpleLogProcessor> processor(new SimpleLogProcessor(nullptr)); + std::vector<std::unique_ptr<LogProcessor>> processors; + processors.push_back(std::move(processor)); + + LoggerProvider lp(std::make_shared<LoggerContext>(std::move(processors))); + + EXPECT_TRUE(lp.Shutdown()); + + // It's safe to shutdown again + EXPECT_TRUE(lp.Shutdown()); +} + +TEST(LoggerProviderSDK, ForceFlush) +{ + std::unique_ptr<SimpleLogProcessor> processor(new SimpleLogProcessor(nullptr)); + std::vector<std::unique_ptr<LogProcessor>> processors; + processors.push_back(std::move(processor)); + + LoggerProvider lp(std::make_shared<LoggerContext>(std::move(processors))); + + EXPECT_TRUE(lp.ForceFlush()); +} + +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_sdk_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_sdk_test.cc new file mode 100644 index 000000000..3747731f1 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_sdk_test.cc @@ -0,0 +1,97 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/logs/log_record.h" +# include "opentelemetry/sdk/logs/logger.h" + +# include <gtest/gtest.h> + +using namespace opentelemetry::sdk::logs; +namespace logs_api = opentelemetry::logs; + +TEST(LoggerSDK, LogToNullProcessor) +{ + // Confirm Logger::Log() does not have undefined behavior + // even when there is no processor set + // since it calls Processor::OnReceive() + + auto lp = std::shared_ptr<logs_api::LoggerProvider>(new LoggerProvider()); + const std::string schema_url{"https://opentelemetry.io/schemas/1.11.0"}; + auto logger = lp->GetLogger("logger", "", "opentelelemtry_library", "", schema_url); + + auto sdk_logger = static_cast<opentelemetry::sdk::logs::Logger *>(logger.get()); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetName(), "opentelelemtry_library"); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetVersion(), ""); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetSchemaURL(), schema_url); + // Log a sample log record to a nullptr processor + logger->Debug("Test log"); +} + +class MockProcessor final : public LogProcessor +{ +private: + std::shared_ptr<LogRecord> record_received_; + +public: + // A processor used for testing that keeps a track of the recordable it received + explicit MockProcessor(std::shared_ptr<LogRecord> record_received) noexcept + : record_received_(record_received) + {} + + std::unique_ptr<Recordable> MakeRecordable() noexcept + { + return std::unique_ptr<Recordable>(new LogRecord); + } + // OnReceive stores the record it receives into the shared_ptr recordable passed into its + // constructor + void OnReceive(std::unique_ptr<Recordable> &&record) noexcept + { + // Cast the recordable received into a concrete LogRecord type + auto copy = std::shared_ptr<LogRecord>(static_cast<LogRecord *>(record.release())); + + // Copy over the received log record's severity, name, and body fields over to the recordable + // passed in the constructor + record_received_->SetSeverity(copy->GetSeverity()); + record_received_->SetBody(copy->GetBody()); + } + bool ForceFlush(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept + { + return true; + } + bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept + { + return true; + } +}; + +TEST(LoggerSDK, LogToAProcessor) +{ + // Create an API LoggerProvider and logger + auto api_lp = std::shared_ptr<logs_api::LoggerProvider>(new LoggerProvider()); + const std::string schema_url{"https://opentelemetry.io/schemas/1.11.0"}; + auto logger = api_lp->GetLogger("logger", "", "opentelelemtry_library", "", schema_url); + + // Cast the API LoggerProvider to an SDK Logger Provider and assert that it is still the same + // LoggerProvider by checking that getting a logger with the same name as the previously defined + // logger is the same instance + auto lp = static_cast<LoggerProvider *>(api_lp.get()); + auto logger2 = lp->GetLogger("logger", "", "opentelelemtry_library", "", schema_url); + ASSERT_EQ(logger, logger2); + + auto sdk_logger = static_cast<opentelemetry::sdk::logs::Logger *>(logger.get()); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetName(), "opentelelemtry_library"); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetVersion(), ""); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetSchemaURL(), schema_url); + // Set a processor for the LoggerProvider + auto shared_recordable = std::shared_ptr<LogRecord>(new LogRecord()); + lp->AddProcessor(std::unique_ptr<LogProcessor>(new MockProcessor(shared_recordable))); + + // Check that the recordable created by the Log() statement is set properly + logger->Log(logs_api::Severity::kWarn, "Log Message"); + + ASSERT_EQ(shared_recordable->GetSeverity(), logs_api::Severity::kWarn); + ASSERT_EQ(shared_recordable->GetBody(), "Log Message"); +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/simple_log_processor_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/simple_log_processor_test.cc new file mode 100644 index 000000000..32c62a508 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/simple_log_processor_test.cc @@ -0,0 +1,152 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/logs/simple_log_processor.h" +# include "opentelemetry/nostd/span.h" +# include "opentelemetry/sdk/logs/exporter.h" +# include "opentelemetry/sdk/logs/log_record.h" + +# include <gtest/gtest.h> + +# include <chrono> +# include <thread> + +using namespace opentelemetry::sdk::logs; +using namespace opentelemetry::sdk::common; +namespace nostd = opentelemetry::nostd; + +/* + * A test exporter that can return a vector of all the records it has received, + * and keep track of the number of times its Shutdown() function was called. + */ +class TestExporter final : public LogExporter +{ +public: + TestExporter(int *shutdown_counter, + std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received, + size_t *batch_size_received) + : shutdown_counter_(shutdown_counter), + logs_received_(logs_received), + batch_size_received(batch_size_received) + {} + + std::unique_ptr<Recordable> MakeRecordable() noexcept override + { + return std::unique_ptr<Recordable>(new LogRecord()); + } + + // Stores the names of the log records this exporter receives to an internal list + ExportResult Export(const nostd::span<std::unique_ptr<Recordable>> &records) noexcept override + { + *batch_size_received = records.size(); + for (auto &record : records) + { + auto log_record = std::unique_ptr<LogRecord>(static_cast<LogRecord *>(record.release())); + + if (log_record != nullptr) + { + logs_received_->push_back(std::move(log_record)); + } + } + return ExportResult::kSuccess; + } + + // Increment the shutdown counter everytime this method is called + bool Shutdown(std::chrono::microseconds timeout) noexcept override + { + *shutdown_counter_ += 1; + return true; + } + +private: + int *shutdown_counter_; + std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received_; + size_t *batch_size_received; +}; + +// Tests whether the simple processor successfully creates a batch of size 1 +// and whether the contents of the record is sent to the exporter correctly +TEST(SimpleLogProcessorTest, SendReceivedLogsToExporter) +{ + // Create a simple processor with a TestExporter attached + std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received( + new std::vector<std::unique_ptr<LogRecord>>); + size_t batch_size_received = 0; + + std::unique_ptr<TestExporter> exporter( + new TestExporter(nullptr, logs_received, &batch_size_received)); + + SimpleLogProcessor processor(std::move(exporter)); + + // Send some log records to the processor (which should then send to the TestExporter) + const int num_logs = 5; + for (int i = 0; i < num_logs; i++) + { + auto recordable = processor.MakeRecordable(); + recordable->SetBody("Log Body"); + processor.OnReceive(std::move(recordable)); + + // Verify that the batch of 1 log record sent by processor matches what exporter received + EXPECT_EQ(1, batch_size_received); + } + + // Test whether the processor's log sent matches the log record received by the exporter + EXPECT_EQ(logs_received->size(), num_logs); + for (int i = 0; i < num_logs; i++) + { + EXPECT_EQ("Log Body", logs_received->at(i)->GetBody()); + } +} + +// Tests behavior when calling the processor's ShutDown() multiple times +TEST(SimpleLogProcessorTest, ShutdownCalledOnce) +{ + // Create a TestExporter + int num_shutdowns = 0; + + std::unique_ptr<TestExporter> exporter(new TestExporter(&num_shutdowns, nullptr, nullptr)); + + // Create a processor with the previous test exporter + SimpleLogProcessor processor(std::move(exporter)); + + // The first time processor shutdown is called + EXPECT_EQ(0, num_shutdowns); + EXPECT_EQ(true, processor.Shutdown()); + EXPECT_EQ(1, num_shutdowns); + + EXPECT_EQ(true, processor.Shutdown()); + // Processor::ShutDown(), even if called more than once, should only shutdown exporter once + EXPECT_EQ(1, num_shutdowns); +} + +// A test exporter that always returns failure when shut down +class FailShutDownExporter final : public LogExporter +{ +public: + FailShutDownExporter() {} + + std::unique_ptr<Recordable> MakeRecordable() noexcept override + { + return std::unique_ptr<Recordable>(new LogRecord()); + } + + ExportResult Export(const nostd::span<std::unique_ptr<Recordable>> &records) noexcept override + { + return ExportResult::kSuccess; + } + + bool Shutdown(std::chrono::microseconds timeout) noexcept override { return false; } +}; + +// Tests for when when processor should fail to shutdown +TEST(SimpleLogProcessorTest, ShutDownFail) +{ + std::unique_ptr<FailShutDownExporter> exporter(new FailShutDownExporter()); + SimpleLogProcessor processor(std::move(exporter)); + + // Expect failure result when exporter fails to shutdown + EXPECT_EQ(false, processor.Shutdown()); +} +#endif |