summaryrefslogtreecommitdiffstats
path: root/src/jaegertracing/opentelemetry-cpp/sdk/test/logs
diff options
context:
space:
mode:
Diffstat (limited to 'src/jaegertracing/opentelemetry-cpp/sdk/test/logs')
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/logs/BUILD75
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/logs/CMakeLists.txt10
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/logs/batch_log_processor_test.cc269
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/logs/log_record_test.cc66
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_provider_sdk_test.cc133
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_sdk_test.cc97
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/logs/simple_log_processor_test.cc152
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